乐趣区

了不起的-Webpack-Scope-Shaking-学习指南

最近原创文章????:

  • 《1.2w 字 | 初中级前端 JavaScript 自测清单 – 1》
  • 《了不起的 Webpack HMR 学习指南(含源码剖析)》
  • 《了不起的 Webpack 构建流程学习指南》
  • 《你不晓得的 WeakMap》番外篇
  • 《你不晓得的 Blob》番外篇
  • 《了不起的 tsconfig.json 指南》
  • 《200 行 JS 代码,带你实现代码编译器》

一、什么是 Scope Hoisting

Scope Hoisting 是 webpack3 的新性能,直译为 “作用域晋升 “,它能够让 webpack 打包进去的 代码文件更小 运行更快

在 JavaScript 中,还有“变量晋升”和“函数晋升”,JavaScript 会将变量和函数的申明晋升到以后作用域顶部,而“作用域晋升”也相似,webpack 将引入到 JS 文件“晋升到”它的引入者的顶部。

首先回顾下在没有 Scope Hoisting 时用 webpack 打包上面两个文件:

// main.js
export default "hello leo~";

// index.js
import str from "./main.js";

应用 webpack 打包后输入文件内容如下:

[(function (module, __webpack_exports__, __webpack_require__) {var __WEBPACK_IMPORTED_MODULE_0__util_js__ = __webpack_require__(1);
    console.log(__WEBPACK_IMPORTED_MODULE_0__util_js__["a"]);
  }),
  (function (module, __webpack_exports__, __webpack_require__) {__webpack_exports__["a"] = ('hello leo~');
  })
]

再开启 Scope Hoisting 后,雷同源码打包输入后果变为:

[(function (module, __webpack_exports__, __webpack_require__) {var util = ('hello leo~');
    console.log(util);
  })
]

比照两种打包形式输入的代码,咱们能够看出,启用 Scope Hoisting 后,函数申明变成一个,main.js 中定义的内容被间接注入到 main.js 对应模块中,这样做的益处:

  • 代码体积更小,因为函数申明语句会产生大量代码,导致包体积增大(模块越多越显著);
  • 代码在运行时因为创立的函数作用域更少,内存开销也随之变小

二、webpack 模块机制

咱们应用上面 webpack.config.js 配置,打包来看看 webpack 模块机制:

// webpack.config.js
const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist'),
    },
    mode: 'none',
    optimization: {usedExports: true,},
};

打包后输入后果(精简后):

通过剖析,咱们能够得出以下论断:

  • webpack 打包输入打是一个 IIFE(匿名闭包);
  • modules  是一个数组,每一项是一个模块初始化函数;
  • 应用 __webpack_require() 来家在模块,返回 module.exports
  • 通过 __webpack_require__(__webpack_require__.s = 0); 启动程序。

三、Scope Hoisting 原理

Scope Hoisting 的实现原理其实很简略:剖析出模块之间的依赖关系,尽可能将打散的模块合并到一个函数中,前提是不能造成代码冗余。因而 只有那些被援用了一次的模块能力被合并

因为 Scope Hoisting 须要剖析出模块之间的依赖关系,因而源码 必须采纳 ES6 模块化语句,不然它将无奈失效。起因和 4 -10 应用 TreeShaking 中介绍的相似。

四、Scope Hoisting 应用形式

1. 主动启用

在 webpack 的 mode 设置为 production 时,会默认主动启用 Scope Hooting。

// webpack.config.js

// ...
module.exports = {
    // ...
    mode: "production"
};

2. 手动启用

在 webpack 中曾经内置 Scope Hoisting,所以用起来很简略,只须要配置 ModuleConcatenationPlugin 插件即可:

// webpack.config.js

// ...
const webpack = require('webpack');
module.exports = {
    // ...
    plugins: [new webpack.optimize.ModuleConcatenationPlugin()
    ]
};

思考到 Scope Hoisting 以来 ES6 模块化语法,而当初很多 npm 包的第三方库还是应用 CommonJS 语法,为了充分发挥 Scope Hoisting 成果,咱们能够减少以下 mainFields 配置:

// webpack.config.js

// ...
const webpack = require('webpack');
module.exports = {
    // ...
    resolve: {
        // 针对 npm 中的第三方模块优先采纳 jsnext:main 中指向的 ES6 模块化语法的文件
        mainFields: ['jsnext:main', 'browser', 'main']
    },
    plugins: [new webpack.optimize.ModuleConcatenationPlugin()
    ]
};

针对非 ES6 模块化语法的代码,webpack 会降级解决不应用 Scope Hoisting 优化,咱们能够在 webpack 命令上减少 --display-optimization-bailout 参数,在输入的日志查看哪些代码做了降级解决:

// package.json
{
  // ...
  "scripts": {"build": "webpack --display-optimization-bailout"}
}

咱们写个简略示例代码:

// index.js
import str from "./main.js";
const {name} = require('./no-es6.js');

// main.js
export default "hello leo~";

// no-es6.js
module.exports = {name : "leo"}

接着打包测试,能够看到控制台输入上面日志:

输入的日志中 ModuleConcatenation bailout 通知咱们哪些文件因为什么起因导致降级解决了。

五、总结

本文次要和大家一起回顾了 Scope Hoisting 基本概念,应用形式和应用后成果比照,心愿大家不要只停留在会用 webpack,也要看看其中一些不常见的常识,比方本文介绍的 Scope Hoisting,它对咱们我的项目优化十分有帮忙,但平时又很少会去留神。

六、参考文章

  • 《通过 Scope Hoisting 优化 Webpack 输入》
  • 《webpack 的 scope hoisting 是什么?》

公众号:前端自习课

退出移动版