はじめに
これまでの連載で、
これまでの連載ではVue.
vue-cliによるアプリケーション開発
前準備
開発にnpm
vue-cli
vue-cliとは、
JavaScriptでアプリケーションを構築する場合、
vue-cliのインストール
では、
$ npm install -g vue-cli
インストールが完了したら、
$ vue --version 2.6.0
本稿では執筆時点での最新バージョンである2.
アプリケーションプロジェクトの作成
vue-cliのインストールが完了したら、
$ vue init webpack vue-cli-gihyo-example
上記コマンドを実行すると、
This will install Vue 2.x version of template. For Vue 1.x use: vue init webpack#1.0 vue-cli-gihyo-example ? Project name vue-cli-gihyo-example ? Project description A Vue.js project ? Author kazuya kawaguchi <[email protected]> ? Vue build standalone ? Use ESLint to lint your code? Yes ? Pick an ESLint preset Standard ? Setup unit tests with Karma + Mocha? Yes ? Setup e2e tests with Nightwatch? Yes
入力が完了すると下記の内容が出力され、
vue-cli · Generated "vue-cli-gihyo-example". To get started: cd vue-cli-gihyo-example npm install npm run dev Documentation can be found at https://vuejs-templates.github.io/webpack
出力されたメッセージには、
アプリケーションの開発を開始する
Vue.
$ cd vue-cli-gihyo-example $ npm install $ npm run dev
npm install
でVue.
次にnpm run dev
で動作確認のための開発用サーバが起動すると、

最小限に動作するアプリケーションの雛形となるコードが生成されています。以降は、
単一ファイルコンポーネントによるモジュール化
vue-cliでVue.
単一ファイルコンポーネントとは
早速実践として開発の紹介に入りたいところですが、
vue-cliでセットアップしたVue.
単一ファイルコンポーネントとは、.vue
拡張子ファイル内に定義したコンポーネントで、
<template>
タグ: コンポーネントにおいてUIのセマンティックな構造をテンプレートとして定義する要素です。HTMLの他、Mustache記法、 v-ifなどのVue. jsで提供する文法がそのまま利用可能です。 <style>
タグ: コンポーネントにおいてUIの見た目を制御する要素です。CSSを使用して定義することができます。<script>
タグ: コンポーネントにおいてUIの振る舞いを制御する要素です。JavaScriptを使用することができ、連載第3回で解説した同じ作法でコンポーネント定義が必要です。
単一ファイルコンポーネントの例を以下に示します。
<template>
<p class="message">メッセージ: {{ msg }}</p>
</template>
<style>
.message { color: #42b983; }
</style>
<script>
export default {
data () {
return { msg: 'こんにちは!' }
}
}
</script>
従来のWeb標準の技術構成
第3回で、Vue.
によるUIをコンポーネント化する仕組みを紹介しましたが、export/
を使用することによりモジュール化は可能です。
しかしながら、
vue-cliによりセットアップされたアプリケーションプロジェクトでは、
単一ファイルコンポーネントの使用方法
単一ファイルコンポーネントは、Vue.
に登録可能なオブジェクトに変換します。このため、
// 単一ファイルコンポーネントでモジュール化されたHello.vueをインポート
import Hello from './Hello'
// インポートされた該当コンポーネントを使用対象となるVueコンポーネントに登録
new Vue({
components: { Hello }
}).$mount('#app')
<div id="app">
<hello></hello>
</div>
単一ファイルコンポーネントのその他機能
その他に、
- BabelによるES2015以降のJavaScript
- スコープ付きCSS
(Scoped CSS) - PostCSSによるCSSプロセッサ
- CSS ModulesによるCSSのモジュール化
- プリプロセッサ
(Pre-Processor) による多言語 (Pug/ SaSS/ CoffeeScriptなど) による単一ファイルコンポーネントの使用 - ホットリロード
(Hot Reload) による開発時のライブリロード
これらを使用することでさらに生産性と再利用性の高いコンポーネントのモジュール化が可能になります。詳細についてはドキュメント
単一ファイルコンポーネントの作成
単一ファイルコンポーネントについて簡単ですが解説しました。vue-cliでセットアップしたVue.msg
プロパティ経由でメッセージが表示し、
<template>
<p class="message">メッセージ: {{ msg }}</p>
</template>
<style>
.message { color: #42b983; }
</style>
<script>
export default {
props: {
msg: {
type: String,
default: 'こんにちは!'
}
}
}
</script>
vue-cliによってHelloコンポーネントとしてセットアップされているので、src/
を開いて置き換えて保存してください。その結果、

'こんにちは!
さて、msg
プロパティに文字列値を設定するとその文字列値が表示される仕様になっているので、msg
プロパティに文字列値を設定してみましょう。エディタで、src/
を開いて内容をtemplateタグの実装内容を以下のように変更してください。
<template>
<div id="app">
<img src="./assets/logo.png">
<hello msg="ようこそ!"></hello>
</div>
</template>
この結果、

単一ファイルコンポーネント作成を体験することができました。初期にセットアップされているコンポーネントで単一ファイルコンポーネントを実装していきました。新規にコンポーネントを作成する場合は、src
ディレクトリ配下に、.vue
ファイルを作成してコンポーネントを実装するとよいでしょう。
テスト
実際のアプリケーション開発においては単体テスト、
$ npm run unit
このコマンドを実行すると、test/
配下にある単体テストコードが実行されテスト結果が出力されます。以下はその内容のテスト結果の部分抜粋です。
Hello.vue ✗ should render correct contents null is not an object (evaluating 'vm.$el.querySelector('.hello h1').textContent') webpack:///test/unit/specs/Hello.spec.js:10:45 <- index.js:165:46
テストが失敗していることがわかります。これは、
ここで変更したHelloコンポーネントの動作内容を検証する単体テストを実装してみましょう。エディタでtest/
を開いて以下のように単体テストを実装します。
// Vue.js本体とHelloコンポーネントをインポートする
import Vue from 'vue'
import Hello from 'src/components/Hello'
describe('Helloコンポーネント', () => {
it('デフォルトメッセージが正しく描画されていること', () => {
const Ctor = Vue.extend(Hello)
const vm = new Ctor().$mount()
expect(vm.$el.textContent).to.equal('メッセージ: こんにちは!')
})
it('msgプロパティで指定した文字列値で正しく描画されていること', () => {
const Ctor = Vue.extend(Hello)
// 初期プロパティ値の変更は、propsData経由で行う
// 公式ドキュメントを参照: https://jp.vuejs.org/v2/api/#propsData
const vm = new Ctor({ propsData: { msg: 'ようこそ!' } }).$mount()
expect(vm.$el.textContent).to.equal('メッセージ: ようこそ!')
})
it('親コンポーネント経由でmsgプロパティに指定された文字列で正しく描画されていること', done => {
const vm = new Vue({
data: { message: '' },
components: { Hello },
// render関数で実装されている内容は
// templateオプションに'<hello :msg="message"></hello>'に指定するのと同義
render (h) { return h('hello', { props: { msg: this.message } }) }
}).$mount()
// マウント後、コンポーネントの状態値(props/data)の変更に対するDOMの更新検証は、
// 非同期に更新されるため、Vue.nextTickを使用する
vm.message = 'ようこそ!'
Vue.nextTick(() => {
expect(vm.$el.textContent).to.equal('メッセージ: ようこそ!')
done()
})
})
})
上記の単体テストコードは、
いくつかDOMを操作するブラウザ環境に依存したコードが入っていますが、
単体テストを実装したので、npm run unit
コマンドで検証してみましょう。以下に抜粋するような検証がパスしているテスト結果が出力されることを確認できましたでしょうか?
Helloコンポーネント ✓ デフォルトメッセージが正しく描画されていること ✓ msgプロパティで指定した文字列値で正しく描画されていること ✓ 親コンポーネント経由でmsgプロパティに指定された文字列で正しく描画されていること PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 3 of 3 SUCCESS (0.02 secs / 0.012 secs) TOTAL: 3 SUCCESS
今回は単体テストのみでしたが、test/
配下にセットアップされているので、
デバッグ
JavaScriptによるフロントエンドのアプリケーション開発においてデバッグにはいろいろ方法がありますが、
本稿で作成しているアプリケーションをvue-devtoolsでデバッグしているときの画面の様子は図4のようになります。

上記のようにvue-devtoolsは、
ビルド
単一ファイルコンポーネントを実装し、
一般にJavaScriptによるフロントエンドエンドのアプリケーションはビルドにwebpackやbrowserifyなどのツールを使用します。これらを使うことでJavaScriptはもちろん、
vue-cliで生成したVue.build
ディレクトリ配下に出力します。このため設定作業の必要はなく、
それでは以下のコマンドでビルドしてみましょう。
$ npm run build
ビルド結果内容をビルドが完了し、
Tip: Built files are meant to be served over an HTTP server. Opening index.html over file:// won't work. ⠋ building for production...cp: no such file or directory: static/* Hash: 9c4ed13ce128ce3a2c03 Version: webpack 1.14.0 Time: 9823ms Asset Size Chunks Chunk Names static/js/manifest.c2ec5fc9f5c8f45dafe2.js 832 bytes 0 [emitted] manifest static/js/app.357c6f5c0fd1d34a3280.js 10.7 kB 1, 0 [emitted] app static/js/vendor.db94a67cc8fd496820cf.js 74.2 kB 2, 0 [emitted] vendor static/css/app.8d55793912407d0c8dcc48d76dc73b3b.css 259 bytes 1, 0 [emitted] app static/js/manifest.c2ec5fc9f5c8f45dafe2.js.map 8.86 kB 0 [emitted] manifest static/js/app.357c6f5c0fd1d34a3280.js.map 29.7 kB 1, 0 [emitted] app static/css/app.8d55793912407d0c8dcc48d76dc73b3b.css.map 660 bytes 1, 0 [emitted] app static/js/vendor.db94a67cc8fd496820cf.js.map 604 kB 2, 0 [emitted] vendor index.html 450 bytes [emitted]
上記コマンドで、dist
ディレクトリに出力されます。こうしてビルドされたアセットを、
さらに高度なアプリケーションを開発するためのその他の機能
Vue.
- スロット
- トランジション
- カスタイムディレクティブ
- ミックスイン
- プラグイン
- 描画関数
- サーバサイドレンダリング
この中から、
スロット
スロットを利用してコンポーネントに対して外部からコンテンツを挿入できます。以下はスロットを使ったModalコンポーネントの例です。
<div class="modal-container">
<!-- ヘッダー -->
<div class="modal-header">
<slot name="header">
デフォルトヘッダー
</slot>
</div>
<!-- ボディ --->
<div class="modal-body">
<slot name="body">
デフォルトボディ
</slot>
</div>
<!-- フッター -->
<div class="modal-footer">
<slot name="footer">
デフォルトフッター
<button class="modal-default-button" @click="$emit('close')"> OK </button>
</slot>
</div>
</div>
名前付きスロット
<modal v-if="showModal" @close="showModal = false">
<!-- ここにモーダルのデフォルトコンテンツを上書きするカスタムコンテンツを使用できます。 -->
<h3 slot="header">カスタムヘッダー</h3>
</modal>
上記の例では、slot
属性として"header"が指定されているので、
トランジション
要素が挿入、transition
コンポーネントを使ったトランジションの例です。
<transition name="modal">
<div class="modal-mask">
<!-- ここには何らかのModalのテンプレートを定義 -->
<!-- ... -->
</div>
</transition>
Vue.
.modal-enter {
opacity: 0;
}
.modal-leave-active {
opacity: 0;
}
.modal-enter .modal-container, .modal-leave-active .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
上記では、
以上、
スロットとトランジションの詳細や解説しなかったその他の機能については、
おわりに
最終回では、
vue-cliというVue.
今回学習用に作成したアプリケーションプロジェクトは筆者のレポジトリに公開しています。参考にしてください。
本連載記事によって、
本連載の執筆メンバーで運営するSlackを使った日本人向けのVue.
それではご愛読ありがとうございました。