了不起的 Webpack Scope Hoisting 学习指南

简介: 了不起的 Webpack Scope Hoisting 学习指南


近期原创文章回顾😄

一、什么是 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";
console.log(str);

使用 webpack 打包后输出文件内容如下:

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

再开启 Scope Hoisting 后,相同源码打包输出结果变为:

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

对比两种打包方式输出的代码,我们可以看出,启用 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,它对我们项目优化非常有帮助,但平常又很少会去注意。

六、参考文章


目录
相关文章
|
7月前
|
JSON JavaScript 前端开发
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)(下)
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)
92 2
|
JSON 前端开发 JavaScript
前端AJAX入门到实战,学习前端框架前必会的(ajax+node.js+webpack+git)(一)
前端AJAX入门到实战,学习前端框架前必会的(ajax+node.js+webpack+git)(一)
593 0
|
2月前
|
JavaScript
webpack学习三:webpack初始化整合配置vue,一步一步的抽离代码块整合vue。
这篇文章是关于如何在webpack环境中配置Vue.js,包括安装Vue.js、解决报错、理解el与template的区别、使用SPA模式、抽离模板为对象、封装为单独的js文件、安装vue-loader时遇到的问题及解决方案,以及整个过程的总结。
72 2
webpack学习三:webpack初始化整合配置vue,一步一步的抽离代码块整合vue。
|
2月前
|
JavaScript
webpack学习五:webpack的配置文件webpack.config.js分离,分离成开发环境配置文件和生产环境配置文件
这篇文章介绍了如何将webpack的配置文件分离成开发环境和生产环境的配置文件,以提高打包效率。
48 1
webpack学习五:webpack的配置文件webpack.config.js分离,分离成开发环境配置文件和生产环境配置文件
|
2月前
|
JavaScript 前端开发 Java
webpack学习一:什么是模块化开发,什么是webpack,以及二者之间的关系。
这篇文章介绍了模块化开发的概念、历史和实现方式,以及webpack作为一个现代JavaScript应用的静态模块打包工具,它如何帮助我们将ES6等高级语法打包成浏览器可以识别的低级语法,并解释了npm在webpack安装和使用中的作用。
43 1
webpack学习一:什么是模块化开发,什么是webpack,以及二者之间的关系。
|
2月前
|
移动开发 JavaScript 前端开发
webpack学习四:使用webpack配置plugin,来使用HtmlWebpackPlugin、uglifyjs-webpack-plugin、webpack-dev-server等插件简化开发
这篇文章主要介绍了如何通过配置Webpack的插件,如HtmlWebpackPlugin、uglifyjs-webpack-plugin和webpack-dev-server,来简化前端开发流程。
52 0
webpack学习四:使用webpack配置plugin,来使用HtmlWebpackPlugin、uglifyjs-webpack-plugin、webpack-dev-server等插件简化开发
|
7月前
|
Web App开发 JSON 前端开发
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
238 0
|
7月前
|
存储 前端开发 JavaScript
Webpack【Webpack中模式(Mode)、Webpack中使用DevServer、Webpack中devtool增强调试过程】(二)-全面详解(学习总结---从入门到深化)
Webpack【Webpack中模式(Mode)、Webpack中使用DevServer、Webpack中devtool增强调试过程】(二)-全面详解(学习总结---从入门到深化)
172 0
|
7月前
|
JSON 前端开发 JavaScript
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)(上)
Webpack【搭建Webpack环境、Webpack增加配置文件、Webpack中使用Loader、Webpack分离CSS文件 】(一)-全面详解(学习总结---从入门到深化)
91 0
|
5月前
|
JavaScript 前端开发 应用服务中间件