Vue.jsとwebpackで非SPA環境を構築する

Vue.jsは今までscriptタグ直接組み込みでしか使ったことなかったのですが、
Atomic Designを用いて単一ファイルコンポーネント(*.vue)を作ったりしたいので、
webpackを使ってビルドできる環境を作ろうと思います。
※将来的にはSPAにもチャレンジしたいと思っています。

vue-cliでテンプレートをインストール

#!/bin/bash

# vue-cliをインストール
$ npm i vue-cli -g

# テンプレートをインストール
$ vue init webpack-simple template

? Project name template 
? Project description A Vue.js project
? Author  <>
? License MIT
? Use sass? Yes # ←sassを使用する

   vue-cli · Generated "template".

   To get started:

     cd template
     npm install
     npm run dev


$ cd template
$ npm i

# その他パッケージをインストール(要らないものがあるかもしれない)
$ npm i axios bootstrap font-awesome jquery vuelidate --save
$ npm i clean-webpack-plugin extract-text-webpack-plugin style-loader url-loader vue-style-loader --save-dev

webpack.vendor.config.jsを作成

Vue.jsやBootstrap等は共通なので、DLLバンドル用のconfigを作成します。

// ほぼ、ASP.NET Core With Vueのパクリです。
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = (env) => {
    const isDevBuild = !(process.env.NODE_ENV === 'production');
    const extractCSS = new ExtractTextPlugin(isDevBuild ? '[name].bundle.css' : '[name].bundle.[hash].css');

    return [{
        stats: { modules: false },
        resolve: {
            alias: {
                'vue$': 'vue/dist/vue.esm.js'
            },
            extensions: ['*', '.js', '.vue', '.json']
        },
        entry: {
            vendor: [
                'bootstrap',
                'bootstrap/dist/css/bootstrap.css',
                'jquery',
                'vue',
                'axios',
                "vuelidate",
                "font-awesome/css/font-awesome.css"
            ]
        },
        module: {
            rules: [
                { test: /\.css(\?|$)/, use: extractCSS.extract({ use: isDevBuild ? 'css-loader' : 'css-loader?minimize' }) },
                //{ test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' },
                { test: /\.png(\?|$)/, use: 'url-loader?limit=100000' },
                { test: /\.(woff|woff2|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, loader: "file-loader", options: { name: '/[name].[ext]?[hash]' } },
            ]
        },
        output: {
            path: path.join(__dirname, './dist/vendor'),
            publicPath: '/dist/vendor',
            filename: isDevBuild ? '[name].bundle.js' : '[name].bundle.[hash].js',
            library: '[name]_[hash]'
        },
        plugins: [
            new CleanWebpackPlugin(['dist/vendor']),
            extractCSS,
            new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': isDevBuild ? '"development"' : '"production"'
            }),
            new webpack.DllPlugin({
                path: path.join(__dirname, 'dist/vendor', '[name]-manifest.json'),
                name: '[name]_[hash]'
            })
        ].concat(isDevBuild ? [] : [
            new webpack.optimize.UglifyJsPlugin({
                sourceMap: true,
                compress: {
                    warnings: false
                }
            })
        ]),
        devtool: isDevBuild ? '#eval-source-map' : '#source-map'
    }];
};

webpack.config.jsを編集

順を追って書きたかったけど、いろいろ試していて結果的にこうなったという感じなので、
怪しい箇所はおいおい直していきます。

const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');

// ASP.NET MVC(.NET Framework 4.7.1)を使用しています。
// エントリーポイントのキーは「{コントローラ名}.{アクション名}」としていますが、
// _Layout.cshtmlで<link>タグ、<script>タグに埋め込むファイル名を解決するためです。
const entries = {
  'Home.Index': './Views/Home/Index.js'
};

// 以下、ASP.NET Core With Vueの書き方を参考にしています。
// 出力するファイル名はproductionの時だけハッシュを付けています。
module.exports = (env) => {
  const isDevBuild = !(process.env.NODE_ENV === 'production');
  const extractCSS = new ExtractTextPlugin(isDevBuild ? './css/[name].bundle.css' : './css/[name].bundle.[hash].css');

  return [{
    entry: entries,
    output: {
      path: path.resolve(__dirname, './dist'),
      publicPath: '/dist/',
      filename: isDevBuild ? 'js/[name].bundle.js' : 'js/[name].bundle.[hash].js'
    },
    module: {
      rules: [
        {
          test: /\.vue$/,
          loader: 'vue-loader',
          options: {
            loaders: {
              'css': ExtractTextPlugin.extract({
                use: 'css-loader',
                fallback: 'vue-style-loader'
              }),
              'scss': ExtractTextPlugin.extract({
                use: 'css-loader!sass-loader',
                fallback: 'vue-style-loader'
              })
            }
          }
        },
        {
          test: /\.js$/,
          loader: 'babel-loader',
          exclude: /node_modules/
        },
        {
          test: /\.css/,
          use: ExtractTextPlugin.extract({
            fallback: "style-loader",
            use: isDevBuild ? 'css-loader' : 'css-loader?minimize'
          })
        },
        {
          test: /\.(png|jpg|gif|svg)$/,
          loader: 'file-loader',
          options: {
            name: './img/[name].[ext]?[hash]'
          }
        }
      ]
    },
    resolve: {
      alias: {
        'vue$': 'vue/dist/vue.esm.js'
      },
      extensions: ['*', '.js', '.vue', '.json']
    },
    devServer: {
      historyApiFallback: true,
      noInfo: true,
      overlay: true
    },
    performance: {
      hints: false
    },
    plugins: [
      new CleanWebpackPlugin(['dist/js/*.*', 'dist/css/*.*', 'dist/img/*.*']),
      new webpack.DefinePlugin({
        'process.env': {
          NODE_ENV: JSON.stringify(isDevBuild ? 'development' : 'production')
        }
      }),
      new webpack.DllReferencePlugin({
        context: __dirname,
        manifest: require('./dist/vendor/vendor-manifest.json')
      }),
      extractCSS
    ].concat(isDevBuild ? [] : [
      new webpack.optimize.UglifyJsPlugin({
        sourceMap: true,
        compress: {
          warnings: false
        }
      }),
      new webpack.LoaderOptionsPlugin({
        minimize: true
      })
    ]),
    devtool: isDevBuild ? '#eval-source-map' : '#source-map'
  }];
};

ビルド用スクリプトを追加

package.jsonに以下を追加します。
Visual Studioから使うのでDebug構成=development、Release構成=productionとしています。

  // webpack-dev-serverは結局使わないとしても、watchできるようにはしておいた方がいいかな。。
  "scripts": {
    "Debug-vendor": "cross-env NODE_ENV=development webpack --progress --hide-modules --display-error-details --config webpack.vendor.config.js",
    "Release-vendor": "cross-env NODE_ENV=production webpack --progress --hide-modules --config webpack.vendor.config.js",
    "Debug": "cross-env NODE_ENV=development webpack --progress --hide-modules --display-error-details",
    "Release": "cross-env NODE_ENV=production webpack --progress --hide-modules",
  }

ASP.NET MVCプロジェクトのビルド前イベントのコマンドラインに以下を追加します。
これで、ビルド時に常にwebpackが実行されることになります。
vendorは常に実行する必要もないのですが。。。

npm run $(ConfigurationName)-vendor
npm run $(ConfigurationName)

一旦ここまで
まだ理解できてないことが多いので、都度記事は修正します。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください