20
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

スクラッチからWebpack+Babel+Vue.jsを使う

Last updated at Posted at 2020-03-08

Vue CLI も Rails+Webpacker も使わずに、Vue.js(単一ファイルコンポーネント)を試す方法です。

スクラッチから作るのが好きな人、WebpackとBabelが吐いたコードを眺めたい人はどうぞ。

ソースコード: https://github.com/kazubon/webpack-babel-vuejs

準備

てきとうなディレクトリを作り、yarnで必要なモジュールをインストールします。

% mkdir scratch && cd scratch
% yarn add @babel/core @babel/preset-env babel-loader core-js vue vue-loader vue-template-compiler webpack webpack-cli

webpack-dev-server は入れずに手動でコンパイルします。

(2020-09-28更新: dependencies と devDependencies を分けるのやめ。)

最終的なディレクトリとファイルの配置は次のようになります。

- dist
  - app.js
  - app.js.map
- node_modules
- src
  - app.js
  - hello.vue
- babel.config.js
- index.html
- package.json
- wbpack.config.js
- yarn.lock

Vueアプリケーション

HTMLと簡単なVueアプリケーションを書きます。特に説明は要らないかと思います。

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>てすと</title>
    <script src="dist/app.js"></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
src/app.js
import Vue from 'vue';
import Hello from './hello.vue';

document.addEventListener('DOMContentLoaded', () => {
  new Vue(Hello).$mount('#app');
});
src/hello.vue
<template>
  <div>
    <h1>{{message}}</h1>
    <p><input v-model="message"></p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: ''
    };
  },
  created() {
    this.message = 'hello';
  }
}
</script>

webpackでコンパイル

webpack.config.js を書きます。src/app.js が dist/app.js に出力されるようにします。

webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const path = require('path');

const env = process.env.NODE_ENV || 'development';

module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'app.js',
    path: path.resolve(__dirname, 'dist'),
  },
  mode: env,
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

2020-12-11 追記: 単一ファイルコンポーネントではなく、HTMLに埋め込まれたテンプレートを使うには、コンパイラ入りのVue.jsが必要です。コンパイラ入りを使うには、resolve.aliasを追加して、vue.esm.js を指定します(参照: さまざまなビルドについて)。

webpack.config.js
module.exports = {
// 略
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  }
}

package.json に scripts.build を追加すると、yarnコマンドでwebpackを呼び出せます。

package.json
{
  "scripts": {
    "build": "webpack --config=webpack.config.js"
  },
  "dependencies": {
    "@babel/core": "^7.11.6",
    "@babel/preset-env": "^7.11.5",
    "babel-loader": "^8.1.0",
    "core-js": "^3.6.5",
    "vue": "^2.6.12",
    "vue-loader": "^15.9.3",
    "vue-template-compiler": "^2.6.12",
    "webpack": "^4.44.2",
    "webpack-cli": "^3.3.12"
  }
}

yarn build とすると、webpackが dist/app.js を生成します。

% yarn build

環境変数 NODE_ENV を production にすると、本番環境用に圧縮された dist/app.js ができます。

% NODE_ENV=production yarn build

index.html を ブラウザーで開いてVueアプリケーションが動けば成功です。

devtool オプション

さて、出来上がった dist/app.js を覗いてみますと、次のような感じで eval が埋め込まれていて、どう変換されたのかよく分からない。

dist/app.js
eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n//\n/* harmony ...

webpack.config.js で devtool オプションを指定すると、出力の形式を変えられるらしい。どういう値を指定できるかは、Devtool | webpack に一覧があります。とりあえず source-map を指定します。

webpack.config.js
module.exports = {
  // 略
  devtool: 'source-map',
  // 略
}

コンパイルすると dist/app.js が読めるようになります。

dist/app.js
/* harmony default export */ __webpack_exports__["default"] = ({
  data() {
    return {
      message: ''
    };
  },

@babel/preset-envの設定

dist/app.js をよく見ると、アロー関数などES2015のシンタックスがそのまま埋め込まれています。Babelを使ってIE 11対応の形に変換したい。

dist/app.js
document.addEventListener('DOMContentLoaded', () => {
  new vue__WEBPACK_IMPORTED_MODULE_0__["default"](_hello_vue__WEBPACK_IMPORTED_MODULE_1__["default"]).$mount('#app');
});

webpack.config.js で .js の loader の設定を次のように変え、@babel/preset-env を指定します。

webpack.config.js
module.exports = {
  // 略
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  },
  // 略
}

コンパイルしたものを見ると、アロー関数がfunctionに変換されていました。

dist/app.js
document.addEventListener('DOMContentLoaded', function () {
  new vue__WEBPACK_IMPORTED_MODULE_0__["default"](_hello_vue__WEBPACK_IMPORTED_MODULE_1__["default"]).$mount('#app');
});

Babel の設定ファイル babel.config.js を使うこともできます。これをアプリケーションのルートに置いておくと設定が反映されます。

api.cacheの設定をしないとエラーが出るので、api.cache(true) としてあります。この設定の意味は分かりません。

babel.config.js
module.exports = api => {
  api.cache(true);

  return {
    presets: [
      [
        "@babel/preset-env",
        {
          targets: { ie: "11" },
          useBuiltIns: "entry",
          corejs: 3
        }
      ]
    ]
  }
}

webpack.config.js のほうは元に戻します。

webpack.config.js
module.exports = {
  // 略
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        loader: 'babel-loader'
      }
    ]
  },
  // 略
}

preset_envのマニュアル に従い、IE 11がサポートしていない新しいメソッドを使うときは、2つのモジュール core-js/stable と regenerator-runtime/runtime をインポートしておきます。

src/app.js
import "core-js/stable";
import "regenerator-runtime/runtime";

import Vue from 'vue';
import Hello from './hello.vue';

document.addEventListener('DOMContentLoaded', () => {
  new Vue(Hello).$mount('#app');
});

2020-03-19追記: Vue.jsはデフォルトでIE 11に対応しているので、この程度のサンプルなら core-js/stable と regenerator-runtime/runtime は要りません。IEが対応していないJavaScriptのメソッドや組み込みクラスを使うときは core-js、asyncとawaitを使うときは regenerator-runtime が必要です。

以上です。

Vue 3版はこちら: スクラッチからWebpack+Babel+Vue 3を使う

参考にしたページ:

20
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?