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>

はあー、なるほどこんな感じですか!
コンポーネント
別のファイルにして 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でちゃんと整えるよりやっぱり簡単じゃないだろうか。