tree shaking(摇树优化)

概念

  • 1个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到bundle 里面去,tree shaking就是只把用到的 方法打入到 bundle,没用到的方法会在 uglify 阶段被擦除掉
  • production 下 默认开启 tree shaking

    使用

  • mode 设置为 production

  • 要求必须是ES6 的语法,Common.js 的方式不支持
    • ES6 module 引入是静态引入,编译时引入
    • Commonjs 引入是动态引入,执行时引入

Commonjs 引入

  1. let apiList = require('../config/api.js')
  2. if (isDev) {
  3. // 可以动态引入,执行时引入
  4. apiList = require('../config/api_dev.js')
  5. }

ES6 module 引入

  1. import apiList from '../config/api.js'
  2. if (isDev) {
  3. // 编译时报错,只能静态引入
  4. import apiList = from'../config/api_dev.js'
  5. }

开发环境:webpack.config.js文件中添加optimization选项。生产模式:不用配置下面的属性(默认)

  1. optimization: {
  2. usedExports: true
  3. }

package.json文件中添加sideEffects选项

  1. "sideEffects": false, //对所有的模块都进行Tree Shaking

如果需要对某个模块不进行Tree Shaking

  1. "sideEffects": ["@babel/poly-fill"], //该模块不进行Tree Shaking

为什么某些引入模块不希望进行Tree Shaking呢?
下面引入的style.css模块,如果也使用tree shaking,由于css文件没有导出任何模块,那么就有可能在打包的时候该引入模块就被摇晃掉了,导致bug。

  1. import './style.css'
  2. import { add } from './math.js'
  3. add(1, 2)

在package.json中进行配置,即匹配到的任何css、 scss文件都不进行Tree Shaking

  1. "sideEffects": [
  2. "*.scss",
  3. "*.css"
  4. ],

原理

  • 利用 ES6 模块的特点
  • 只能作为模块顶层的语句出现
  • import 的模块名只能是字符串常量
  • import binding是 immutable 的

    DCE

  • 代码不会被执行,不可达到

  • 代码的执行结果不会被用到
  • 代码只会影响死变量 (只写不读)

    Scope Hoisting

    现象

  • 构建后的代码存在大量闭包代码

    导致问题

  • 大量函数闭包包裹代码,导致体积增大(模块越多越明显)

  • 运行代码时创建的函数作用域变多,内存开销变大

    模块转换分析

    分析:

  • 打包出来的是一个LLFE(匿名闭包)(立即执行函数)

  • modules 是一个数组,每一项是一个模块初始化函数
  • _webpack_require 用来加载模块,返回module.exports
  • 通过 _webpack_require_method(0) 启动程序

结论

  • 被webpack 转换后的模块带上一层包裹
  • import 会被转换成 webpack_rquire

image.png

Scope Hoisting 原理

  • 将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重名一些变量,以防止变量名冲突
  • 优势: 减少函数声明代码和内存开销

    使用

  • mode 为 production 默认开启

  • 必须是 ES6 语法,CJS 不知此

    1. new webpack.optimize.ModuleConcatenationPlugin()

    代码分割

    意义

  • 对于大的web应用来讲,将所有的代码都放在一个文件中显然是不够有效的,特别是当你的某些代码块是在某些特殊的时候才会被使用到。webpack有一个功能就是将你的代码库分割成chunks(语块),当代码运行到需要它们的时候再进行加载。

  • 问题:如何在代码分割的时候判断哪些是需要按需加载的呢?这在其他的打包工具中将这些chunks成为layers,rollups或者是fragments。这种特性被叫做代码分割(code splitting)。

使用场景

  • 脚本懒加载,使得初始下载的代码更小

    使用

  • 安装 @bable/plugin-syntax-dynamic-import —save-dev

  • 配置 .babelrc 文件 ```javascript npm install —save-dev @babel/plugin-syntax-dynamic-import@7.2.0

plugins: [‘@babel/plugin-syntax-dynamic-import’]

  1. - 按需加载 .js 文件
  2. ```javascript
  3. import('./a.js').then(()=>{
  4. console.log('a.js is loaded dynamically');
  5. });

ESLint 代码检查

  • 帮助发现代码错误的规则
  • 帮助保持团队的代码保持统一,而不是限制开发体验

    如何落地

  1. 和 CI/CD 系统集成
  2. 和 webpack 集成
    npm i eslint@5.16.0 eslint-plugin-import@2.17.3 eslint-plugin-react@7.13.0 eslint-plugin-jsx-a11y@6.2.1 -D
    npm i eslint-loader@2.1.2 -D
    npm i babel-eslint@10.0.1 -D
    npm i eslint-config-airbnb@17.1.0 -D
    

    打包库 | 组件

服务端渲染(SSR)

参考配置 webpack.ssr.js

存在问题

  • 浏览器的全局变量(nodejs 中没有 document 和 window 对象)
    • 组件适配: 将不兼容的组件根据打包环境进行适配
    • 请求适配: 将 ajax 写法改写成 axios
  • 样式问题(nodejs 无法解析css)
  • 方案1: 服务端打包通过 ignore-loader 忽略掉css 的解析
  • 方案2: style-loader 替换成 isomorphic-style-loader

    优化构建时命令行的显示日志

    stats 配置

  • 在 production 中 直接 添加 stats: “”

  • 在 dev中,在 devServer 中 添加 stats: “”
  • 使用 friendly-errors-webpack-plugin 时,stats 设置成 errors-only

    image.png

    npm i friendly-errors-webpack-plugin@1.7.0 -D
    

    webpack 配置

    const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
    
    plugins: [
     new FriendlyErrorsWebpackPlugin(),
    ],
    

    构建异常和中断处理

    主动捕获并处理构建错误

    参考

  • compiler 在每次构建结束后会触发 done 这个 hook

  • compiler 中 有很多钩子 函数,可参考官方文档 ```javascript // 每次 构建完成后输入 echo $? 获取错误码

// 构建异常捕获,在plugin 中添加即可 function () { this.hooks.done.tap(‘done’, (stats) => { if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf(‘—watch’) === -1) { console.log(‘build error’); process.exit(1) } }) } ```