LoginSignup
5
3

More than 5 years have passed since last update.

Sails.jsでwebpack-dev-serverを使ってVue.jsのリビルドを高速化した

Last updated at Posted at 2017-06-10

Webpackの通常のビルドは遅い

Sailsはデフォルトでgrunt-watchタスクを使っているので、最初はこれを利用してファイルを変更するたびにgrunt-webpackを実行してjsをリビルドしていました。
しかし、WebpackでVue.jsのソースを普通にビルドすると、私のローカル環境では15秒以上かかっていました。開発効率を著しく下げていたので高速化する方法を調べました。

webpackのビルド高速化の効果を測ってみたという記事によると、インクリメンタルビルドにすれば早くなりそう。公式のドキュメントによると、インクリメンタルビルドはwebpack-dev-serverの機能らしいので、webpack-dev-serverをsails.jsに導入する方法を調べました。

webpack-dev-serverを既存のサーバーと結合する

webpack-dev-serveはHTML/CSS/JSなどの静的ファイルをサーブするだけのExpressサーバーです。既存のAPIサーバー(今回はSails.js )と結合して開発するための設定が必要です。

webpack-dev-serverからjsを取得する

webpack-dev-serverは、Expressのサーバーをポート8080(デフォルト)で起動して、ビルドしたjsをメモリに保持し、メモリからサーブします。ファイルシステムに書き込まないので通常のビルドに比べて高速です。
しかし、ファイルに書き出されないため、既存のサーバー(Sailsなど)がwebpack-dev-serverでビルドされたコードを取得するには、localhost:8080から取得するように指定する必要があります。

例:

本番用
<script src="/js/app.js"></script>
開発用
<script src="http://localhost:8080/js/app.js"></script>

Sailsでテンプレートエンジンにejsを使っている場合、sail.config.environmentでソースの参照元を分ければOK。

<% if (sails.config.environment === 'development') { %>
  <script src="http://localhost:8080/js/app.js"></script>
<% } else { %>
  <script src="/js/app.js"></script>
<% } %>

contentBaseとoutput.path

Sailsでは、デフォルトで .tmp/public/以下にコンパイルされたアセットファイルを出力し、このディレクトリからファイルをサーブする仕様になっています。よって、webpackのoutputの設定は以下のようになります。

tasks/config/tasks/webpack.js
  output: {
    path: path.join(__dirname, '../../.tmp/public'),
    filename: 'js/[name].js',  // ※この`js/`をpathに含めると動かない!
  },

そして、localhost:8080/js/app.jsへのリクエストが.tmp/public/js/app.jsを返すようにするための設定がcontentBase。

tasks/config/tasks/webpack.js
  grunt.config.set('webpack-dev-server', {
    options: {
      contentBase: '.tmp/public',  // このディレクトリ以下のファイルをサーブする
      inline: true,
      hot: true
    },

outputの設定が非常に気持ち悪いのですが、どうやらoutput.pathがcontentBaseと一致している必要があるようで、js/をfilenameではなくpathの方に含めると、localhost:8080/js/app.jsはファイルとして保存された.tmp/public/js/app.jsを返してしまい、リビルドの内容が反映されませんでした。
webpackの公式ドキュメントにはそんなこと書いてないから、リビルドが反映されない原因を探すのに苦労しました・・・

CORSエラー

ここまで設定してsailsサーバーとwebpack-dev-serverを起動すると、CORSエラー(No 'Access-Control-Allow-Origin' header is present on the requested resource. )が出ました。localhost:1337からlocalhost:8080にリクエストを送っているからですね。以下のようにheadersの設定をdev-serverの設定に追加して解決しました。

tasks/config/tasks/webpack.js
  grunt.config.set('webpack-dev-server', {
    options: {
      contentBase: '.tmp/public',
      headers: {
        'Access-Control-Allow-Origin': '*'  // CORSエラー回避
      }
    },

これでwebpackの設定は基本的に完了です。

HotModuleReplacement

webpack-dev-serverとVue.jsでHotModuleReplacementができます!HotModuleReplacementとは、ファイル変更を検知したら自動的にブラウザのjsファイルをアップデートしてくれるというもの。ブラウザ自体をリフレッシュするのとは違い、フロントエンドで保持してるデータは消えません。

  1. webpack-dev-serverの設定にinline: truehot: trueを追加し、
  2. HotModuleReplacementPluginというプラグインを入れ、
  3. output.publicPathにdev serverのURL
  4. を指定することで使えます。
tasks/config/tasks/webpack.js
  grunt.config.set('webpack-dev-server', {
    options: {
      contentBase: '.tmp/public',
      inline: true,  // inlineモード
      hot: true  // HotModuleReplacementをしますよ、の設定
    },
    dev: {
      webpack: {
        plugins: [
          new webpack.HotModuleReplacementPlugin(),  // HotModuleReplacementに必要なプラグイン
        ],
        output: {
          path: path.join(__dirname, '../../.tmp/public'),
          filename: 'js/build/[name].js',
          publicPath: "http://localhost:8080/"  // dev serverのURL。
        },

これで動いたけど、ブラウザのコンソールでUncaught RangeError: Maximum call stack size exceededのエラーが。直し方わからず、とりあえず実害もなさそうなので、一旦放置してます。わかる方いたら教えてください。

grunt-webpackの設定ファイル

上記の設定を踏まえたgrunt-webpackの設定ファイル例です。

tasks/config/webpack.js
const webpack = require('webpack');
const path = require('path');
const _ = require('lodash');

// プロダクションビルド用の設定。繰り返したくないので変数にまとめておく。
const buildConfig = {
  entry: {
    app: './client/web/app.js',  // ビルド対象のエントリーファイル
  },
  output: {
    path: path.join(__dirname, '../../.tmp/public'),  // 絶対パスが必要。__dirnameは{アプリの絶対パス}/tasks/config
    filename: 'js/[name].js',  // js/はpathには含めちゃダメ
  },
  module: {
    loaders: [
      {test: /\.vue$/, loader: 'vue-loader'},
      {test: /\.js$/, loader: 'babel-loader', query: {presets: ['es2015']}}
    ]
  },
};

module.exports = function (grunt) {
  // webpackの設定
  grunt.config.set('webpack', {
    build: buildConfig,  // 上で作った設定そのまま
  });

  // webpack-dev-serverの設定
  grunt.config.set('webpack-dev-server', {
    options: {
      contentBase: '.tmp/public',  // サーブするディレクトリの設定
      inline: true,  // HMRのため
      hot: true,  // HMRしますよのサイン
      headers: {
        'Access-Control-Allow-Origin': '*'  // CORSエラー対策
      }
    },
    dev: {
      webpack: _.extend(buildConfig, {  // webpackの設定と違うところを設定
        plugins: [
          new webpack.HotModuleReplacementPlugin(),  // HMRのためのプラグイン
        ],
        output: {
          path: path.join(__dirname, '../../.tmp/public'),
          filename: 'js/[name].js',
          publicPath: 'http://localhost:8080/'  // HMRのために必要
        },
        devtool: '#source-map',  // source-mapは本番ビルドには不要
      })
    }
  });

  grunt.registerTask('webpack', [
    'webpack:build'
  ]);
  grunt.registerTask('webpack-dev-server', [
    'webpack-dev-server:dev'
  ]);

  grunt.loadNpmTasks('grunt-webpack');
};

sails lift でwebpack-dev-serverも起動する

最後に、sailsの起動コマンドsails liftでwebapck-dev-serverも起動する設定を行います。
sailsでjsのビルドを担うのはgruntのcompileAssetsタスク。これがプロダクションモードでも開発モードでも呼ばれています。

tasks/register/compileAssets.js
module.exports = function(grunt) {
  grunt.registerTask('compileAssets', [
    'clean:dev',
    'webpack:build',  // webpackのタスクを追加。cleanタスクより後におく。
    'jst:dev',
    'less:dev',
    'copy:dev',
    'coffee:dev'
  ]);
};

開発環境では、webpackタスクの代わりにwebpack-dev-serverタスクを実行したいので、新しいタスクをcompileAssetsDevとして追加します。

tasks/register/compileAssetsDev.js
module.exports = function(grunt) {
  grunt.registerTask('compileAssetsDev', [
    'clean:dev',
    'jst:dev',
    'less:dev',
    'copy:dev',
    'coffee:dev',
    'webpack-dev-server',  // 最後に置かないと後続のタスクが実行されないっぽい
  ]);
};

そしてdefaultタスクでこれを実行します。

tasks/register/default.js
module.exports = function (grunt) {
  grunt.registerTask('default', [
    'compileAssetsDev',
    'linkAssets',
    'watch'
  ]);
};

まとめ

webpack-dev-serverは、設定はハマりポイントが多くて大変でしたが、超便利です(特にHotModuleReload)。後公式ドキュメントがいまいちわかりにくい・・・
もうちょっとWEBに情報が充実してくると使いやすくなると思います。

5
3
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
5
3