返回介绍

七、开发和生产环境的构建配置差异

发布于 2024-09-07 12:24:19 字数 8527 浏览 0 评论 0 收藏 0

  • 我们在日常的前端开发工作中,一般都会有两套构建环境:一套开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印 debug 信息,包含 sourcemap 文件
  • 另外一套构建后的结果是直接应用于线上的,即代码都是压缩后,运行时不打印 debug 信息,静态文件不包括 sourcemap 的。有的时候可能还需要多一套测试环境,在运行时直接进行请求 mock 等工作
  • webpack 4.x 版本引入了 mode 的概念,在运行 webpack 时需要指定使用 productiondevelopment 两个 mode 其中一个,这个功能也就是我们所需要的运行两套构建环境的能力。

7.1 在配置文件中区分 mode

之前我们的配置文件都是直接对外暴露一个 JS 对象,这种方式暂时没有办法获取到 webpackmode 参数,我们需要更换一种方式来处理配置。根据官方的文档多种配置类型,配置文件可以对外暴露一个函数,因此我们可以这样做

module.exports = (env, argv) => ({
  // ... 其他配置
  optimization: {
    minimize: false,
    // 使用 argv 来获取 mode 参数的值
    minimizer: argv.mode === 'production' ? [
      new UglifyJsPlugin({ /* 你自己的配置 */ }), 
      // 仅在我们要自定义压缩配置时才需要这么做
      // mode 为 production 时 webpack 会默认使用压缩 JS 的 plugin
    ] : [],
  },
})...

这样获取 mode 之后,我们就能够区分不同的构建环境,然后根据不同环境再对特殊的 loaderplugin 做额外的配置就可以了

  • 以上是 webpack 4.x 的做法,由于有了 mode 参数,区分环境变得简单了。不过在当前业界,估计还是使用 webpack 3.x 版本的居多,所以这里也简单介绍一下 3.x 如何区分环境

webpack 的运行时环境是 Node.js ,我们可以通过 Node.js 提供的机制给要运行的 webpack 程序传递环境变量,来控制不同环境下的构建行为。例如,我们在 npm 中的 scripts 字段添加一个用于生产环境的构建命令…

{
  "scripts": {
    "build": "NODE_ENV=production webpack",
    "develop": "NODE_ENV=development webpack-dev-server"
  }
}...

然后在 webpack.config.js 文件中可以通过 process.env.NODE_ENV 来获取命令传入的环境变量

const config = {
  // ... webpack 配置
}

if (process.env.NODE_ENV === 'production') {
  // 生产环境需要做的事情,如使用代码压缩插件等
  config.plugins.push(new UglifyJsPlugin())
}

module.exports = config...

7.2 运行时的环境变量

我们使用 webpack 时传递的 mode 参数,是可以在我们的应用代码运行时,通过 process.env.NODE_ENV 这个变量获取的。这样方便我们在运行时判断当前执行的构建环境,使用最多的场景莫过于控制是否打印 debug 信息…

  • 下面这个简单的例子,在应用开发的代码中实现一个简单的 console 打印封装
export default function log(...args) {
  if (process.env.NODE_ENV === 'development' && console && console.log) {
    console.log.apply(console, args)
  }
}...

同样,以上是 webpack 4.x 的做法,下面简单介绍一下 3.x 版本应该如何实现。这里需要用到 DefinePlugin 插件,它可以帮助我们在构建时给运行时定义变量,那么我们只要在前面 webpack 3.x 版本区分构建环境的例子的基础上,再使用 DefinePlugin 添加环境变量即可影响到运行时的代码…

module.exports = {
  // ...
  // webpack 的配置

  plugins: [
    new webpack.DefinePlugin({
      // webpack 3.x 的 process.env.NODE_ENV 是通过手动在命令行中指定 NODE_ENV=... 的方式来传递的
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    }),
  ],
}...

7.3 常见的环境差异配置

常见的 webpack 构建差异配置

  • 生产环境可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件
  • 生产环境需要压缩 HTML/CSS/JS 代码
  • 生产环境需要压缩图片
  • 开发环境需要生成 sourcemap 文件
  • 开发环境需要打印 debug 信息
  • 开发环境需要 live reload 或者 hot reload 的功能…

webpack 4.xmode 已经提供了上述差异配置的大部分功能, modeproduction 时默认使用 JS 代码压缩,而 modedevelopment 时默认启用 hot reload ,等等。这样让我们的配置更为简洁,我们只需要针对特别使用的 loaderplugin 做区分配置就可以了…

  • webpack 3.x 版本还是只能自己动手修改配置来满足大部分环境差异需求,所以如果你要开始一个新的项目,建议直接使用 webpack 4.x 版本

7.4 拆分配置

前面我们列出了几个环境差异配置,可能这些构建需求就已经有点多了,会让整个 webpack 的配置变得复杂,尤其是有着大量环境变量判断的配置。我们可以把 webpack 的配置按照不同的环境拆分成多个文件,运行时直接根据环境变量加载对应的配置即可。基本的划分如下…

  • webpack.base.js :基础部分,即多个文件中共享的配置
  • webpack.development.js :开发环境使用的配置
  • webpack.production.js :生产环境使用的配置
  • webpack.test.js :测试环境使用的配置…

如何处理这样的配置拆分

首先我们要明白,对于 webpack 的配置,其实是对外暴露一个 JS 对象,所以对于这个对象,我们都可以用 JS 代码来修改它,例如

const config = {
  // ... webpack 配置
}

// 我们可以修改这个 config 来调整配置,例如添加一个新的插件
config.plugins.push(new YourPlugin());

module.exports = config;...

因此,只要有一个工具能比较智能地合并多个配置对象,我们就可以很轻松地拆分 webpack 配置,然后通过判断环境变量,使用工具将对应环境的多个配置对象整合后提供给 webpack 使用。这个工具就是 webpack-merge

  • 我们的 webpack 配置基础部分,即 webpack.base.js 应该大致是这样的
module.exports = {
  entry: '...',
  output: {
    // ...
  },
  resolve: {
    // ...
  },
  module: {
    // 这里是一个简单的例子,后面介绍 API 时会用到
    rules: [
      {
        test: /\.js$/, 
        use: ['babel'],
      },
    ],
    // ...
  },
  plugins: [
    // ...
  ],
}...

然后 webpack.development.js 需要添加 loaderplugin ,就可以使用 webpack-mergeAPI ,例如

const { smart } = require('webpack-merge')
const webpack = require('webpack')
const base = require('./webpack.base.js')

module.exports = smart(base, {
  module: {
    rules: [
      // 用 smart API,当这里的匹配规则相同且 use 值都是数组时,smart 会识别后处理
      // 和上述 base 配置合并后,这里会是 { test: /\.js$/, use: ['babel', 'coffee'] }
      // 如果这里 use 的值用的是字符串或者对象的话,那么会替换掉原本的规则 use 的值
      {
        test: /\.js$/,
        use: ['coffee'],
      },
      // ...
    ],
  },
  plugins: [
    // plugins 这里的数组会和 base 中的 plugins 数组进行合并
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    }),
  ],
})...

可见 webpack-merge 提供的 smart 方法,可以帮助我们更加轻松地处理 loader 配置的合并。 webpack-merge 还有其他 API 可以用于自定义合并行为 https://github.com/survivejs/webpack-merge

7.5 完整代码

webpack.config.js

module.exports = function(env, argv) {
  return argv.mode === 'production' ?
    require('./configs/webpack.production') :
    require('./configs/webpack.development')
}

configs/webpack.base.js

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

module.exports = {
  entry: './src/index.js',

  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js',
  },

  module: {
    rules: [
      {
        test: /\.jsx?/,
        include: [
          path.resolve(__dirname, '../src'),
        ],
        use: 'babel-loader',
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader'
          },
        ],
      },
    ],
  },

  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html', // 配置输出文件名和路径
      template: 'src/index.html', // 配置文件模板
    }),
  ],
}

configs/webpack.development.js

const webpack = require('webpack')
const merge = require('webpack-merge')
const baseConfig = require('./webpack.base')

const config = merge.smart(baseConfig, {
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          'less-loader'
        ],
      },
    ],
  },

  devServer: {
    port: '1234',
    before(app){
      app.get('/api/test.json', function(req, res) {
        res.json({ code: 200, message: 'hello world' })
      })
    },
  },
})

config.plugins.push(
  new webpack.DefinePlugin({
    __DEV__: JSON.stringify(true),
  })
)

module.exports = config

configs/webpack.production.js

const merge = require('webpack-merge')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const baseConfig = require('./webpack.base')

const config = merge.smart(baseConfig, {
  module: {
    rules: [
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            {
              loader: 'css-loader',
              options: {
                minimize: true
              }
            },
            'less-loader',
          ],
        }),
      },
    ],
  }
})

config.plugins.push(new ExtractTextPlugin('[name].css'))

module.exports = config

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文