Motomichi Works Blog

モトミチワークスブログです。その日学習したことについて書いている日記みたいなものです。

Vuexを使用する基本的なビルド環境を構築する | webpack4.x+babel7+vue.js 2.x 環境構築 2019年3月版 ステップ0001

はじめに

Windowsでもできます。

MacUbuntuなどでもできると思います。

これから何回かに分けてnpm initするところから、以下のようなことをできる環境を構築していきます。

  • webpack4 + babel7 を使ってビルド
  • vue-test-utils + jest を使ってテスト実行
  • eslintを使って .vue と .js の構文チェック

今日の環境

  • Windows10 Home
  • node v8.11.1
  • npm 5.8.0

プロジェクトディレクトリを作成

mkdir webpack_4_vue_introduction

移動してnpm init する

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

今回webpack_4_vue_introductionディレクトリ内に作成するディレクトリ構造とファイル

./
├── 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の場合

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

macの場合

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

.babelrcの編集

以下の通り記述します。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": [
          "last 2 versions",
          "ie >= 11"
        ],
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ]
}

webpack.config.jsの編集

以下の通り記述します。

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()
  ],
}

first_page.jsの編集

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 },
});

App.vueの編集

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>

store.jsの編集

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,
  },
})

TextFieldUnit.vueの編集

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>

TextField.vueの編集

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>

textFieldUnit.jsの編集

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,
  },
}

textField.jsの編集

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,
}

first_page.htmlの編集

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>

package.jsonのscriptsに追記する

以下のような感じになるようにwatchを追記しました。

  "scripts": {
    "watch": "webpack -d --watch",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

watchしてトランスパイルしてみる

npm run watch

2020年3月14日追記

2020年3月14日にここまでの手順をやり直してみたところ、以下のようなエラーが出ました。

Can't resolve 'core-js/modules/es6.array.iterator'

core.jsのバージョンに関するエラーのようです。

以下のコマンドでインストールします。

npm install --save-dev core-js@3

もう一度トランスパイルしてみたらエラーは解消されていました。

npm run watch

ここまでやった段階のpackage.jsonの内容

{
  "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をブラウザで開いて確認します。

  • JSエラーが出ないこと
  • ボタンをクリックするとカウントアップすること
  • テキストフィールドを編集するとその下のテキストも一緒に変化すること

今日はここまで。

次回はVuexで作っているアプリケーションをvue-test-utils + jestでテストできるようにしていきます。

続きはこちら

motomichi-works.hatenablog.com