一:什么是Scope Hoisting? 它有什么作用?
Scope Hoisting 它可以让webpack打包出来的代码文件更小,运行更快,它可以被称作为 "作用域提升"。是在webpack3中提出来的,当然现在webpack4也是支持的。
在介绍之前,我们还是来和之前一样,看看我们项目整个目录架构如下:
### 目录结构如下:
demo1 # 工程名
| |--- dist # 打包后生成的目录文件
| |--- node_modules # 所有的依赖包
| |--- app
| | |---index
| | | |-- views # 存放所有vue页面文件
| | | | |-- home.vue
| | | | |-- index.vue
| | | | |-- xxx.vue
| | | |-- components # 存放vue公用的组件
| | | |-- js # 存放js文件的
| | | |-- app.js # vue入口配置文件
| | | |-- router.js # 路由配置文件
| |--- views
| | |-- index.html # html文件
| |--- webpack.config.js # webpack配置文件
| |--- .gitignore
| |--- README.md
| |--- package.json
| |--- .babelrc # babel转码文件
首先我们在 app/index/js 下新建 index.js 代码如下:
export default 'xxxx';
然后在我们的入口文件 app/index/app.js 代码如下:
import index from './js/index';
console.log(index);
然后运行 npm run build 打包后,bundle.js 代码如下:
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./app/index/app.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./app/index/app.js":
/*!**************************!*\
!*** ./app/index/app.js ***!
\**************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _js_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./js/index */ "./app/index/js/index.js");
console.log(_js_index__WEBPACK_IMPORTED_MODULE_0__["default"]);
/***/ }),
/***/ "./app/index/js/index.js":
/*!*******************************!*\
!*** ./app/index/js/index.js ***!
\*******************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ('xxxx');
/***/ })
/******/ });
//# sourceMappingURL=bundle.js.map
如上代码我们没有使用 Scope Hoisting 功能,输出如上那么多代码,下面我们来看看引入 Scope Hoisting 功能,打包后的代码;
要使用 Scope Hoisting 功能,首先我们需要 的是我们JS文件都使用ES6的语法来编写的,否则它是不会生效的。还是上面的代码不变。
使用 Scope Hoisting(编写的代码需要支持ES6规范)
Scope Hoisting 是webpack内置的功能,只要配置一个插件即可,如下在webpack.config.js 代码如下配置:
module.exports = {
plugins: [
// 开启 Scope Hoisting 功能
new webpack.optimize.ModuleConcatenationPlugin()
]
}
然后执行npm run build 打包后的bundle.js 代码如下:
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./app/index/app.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./app/index/app.js":
/*!**************************************!*\
!*** ./app/index/app.js + 1 modules ***!
\**************************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// CONCATENATED MODULE: ./app/index/js/index.js
/* harmony default export */ var js = ('xxxx');
// CONCATENATED MODULE: ./app/index/app.js
console.log(js);
/***/ })
/******/ });
//# sourceMappingURL=bundle.js.map
如上代码可以看到,开启 Scope Hoisting后,函数声明由两个变成了一个,app/index/js/index.js 代码直接被注入到 app.js里面去了,如上代码 var js = ('xxx');
因此 启用 Scope Hoisting的优点如下:
1. 代码体积会变小,因为函数声明语句会产生大量代码,但是第二个没有函数声明。
2. 代码在运行时因为创建的函数作用域减少了,所以内存开销就变小了。
具体的可以看官网(https://webpack.js.org/plugins/module-concatenation-plugin/)
但是对于有很多第三方库并没有使用ES6模块语法的代码,webpack它会降级处理这些非ES6编写的代码,不使用 Scope Hoisting 优化。
因此在webpack中还需要加上如下代码配置:
module.exports = {
resolve: {
// 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
};
我们可以在启动webpack时候带上 --display-optimization-bailout 参数,在输出日志中就会包含类似如下的日志:如下图
其中的 ModuleConcatenation bailout 告诉了你哪个文件因为什么原因导致了降级处理。
因此在webpack中所有的配置代码如下:
module.exports = {
resolve: {
// 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
mainFields: ['jsnext:main', 'browser', 'main']
},
plugins: [
// 开启 Scope Hoisting 功能
new webpack.optimize.ModuleConcatenationPlugin()
]
};
网友评论