webpack 可能确实是太难用了,所以 社区也除了 parcel 这样的打包工具, 但 webpack 在大型项目中还拥有不可动摇的地位. 以下最佳实践来自 Google.

插件选项

UglifyJS

压缩 js 代码

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
  ],
};

style-loader

因为 css 代码在引入后, 是一个字符串, uglifyjs 无法压缩, 所以, 需使用 css-loader 去进行压缩

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { minimize: true } },
        ],
      },
    ],
  },
};

DefinePlugin

 // webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': '"production"',
    }),
    new webpack.optimize.UglifyJsPlugin(),
  ],
};

使用 ES-Modle 进行 Tree-Shaking

TreeShaking 是通过 UglifyJs 进行的

Note: In webpack, tree-shaking doesn’t work without a minifier. Webpack just removes export statements for exports that aren’t used; it’s the UglifyJsPlugin that removes unused code. Therefore, if you compile the bundle without the minifier, it won’t get smaller.

You aren’t required to use precisely this plugin though. Any minifier that supports dead code removal (e.g. Babel Minify plugin or Google Closure Compiler plugin) will do the trick.

Warning: Don’t accidentally compile ES modules into CommonJS ones.

If you use Babel with babel-preset-env or babel-preset-es2015, check the settings of these presets. By default, they transpile ES’ import and export to CommonJS’ require and module.exports. Pass the { modules: false } optionto disable this.

图片压缩

url loader

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif)$/,
        loader: 'url-loader',
        options: {
          // Inline files smaller than 10 kB (10240 bytes)
          limit: 10 * 1024,
        },
      },
    ],
  }
};

svg-url-loader

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        loader: 'svg-url-loader',
        options: {
          // Inline files smaller than 10 kB (10240 bytes)
          limit: 10 * 1024,
          // Remove the quotes from the url
          // (they’re unnecessary in most cases)
          noquotes: true,
        },
      },
    ],
  },
};

image-webpack-loader

 // webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/,
        loader: 'image-webpack-loader',
        // This will apply the loader before the other ones
        enforce: 'pre',
      },
    ],
  },
};

ModuleConcatenationPlugin

将模块进行合并

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin(),
  ],
};

externals

外部依赖通用框架

// webpack.config.js
module.exports = {
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM',
  },
};
// webpack.config.js
module.exports = {
  output: { libraryTarget: 'amd' },

  externals: {
    'react': { amd: '/libraries/react.min.js' },
    'react-dom': { amd: '/libraries/react-dom.min.js' },
  },
};

HtmlWebpackPlugin

使用长缓存

// webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    filename: 'bundle.[chunkhash].js',
        // → bundle.8e0d62a03.js
  },
  plugins: [new HtmlWebpackPlugin()],
};

CommonsChunkPlugin

将公共依赖区分打包

// webpack.config.js
module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',

      minChunks: module => module.context &&
        module.context.includes('node_modules'),
    }),

    // This plugin must come after the vendor one (because webpack
    // includes runtime into the last chunk)
    new webpack.optimize.CommonsChunkPlugin({
      name: 'runtime',

      // minChunks: Infinity means that no app modules
      // will be included into this chunk
      minChunks: Infinity,
    }),
  ],
};

将部分代码 inline 到页面

// webpack.config.js
module.exports = {
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'runtime',
      minChunks: Infinity,
      filename: 'runtime.js',
        // → Now the runtime file will be called
        // “runtime.js”, not “runtime.79f17c27b335abc7aaf4.js”
    }),
  ],
};

Analyze Bundle

webpack-dashboard

// webpack.config.js
const DashboardPlugin = require('webpack-dashboard/plugin');

module.exports = {
  plugins: [
    new DashboardPlugin(),
  ],
};

webpack-bundle-analyzer

// webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin(),
  ],
};

References