#初めに
正直JS界隈のエコシステムは難しいです。わからないことが目白押しです。
今までVue.jsをブラウザ上でなんのエコシステムも使わずに使ってきましたが、
規模が大きくなるとしんどくなるのは明らかなので
Vue.jsガイド:大規模アプリケーションの構築を参考にwebpackを使ったりnpmを使って、
それっぽい環境を作って一からコードを書いてみました。
(編注:上記リンクはv1.0当時のものです。v2.0のガイドは単一ファイルコンポーネント、プロダクション環境への配信のヒント、ルーティング、状態管理、単体テスト、サーバサイドレンダリングなどをご覧ください。)
#前提
Vue.jsガイド:大規模アプリケーションの構築の指示に従い構築するだけですが、この時点で以下の様な選択肢がある模様です。
-
プリプロセッサ?の違い
Webpack(こっちだけ触ります)
Browserify -
ルーティング
オフィシャルでvue-router推奨なのでそれに従います。 -
サーバとの通信
vue-resourceを使うといいみたいですが、今回はjqueryを使います。 -
単体テスト
Karma
上記以外の技術は使わない方向でやってみます。
#はじめよう
まずはnpmを使えるようにします。
$ brew install npm
##環境構築
###npmのパッケージ導入
プロジェクトの為のディレクトリを作りそのディレクトリに移動します。
そして、以下のコマンドでpackage.jsonを作成します。
$ npm init
対話プロンプトに適当に答えると以下の様なファイルが作成されます。
{
"name": "vuejssample",
"version": "0.0.1",
"description": "vue.js + webpack+ vue-loader mysample",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "m0a",
"license": "MIT"
}
webpack,vue-loaderの導入(local)
#ローカルインストール
npm install webpack vue-loader --save-dev
npm install vue --save
#グローバルインストール
npm install webpack -g
--save-dev
オプションはインストール時に開発用の依存関係としてpackage.jsonに登録するってことです。
--save
は実際に必要なライブラリをpackage.jsonに登録します。
ローカルインストールなのでnode_modules
にインストールされますが、
リモートリポジトリに登録する際に上記を除外します。
除外するために.gitignore
を作っておきます。自分の場合は環境を作っているので
gi >.gitignore
で一発です。
以降必要なライブラリはnpmで管理できるようになりました。
###webpackの設定
webpack用のconfigはwebpack.config.js
となります。
webpackがビルドプロセスでvue-loaderを動かして.vueファイルを解釈してくれます。
以下の様なconfigを作ります。
// webpack.config.js
module.exports = {
entry: './src/main.js',
output: {
path: "./build",
filename: 'build.js'
},
module: {
loaders: [
{ test: /\.vue$/, loader: 'vue' },
]
}
}
src/main.js
にコードを書いていき最終的な結果を得るのはbuild/build.js
というわけですね。
##開発します。(最初はシンプルに)
まずはディレクトリを作ってみます。
webpack.config.jsに書いたようにsrcディレクトリが開発用
buildディレクトリが出力用です。
以下のように基本的なファイルを作成します。
var Vue = require('vue')
var appOptions = require('./app.vue')
var app = new Vue(appOptions).$mount('#app')
<style>
.red {
color: #f00;
}
</style>
<template>
<h1 class="red">{{msg}}</h1>
</template>
<script>
module.exports = {
data: function () {
return {
msg: 'Hello world!'
}
}
}
</script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue simple sample </title>
</head>
<body>
<div id="app"></div>
<script src="build/build.js"></script>
</body>
</html>
editorにatomを使っているならLanguage Vue Component
を導入するとシンタックスハイライトが効きます。
作り終わったら
$ webpack --watch
でファイル修正時に自動でビルドが効くようになります。
参考:
https://github.com/vuejs/vue-loader-example
ここまでの作業を以下に記録します
https://github.com/m0a-mystudy/vuejssample/tree/v1.0
##vue-router導入
Vue.jsはシンプルで素敵ですがrouterがないのが辛いです。
公式推奨のvue-routerを導入します。
以下のコマンドを実行するだけで導入できます。
$ npm install vue-router --save
おお、簡単に導入できた。環境を作るメリットがやっとわかりました。
main.jsをいじってモジュールをロードしルーティングの設定コードを記述します。
var Vue = require('vue')
+ var VueRouter = require('vue-router')
+ Vue.use(VueRouter)
- var appOptions = require('./app.vue')
- var app = new Vue(appOptions).$mount('#app')
+ var PageA = Vue.extend(require('./components/pageA.vue'));
+ var PageB = Vue.extend(require('./components/pageB.vue'));
+ var App = Vue.extend({});
+ var router = new VueRouter();
+ router.map({
+ '/pageA': {
+ component: PageA
+ },
+ '/pageB': {
+ component: PageB
+ }
+ })
+
+ router.start(App, '#app')
app.vueは削除して以下のファイルを追加します。
<style>
.yellow {
color: #f0f;
}
</style>
<template>
<h1 class="yellow">apge A</h1>
<p>
this page is A!!!
</p>
<p>
msg = {{msg}}
</p>
</template>
<script>
module.exports = {
data: function () {
return {
msg: 'yes page A'
}
}
}
</script>
<style>
.blue {
color: #00f;
}
</style>
<template>
<h1 class="blue">apge B</h1>
<p>
this page is B!!!
</p>
<p>
msg = {{msg}}
</p>
</template>
<script>
module.exports = {
data: function () {
return {
msg: 'yes page B!!!!!'
}
}
}
</script>
index.htmlを以下のように修正します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue simple sample </title>
</head>
<body>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- ナビゲーション向けに v-link ディレクティブを使用 -->
<a v-link="{ path: '/pageA' }">Go to PageA</a>
<a v-link="{ path: '/pageB' }">Go to PageB</a>
</p>
<!-- route outlet -->
<router-view></router-view>
</div>
<script src="build/build.js"></script>
</body>
</html>
ページ遷移ができました。
以下に記録を残します。
https://github.com/m0a-mystudy/vuejssample/tree/v2.0/
jQueryをglobalに設定する
さてjQueryをインストールします。
npm install jquery --save
以下の設定でglobalにします。
+var webpack = require('webpack');
+
module.exports = {
entry: './src/main.js',
output: {
path: './build',
filename: 'build.js'
},
module: {
loaders: [
{ test: /\.vue$/, loader: 'vue' },
]
- }
+ },
+ plugins: [
+ new webpack.ProvidePlugin({
+ $: "jquery",
+ })
+ ]
}
試しに使ってみます。
以下のようにコードを変更
<template>
<h1 class="yellow">apge A</h1>
<p>
this page is A!!!
</p>
<p>
msg = {{msg}}
</p>
+ <pre>
+ {{items | json 2}}
+ </pre>
+
</template>
<script>
+ // webpack.ProvidePluginがないと以下を追加しないとjqueryが使えません
+ // var $ = require('jquery');
module.exports = {
data: function () {
return {
- msg: 'yes page A'
+ msg: 'yes page A',
+ items:''
+ }
+ },
+ ready:function(){
+ this.getJson();
+ },
+ methods: {
+ getJson:function(){
+ var that = this;
+ $.ajax({
+ type: 'GET',
+ crossDomain: true,
+ url: 'https://qiita.com/api/v2/items?page=1&per_page=5',
+ dataType: 'json',
+ success: function(json) {
+ that.$data.items = json;
+ },
+ data: null
+ });
+
}
}
}
こうして書いてみるとテンプレートとコードが一つにまとめられるのは便利っぽいですね。
以下にコードを残します。
https://github.com/m0a-mystudy/vuejssample/tree/V3.0
#テスト
テスト環境を作ります。
##karmaの導入
karma自体はテストランナー(テストを自動実行するツール)で実際のテスト用のフレームワークはjasmineを使います。
jsのエコシステムはこんな感じでいろんな名前が付いてるツールを組み合わせるし、デファクトスタンダードもよくわからないので、本当に途方にくれます。
まずはシンプルな環境から入れていきます。
karma-chrome-launcher
を入れていることからわかるようにテスト自体はchromeで行う想定です。
別なブラウザを使う方は逐次入れ替えてください。
# Install Karma:
$ npm install karma --save-dev
# Install plugins that your project needs:
$ npm install karma-jasmine karma-chrome-launcher --save-dev
$ npm install -g karma-cli
参考:http://karma-runner.github.io/0.13/intro/installation.html
さてここでさらに今回の環境のプラグインを導入します。
npm install --save-dev karma-webpack
npm install --save-dev karma-sourcemap-loader
karma-webpack
を使うことでテストを実行するファイルをプリプロセスでワンファイル化します。今回.vue
ファイルにまとまっていますが、
karma自体にこのファイルを解釈する機能がないのでwebpackでjsに変換後にテストを走らせる想定です。
ただしそのままではエラー時に行番号が分からなくなるので、karma-sourcemap-loader
を入れることで行番号を出すようにします。
###karma用のconfig作成
以下のコマンドにてkarma.conf.jsを作成します。
krama init
対話インターフェースには全部イエスと答えておきます。
作成後以下のように修正します。
var webpack = require('webpack');
// Karma configuration
// Generated on Sat Oct 03 2015 17:25:04 GMT+0900 (JST)
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'test/*_test.js', //<-- testディレクトリ内にtestを書く想定です。
],
exclude: [
],
preprocessors: {
'test/*_test.js': ['webpack','sourcemap'], //<-- 追加
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
// <-- 追加(start)
webpack: {
// webpack configuration
module: {
loaders: [
{ test: /\.vue$/, loader: 'vue' },
]
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
})
],
devtool: 'inline-source-map' //<-- 基本的にほとんどwebpack.config.jsと同じですが、ここの追加を忘れないようにします。
},
webpackMiddleware: {
// webpack-dev-middleware configuration
// i. e.
// inoInfo: true
},
plugins: [
require("karma-jasmine"),
require("karma-webpack"),
require("karma-chrome-launcher"),
require("karma-sourcemap-loader"),
]
// <-- 追加(end)
})
}
##テストの実行
プロジェクト直下から以下のコマンドで実行します。
karma start
専用のブラウザが立ち上がりファイルが変更されるたびに自動実行されます。
ログはターミナルに出るので、別途ターミナルを立ち上げて実行した方がいいでしょう。
##テストコードを書いていく
testディレクトリを作成し以下のようにテストコードを記載していきます。
describe('pageA', function () {
// require source module
var pageA = require('../src/components/pageA.vue');
it('should have a ready hook', function () {
expect(typeof pageA.ready).toBe('function')
})
it('should set correct default data', function () {
expect(typeof pageA.data).toBe('function')
var defaultData = pageA.data()
expect(defaultData.msg).toBe('yes page A')
})
it('set method cheeck', function() {
expect(typeof pageA.methods.getJson).toBe('function')
})
})
//非同期実行のテスト
describe('pageA.methods.getJson', function () {
// require source module
var pageA = require('../src/components/pageA.vue');
var pageAThis = {$data:{}}; //pageA.methods.getJsonはthisにアクセスするのでここで作成しておく
beforeEach(function(done){
pageAThis.$data = pageA.data(); //$data手動初期化
// console.log(pageAThis);
pageA.methods.getJson.call(pageAThis,done); //thisを指定して呼び出し。callバックにてdoneを実行されないと次に進まない
})
it('pageA.methods.getJson result', function() { //done実行後に呼び出される
// console.log(pageAThis);
expect(typeof pageAThis.$data.items).toBe('object')
})
})
テストが自動実行されるなら以下のようなエラーが出るかと思います。
Chrome 45.0.2454 (Mac OS X 10.11.0) pageA.methods.getJson pageA.methods.getJson result FAILED
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Chrome 45.0.2454 (Mac OS X 10.11.0): Executed 4 of 4 (1 FAILED) (5.006 secs / 5.003 secs)
pageAのgetJsonはcallbackを受け取らない実装になっていたので修正します
methods: {
- getJson:function(){
+ getJson:function(callback){
var that = this;
$.ajax({
type: 'GET',
@@ -42,6 +42,9 @@
dataType: 'json',
success: function(json) {
that.$data.items = json;
+ if(typeof callback === 'function') {
+ callback();
+ }
},
data: null
});
保存すると自動実行されて今度は成功しているかと思います。
ここまでの作業は以下に記録しています。
https://github.com/m0a-mystudy/vuejssample/tree/V4.0
次やるとしたらプロダクション環境の構築になります。