Webpack-CodeSplit(按需加载)
Webpack-CodeSplit(动态文件篇)
在静态文件处理代码切割时,我们提到了如何处理vendor以及manifest的分离,那么抛开这两点,如果在业务逻辑中,我们如何做到代码最小加载,让浏览器加载效率提速呢,这里我们就利用了webpack codesplit的另一个特性,代码按需加载打包实践方案,webpack真的是太牛逼了,推荐各位看看原码,看看打包思路以及代码实现方式。
常规实践
import foo from './foo';
import bar from './bar';
我们要使用foo,或者bar时候。我们通常按照上述方式引用,构建的效果(没有manifest)
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([ ["app"], { /*foo*/ CKuZ: function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); __webpack_exports__["default"] = { test: "111", }; }, /*入口文件*/ fhko: function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var _foo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("CKuZ"); var _bar__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("tRux"); }, /*bar*/ tRux: function (module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); __webpack_exports__["default"] = function () { console.log("this is bar"); }; }, }, [["fhko", "manifest"]], ]);
三个文件分配了三个名称,入口文件以webpack规则"webpack_require_"模块加载bar跟foo,从bundle中很清晰的看到三个文件彼此的规则,至于怎么调用的,就看manifest输出文件吧,简单来讲,就是遍历 window["webpackJsonp"] ,然后挨个记录到modules中,并执行。重点来了,我们不一定非要引入foo,跟bar呢,按需求引入呢,比如一个button触发的回调函数,比如路由的正则匹配路径渲染呢。还需要浏览器一并加载吗?并不是,写法如下。
import("./foo") .then((res) => { console.log(res); }) .catch((e) => { console.log(e); }); setTimeout(function () { import("./bar") .then((res) => { console.log(res); }) .catch((e) => { console.log(e); }); }, 3000);
采用动态加载的方式,按需引入目标组件。我们看下效果
三秒之后出现
达到了我们的目的。这样做可以减少浏览器加载js的体积,从而提升页面效率。
bundle.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["app"],{ "fhko": (function(module, exports, __webpack_require__) { __webpack_require__.e(/*! import() */ 1).then(__webpack_require__.bind(null, /*! ./foo */ "CKuZ")) .then((res) => { console.log(res); }) .catch((e) => { console.log(e); }); setTimeout(function () { __webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./bar */ "tRux")) .then((res) => { console.log(res); }) .catch((e) => { console.log(e); }); }, 3000); /***/ }) },[["fhko","manifest"]]]);
通过webpackRequire加载动态文件。
webpackRequire实现方式,这里做了一个简化版
__webpack_require__.e = function requireEnsure(chunkId) { var promises = []; var promise = new Promise(function (resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; }); promises.push((installedChunkData[2] = promise)); var script = document.createElement("script"); var onScriptComplete; script.charset = "utf-8"; script.timeout = 120; if (__webpack_require__.nc) { script.setAttribute("nonce", __webpack_require__.nc); } script.src = jsonpScriptSrc(chunkId); document.head.appendChild(script); return Promise.all(promises); };
加载的目标文件写入head,浏览器引入目标文件,然后返回promise数组。还是很容易理解的。
webpack输出文件列表
看上去有点迷~如何改进下,至少让我们知道哪个文件是哪个啊,不能阿拉伯数字啊,改进如下
import(/* webpackChunkName: "foo" */ "./foo") .then((res) => { console.log(res); }) .catch((e) => { console.log(e); }); setTimeout(function () { import(/* webpackChunkName: "bar" */ "./bar") .then((res) => { console.log(res); }) .catch((e) => { console.log(e); }); }, 3000);
输入文件列表
这回清晰了很多。
除了import可以实现动态加载,常用的方式还有require.ensure方式。有兴趣的可以自己实践一下。
此特性好处
按需加载,提高浏览器加载脚本文件速度,减少一次性加载的体积。
此特性优化场景
-
微前端架构设计,工程级别懒加载
-
模块路由按需加载
-
页面业务需求按需加载
好处还是大大滴,希望有性趣的可以自己实践一下,毕竟优化才是王道啊。