自分用の備忘録

VimとかJSとか。やったことのメモ。自分のため。

Vue Programmaticで出したモーダルのprops変更

最近コンポーネントライブラリにBuefyをちょいちょい触っている中で、
モーダルでちょっとハマったのでメモ。

Programmaticにモーダルを出す

Buefyのドキュメント読むと分かりますが、

vm.$modal.open({
  parent: true,
  component: Component,
  props: {
    // props
  },
  events: {
    // emit event
  }
})

要はこんな感じで書けば、プログラムからモーダルが呼び出せます。
よくあるやつですね。

だいたいこの手のやつは

<b-modal :visible.sync="dialogVisible">
</b-modal>

みたいな感じにして、stateのtrue/falseで出したり消したりができます。 自分はこのテンプレートに書くやり方があまり好きではなくて、どちらかというとvim.$modal.openとかで呼びたい派。
まぁ、全てが全てではないです。

propsが更新されない

ここで問題が発生。

モーダルの中で何かを切り替えると、API叩き直して一覧更新するようなUIのとき、
そのデータをpropsで渡していた場合に更新されずハマりました。

vm.$modal.open({
  props: {
    categoryList: this.categoryList,
    itemList: this.itemList
  },
  events: {
    'change-category': categoryId => {
      // categoryIdを元にitemListを更新するような処理
    }
  }
})

中でchangeがemitされると、itemListが更新されるので、モーダルに渡してるitemListも更新されるかと思いきや・・・。
うんともすんとも言いません。

解決

issueに同じようなことが書いてあって、そこの通りなのですが。

const $modal = vm.$modal.open({
  props: {
    categoryList: this.categoryList,
    itemList: this.itemList
  },
  events: {
    'change-category': categoryId => 
      // categoryIdを元にitemListを更新するような処理
      // propsを時前で更新
      $modal.props.itemList = this.itemList
      $modal.$forceUpdate()
    }
  }
})

自前でprops入れて、モーダルのインスタンスを$forceUpdateでいけました。

templateに書いておくのと比べると、programmaticにやると、vm.$elをappendChildなりしてるからですかね・・・。
ちょっとつらい感じですけど、一応これでいけました。。。

Component modal props won't update

Vueで自前のUtil関数をテンプレートで使う

Vueやってると出てくる、Util関数をテンプレートで呼びたい問題。

何が問題かというと・・・順番に見ていきましょう。

問題

まずはこんな関数を定義。
日付表記をYYYY/MM/DDにするだけの関数です。

import dayjs from 'dayjs'

export function formatDate(date) {
  return dayjs(date).format('YYYY/MM/DD')
}

さて、これを他のファイルで使いたい。。。
Vueのスクリプト部分だと単純にimportして使えば良いんですが、テンプレートで使いたいとなると、
一旦メソッド作るしかなくて、「あれ、Utilの意味・・・」となりがちです。

<table>
  <thead>
    <th>名前</th>
    <th>作成日</th>
  </thead>
  <tbody>
    <tr v-for="data in dataList">
      <td>{{ data.name }}</td>
      <td>{{ formatDate(data.insert_date) }}</td>
    </tr>
  </dbody>
</teble>
import { formatDate } from 'util'
export default {
  name: 'Foo',
  methods: {
    formatDate(date) {
      return formatDate(date)
    }
  }
}

意味ないやんこれ。。。

解決

UtilをプラグインとしてVueに登録してやりましょう。

import dayjs from 'dayjs'
export function formatDate(date) {
  return dayjs(date).format('YYYY/MM/DD')
}

export default {
  formatDate
}
import Util from 'util'

const myUtil = {
  install(Vue, options) {
    Vue.prototype.$myUtil = Util
  }
}
Vue.use(myUtil)

new Vue({
  // ...
})

これで、this.$myUtil.dateFormatで使えます。
さっきのコードはこれだけで良く、script部分からはimportもわざわざmethod作る必要もないです。

<table>
  <thead>
    <th>名前</th>
    <th>作成日</th>
  </thead>
  <tbody>
    <tr v-for="data in dataList">
      <td>{{ data.name }}</td>
      <td>{{ $myUtil.formatDate(data.insert_date) }}</td>
    </tr>
  </dbody>
</teble>

もっと良い解決方法あればいいけど・・・。
prototypeにぶち込むので、できるだけ被らない名前にしないとなーとぐらいは思いました。

Vueで動的にコンポーネントを追加する

ちょっとしたダイアログを出したいとか、ポップアップみたいなの出したいとか・・・。
そういうことありませんか?

コンポーネントライブラリ使ってれば、大体網羅できますが、要件に合わないとか、思ってるのとは違うなーみたいな状況とか。

方法

Vue.componentコンポーネント作って、その場で$mountまでしてあげます。 マウントしたやつをappendChildなりで入れたいとこに追加。

import PopupComponent from '@/components/popup'

export default {
  handleClick(e) {
    const Popup = Vue.component('popup', PopupComponent)
    const popup = new Popup().$mount()
    document.body.appendChild(popup.$el)
  }
}

props渡したいときは、new Popup({ propData: { } })で渡せます。

破棄もしっかりと

上で追加はできますが、閉じたときとかに消す必要がありますね。
例えば、画面上のどこかしらクリックされたら消す場合、上でいうとPopup側をこんな感じにしてやるといけます。

export default {
  created() {
    document.addEventListener('click', this.handleClickRemove)
  },
  beforeDestroy() {
    document.removeEventListener('click', this.handleClickRemove)
  },
  methods: {
    handleClickRemove() {
      this.$destroy()
      this.$el.parentNode.removeChild(this.$el)
    }
  }
}

createdでイベントつけてあげて、beforeDestroyで消してあげましょう。
あとはthis.$destroy()で自らをdestroyして、追加されたものもremoveChildで消してしまうと良いです。

Observableの結合のこと

今年はブログ書くって言うたので書いてみます。
ネタは別のとこに書いたやつではあるけど。

前の記事でも触れていますが、昨年の秋ごろからAngularを触っています。 Angularでngrx使いつつです。

ことのなりゆき

(本題だけなら飛ばしてね)

年末にふとng-japanのSlackに以下の質問。

  • ngrxのeffects内でstoreのデータを使いたい場面に出くわした。
  • withLatestFrom(this.store$.select(fromRoot.getSelectedId))みたいにして繋げてしまうのはありなのか?

背景的には、選択中のカテゴリに属する記事を一覧表示。
その一覧から記事を削除したとき、表示中の一覧を更新したい。
更新用のAPI呼びたいけど、選択中のカテゴリIDがほしい。  みたいな、結構あるあるな状況ではないでしょうか?

もらった回答が、

  • withLatestFromでも良いけど、このケースならActionのpayloadにID含めて渡すのはどうか?

言われてみれば、確かに。。。

いざ実装してみると、payloadを次に渡してあげないといけなくて、どうするんだろう?

foo$: this.actions$.pipe(
  ofType(ActionName),
  map(action => action.payload),
  mergeMap(payload => {
    return this.api.fetchList().pipe(
      map(...)
      catchError(...)
    )
  }),
  mergeMap( /*ここにpayloadがほしい */
  ...

forkJoinを使ってまとめたらとやりたいことはできたのですが、テストが通らない。。。
なんで・・・?

理由は、結合方法にも何パターンかあって、それぞれの動きが異なるから。
この辺りちゃんと理解せずやったのでハマりました。

結合方法

Observableを結合するには、何パターンか方法があります。

  1. zip
  2. combineLatest
  3. forkJoin
  4. withLatestFrom

ざっくりこの4つ。

こんなObservableがあるとして・・・

const observable1 = Rx.Observable.interval(1000).map(x => x).take(5)
const observable2 = Rx.Observable.interval(2000).map(y => y + 1).take(5)

それぞれsubscribeすると、

observable1.subscribe(x => console.log(x))
// 0 - 1秒間隔で
// 1
// 2
// 3
// 4

observable2.subscribe(y => console.log(y))
// 1 - 2秒間隔で
// 2
// 3
// 4
// 5

こんな感じでobservable1は1秒間隔で0〜4observable21〜5が出力されます。
これを使ってそれぞれの違いを見ていきます。

zip

まずはzip

Observableのそれぞれの値から順に値が計算されるObservableを返す。

訳はChromeの翻訳。
意味を汲み取りましょう。

Rx.Observable.zip(observable1, observable2).subscribe(value => console.log('zip', value))

// 出力間隔を見るためのカウンター
const counter = 0
const interval = setInterval(() => {
  console.log(counter++)
  if (counter === 10) clearInterval(interval)
})

コンソールの出力は以下。[x, y]の形になっています。

0
1
zip [0, 1]
2
3
zip [1, 2]
4
5
zip [2, 3]
6
7
zip [3, 4]
8
9
zip [4, 5]

普通にsubscribeしたやつが、配列になっただけのような感じです。
間隔に注目してみると、observable1も2秒間隔になっています。

zipは、observable2を待って、1ペアずつ出力されている!

combineLatest

次、combineLatest

Observableのそれぞれの最新値から計算されるObservableを返す。

Rx.Observable.combineLatest(observable1, observable2).subscribe(value => console.log('combineLatest', value))

これを同じように出力してみます。

0
combineLatest [1, 1]
1
combineLatest [2, 1]
2
combineLatest [3, 1]
combineLatest [3, 2]
3
combineLatest [4, 2]
4
combineLatest [4, 3]
5
6
combineLatest [4, 4]
7
8
combineLatest [4, 5]
9

zipよりも出力回数が増えています。
zipobservable1observable2が揃って出力されていたのに対して、combineLatestはお構いなしに、その時の最新の値を出してきます。
ところどころ2連続で出力があるところはobservable2の間隔。
observable1が先に終わるので、最後の出力回数が減っているのも分かりますね。

forkJoin

次、forkJoin

全てのObservableが完了するのを待って、最後の値を返す。

Rx.Observable.forkJoin(observable1, observable2).subscripbe(value => console.log('forkJoin', value))

同じく出力します。

0
1
2
3
4
5
6
7
8
9
forkJoin [4, 5]

0〜9の間の出力がなくなりました。
observable1observable2の両方が完了したタイミングで出力されています。

withLatestFrom

withLatestFromも見ておきましょう。

ソースObservableが値を発行するたびに、その値と他の入力Observablesからの最新の値を使用して式を計算し、次にその式の出力を発行します。

これは前の3つとは少し違っています。
今回はobservable2observable1を入れて使ってみます。

observable2.withLatestFrom(observable1, (y, x) => y * x)
  .subscribe(value => console.log('withLatestFrom', value))

出力は

0
1
withLatestFrom 1 -- 1 * 1
2
3
withLatestFrom 6 -- 2 * 3
4
5
withLatestFrom 12 -- 3 * 4
6
7
withLatestFrom 16 -- 4 * 4
8
9
withLatestFrom 20 -- 5 * 4

2秒間隔で、observable2 * observable1が行われていますね。

まとめ

ユニットテストforkJoinが上手くいかなかったのは、おそらく完了を待ち続けていたのかな?
値が揃えばOKなら、zip
常に最新値でやってほしいならcombineLatest
1つのObservableに別のObservableを急に入れたいみたいな場合はwithLatestFrom

今回のも似てるようで全然違う挙動をするので、使い分けできるようにならないとなーと思いました。

mergeMapswitchMapあたりも怪しい・・・。

Vue.js / Nuxt.js Meetup Osaka #0に参加した

Vue.js / Nuxt.js Meetup Osaka #0に参加したので、そのレポ。

デザイナーの私が Vue.js を触ってみた / Yasui Risaさん

資料→ デザイナーの私が Vue.js を触ってみた

タイトル通り。内容は初心者向け。
環境構築のやり方は飛ばして、CodePenで実演しつつの発表。

なぜCodePenか?→環境構築からやると難しくて心が折れる可能性が・・・

この考え方には納得。
自分も何か作るときは、JSFiddleで断片を作ってというのが多い。

Vue.jsを始めるキッカケにはとても良いセッションだった。
データの表示から、各ディレクティブの説明が丁寧。

AVAでのテストとpuppeteer / 豊川 泰弘さん

AVAでJavaScriptのテストとE2Eテストのこと。

AVA → JavaScriptのテストフレームワーク
mochaと比べるとassertの書き方が見やすい?

どんなときにテストを書いていたか?
→ コードレビュー時とか。テストケースの追加、バグの発見などにつながるかも。
puppeteer
Chrome Devtools Protocolでブラウザの情報を取得・操作するためのライブラリ。

自分は、vue-cliでてきとーに作って、E2E環境はNightWatch。
puppeteer試してみたい。

全体的にJavaScriptでテスト書いてる人は少ない感じだった。
理由は様々。テストの書き方が分からないとかから、時間がないなど。

自分も分からないなりに探り探りでやってる状態。
書き出して思うのは、「これ、書かないと一生書けないし、書かないな」。
Qiitaとかにテストの書き方みたいな記事は溢れているけど・・・。
結局add(a, b) assert(add(1,2)).to.equal(3)みたいな単純なもの。
これじゃ書けないよなとも思う。

Nuxt ディレクトリ設計パターン / andoshin11さん

資料→ Nuxt ディレクトリ設計パターン

Clear Architectureについて。
Clear Architectureは初めて聞いた。

アーキテクチャ周り、しっかりと見に付けてあげると、良い設計ができそうだと感じた。
セッション内でもあった通り、2、3人程度でならない方が良い。

結局VuexだとActionsにロジックが集中して太っていくのは実体験としてあって、どうにかならないかなとは思っていた。
例えば、ファイルアップロードするAPIを叩くにしても、
Viewでファイル受け取って、それをActionsのfileUploadみたいなのに渡す。
何も考えずにできるならそれだけで終わるけど、ならそのファイルのバリデーション系はどこに置くの?みたいな悩み。

またそのバリデーション内でもAPI叩いて、同名の存在確認とか容量確認とかしなきゃならないみたいな。。。
canUploadFileみたいなのをActionsに書いて、Promise返してresolveされたらfileUploadみたいにした。

なんかおかしいなと思いつつ・・・。安易にUtilsみたいなのは怖い。

こういった悩みの解決にも繋がる考えな気もするので、役立つセッションだった。

Vue.js とはなにか、Nuxt.js とはなにか / 後藤知宏さん

Vue.jsとNuxt.jsのことを分かりやすく。
いったいどんなことができるのか。どういったものなのか。

Vue.jsはCDNで読み込むだけで使える。

普段、vue-cliで一からVueを使うということをやっているが、CDNで読み込む方は盲点だった。
new Vueだけして、データバインディングとカスタムコンポーネントを軽く入れられる。
jQueryガリガリ書かれた辛いコードでも、これなら少し楽になりそうだと感じた。
実務でjQueryで辛くなってしまったPJがあるので、そこに導入して試したい。
(API一気に8個とか叩くんですよ・・・それ整形して画面に・・・辛い)

Vue.jsでスタイルを動的に変更する3つの方法(LT) / qwerty8tさん

Vue.jsのスタイル変更方法。

classオブジェクトがいいよ!っていう結論。
あれ初めて使い方知ったときは感動したのを思い出した。

Vue.jsのmethodsとcomputed(LT) / ショウノシオリさん

methodsのcomputedの違いについて。

あまり考えないとmethodsだけでもどうにでもできてしまう。
computedはキャッシュされるので、毎回処理が走らない。computedで使っているデータが変化したときだけ走る。
methodsは毎回。

全体的な感想

参加者の様子、Vue.jsを触ったことがある人もちらほら。
まだない人のほうがちょっと多めな印象だった。

触ってない人も多いところから考えると、Vue.jsに対する関心は高まっているのだなと感じた。

もっと難しいことを話さないといけないのかなと思っていたが、connpassのページにも書いてある通り、
「難しい話をしないと...!」ではなかった。

あ、こういうのでも良いんだ。と感じた。
Vueを使ってるなら当たり前なことでも全然大丈夫そう。
登壇のハードルは今回参加してみて自分中ではぐっと下がった。

何か話したいことがあったら、LTでもしてみたいな。

次回が楽しみ!

にしても、関西圏でVue.jsの勉強会は嬉しすぎた。

NativeScript-Vueを触った

久々の更新。

2月にこんな発表がありました。
Announcing NativeScript-Vue 1.0

要約。「nativescript-vueの1.0が出たよ」

NativeScript

JavaScriptとかTypeScript、Angularを使って、ネイティブなモバイルアプリを作ることができるフレームワークです。
詳しいことは公式参照

JavaScriptでモバイルアプリと聞くと、React Nativeなんかもありますね。

で、今回はそれをVueで書けるでっていうやつです。
最近Vue推しの自分にとっては格好のおもちゃですね。

早速遊びます。

準備

まずはNativeScriptがないと始まりません。

npm install -g native-script

次にモバイルアプリなので、こっちもやっておいてください。
IOSだけなら、JDKは入れなくてもまぁOK。
NativeScript Advanced Setup: macOS

長いのでカット。

次に、vue-cli放り込みましょう。プロジェクトのテンプレート作ってくれます。

npm install -g @vue/cli @vue/cli-init
vue init nativescript-vue/vue-cli-template <project-name>
cd <project-name>
npm install

npm installまで終わったら、npm run watch:<platform>を実行します。
今回はIOSなので、npm run watch:iosとします。

すると、シミュレーターが開くはず。
f:id:lisia:20180310220241p:plain

ひとまずここまで。
基本はここの通りです。

アプリを動かす

シミュレーター動いたのは良いけど、アプリないやん!って話ですよね。
多分そもそもビルドされてないから・・・?

package.jsonのscript内を見てみると、使えるコマンドが分かります。
package.json抜粋

  "scripts": {
    "build": "webpack --env.tnsAction build",
    "build:android": "npm run build -- --env.android",
    "build:ios": "npm run build -- --env.ios",
    "debug": "webpack --watch --env.tnsAction debug",
    "debug:android": "npm run debug -- --env.android",
    "debug:ios": "npm run debug -- --env.ios",
    "watch": "webpack --watch --env.tnsAction run",
    "watch:android": "npm run watch -- --env.android",
    "watch:ios": "npm run watch -- --env.ios",
    "clean": "rimraf dist"
  },

それっぽいnpm run debug:ios叩いてみます。

f:id:lisia:20180310220514p:plain

こんな感じでサンプルアプリが立ち上がりました。
(この後、watchしたら大丈夫でした。)

ちょっとサンプルを作ってみる

APIも叩きたいので、GiphyのAPI叩いてgifを表示するようなものにしてみます。
Vuexのサンプルで作ったことがあるこれが元ネタ。

サンプルなので、ひとつのコンポーネントにまとめてしまいます。

とりあえず画面を作る

src以下がコードを書くところです。
src/components以下にGiphy.vueを作成します。
touch src/components/Giphy.vue

<template>
 <Page class="page">
    <ActionBar class="action-bar" title="Giphy">
      <NavigationButton
        text="Go Back"
        android.systemIcon="ic_menu_back"
        @tap="$router.push('/home')"
      />
    </ActionBar>

    <StackLayout>
      <label text="Welcome to GiphyPage!"/>
    </StackLayout>
  </Page>
</template>

HelloWorldを真似て、こんな感じでtemplateを書いておきます。
次に、ページへのルーティングが必要なのでsrc/router/index.jsに書き加えます。

// ...
import Giphy from '../components/Giphy'

const router = new VueRouter({
  pageRouting: true,
  routes: [
    // ...
    {
      path: '/giphy',
      component: Giphy,
      meta: {
        title: 'Giphy'
      }
    }
  ]
}) 
// ...

homeの画面もボタンを追加しておかないとですね。

<StackLayout>
  <Button class="btn btn-primary" @tap="$router.push('/counter')">Counter</Button>
  <Button class="btn btn-primary" @tap="$router.push('/hello')">Hello World</Button>
  <Button class="btn btn-primary" @tap="$router.push('/giphy')">Giphy</Button>
</StackLayout>

画面を確認してみましょう。
f:id:lisia:20180310220536p:plain

タップして、画面が遷移することを確認します。
f:id:lisia:20180310220549p:plain

文字見えにくいけど気にしない。とりあえず、画面遷移を文字が表示できました。

処理とか入れていく

テンプレートに関しては、検索用のテキストボックスと表示領域があれば事足ります。
処理の流れは、テキストが入力されたらGhipyのAPIを叩いて、その結果をテンプレートに表示する。

これだけです。
では早速、まずはテンプレート。

<template>
  <Page class="page">
    <ActionBar class="action-bar" title="Giphy">
      <NavigationButton text="Go Back" android.systemIcon="ic_menu_back" @tap="$router.push('/home')"/>
    </ActionBar>

    <ScrollView>
      <StackLayout>
        <SearchBar
          :text="searchWord"
          hint="Please input search word"
          color="#000"
          margin="20"
          v-model="searchWord"
          @textChange="handleChangeSearchWord"
        />
        <template v-if="gifList.length">
          <Image
            v-for="(gif, index) in gifList"
            :src="gif.images.fixed_height_still.url" stretch="none"
          />
        </template>
      </StackLayout>
    </ScrollView>
  </Page>
</template>

続いて、script部分。

export default {
  name: 'Giphy',

  data() {
    return {
      searchWord: '',
      gifList: []
    }
  },

  methods: {
    handleChangeSearchWord() {
      const params = encodeURIComponent(this.searchWord).replace(/%20/g, '+')
      const url = `https://api.giphy.com/v1/gifs/search?api_key=YOURAPIKEY&q=${params}`

      fetch(url)
      .then(res => res.json())
      .then(json => {
        this.gifList = json.data
      })
    }
  }
}

軽く説明。

テンプレート側は、スクロールさせたいので、<ScrollView>で囲んでいます。
検索は<TextFiled>を最初は使っていたのですが、ドキュメント読んでると、<SearchBar>なるものがあったので、こちらを採用。

script側も特に言うことはなくて、単純にAPIを叩いているだけです。
結果をdataのgifListに放り込みます。

完成!

f:id:lisia:20180310220403g:plain

まとめ

  • JavaScriptでモバイルアプリ書ける 便利
  • しかもVue使える 便利
  • 慣れ親しんだもので書けるのはやっぱり 便利

以上!

Gulpを触ってみた

ひっさびさにこっちのブログを書きます。
Gulpを少し触ってみたので、備忘録として、導入からJavaScriptの結合とミニファイまでを書いておきます。

Gulpってなんぞや。
フロントエンドのビルドツールで良いのかな。
Gruntで気になったところが改善されているとかされてないとか。。。
Gruntも一応、結合ミニファイぐらいまでは触ってはみましたが。

ひとまず、ググッてもらえれば!

ではでは先に進みましょう。

Node.jsは入れていること前提。
%brew install node

導入

%npm install -g gulp
gulp -vでバージョン確認しておきましょう。

ここでgulpとか打って、not found的なこと言われたら、%npm link gulpとかで多分。。。
あとは%npm initして、package.jsonも作っておきましょう。

準備

使い方を軽くやるだけなので、試すためのディレクトリを作成しておきます。 ここはなんでも。
中にはgulpfile.jsを作成しておきます。

タスクを作ってみる

お決まりのHello Worldやっておきます。

// gulpfile.js
var gulp = require('gulp');

// タスクを登録
gulp.task('hello', function () {
  console.log('Hello World');
});

gulp.task('default', ['hello']);

ここまでで保存して、%gulpを叩けば、Hello Worldと出力されるはずです。
%gulp helloでも構いません。

gulp.task('task_name', function () { /* 処理 */ });みたいなイメージ。
defaultに登録したものは%gulpとした場合に実行されます。

動くことが確認できたので、次いきます。

結合、ミニファイを行う

プラグインを導入

プラグインを導入します。
%npm install gulp-concat --save-dev
%npm install gulp-uglify --save-dev

タスクを書く

先ほどはgulpfileに直接書きましたが、今回はファイルを分けます。 さっき作ったディレクトリ内に、tasksディレクトリを作ります。(好きな名前で良いです) 今回は結合→ミニファイをするタスクということで、tasks/concat-minify.jsみたいにしておきましょう。

module.exports = function (gulp) {
  // プラグイン
  var concat = require('gulp-concat'),
      uglify = require('gulp-uglify');

  // タスク登録
  gulp.task('concat-minify', function () {
    gulp.src('js/*.js') // 対象ファイル
        .pipe(concat('sample.min.js')) // 出力ファイル名
        .pipe(uglify()) // ミニファイ
        .pipe(gulp.dest('js/min')); // 出力先ディレクトリ
  });
};

内容はコメント通りです。 今回はjs以下の.jsファイルをjs/min以下にsample.min.jsとして出力させます。

gulpfile.jsを書き換えます。

var gulp = require('gulp');

require('./tasks/concat-minify.js')(gulp);

gulp.task('default', ['concat-minify']);

こんな感じで。

実行

ここまでできたら後は実行するだけです。 %gulp concat-minifyまたは%gulpで。。。

すると、js以下のjsファイルが結合され、ミニファイされたものがjs/min以下に出力されているのが確認できるはずです。

// js/sample1.js
var sample1 = function () {};

sample1.prototype = {
  showSample: function () {
    alert('Sample1');
  }
};

// js/sample2.js
var sample2 = function () {};

sample2.prototype = {
  showSample: function () {
    alert('Sample2');
  }
};

↑例えばこんなファイル。

実行後に

// js/min/sample.min.js
var sample1=function(){};sample.prototype={showSample:function(){alert("Sample1")}};var sample2=function(){};sample.prototype={showSample:function(){alert("Sample2")}};

こんな感じになっていれば!

さいご

簡単にですが、結合ミニファイまで。 今回は簡単やりたいので、結合とミニファイを同時にやりましたが、 これを結合は結合、ミニファイはミニファイなど分けることも可能です。

また、watchなんかを使えば、ファイルを監視して、編集されたら自動実行なんてのも可能です。

結合→ミニファイを手動でやるのは正直手間ですし、不具合の元なので、 このように自動化できると便利ですね。

あと、まとめたものを即時関数で囲いたい!場合は、concatよりも、concat-utilプラグインが良さそうです。

また別のところで似たような記事を書きそうです・・・w