Open xiaokeqi opened 4 years ago
无论在vue里,还是在react中,我们都知道通过动态import()可以按需加载组件、页面路由。那么,动态import是什么?用于哪里,怎么用,如何实现?跟着我一起揭开其面纱吧!
import()是一个“function-like”的动态模块引入,其现在处于TC39的提案中,且在4个月前,也就是2019年6月份,移到stage 4中,而chrome 61、edge16、ios10.3、firefox60、opera48、safari10.1版本以上就开始支持了,预计不久的将来会成为正式的语法标准。
现有的语法形式是静态声明的,他们只接受字符串字面量,不能是变量,因为其是在编译阶段对模块进行静态分析、打包、tree shaking,而非运行时。这对于90%的设计来说是great!然而,动态加载js模块的场景也存在很多,如多个模块择优选择加载、静态模块加载失败后通过动态引入来增加程序健壮性等等。但是现有的语法不支持动态加载js模块。
因此 Domenic Denicola 和module-loading社区产生了增加动态导入想法,并被tc39列入提案,提案中,建议增加import(specifier)语法形式,其用法类似于函数,却没有函数的某些特性。它返回一个Promise。
<!DOCTYPE html> <nav> <a href="books.html" data-entry-module="books">Books</a> <a href="movies.html" data-entry-module="movies">Movies</a> <a href="video-games.html" data-entry-module="video-games">Video Games</a> </nav> <main>Content will load here!</main> <script> const main = document.querySelector("main"); for (const link of document.querySelectorAll("nav > a")) { link.addEventListener("click", e => { e.preventDefault(); import(`./section-modules/${link.dataset.entryModule}.js`) .then(module => { module.loadPageInto(main); }) .catch(err => { main.textContent = err.message; }); }); } </script>
我们可以看到
再举个例子
a.js
console.log('from static import') let number = 0; export default function count() { number ++; return number }
main.js
import count from './a.js' console.log('start----') console.log(count()) import('./a.js').then(_module => { console.log('a.js----') console.log(_module.default()) }) import('./a-dynamic.js').then(_module => { console.log('dy 1----') console.log(_module.default()) }) import('./a-dynamic.js').then(_module => { console.log('dy 2----') console.log(_module.default()) }) console.log('end')
a-dynamic.js
console.log('from dynatic import') let number = 0 export default function count() { number ++; return number }
运行 main.js,在node中输出结果如下:
from static import start---- 1 end a.js---- 2 from dynatic import dy 2---- 1 dy 1---- 2
我们更改main.js为如下
import('./a.js').then(_module => { console.log('a.js----') console.log(_module.default()) }) import('./a-dynamic.js').then(_module => { console.log('dy 1----') console.log(_module.default()) }) import('./a-dynamic.js').then(_module => { console.log('dy 2----') console.log(_module.default()) }) console.log('end') import count from './a.js' console.log('start----') console.log(count())
其输出结果如下:
from static import end start---- 1 a.js---- 2 from dynatic import dy 2---- 1 dy 1---- 2
通过以上例子我们可以看到
从上所示,输出顺序,不如我们期望看到的那样,那通过babel转换后的呢?我们看下,通过babel转换后的代码,如下:
"use strict"; require("core-js/modules/web.dom.iterable"); require("core-js/modules/es6.array.iterator"); require("core-js/modules/es6.string.iterator"); require("core-js/modules/es6.weak-map"); require("core-js/modules/es6.promise"); require("core-js/modules/es6.object.to-string"); var _a = _interopRequireDefault(require("./a.js")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } Promise.resolve().then(function () { return _interopRequireWildcard(require('./a.js')); }).then(function (_module) { console.log('a.js----'); console.log(_module.default()); }); Promise.resolve().then(function () { return _interopRequireWildcard(require('./a-dynamic.js')); }).then(function (_module) { console.log('dy 1----'); console.log(_module.default()); }); Promise.resolve().then(function () { return _interopRequireWildcard(require('./a-dynamic.js')); }).then(function (_module) { console.log('dy 2----'); console.log(_module.default()); }); Promise.resolve().then(function () { return _interopRequireWildcard(require('./a-dynamic.js')); }).then(function (_module) { console.log('dy 3----'); console.log(_module.default()); }); console.log('end'); console.log('start----'); console.log((0, _a.default)());
babel将其转换成了Promise的写法,从上图我们可看出,编译后的代码,运行结果如下:
其执行顺序与转换前完全不一致。前者由于我们没有看node 中import()的实现,不知其具体原因,而后者,完全采用promise及通过weakMap 弱引用的方式设置缓存、防止内存消耗与泄露实现一次加载、多次执行。
好了,到这里,我们已经知道了,import()的用法,大致实现过程、以及import()用法上的注意点。接下来说下webpack如何通过import()实现代码分割
这期间,webpack充当什么角色呢?由于webpack实在babel对代码转换之前运行的,因此webpack会对代码进行解析,将调用import()之处作为分离的模块起点,并打包它和它引用的子模块分离到单独的chunk中。再通过babel转换器,进行转换。
完!
水平有限
如有理解错误之处,请指正~
代码分割之动态import和webpack
无论在vue里,还是在react中,我们都知道通过动态import()可以按需加载组件、页面路由。那么,动态import是什么?用于哪里,怎么用,如何实现?跟着我一起揭开其面纱吧!
介绍
import()是一个“function-like”的动态模块引入,其现在处于TC39的提案中,且在4个月前,也就是2019年6月份,移到stage 4中,而chrome 61、edge16、ios10.3、firefox60、opera48、safari10.1版本以上就开始支持了,预计不久的将来会成为正式的语法标准。
现有的语法形式是静态声明的,他们只接受字符串字面量,不能是变量,因为其是在编译阶段对模块进行静态分析、打包、tree shaking,而非运行时。这对于90%的设计来说是great!然而,动态加载js模块的场景也存在很多,如多个模块择优选择加载、静态模块加载失败后通过动态引入来增加程序健壮性等等。但是现有的语法不支持动态加载js模块。
因此 Domenic Denicola 和module-loading社区产生了增加动态导入想法,并被tc39列入提案,提案中,建议增加import(specifier)语法形式,其用法类似于函数,却没有函数的某些特性。它返回一个Promise。
用法
我们可以看到
再举个例子
a.js
main.js
a-dynamic.js
运行 main.js,在node中输出结果如下:
我们更改main.js为如下
其输出结果如下:
通过以上例子我们可以看到
从上所示,输出顺序,不如我们期望看到的那样,那通过babel转换后的呢?我们看下,通过babel转换后的代码,如下:
babel将其转换成了Promise的写法,从上图我们可看出,编译后的代码,运行结果如下:
其执行顺序与转换前完全不一致。前者由于我们没有看node 中import()的实现,不知其具体原因,而后者,完全采用promise及通过weakMap 弱引用的方式设置缓存、防止内存消耗与泄露实现一次加载、多次执行。
好了,到这里,我们已经知道了,import()的用法,大致实现过程、以及import()用法上的注意点。接下来说下webpack如何通过import()实现代码分割
webpack import()代码分割
这期间,webpack充当什么角色呢?由于webpack实在babel对代码转换之前运行的,因此webpack会对代码进行解析,将调用import()之处作为分离的模块起点,并打包它和它引用的子模块分离到单独的chunk中。再通过babel转换器,进行转换。
完!
水平有限
如有理解错误之处,请指正~