Vue.js始めるおれおれアドベントカレンダー2016 – 7日目

色々やろうとしたらやっぱりファイルの管理とかが手間なので、ぼちぼちツールを導入します。

なにそれ

Browserify transform for Vue.js components, with scoped CSS and component hot-reloading.

(Broserifyを使って)Vueのコンパイル的なアレをしてくれるツールです。対象のファイルはJSではなく、HTMLテンプレートとJSの両方を含む *.vue という拡張子のファイルだそうだ。

hot-reloading は自分の環境だと特にそれっぽい動きはしてくれてない。ウェブブラウザが別マシンだから?

ところでこういうのをコンパイルと呼んで良いんだろうか。

試行錯誤の記録

色々エラー出たりしたので置いておきます。

まず今まで使ってたひとつしかないJSファイルを単純にvueifyに突っ込んだら、ちゃんと動いてくれた。変換なしで通してくれたみたい。

続いてVueとVuexを require() で読み込むようにしたら、エラー。

Error: [vuex] must call Vue.use(Vuex) before creating a store instance.

あ、まあそうですね。さくっと Vue.use(Vuex) を追加。

で次。

[Vue warn]: Failed to mount component: template or render function not defined.
(found in root instance)

んーコンポーネントの template をHTMLに書いてちゃ駄目か。そりゃそうだ。

でJS側に持ってきても駄目だった。あれ? あーそうか本体(コンポーネントにしてない部分)もこっちに持ってこないとか。どうやら main.app から全体を司る App を render() で出力するのが定石らしい。(合ってる?)

でやったら、何故か画面が空っぽになる、何も出てこない……。と思ったら return してなかった。ああーそっか!

よしまあじゃあ一度全部消して、READMEの例の通りにやってみよう。

Error: Parsing file /path/to/you/work/dir/App.vue: 'import' and 'export' may only appear at the top level (23:0)

えー。

export default {} を module.exports = {} にしたら解決。よくわかってない。

ともかくこれで動いた!

ただ、最終的な出力ファイルの位置でエラーが出てくるのでわかりづらい。browserifyの方のオプションで --debug を与えるとソースマップを付けてくれるんだけど、うーんわかりづらい。

まとめ

環境

こんな感じになりました。

{
  "scripts": {
    "start": "npm run vue-watch",
    "vue-build": "browserify -t vueify -e src/main.js -o out/app.js ",
    "vue-watch": "chokidar src/* -c 'npm run vue-build'"
  },
  "dependencies": {
    "browserify": "^13.1.1",
    "chokidar-cli": "^1.2.0",
    "vue": "^2.1.4",
    "vueify": "^9.3.0",
    "vuex": "^2.0.0"
  },
}
  • src/ に元のファイルを、 out/ に出力ファイルを置くものとします。
  • npm run vue-build でビルドします。
  • npm run vue-watch でファイル監視してビルドします。
  • chokidar はファイル監視してコマンド実行するやつです。べんり!

Hello World!

  • src/main.js
const Vue = require('vue');
const Vuex = require('vuex');  // 今回使ってないけど
Vue.use(Vuex);

const App = require('./App.vue');

new Vue({
  el: '#app',
  render: function(createElement) {
    return createElement(App);
  },
});
  • src/App.vue
<style>
  h1 {
    font-style: italic;
  }
</style>

<template>
  <div class="container">
    <h1>{{message}}</h1>
  </div>
</template>

<script>
  module.exports = {
    data: function() {
      return {
        message: 'Hello World!',
      },
    },
  };
</script>

hello-vueify-world

はあー、なるほどこんな感じですか!

コンポーネント

別のファイルにして require() する感じ。無理やり感あるけど分割して、ついでにVuexも突っ込みます。

  • App.js
<template>
  <div class="container">
    <my-title></my-title>
  </div>
</template>

<script>
  var MyTitle = require('./MyTitle.vue');

  module.exports = {
    components: {
      MyTitle: MyTitle,
    },
  };
</script>
  • MyTitle.js
<template>
  <h1>{{title}}</h1>
</template>

<script>
  var store = require('./store.js');

  module.exports = {
    data: function() {
      return store.state;
    },
  };
</script>
  • store.js
const Vuex = require('vuex');

module.exports = new Vuex.Store({
  state: {
    title: 'Hello World!',
  },
});

見た目はさっきと一緒。

よくわからないこと

store.state を直接 data に指定しない?

data: store.state と書いたらこんなん言われた。

[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.

毎回 function() 書いて return しないといけない?

export default を使うには?

これも未来JavaScriptの機能だよね。 import するやつ。例の通りに書いたのに駄目だった。コンパイル時にエラーになる。どうしたら良かったんだろうか。バベる? いつ?

hot-reloading

使えたら便利そうだけど。

lint

そろそろVue的な書き方に合わせたい。セミコロン欲しい派だけど、しばらく書いてれば慣れるだろう……。

おしまい

ビルドなしで強力なのが導入できて便利~というのがVueの最初の印象だったんだけど、結局ビルドするようになりました。まあ「ちゃんと」やろうとしたらそうなるよね。例えjQueryでも。とはいえ簡単だし、jQueryでちゃんと整えるよりやっぱり簡単じゃないだろうか。