Vue.js/Nuxt.js
プロジェクト構築
vue-cliを使う
開発環境の準備
# install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
# install vue
sudo yarn -g install vue-cli
プロジェクトの作成
TypeScriptを使うためtypescript-templateを使っている
# install vue and create project using typescript
sudo npm install -g vue-cli
vue init nuxt-community/typescript-template <project-name>
cd <project-name>
yarn
開発時の作成しているページの確認
yarn run dev
今回はGithub pageを作るために使っているので静的ページの作成する。以下のコマンドで作れる。
yarn run generate
注意点として、vue init nuxt-community/typescript-template
を使って作ったプロジェクトはサーバーサイドレンダリングを使うように設定されている。
静的ページを作成する時はサーバーは使わないので、Nuxt.jsのモードをspa
に指定する必要がある。
その方法はnuxt.config.js
のexport
にmode: 'spa'
を追記することでできる。
//nuxt.config.js
export default {
mode: 'spa',
// ... 以下、他の設定
}
SASSを使う
SASSを使う時はまずnode-sass
とsass-loader
をインストールする
yarn add --dev node-sass sass-loader
SASSの定義にはファイルを使うかVueのプリプロセッサを使うかの二通りの方法がある
ファイルを使う方法
nuxt.config.js
ファイル内でSASSファイルを指定すればいい。
export default {
css: [
'@/assets/css/main.sass'
]
}
プリプロセッサを使う方法
こちらはVueファイルに埋め込む形になる。
//直接指定できる
<style lang="sass">
.red {
color: red;
.inner {
background-color: black;
}
}
</style>
// ファイルを読み込むこともできる
<style lang="sass" src="#/styles/hoge.sass"></style>
ESLintとPrettier
インストールには以下のコマンドを使う
yarn add --save-dev babel-eslint eslint eslint-config-prettier eslint-loader eslint-plugin-vue eslint-plugin-prettier prettier
その後に.eslintrc.js
をルートディレクトリに置くことでESLintの設定ができる。
nuxt.config.js
のbuild
を修正することで、ホットリロードモードの時にESLintを実行することができるので、必ず有効化すること。
build: {
//ここで webpack config を拡張できます
extend(config, ctx) {
// Run ESLint on save
if (ctx.isDev && ctx.isClient) {
config.module.rules.push({
enforce: "pre",
test: /\.(js|vue)$/,
loader: "eslint-loader",
exclude: /(node_modules)/
})
}
}
}
ファイルパスにおいての~と@の扱い
~
と@
はWebpackが認識するパス記法のエイリアスになっている。
エイリアスには以下のものがある。
srcDirとrootDirはnuxt.config.js
で設定できる。
imgタグに画像のURLを指定する時の注意点(Nuxt.js)
v-bind:srcで設定するときと、直接srcを書き込む時でパスの指定方法が異なる。
webpackが関係している問題。(が、別のうまい方法がある気がする。)
v-bind:srcのとき
staticディレクトリに画像を保存した状態で下のコードみたいに書く
<img v-bind:src="`images/${path}`">
How to link img src to a rendered list #448
直接srcを書き込む時
この場合はassetsディレクトリに画像を保存した状態で以下のように書く。
<img src="~assets/images/QR_URL_itch.png">
.VueファイルでSASS/SCSSを使う
.Vueファイル内でCSSを定義する時にlang属性を付けるとSASS/SCSSなど使えるようになる。
その際にはyarnなどで別個でsass-loaderとnode-sassパッケージをインストールする必要がある。
<style lang="sass">
/* ここにSassを書きます */
</style>
プロジェクト環境の構築
yarn create nuxt-app my-project
# いろいろな設定を行う
# vue
yarn add -D vue-propetry-decorator vue-eslint-parser
# typescript
yarn add -D nuxt-ts ts-loader typescript
ESLint対応
ESLintをVueに対応させるにはvue-eslint-parser
をインストールする必要がある。
yarn add -D vue-eslint-parser
あとは.exlintrc.js
も修正すればOK。
eslint
を実行する時に.vue
も対象に含めるために、--ext
オプションを付けるようにすること。
(Nuxt.jsではデフォルトでそうなっている)
module.exports = {
// ...
parser: "vue-eslint-parser",
// ...
extends: [
// ...
'eslint:recommended',
// ...
],
// ...
}
テスト
@vue/test-utils
とava
を使って行う。
どちらもnuxt-app
で自動的にインストールできるが、後からインストールする時は以下のコマンドを使う。
それとjsdom
が必要になるので忘れずにインストールすること。
yarn add -D @vue/test-utils ava jsdom
※2019/2/15現在 jsdomが原因によるエラーが起きてVueコンポーネントをインポートするとエラーが発生する状態にある
その際はどこかで以下のコードを追加する必要がある。 ava.config.jsのrequireプロパティに指定したテスト前に実行されるスクリプトに追記するのがお手軽。
jsdom-global in mocha example may break modules bundled by rollup
window.Date = Date
ava
をテスト実行用に使用し、@vue/test-utils
でVueコンポーネントの検証に使う形になる。
AVA
ava
を使ったテストは以下の形になる。
import test from 'ava';
// 現在テストしているファイル名を表示する
console.log('Test currently being run:', test.meta.file)
//基本
test('test name', t => {
//組み込みのアサーション関数
t.pass('successed') //テストを成功させる
t.fail('failed') //テストに失敗させる
t.truthy(value, 'message')
t.falsy(value, 'message')
t.is(value, expected, 'message')
t.not(value, expected, 'message')
t.deepEqual(value, expected, 'message')
t.notDeepEqual(value, expected, 'message')
t.regex('abcde',/[a-e]+/)
t.notRegex('abcde', /[A-E]+/)
//前に記録したsnapshotとの比較を行うテスト
// snapshotについてはあとで
t.shapshot(expected, 'message')
t.shapshot(expected, {id: 'specify snapshot name'}, 'message')
// 例外のテスト
const error = t.throws(()=> {
throw new ExpectedExceptionType('')
}, ExpectedExceptionType, 'message')
//例外を投げない
t.notThrows(() => {}, 'message')
// asyncを使った例外のテスト。個々にあるもの以外にもパラメータがいくつかある
await t.throwsAsync(async () => {
throw new ExpectedExceptionType('check message')
}, {instanceOf: ExpectedExceptionType, message: 'check message'})
//例外を投げない
await t.notThrowsAsync(promise);
})
//promiseを使ったもの
test('used promise test', t => {
return somePromise().then(result => {
t.is(result, 'apple')
})
})
//asyncを使ったもの
test('used async test', t => {
const result = await somePromise();
t.is(result, 'apple')
})
//AVAにはビルドインのobservablesがある。
test('used observable test', t => {
t.plan(3);
return Observable.of(1, 2, 3, 4, 5, 6)
.filter(n => {
return n % 2 == 0;
})
.map(() => t.pass())
})
// onlyがついたものがあるとonlyがあるものだけがテストされる
test.only('only run this test', t => {
t.pass()
})
// スキップするテスト
test.skip('skip this test', t => {
})
// 同時に実行させないテストの定義
// 並列に実行されるものの前にテストされる
test.serial('pass', t => {
t.pass()
})
//Node.jsスタイルのエラーファーストコールバック
test.cb('Node.js-style error-first callback API', t => {
fs.readFile('data.txt', t.end)
})
各テストにはHookを付けることもできる。
// テスト予定のものを定義
test.todo('todo')
test.only.todo('todo')
//全てのテストの前/後処理
test.before(t => {})
test.after(t => {})
//各テストの前/後処理
test.beforeEach(t => {})
test.afterEach(t => {})
//afterXXXの後ろにalwaysを付けると失敗しても必ず実行される
test.after.always(t => {})
test.afterEach.always(t => {})
// serialを付けるとserialなテストのみに対しての処理になる
test.serial.before.always(t => {})
test.serial.after.always(t => {})
Snapshot
AVAではファイルにある内容とテスト中の値を比較することができる。 この機能のことをSnapshotと呼ぶ。
Jestが発祥だそうだ。
Snapshotを使うと、1回目はテスト中に渡した値を元にテストファイルと同じ名前を持った2種類のファイルを作られ、渡した値がそこに記録される。
そして2回目以降は1回目に記録した値と比較され、異なっているとテストに失敗する。
例えば、main.js
というファイルで使うなら、以下のファイルが新しく作られる。
- snapshots/main.js.snap : 比較対象となるファイル。手で書くのではなくテスト実行中のデータを保存する
- snapshots/main.js.md : 上のファイルの内容
比較内容を更新したい時は以下のコマンドで行える。
ava --update-snapshots
デフォルトだと生成されるディレクトリはmain.js
と同じディレクトリに作られるが、設定で生成先を変更することができる。
//ava.config.js
export default {
// ...
snapshotDir: "ava-snapshots",
// ...
}
Vue Test Utils
この中のselectorについては下で説明する。
import {mount, shallowMount} from '@vue/test-utils'
import test from 'ava'
import TargetVueComponent from './' //todo ルートパスの書き方。
test('test title', t => {
const wrapper = mount(TargetVueComponent) // Vueコンポーネントを作成。
// 大規模なコンポーネントで子要素にアクセスしない時はshallowMountを使うと効率的
const shallowWrapper = shallowMount(TargetVueComponent) // 子コンポーネントを作成せずに作成。
let instance = wrapper.vm // 実体はvmの中にある
wrapper.html() // 生のHTMLソース
wrapper.text() // テキスト
wrapper.props() // VueコンポーネントのProps
wrapper.attibutes().id // Domノードの属性にアクセスする
wrapper.attibutes('id')
wrapper.classes() // DomノードのCSSクラスのリスト
wrapper.classes('bar') // barを取得
wrapper.trigger('click', { /*引数*/}) // イベント実行
wrapper.$emit('custom-event', 123) // イベント発行
//状態確認
wrapper.exists() //作成に成功したか?
wrapper.is('div') // 指定したselectorがあるか?
wrapper.isEmpty() // 子要素がないとtrue
wrapper.isVueInstance() // Vueインスタンスか?
wrapper.isVisible() // v-showで非表示になっているか?
// 検索
let exist = wrapper.contains('div') // コンポーネントの中にdivタグがあるか?
let div = wrapper.find('div') // コンポーネントの内のdivタグを探す
let divList = wrapper.findAll('div') // コンポーネントの内のdivタグを探す
t.is(wrapper.isVueInstance(), true) // Vueインスタンスかチェック
// 値設定
// Vue関係
wrapper.setMethods({clickMethod: hoge()})
wrapper.setData({hoge: 'foo'})
wrapper.setProps({foo: 'bar'}) //VueのPropsを設定
// form関係
wrapper.find('input[type="radio"]').setChecked() // ラジオボタンをチェックする
wrapper.findAll('option').at(1).setSelected() // option要素を選択する
wrapper.find('input').setValue('set to input element') // input要素に値を設定。v-modelにバインドされている
})
Selector
'@vue/test-utils'にはSelectorがあり、関数のいくつかはそれを利用できる。
Selector
を使うと以下のものが検索できる。
- タグ
- CSSクラス
- タグの属性
- タグid
- Vueコンポーネント
- $refによる検索もできる