# 高级使用场景

# 性能、体积优化

# 代码压缩

# 提取公共代码、文件

使用 html-webpack-externals-plugin 将 React 提取到 CDN
const path = require('path')
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-react']
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin(),
    new HtmlWebpackExternalsPlugin({
      externals: [
        {
          module: 'react',
          entry: 'https://unpkg.com/react@17/umd/react.production.min.js',
          global: 'React',
        },
        {
          module: 'react-dom',
          entry: 'https://unpkg.com/react-dom@17/umd/react-dom.production.min.js',
          global: 'ReactDOM',
        }
      ]
    })
  ]
}
const React = require('react')
const ReactDOM = require('react-dom')

const root = document.createElement('div')
document.body.appendChild(root)

const App = () => {
  const msg = 'world'
  return <h1>hello {msg}</h1>
}

ReactDOM.render(<App />, root)
使用 SplitChunksPlugin 分离公共代码
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode: 'development',
  devtool: false,
  entry: {
    app: './src/index.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-react']
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin(),
    new CleanWebpackPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 0,
      minRemainingSize: 0,
      maxSize: 0,
      minChunks: 2,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'all',
          minChunks: 2
        }
      }
    }
  }
}

# 图片压缩

# 初级分析:Webpack stats

# 速度优化:speed-measure-webpack-plugin

# 体积优化:webpack-bundle-analyzer

# 工程、效率

# 文件监听、自动刷新、HMR

【实现文件监听自动构建】在命令行参数加上 --watch 或在配置文件加上 watch: true 可以实现文件变动的时候自动重新构建,但是网页还是需要手动刷新。

【实现浏览器自动刷新】使用 webpack-dev-server 可以在构建完成后自动刷新浏览器,但刷新后页面的一些状态都会被重置,开发体验并不是最好。

【使用 HMR】HMR 即实现页面的局部刷新,而不是全页面刷新,可以保留页面上的数据状态。将配置文件中 devServer 的 hot 字段配置设为 true 即可打开 HMR 功能。

如果你使用了 style-loader,那么修改 css 之后可以体验到 HMR。如果你使用的是 Vue、React、Angular 这样的框架,也能体验到 HMR。其它情况基本体验不到 HMR,这背后的原因比较复杂,我们后续会继续介绍 HMR 的原理。

启用 HMR 的配置文件如下:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  devServer: {
    contentBase: './dist',
    hot: true
  },
  mode: 'development',
  entry: './src/index.js',
  devtool: false,
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader' ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin()
  ],
}

从 Webpack 5 开始,运行 webpack-dev-server 的方式为:webpack serve

# 多进程构建

# 多进程压缩代码

# 利用缓存提升第二次构建速度

# 服务端渲染 (SSR)

在 CSR 中,React 通过 ReactDOM.render() 将组件渲染到 DOM 上。而在 SSR 中,React 通过 renderToString() 将组件渲染成字符串。

# 持续集成、自动化

# 持续集成

CI 系统需要知道 Webpack 命令是否执行成功,一般来说命令行程序退出码非零表示程序出错。在 Linux Shell 下可以用 echo $? 命令打印上一个进程的退出码。在 Node.js 中可以通过 process.exit() 指定错误码并退出进程。

在 Webpack 4 之前的版本,不管成功失败错误码都是 0,因此我们要编写插件来实现。在 Webpack 4 之后虽然支持错误码了,但如果我们想根据不同的错误执行自定义功能的话也可以编写插件。例如可以编写插件让 CI 系统实现错误日志的上报。

编写插件的思路是在 done 钩子 (opens new window) 下添加回调函数,回调函数的入参是 stats (opens new window)

# 自动发包