生产环境

在本指南中,我们将深入一些最佳实践和工具,将站点或应用程序构建到生产环境中。

以下示例来源于 tree shaking开发环境。在继续之前,请确保你已经熟悉这些指南中所介绍的概念/配置。

配置

development(开发环境)production(生产环境) 这两个环境下的构建目标存在着巨大差异。在开发环境中,我们需要:强大的 source map 和一个有着 live reloading(实时重新加载) 或 hot module replacement(热模块替换) 能力的 localhost server。而生产环境目标则转移至其他方面,关注点在于压缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置

虽然,以上我们将生产环境开发环境做了略微区分,但是,请注意,我们还是会遵循不重复原则(Don’t repeat yourself - DRY),保留一个 “common(通用)” 配置。为了将这些配置合并在一起,我们将使用一个名为 webpack-merge 的工具。此工具会引用 “common” 配置,因此我们不必再在环境特定(environment-specific)的配置中编写重复代码。

我们先从安装 webpack-merge 开始,并将之前指南中已经成型的那些代码进行分离:

  1. npm install --save-dev webpack-merge

project

  1. webpack-demo
  2. |- package.json
  3. - |- webpack.config.js
  4. + |- webpack.common.js
  5. + |- webpack.dev.js
  6. + |- webpack.prod.js
  7. |- /dist
  8. |- /src
  9. |- index.js
  10. |- math.js
  11. |- /node_modules

webpack.common.js

  1. + const path = require('path');
  2. + const CleanWebpackPlugin = require('clean-webpack-plugin');
  3. + const HtmlWebpackPlugin = require('html-webpack-plugin');
  4. +
  5. + module.exports = {
  6. + entry: {
  7. + app: './src/index.js'
  8. + },
  9. + plugins: [
  10. + new CleanWebpackPlugin(['dist']),
  11. + new HtmlWebpackPlugin({
  12. + title: 'Production'
  13. + })
  14. + ],
  15. + output: {
  16. + filename: '[name].bundle.js',
  17. + path: path.resolve(__dirname, 'dist')
  18. + }
  19. + };

webpack.dev.js

  1. + const merge = require('webpack-merge');
  2. + const common = require('./webpack.common.js');
  3. +
  4. + module.exports = merge(common, {
  5. + mode: 'development',
  6. + devtool: 'inline-source-map',
  7. + devServer: {
  8. + contentBase: './dist'
  9. + }
  10. + });

webpack.prod.js

  1. + const merge = require('webpack-merge');
  2. + const common = require('./webpack.common.js');
  3. +
  4. + module.exports = merge(common, {
  5. + mode: 'production',
  6. + });

现在,在 webpack.common.js 中,我们设置了 entryoutput 配置,并且在其中引入这两个环境公用的全部插件。在 webpack.dev.js 中,我们将 mode 设置为 development,并且为此环境添加了推荐的 devtool(强大的 source map)和简单的 devServer 配置。最后,在 webpack.prod.js 中,我们将 mode 设置为 production,其中会引入之前在 tree shaking 指南中介绍过的 TerserPlugin

注意,在环境特定的配置中使用 merge() 功能,可以很方便地引用 devprod 中公用的 common 配置。webpack-merge 工具提供了各种 merge(合并) 高级功能,但是在我们的用例中,无需用到这些功能。

npm scripts

现在,我们把 scripts 重新指向到新配置。让 npm start script 中 webpack-dev-server 使用 development(开发环境) 配置文件,而让 npm run build script 使用 production(生产环境) 配置文件:

package.json

  1. {
  2. "name": "development",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "src/index.js",
  6. "scripts": {
  7. - "start": "webpack-dev-server --open",
  8. + "start": "webpack-dev-server --open --config webpack.dev.js",
  9. - "build": "webpack"
  10. + "build": "webpack --config webpack.prod.js"
  11. },
  12. "keywords": [],
  13. "author": "",
  14. "license": "ISC",
  15. "devDependencies": {
  16. "clean-webpack-plugin": "^0.1.17",
  17. "css-loader": "^0.28.4",
  18. "csv-loader": "^2.1.1",
  19. "express": "^4.15.3",
  20. "file-loader": "^0.11.2",
  21. "html-webpack-plugin": "^2.29.0",
  22. "style-loader": "^0.18.2",
  23. "webpack": "^3.0.0",
  24. "webpack-dev-middleware": "^1.12.0",
  25. "webpack-dev-server": "^2.9.1",
  26. "webpack-merge": "^4.1.0",
  27. "xml-loader": "^1.2.1"
  28. }
  29. }

随便运行下这些脚本,然后查看输出结果的变化,然后我们会继续添加一些生产环境配置。

指定 mode

许多 library 通过与 process.env.NODE_ENV 环境变量关联,以决定 library 中应该引用哪些内容。例如,当不处于生产环境中时,某些 library 为了使调试变得容易,可能会添加额外的 log(日志记录) 和 test(测试) 功能。并且,在使用 process.env.NODE_ENV === 'production' 时,一些 library 可能针对具体用户的环境,删除或添加一些重要代码,以进行代码执行方面的优化。从 webpack v4 开始, 指定 mode 会自动地配置 DefinePlugin

webpack.prod.js

  1. const merge = require('webpack-merge');
  2. const common = require('./webpack.common.js');
  3. module.exports = merge(common, {
  4. mode: 'production',
  5. });

技术上讲,NODE_ENV 是一个由 Node.js 暴露给执行脚本的系统环境变量。通常用于决定在开发环境与生产环境(dev-vs-prod)下,server tools(服务期工具)、build scripts(构建脚本) 和 client-side libraries(客户端库) 的行为。然而,与预期相反,无法在构建脚本 webpack.config.js 中,将 process.env.NODE_ENV 设置为 "production",请查看 #2537。因此,在 webpack 配置文件中,process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js' 这样的条件语句,无法按照预期运行。

如果你正在使用像 react 这样的 library,那么在添加此 DefinePlugin 插件后,你应该看到 bundle 大小显著下降。还要注意,任何位于 /src 的本地代码都可以关联到 process.env.NODE_ENV 环境变量,所以以下检查也是有效的:

src/index.js

  1. import { cube } from './math.js';
  2. +
  3. + if (process.env.NODE_ENV !== 'production') {
  4. + console.log('Looks like we are in development mode!');
  5. + }
  6. function component() {
  7. var element = document.createElement('pre');
  8. element.innerHTML = [
  9. 'Hello webpack!',
  10. '5 cubed is equal to ' + cube(5)
  11. ].join('\n\n');
  12. return element;
  13. }
  14. document.body.appendChild(component());

minification(压缩)

设置 production mode 配置后,webpack v4+ 会默认压缩你的代码。

注意,虽然生产环境下默认使用 TerserPlugin ,并且也是代码压缩方面比较好的选择,但是还有一些其他可选择项。以下有几个同样很受欢迎的插件:

如果决定尝试一些其他压缩插件,只要确保新插件也会按照 tree shake 指南中所陈述的具有删除未引用代码(dead code)的能力,以及提供 optimization.minimizer

source mapping(源码映射)

我们鼓励你在生产环境中启用 source map,因为它们对 debug(调试源码) 和运行 benchmark tests(基准测试) 很有帮助。虽然有着如此强大的功能,然而还是应该针对生产环境用途,选择一个可以快速构建的推荐配置(更多选项请查看 devtool)。对于本指南,我们将在生产环境中使用 source-map 选项,而不是我们在开发环境中用到的 inline-source-map

webpack.prod.js

  1. const merge = require('webpack-merge');
  2. const common = require('./webpack.common.js');
  3. module.exports = merge(common, {
  4. mode: 'production',
  5. + devtool: 'source-map'
  6. });

避免在生产中使用 inline-***eval-***,因为它们会增加 bundle 体积大小,并降低整体性能。

最小化 CSS

将生产环境下的 CSS 进行压缩会非常重要,请查看 在生产环境下压缩 章节。

CLI 替代选项

以上所述也可以通过命令行实现。例如,--optimize-minimize 标记将在幕后引用 TerserPlugin。和以上描述的 DefinePlugin 实例相同,--define process.env.NODE_ENV="'production'" 也会做同样的事情。而且,webpack -p 将自动地配置上述这两个标记,从而调用需要引入的插件。

虽然这种简短的方式很好,但通常我们建议只使用配置方式,因为在这两种方式中,配置方式能够更准确地理解现在正在做的事情。配置方式还为可以让你更加细微地控制这两个插件中的其他选项。


贡献人员

SevenOutman SevenOutman bring2dip bring2dip chrisVillanueva chrisVillanueva henriquea henriquea kelset kelset kisnows kisnows markerikson markerikson mehrdaad mehrdaad rajagopal4890 rajagopal4890 redian redian simon04 simon04 skipjack skipjack swapnilmishra swapnilmishra xgirma xgirma xgqfrms xgqfrms