node-sassでディレクトリごとwatch(監視)する
例えば以下のような感じです。
$ node-sass src/entry_points/ -o public/stylesheets/ -w
一応解説すると以下の通りです。-w
はwatchです。
$ node-sass [監視したいディレクトリ] -o [書き出したいディレクトリ] -w
npm install -g eslint
まず以下の4つのモジュールをインストールします。
npm install --save-dev eslint eslint-loader babel-eslint eslint-config-vue eslint-plugin-vue
「eslint-config-standard - npm」を入れます。依存するモジュールが4つあるのでまとめてインストールします。
npm install --save-dev eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard
{ "name": "webpack_4_vue_introduction", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "watch": "webpack -d --watch", "test": "jest" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.4.3", "@babel/core": "^7.4.3", "@babel/preset-env": "^7.4.3", "@vue/test-utils": "^1.0.0-beta.29", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.0.1", "babel-jest": "^24.7.1", "babel-loader": "^8.0.5", "css-loader": "^2.1.1", "eslint": "^5.16.0", "eslint-config-standard": "^12.0.0", "eslint-config-vue": "^2.0.2", "eslint-loader": "^2.1.2", "eslint-plugin-import": "^2.17.1", "eslint-plugin-node": "^8.0.1", "eslint-plugin-promise": "^4.1.1", "eslint-plugin-standard": "^4.0.0", "eslint-plugin-vue": "^5.2.2", "jest": "^24.7.1", "jest-serializer-vue": "^2.0.2", "node-sass": "^4.11.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "vue-jest": "^3.0.4", "vue-loader": "^15.7.0", "vue-template-compiler": "^2.6.10", "webpack": "^4.30.0", "webpack-cli": "^3.3.0" }, "dependencies": { "vue": "^2.6.10", "vuex": "^3.1.0" } }
rulesに以下のeslint-loaderをrulesの.vueと.jsの二か所に追記します。
{ loader: 'eslint-loader', },
以下のようになりました。
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { // entry point entry: { 'javascripts/first_page': './src/javascripts/entry_points/first_page.js', }, // 出力するパスは絶対パスで書きます output: { path: `${__dirname}/webroot/packed`, filename: (arg) => { return '[name].bundle.js' }, }, // webpack4はlordersではなくなりました module: { rules: [ // 拡張子.vueのファイルに対する設定 { test: /\.vue$/, use: [ { loader: "vue-loader", options: { loaders: { js: 'babel-loader', }, }, }, { loader: 'eslint-loader', }, ] }, // 拡張子.jsのファイルに対する設定 { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', }, { loader: 'eslint-loader', }, ] }, // 拡張子.scssのファイルに対する設定(.vueの中にscssを書きたい場合もここに設定します。) { test: /\.scss$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'sass-loader', }, ] }, ] }, // デフォルトの設定値だけでは足りないことについて解決します resolve: { // モジュールを読み込むときに検索するディレクトリの設定 modules: [path.join(__dirname, 'src'), 'node_modules'], // importするときに省略できる拡張子の設定 extensions: ['.js', '.vue'], alias: { // importのファイルパスを相対パスで書かないようにsrcのrootを設定 '@': path.join(__dirname, 'src'), // 例えばmain.js内で `import Vue from 'vue';` と記述したときの`vue`が表すファイルパスを指定 'vue$': 'vue/dist/vue.esm.js' }, }, // プラグインを列挙 plugins: [ new VueLoaderPlugin() ], }
.eslintrcは以前は拡張子がありませんでしたが、今は拡張子を付けることが推奨されています。
.eslintrcは色々な拡張子で作成できます。
jsonの例をよく見かける気がしますが、jsonは記述ルールが厳しいので、.jsにしようと思います。
copy nul .eslintrc.js
touch .eslintrc.js
以下の通り記述します。
module.exports = { "extends": [ // eslint-plugin-vue(.vueファイルのtemplateとscriptのlint)をextend "plugin:vue/recommended", // eslint-config-standardをextend "standard", ], "parserOptions": { // ecmaVersionを指定 "ecmaVersion": 6, // type="module"をサポート "sourceType": "module", // parserを指定 "parser": "babel-eslint", }, "env": { // browserが持っているオブジェクトをサポート "browser": true, // ES2015以降に追加された組み込みオブジェクトをサポート "es6": true }, "globals": {}, "rules": { "semi": ["error", "always"], "comma-dangle": ["error", "always-multiline"], } }
webpack -d
以下のような感じでとても多くの構文エラーが出ました。
何行目の何文字目がエラーなのか書いてあります。
1:22 error Extra semicolon semi 6:1 error Import in body of module; reorder to top import/first 6:70 error Extra semicolon semi 8:33 error Extra semicolon semi 10:1 error Do not use 'new' for side effects no-new 14:3 warning The "components" property should be above the "template" property on line 13 vue/order-in-components 15:3 error Extra semicolon semi 16:1 error Too many blank lines at the end of file. Max of 0 allowed no-multiple-empty-lines
以下はルールの一例です。コツコツ修正するか、後述するautofixのoptionを追加してから再度ビルドします。
'mapGetters' is defined but never used
: 変数を定義しているが使われていない'mapActions' is defined but never used
: 変数を定義しているが使われていないExtra semicolon
: 余計なセミコロンがあるThe "components" property should be above the "template" property on line 13
: 記述する順番が規約に沿ってないExpected 1 line break after opening tag ('<button>'), but no line breaks found
: 改行してないMissing trailing comma
: カンマが無いMissing space before function parentheses
: (の前に半角スペースが無いExpected a space before '/>', but not found
: /の前に半角スペースが無いProperty name "app" is not PascalCase
: 一文字目は大文字にするDo not use 'new' for side effects
: newする場合は変数に入れるwebpack.config.jsに以下のようにoptionsを設定することで、ビルドの度に自動修正することができるので、自動修正したい場合は設定します。
webpack.config.jsを二か所修正しておきます。
{ loader: 'eslint-loader', options: { fix: true, }, },
以下のようになりました。
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { // entry point entry: { 'javascripts/first_page': './src/javascripts/entry_points/first_page.js', }, // 出力するパスは絶対パスで書きます output: { path: `${__dirname}/webroot/packed`, filename: (arg) => { return '[name].bundle.js' }, }, // webpack4はlordersではなくなりました module: { rules: [ // 拡張子.vueのファイルに対する設定 { test: /\.vue$/, use: [ { loader: "vue-loader", options: { loaders: { js: 'babel-loader', }, }, }, { loader: 'eslint-loader', options: { fix: true, }, }, ] }, // 拡張子.jsのファイルに対する設定 { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', }, { loader: 'eslint-loader', options: { fix: true, }, }, ] }, // 拡張子.scssのファイルに対する設定(.vueの中にscssを書きたい場合もここに設定します。) { test: /\.scss$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'sass-loader', }, ] }, ] }, // デフォルトの設定値だけでは足りないことについて解決します resolve: { // モジュールを読み込むときに検索するディレクトリの設定 modules: [path.join(__dirname, 'src'), 'node_modules'], // importするときに省略できる拡張子の設定 extensions: ['.js', '.vue'], alias: { // importのファイルパスを相対パスで書かないようにsrcのrootを設定 '@': path.join(__dirname, 'src'), // 例えばmain.js内で `import Vue from 'vue';` と記述したときの`vue`が表すファイルパスを指定 'vue$': 'vue/dist/vue.esm.js' }, }, // プラグインを列挙 plugins: [ new VueLoaderPlugin() ], }
自動修正の設定をした場合、今回の構文エラーのほとんどはこれで直ります。
webpack -d
.eslintrc.js
を編集します。
以下のようにrulesに追記しました。
"rules": { 〜略〜 "no-new": "off", }
eslint実行時に以下のWarningメッセージが出力されています。
これはeslint5から表示されるようになったようです。
parserOptions.ecmaFeatures.experimentalObjectRestSpread
が非推奨でparserOptions.ecmaVersion
を使用してくださいということらしいですが解決の仕方がわからず一旦このまま使っています。
(node:16602) [ESLINT_LEGACY_OBJECT_REST_SPREAD] DeprecationWarning: The 'parserOptions.ecmaFeatures.experimentalObjectRestSpread' option is deprecated. Use 'parserOptions.ecmaVersion' instead. (found in "vue")
またそのうちわかったら。
src/javascripts/vue_applications/common/components/TextField.vue
のtemplate に以下の通り追記します。
<div class="mod-error-messages"> <ul class="mod-error-messages__list"> <li class="mod-error-messages__item"> sample message </li> </ul> </div>
ファイル末尾に以下の通り追記します。
<style lang="scss" scoped> .mod-error-messages { .mod-error-messages__list { box-sizing: border-box; } .mod-error-messages__item { color: #ff0000; } } </style>
TextField.vueは以下のようになりました。
<template> <section> <label>TextField: </label> <input :value="value" type="text" @input="onInput" > <div class="mod-error-messages"> <ul class="mod-error-messages__list"> <li class="mod-error-messages__item"> sample message </li> </ul> </div> <div class="test-synced-text"> value: {{ value }} </div> </section> </template> <script> import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'; export default { name: 'TextField', computed: { ...mapState('textFieldUnit/textField', [ 'value', ]), }, methods: { ...mapMutations('textFieldUnit/textField', [ 'setState', ]), onInput(e) { this.setState({ key: 'value', value: e.target.value, }); }, }, } </script> <style lang="scss" scoped> .mod-error-messages { .mod-error-messages__list { box-sizing: border-box; border-radius: 4px; } .mod-error-messages__item { color: #ff0000; } } </style>
以下のコマンドでビルドを実行してみます。
webpack -d
以下のようなエラーが出ました。
ERROR in ./src/vue_applications/components/TextField.vue?vue&type=style&index=0&id=273a995e&lang=scss&scoped=true& (./node_modules/vue-loader/lib??vue-loader-options!./src/vue_applications/components/TextField.vue?vue&type=style&index=0&id=273a995e&lang=scss&scoped=true&) 40:0 Module parse failed: Unexpected token (40:0) You may need an appropriate loader to handle this file type. | | > .mod-error-message { | color: red; | } @ ./src/vue_applications/components/TextField.vue?vue&type=style&index=0&id=273a995e&lang=scss&scoped=true& 1:0-160 1:176-179 1:181-338 1:181-338 @ ./src/vue_applications/components/TextField.vue @ ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/vue_applications/components/TextFieldUnit.vue?vue&type=script&lang=js& @ ./src/vue_applications/components/TextFieldUnit.vue?vue&type=script&lang=js& @ ./src/vue_applications/components/TextFieldUnit.vue @ ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/vue_applications/App.vue?vue&type=script&lang=js& @ ./src/vue_applications/App.vue?vue&type=script&lang=js& @ ./src/vue_applications/App.vue @ ./src/index.js
.vueにscssを記述できるように以下の4つのモジュールを追加します。
npm install --save-dev style-loader css-loader sass-loader node-sass
{ "name": "webpack_4_vue_introduction", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "watch": "webpack -d --watch", "test": "jest" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.2.3", "@babel/core": "^7.4.0", "@babel/preset-env": "^7.4.2", "@vue/test-utils": "^1.0.0-beta.29", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^24.5.0", "babel-loader": "^8.0.5", "css-loader": "^2.1.1", "jest": "^24.5.0", "jest-serializer-vue": "^2.0.2", "node-sass": "^4.11.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "vue-jest": "^3.0.4", "vue-loader": "^15.7.0", "vue-template-compiler": "^2.6.10", "webpack": "^4.29.6", "webpack-cli": "^3.3.0" }, "dependencies": { "vue": "^2.6.10", "vuex": "^3.1.0" } }
rulesに渡している配列に以下の行を追加します。
.vueの中にscssを書くのですが、test: /\.scss$/,
のルールを追加することになります。
// 拡張子.scssのファイルに対する設定(.vueの中にscssを書きたい場合もここに設定します。) { test: /\.scss$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'sass-loader', }, ] },
以下のようになっています。
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { // entry point entry: { 'javascripts/first_page': './src/javascripts/entry_points/first_page.js', }, // 出力するパスは絶対パスで書きます output: { path: `${__dirname}/webroot/packed`, filename: (arg) => { return '[name].bundle.js' }, }, // webpack4はlordersではなくなりました module: { rules: [ // 拡張子.vueのファイルに対する設定 { test: /\.vue$/, use: [ { loader: "vue-loader", options: { loaders: { js: 'babel-loader', }, }, }, ] }, // 拡張子.jsのファイルに対する設定 { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', }, ] }, // 拡張子.scssのファイルに対する設定(.vueの中にscssを書きたい場合もここに設定します。) { test: /\.scss$/, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'sass-loader', }, ] }, ] }, // デフォルトの設定値だけでは足りないことについて解決します resolve: { // モジュールを読み込むときに検索するディレクトリの設定 modules: [path.join(__dirname, 'src'), 'node_modules'], // importするときに省略できる拡張子の設定 extensions: ['.js', '.vue'], alias: { // importのファイルパスを相対パスで書かないようにsrcのrootを設定 '@': path.join(__dirname, 'src'), // 例えばmain.js内で `import Vue from 'vue';` と記述したときの`vue`が表すファイルパスを指定 'vue$': 'vue/dist/vue.esm.js' }, }, // プラグインを列挙 plugins: [ new VueLoaderPlugin() ], }
以下のコマンドでビルドを実行してみます。
webpack -d
今度はビルド成功したと思います。
スタイルが適用されて赤い文字になっているのがわかります。
また、開発者ツールなどで確認してみると、セレクタが追加されてscopedな状態になっていることがわかります。
今回のポイントは.vueにstyleを書くために、.scssのルールを追加するところだと思います。
変数やmixinなど複数のコンポーネントで共通して使用する部分をsass-resources-loader
で読み込むことができるのでそのうちこの記事に追記するかもしれません。
次回はeslintで.vueと.jsの構文チェックをしようと思います。
Windowsでもできます。
これから何回かに分けてnpm initするところから、以下のようなことをできる環境を構築していきます。
以下の記事の手順で基本的なビルド環境の構築をしたところまでが前提です。
続きとしてテストを実行できるようにしていきます。
以下の5つのモジュールを追加する必要があるようです。
Additional Dependencies
vue-test-utils jest babel-jest (for ES2015+ features in tests) vue-jest (for handling *.vue files in tests) jest-serializer-vue (for snapshot tests)
以下のコマンドでインストールしました。
npm install --save-dev @vue/test-utils jest babel-jest vue-jest jest-serializer-vue
package.jsonのscriptsのtestを編集してjestを実行するようにします。
"scripts": { "watch": "webpack -d --watch", "test": "jest" },
copy nul jest.config.js
touch jest.config.js
以下のように記述します。
module.exports = { // テスト対象の拡張子 "moduleFileExtensions": [ "js", "vue", ], // specファイル中の"^@/(.*)$"にマッチする文字列を"<rootDir>/src/$1"のパスに置き換えてテストを実行 "moduleNameMapper": { "^@/(.*)$": "<rootDir>/src/$1", }, // transformerを設定 "transform": { "^.+\\.js$": "<rootDir>/node_modules/babel-jest", ".*\\.(vue)$": "<rootDir>/node_modules/vue-jest", }, "snapshotSerializers": [ "<rootDir>/node_modules/jest-serializer-vue", ], }
以下のようになっています。
{ "name": "webpack_4_vue_introduction", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "watch": "webpack -d --watch", "test": "jest" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.2.3", "@babel/core": "^7.4.0", "@babel/preset-env": "^7.4.2", "@vue/test-utils": "^1.0.0-beta.29", "babel-jest": "^24.5.0", "babel-loader": "^8.0.5", "jest": "^24.5.0", "jest-serializer-vue": "^2.0.2", "vue-jest": "^3.0.4", "vue-loader": "^15.7.0", "vue-template-compiler": "^2.6.10", "webpack": "^4.29.6", "webpack-cli": "^3.3.0" }, "dependencies": { "vue": "^2.6.10", "vuex": "^3.1.0" } }
mkdir test
以下のコマンドで作成できると思います。
copy nul test\TextFieldUnit.spec.js copy nul test\TextField.spec.js
以下のコマンドで作成できると思います。
touch test/TextFieldUnit.spec.js touch test/TextField.spec.js
import { shallowMount, createLocalVue } from '@vue/test-utils' import Vuex from 'vuex' // modules import textFieldUnit from '@/javascripts/vue_applications/common/modules/textFieldUnit.js' // components import TextFieldUnit from '@/javascripts/vue_applications/common/components/TextFieldUnit.vue' const localVue = createLocalVue() localVue.use(Vuex) describe('TextFieldUnit.vue', () => { let store; beforeEach(() => { // 本来のthis.$storeに入るものと同様のオブジェクト構造を作成します。 store = new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {}, modules: { textFieldUnit, }, }) }) it('mutationsの動作確認', () => { const state = store.state.textFieldUnit; const key = 'count'; textFieldUnit.mutations.setState(state, { key, value: state[key] + 1, }); expect(state[key]).toBe(1); }); it('ボタンのclickイベントが発火したとき', () => { const wrapper = shallowMount(TextFieldUnit, { store, localVue }) const button = wrapper.find('button') // 初期値の検証 expect(button.text()).toBe('count: 0') // イベント発火 button.trigger('click') // 変更後の値の検証 expect(button.text()).toBe('count: 1') }); })
import { shallowMount, createLocalVue } from '@vue/test-utils' import Vuex from 'vuex' // modules import textFieldUnit from '@/javascripts/vue_applications/common/modules/textFieldUnit.js' import textField from '@/javascripts/vue_applications/common/modules/textField.js' // components import TextField from '@/javascripts/vue_applications/common/components/TextField.vue' const localVue = createLocalVue() localVue.use(Vuex) describe('TextField.vue', () => { let store; beforeEach(() => { // 本来のthis.$storeに入るものと同様のオブジェクト構造を作成します。 store = new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {}, modules: { textFieldUnit, }, }); }); it('mutationsの動作確認', () => { const state = store.state.textFieldUnit.textField; const key = 'value'; textFieldUnit.mutations.setState(state, { key, value: 'editedValue', }); expect(state[key]).toBe('editedValue'); }); it('テキストフィールドのinputがイベント発火したとき', () => { const wrapper = shallowMount(TextField, { store, localVue }); const input = wrapper.find('input'); const syncedText = wrapper.find('.test-synced-text'); // 初期値の検証 expect(input.element.value).toBe('defaultValue'); expect(syncedText.text()).toBe('value: defaultValue'); // イベント発火 input.element.value = 'editedValue'; input.trigger('input'); // 変更後の値の検証 expect(input.element.value).toBe('editedValue'); expect(syncedText.text()).toBe('value: editedValue'); }); });
package.jsonのscriptsに定義してあるので、以下のコマンドで実行できます。
npm run test
scoped packagesになったバージョンの@babel/babel-core
を使っているため以下のようなメッセージが出てテスト実行に失敗します。
● Test suite failed to run Cannot find module 'babel-core' at Object.<anonymous> (node_modules/vue-jest/lib/compilers/babel-compiler.js:1:15)
GitHub - babel/babel-bridge: A placeholder package that bridges babel-core to @babel/core.
さきほどのエラーでは Cannot find module 'babel-core'
でした。
'babel-core' を探しにいったときに '@babel/core' へ橋渡しをできるようにします。
npm install --save-dev babel-core@^7.0.0-bridge.0 @babel/core
もう一度testを実行してみます。
npm run test
以下のように4つ成功したかと思います。
Test Suites: 2 passed, 2 total Tests: 4 passed, 4 total
{ "name": "webpack_4_vue_introduction", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "watch": "webpack -d --watch", "test": "jest" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.2.3", "@babel/core": "^7.4.0", "@babel/preset-env": "^7.4.2", "@vue/test-utils": "^1.0.0-beta.29", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^24.5.0", "babel-loader": "^8.0.5", "jest": "^24.5.0", "jest-serializer-vue": "^2.0.2", "vue-jest": "^3.0.4", "vue-loader": "^15.7.0", "vue-template-compiler": "^2.6.10", "webpack": "^4.29.6", "webpack-cli": "^3.3.0" }, "dependencies": { "vue": "^2.6.10", "vuex": "^3.1.0" } }
以下のような感じでvmのcomputedやmethodsにもアクセスできます。
wrapper.vm.hoge
testディレクトリに入っている.spec.jsファイルはもっとディレクトリ階層を深くしてもテストを実行できます。
以下のようなテストが実行できるようになりました。
今日はここまで。
次回は.vueの中にstyleを書けるようにしていこうと思います。
Windowsでもできます。
これから何回かに分けてnpm initするところから、以下のようなことをできる環境を構築していきます。
mkdir webpack_4_vue_introduction
cd webpack_4_vue_introduction npm init -y
npm install -g webpack webpack-cli babel
npm install --save-dev webpack webpack-cli vue-loader vue-template-compiler babel-loader @babel/core @babel/cli @babel/preset-env
npm install --save vue vuex
./ ├── package-lock.json ├── package.json ├── src │ └── javascripts │ ├── entry_points │ │ └── first_page.js │ └── vue_applications │ ├── common │ │ ├── components │ │ │ ├── TextField.vue │ │ │ └── TextFieldUnit.vue │ │ └── modules │ │ ├── textField.js │ │ └── textFieldUnit.js │ └── pages │ └── first_page │ ├── App.vue │ └── store.js ├── webpack.config.js └── webroot └── first_page.html
windowsの場合は以下のコマンドでできると思います。
mkdir src\javascripts\entry_points mkdir src\javascripts\vue_applications\pages\first_page mkdir src\javascripts\vue_applications\common\components mkdir src\javascripts\vue_applications\common\modules mkdir webroot copy nul .babelrc copy nul webpack.config.js copy nul src\javascripts\entry_points\first_page.js copy nul src\javascripts\vue_applications\pages\first_page\App.vue copy nul src\javascripts\vue_applications\pages\first_page\store.js copy nul src\javascripts\vue_applications\common\components\TextFieldUnit.vue copy nul src\javascripts\vue_applications\common\components\TextField.vue copy nul src\javascripts\vue_applications\common\modules\textFieldUnit.js copy nul src\javascripts\vue_applications\common\modules\textField.js copy nul webroot\first_page.html
mkdir -p src/javascripts/entry_points mkdir -p src/javascripts/vue_applications/pages/first_page mkdir -p src/javascripts/vue_applications/common/components mkdir -p src/javascripts/vue_applications/common/modules mkdir webroot touch .babelrc touch webpack.config.js touch src/javascripts/entry_points/first_page.js touch src/javascripts/vue_applications/pages/first_page/App.vue touch src/javascripts/vue_applications/pages/first_page/store.js touch src/javascripts/vue_applications/common/components/TextFieldUnit.vue touch src/javascripts/vue_applications/common/components/TextField.vue touch src/javascripts/vue_applications/common/modules/textFieldUnit.js touch src/javascripts/vue_applications/common/modules/textField.js touch webroot/first_page.html
以下の通り記述します。
{ "presets": [ [ "@babel/preset-env", { "targets": [ "last 2 versions", "ie >= 11" ], "useBuiltIns": "usage", "corejs": 3 } ] ] }
以下の通り記述します。
const path = require('path'); const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { // entry point entry: { 'javascripts/first_page': './src/javascripts/entry_points/first_page.js', }, // 出力するパスは絶対パスで書きます output: { path: `${__dirname}/webroot/packed`, filename: (arg) => { return '[name].bundle.js' }, }, // webpack4はlordersではなくなりました module: { rules: [ // 拡張子.vueのファイルに対する設定 { test: /\.vue$/, use: [ { loader: "vue-loader", options: { loaders: { js: 'babel-loader', }, }, }, ] }, // 拡張子.jsのファイルに対する設定 { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', }, ] }, ] }, // デフォルトの設定値だけでは足りないことについて解決します resolve: { // モジュールを読み込むときに検索するディレクトリの設定 modules: [path.join(__dirname, 'src'), 'node_modules'], // importするときに省略できる拡張子の設定 extensions: ['.js', '.vue'], alias: { // importのファイルパスを相対パスで書かないようにsrcのrootを設定 '@': path.join(__dirname, 'src'), // 例えばmain.js内で `import Vue from 'vue';` と記述したときの`vue`が表すファイルパスを指定 'vue$': 'vue/dist/vue.esm.js' }, }, // プラグインを列挙 plugins: [ new VueLoaderPlugin() ], }
src/javascripts/entry_points/first_page.js
に以下の通り記述します。
import Vue from 'vue'; import Vuex from 'vuex' import store from '@/javascripts/vue_applications/pages/first_page/store' Vue.use(Vuex) import App from '@/javascripts/vue_applications/pages/first_page/App'; Vue.config.productionTip = false; new Vue({ el: '#app', store, template: '<App/>', components: { App }, });
src/javascripts/vue_applications/pages/first_page/App.vue
に以下の通り記述します。
<template> <div id="app"> <TextFieldUnit/> </div> </template> <script> import TextFieldUnit from '@/javascripts/vue_applications/common/components/TextFieldUnit' export default { name: 'app', components: { TextFieldUnit, }, } </script>
src/javascripts/vue_applications/pages/first_page/store.js
に以下の通り記述します。
import Vue from 'vue' import Vuex from 'vuex' import textFieldUnit from '@/javascripts/vue_applications/common/modules/textFieldUnit' Vue.use(Vuex) const state = () => { } const getters = { } const mutations = { } const actions = { } export default new Vuex.Store({ state, getters, mutations, actions, modules: { textFieldUnit, }, })
src/javascripts/vue_applications/common/components/TextFieldUnit.vue
に以下の通り記述します。
<template> <section> <h1>TextFieldUnit</h1> <button @click="increment">count: {{ count }}</button> <TextField /> </section> </template> <script> import { mapState, mapMutations } from 'vuex'; import TextField from '@/javascripts/vue_applications/common/components/TextField'; export default { name: 'TextFieldUnit', components: { TextField, }, computed: { ...mapState('textFieldUnit', [ 'count', ]), }, methods: { ...mapMutations('textFieldUnit', [ 'setState' ]), increment() { this.setState({ key: 'count', value: this.count + 1, }); }, } } </script>
src/javascripts/vue_applications/common/components/TextField.vue
に以下の通り記述します。
<template> <section> <label>TextField: </label> <input :value="value" type="text" @input="onInput" > <div class="test-synced-text"> value: {{ value }} </div> </section> </template> <script> import { mapState, mapMutations } from 'vuex'; export default { name: 'TextField', computed: { ...mapState('textFieldUnit/textField', [ 'value', ]), }, methods: { ...mapMutations('textFieldUnit/textField', [ 'setState', ]), onInput(e) { this.setState({ key: 'value', value: e.target.value, }); }, }, } </script>
src/javascripts/vue_applications/common/modules/textFieldUnit.js
に以下の通り記述します。
import textField from '@/javascripts/vue_applications/common/modules/textField' const state = () => { return { count: 0, } } const getters = { } const actions = { } const mutations = { setState(state, payload) { state[payload.key] = payload.value; }, } export default { // strictとnamespacedをそれぞれtrueにしておきます。 strict: true, namespaced: true, state, getters, actions, mutations, modules: { textField, }, }
src/javascripts/vue_applications/common/modules/textField.js
に以下の通り記述します。
const state = () => { return { value: 'defaultValue', }; } const getters = { } const actions = { } const mutations = { setState(state, payload) { state[payload.key] = payload.value; }, } export default { // strictとnamespacedをそれぞれtrueにしておきます。 strict: true, namespaced: true, state, getters, actions, mutations, }
webroot/first_page.html
に以下の通り記述します。
<!DOCTYPE html> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta charset="utf-8"> <title>vue-tutorial</title> </head> <body> <div id="app"> </div> <script type="text/javascript" src="packed/javascripts/first_page.bundle.js"></script> </body> </html>
以下のような感じになるようにwatchを追記しました。
"scripts": { "watch": "webpack -d --watch", "test": "echo \"Error: no test specified\" && exit 1" },
npm run watch
2020年3月14日にここまでの手順をやり直してみたところ、以下のようなエラーが出ました。
Can't resolve 'core-js/modules/es6.array.iterator'
core.jsのバージョンに関するエラーのようです。
以下のコマンドでインストールします。
npm install --save-dev core-js@3
もう一度トランスパイルしてみたらエラーは解消されていました。
npm run watch
{ "name": "vuex_design", "version": "1.0.0", "description": "vuexを使用する際のディレクトリ構造やコンポーネント設計のベースを作成します。", "main": "index.js", "scripts": { "watch": "webpack -d --watch", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/motomichi-works/vuex_design.git" }, "keywords": [], "author": "", "license": "ISC", "bugs": { "url": "https://github.com/motomichi-works/vuex_design/issues" }, "homepage": "https://github.com/motomichi-works/vuex_design#readme", "devDependencies": { "@babel/cli": "^7.8.4", "@babel/core": "^7.8.7", "@babel/preset-env": "^7.8.7", "babel-loader": "^8.0.6", "core-js": "^3.6.4", "vue-loader": "^15.9.0", "vue-template-compiler": "^2.6.11", "webpack": "^4.42.0", "webpack-cli": "^3.3.11" }, "dependencies": { "vue": "^2.6.11", "vuex": "^3.1.3" } }
webroot/first_page.html
をブラウザで開いて確認します。
今日はここまで。
次回はVuexで作っているアプリケーションをvue-test-utils + jestでテストできるようにしていきます。
業務でIE9に対応したJSを書くことになりました。
babelは導入されていないのですが、つい癖でうっかりES6のシンタックスで書いてしまいます。
それがそのままマージされてしまうとIE9で動かなくなるので、せめてマージされないようにeslintでエラーを出してもらおうと思います。
npm install -g eslint
以下の通り書きました。
{ "env": { "es6": false, "browser": true } }
以下は引用ですが、公式ページによると最近は拡張子を付けた方が良いらしい。
Deprecated - use .eslintrc, which can be either JSON or YAML.
ES6を使わない記述で以下の通りファイルを作成します。
var hoge = { example: function() { console.log('example'); } }
構文チェックを実行します。
eslint sample.js
エラーは出ませんでした。
ES6を使った記述で以下の通りファイルを作成します。
const hoge = { example() { console.log('example'); } }
構文チェックを実行します。
eslint sample_es_6.js
以下のようにエラーが出ました。
2:1 error Parsing error: The keyword 'const' is reserved ✖ 1 problem (1 error, 0 warnings)
constをvarに書き換えて再度実行してみます。
以下のように3行目がエラーになりました。 意図通りの挙動です。
3:10 error Parsing error: Unexpected token ( ✖ 1 problem (1 error, 0 warnings)
例えばjavascriptsというディレクトリにJSファイルを複数入れて、以下のコマンドを実行します。
eslint javascripts/**.js
例えば以下のようにファイル名を指定して実行できます。
eslint -c .eslintrc_for_ie_9.json javascripts/**.js
これであとはCircleCIで実行するだけかしら。
なんだか本当にこれでいいのか心配だけど。
【 xargs 】コマンド――コマンドラインを作成して実行する:Linux基本コマンドTips(176) - @IT
git branch
の実行結果を一つずつ引数にしてくれます。
このときmasterブランチなども一緒に削除されてしまうと思うので、originからcheckoutし直すと良いと思います。
git branch | xargs git branch -d