生产环境优化 - sideEffects 副作用

sideEffects 这是一个很重要但是容易被忽略的一个字段,错误配置的话会导致出现意料之外的问题。

首先这个字段是被 Webpack 使用的,作用是协助 Webpack 进行 tree shaking

官方文档中将它和 Tree Shaking 放在一起讲,所以容易误解为它们是因果关系,实际上二者没什么关系。

什么是副作用

模块执行时除了导出成员之外所作的事情。

// /src/components/button.js ---------------
export default () => {
  return document.createElement('button')
}

// /src/components/link.js ---------------
export default () => {
  return document.createElement('a')
}
// 添加一个副作用代码
document.write('This is a side effect code')

// /src/components/heading.js ---------------
export default level => {
  return document.createElement('h' + level)
}

// src/components/index.js ---------------
export { default as Button } from './button'
export { default as Link } from './link'
export { default as Heading } from './heading'

// src/index.js ---------------
import { Button } from './components'

document.body.append(Button())

关闭babel的modules配置,optmization只启用usedExports。

// webpack.config.js
module.exports = {
  mode: 'none',
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [ '@babel/preset-env' ]
          },
        },
      },
    ],
  },
  optimization: {
    usedExports: true,
    // 压缩代码
    // minimize: true,
  },
}

打包查看结果,3个模块中的成员正常被标记已使用和未使用,但是/src/comonents/index.js中保留了3个模块的「导入」代码。即使link和headingm没有副作用,导入它们已经没有了意义。

// 包裹/src/components/index.js的函数
(function(module, __webpack_exports__, __webpack_require__) {

  "use strict";
  // 导入button.js
  /* harmony import */ var _button__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
  // 导出button.js
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _button__WEBPACK_IMPORTED_MODULE_0__["a"]; });

  // 虽然没有用到link和heading中的成员,但还是保留了「导入模块行为」的代码
  /* harmony import */ var _link__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
  /* harmony import */ var _heading__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);

})

webpack 和 package.json 中的 sideEffects

webpack通过配置optimization.sideEffectstrue,表示打包时跳过那些没有被使用的且被package.json标记为无副作用的模块。

表现为:

  • webpack打包结果不会保留这些没有意义的「导入模块行为」的代码。
  • webpack打包结果不会保留没有使用(导入)的模块。

开启后,webpack会去package.json中寻找sideEffects字段。

// webpack.config.js
module.exports = {
    optimization: {
    sideEffects: true // 默认false,表示是否移除无副作用的模块
    usedExports: true,
  }  
}

package.json中的sideEffects字段表示,当前项目中的模块是否有副作用,默认为true

{
  "sideEffects": false // 默认true,表示当前项目中的模块是否有副作用
}

webpack找到这个字段,且它定义的如果是false(当前项目中的模块无副作用),那么这些没有被用到的模块就不会被打包。从而实现,一个模块中的成员全都没有被外部使用时,即这个模块没有被使用,这个模块就不会被打包进结果

注意:

package.json和webpack配置文件中的sideEffects虽然同名,但表示的意义不同。

  • package.json的sideEffects:标识当前package.json所影响的项目,当中所有的代码是否有副作用。默认true,表示当前项目中的代码有副作用。
  • webpack配置文件中的sideEffects:开启功能,是否移除无副作用的代码。默认false,表示不移除无副作用的模块。在production模式下自动开启。

webpack不会识别代码是否有副作用,只会读取package.json的sideEffects字段,二者需要配合使用,才能处理无副作用的模块。

sideEffects 使用注意

使用webpack sideEffects功能的前提是,确保代码没有副作用。否则webpack打包时就会误删那些有副作用的代码。

例如导入一个模块,它的内容是立即执行的,而不是通过导出成员等待被调用。

// /src/extend.js
window.onload = () => {
  alert('This is a side effect code')
}

// /src/index.js
import './extend'

当使用sideEffects功能后,这个模块就不会被打包。

多数情况下我们可以直接设置值为 false ,这样 Webpack 就会自动帮助我们删除未被 import 的代码,但是在一些情况下不能这样设置。比如说:

  • 是否会对包之外的对象进行修改,比如说在 window 上挂载属性等等行为
  • 是否在包内 import 了 CSS 文件,尤其对于组件库而言。如有此行为并且将 sideEffects 设置为了 false ,那么用户使用组件库的时候就会出现样式丢失的情况,因为 Webpack 在打包过程中没有把 CSS 文件包含进来

当遇到上述情况时,我们可以将 sideEffects 设置为 true 或者通过类似 sideEffects: ["*.css"] 的方式告知 Webpack 有哪些文件是需要被打包进来的。

// package.json
{
  "sideEffects": [
    "./src/extend.js", // 标识有副作用的文件
    "*.css", // 也可以使用路径通配符
    "style/" // 注意目录必须带后面的/
  ]
}

 

posted @ 2024-11-15 11:57  李小菜丶  阅读(196)  评论(0)    收藏  举报