1、webpack代码分割的方式
- entry配置,通过多个文件来实现
- 动态加载(按需加载) 通过主动使用import来动态加载
- 抽离公共代码,使用
splitChunks配置类抽取公共代码
2、基础概念
| 概念 | 含 |
|---|---|
| Entry | 入口 Webpack执行第一步,可抽象成输入 |
| module | 模块,一切皆模块,webpack会配置Entry递归所有模块 |
| chunk | 代码块,一个chunk由多个模块组合,用于代码合并与分割 |
| bundle | webpack打包后的各个文件,一般和chunk一对一的关系 |
3、项目初始化
mkdir lazy_webpack cd lazy_webpack cnpm init -y cnpm i webpack -D
5、entry分割
5-1、 webpack.config.js
let path = require("path");module.exports = {mode: "development",entry: {index:"./src/index.js",login:"./src/login.js",},devtool:'source-map',output: {filename: "[name].js",path: path.resolve(__dirname, "dist"),},};
6、按需加载
import 动态导入(在需要的时机触发,异步执行才是按需加载), webpack当遇到import时,会把这个import的模块单独独立到一个代码快里,这个代码块会单独生成一个文件
首次加载值加载main.js 当遇到import语句时,会想服务器发送一个jsonp请求,请求被分割出去异步代码,然后合并到原来modules,然后加载这个新的模块,把模块的exports导出对象向后传递


6-1、webpack.config.js
let path = require("path");module.exports = {mode: "development",entry: "./src/index.js",devtool:'source-map',output: {filename: "bundle.js",path: path.resolve(__dirname, "dist"),},};
6-2、index.js
let button = document.createElement("button");button.innerHTML = "按钮";button.addEventListener("click", function () {import("./hello").then((result) => {console.log(result.default);});});console.log("index");document.body.appendChild(button)
6-3、 hello.js
console.log('hello')
7、splitChunks
- webpack奖给予以下条件自动分割代码块
- 新的代码被共享或者来自node_modules文件夹
- 新的代码块大雨30kb
- 按需加载代码块的请求数量应该<=5
- 页面初始化时加载代码块的请求数量<=3
webpack.config.js
let path = require("path");module.exports = {mode: "development",entry: {page1: "./src/page1.js",page2: "./src/page2.js",page3: "./src/page3.js",},output: {filename: "[name].js",path: path.resolve(__dirname, "dist"),},optimization: {splitChunks: {cacheGroups: {vendors: {chunks: "initial", // 指定分割类型, 3种类型 all 、 async 异步(默认)、initial 同步name: "vendors", // 给分割出去的代码块起一个名字叫vendorstest: /node_modules/, // 如果模块路径匹配这个正则的话,就会添加一个vendors代码块priority: -10, // 优先级},commons: {chunks: "initial",name: "commons",minSize: 0, // 最小提取字节 大于多少才用提取minChunks: 2, //最少被几个chunk引用提取priority: -20,},},},},};
8、实现webpack
…
9、实现懒加载
实现懒加载
实现原理 通过 ast 解析 将 import 语法 进行拆分 分成和 main 相互独立的 chunk, 根据不同的 chunk emit 成不同的模块
实现了 webpack.e webpack.t 在 e 中主要是返回了 promise 格式的代码 将 chunkid = resolve 创建 script 标签 src 等于 chunkid
在每次异步执行时 都会创建一个 script 标签 后面通过 installedChunk 全局维护 加入缓存 如果创建过 就直接然后 promise.resolve 否则在进行上步骤
- index.js
let button = document.createElement("button");button.innerHTML = "按钮";button.addEventListener("click", function () {debugger;import("./hello").then((result) => {console.log(result.default);});});console.log("index");document.body.appendChild(button);
- Compiler 模块
改造的点 按照 import 区分 成两个 chunk main 模块 和 其他模块
替换 import 节点 改为 replaceWithSourceString
// 整个替换上面代码 异步加载我们的代码 封装一下 通过.default 拿到值nodePath.replaceWithSourceString(`__webpack_require__.e("${dependencyChunkId}").then(__webpack_require__.t.bind(__webpack_require__, "${dependencyModuleId}"))`);// ... 发射代码阶段 循环chunks 按照不同的chunId 发射不同的ejs模版emitFiles() {// 发射文件 用数据 渲染模版// 用数据渲染// 拿到输出到哪个目录下console.log(this.chunks,'chunks');// 现在 增加 懒加载写法Object.keys(this.chunks).forEach(chunkId=>{if(chunkId == 'main'){let outputFile = path.join(this.config.output.path, this.config.output.filename);let moduleTemplateStr = this.getSource(path.join(__dirname, "main.ejs"));let bundle = ejs.compile(moduleTemplateStr)({ entryId: this.entryId,modules:this.chunks[chunkId]})fs.writeFileSync(outputFile, bundle , 'utf8');}else{let outputFile = path.join(this.config.output.path, chunkId);let chunkTemplateStr = this.getSource(path.join(__dirname, "chunk.ejs"));let bundle = ejs.compile(chunkTemplateStr)({ chunkId, modules:this.chunks[chunkId]})fs.writeFileSync(outputFile, bundle , 'utf8');}})
- chunk.ejs
window.webpackJsonp("<%-chunkId%>",{<%for(moduleId in modules){%>"<%-moduleId%>":(function(module,exports,__webpack_require__){<%-modules[moduleId]%>}),<%}%>});
- main.ejs
(() => {var installedModules = []var installedChunks = {main: 0}var __webpack_modules__ = {<%for(let key in modules){%>"<%-key%>": (module, __unused_webpack_exports, __webpack_require__) => {eval(`<%-modules[key]%>`) },<%}%>};function __webpack_require__(moduleId) {if(installedModules[moduleId]) {return installedModules[moduleId].exports;}var module = installedModules[moduleId] = {i: moduleId,l: false,exports: {}}__webpack_modules__[moduleId](module, module.exports, __webpack_require__);return module.exports;}__webpack_require__.o = (obj, chunkId) => obj.hasOwnProperty(chunkId);__webpack_require__.e = function(chunkId){var installedChunkData = __webpack_require__.o(installedChunks, chunkId)? installedChunks[chunkId]: undefined;if(installedChunkData !== 0) {return new Promise((resolve,reject)=>{installedChunks[chunkId] = resolvelet script = document.createElement('script')script.src = chunkId;document.body.appendChild(script)})}else{return new Promise(res=> {res()installedChunks[chunkId] = 0;})}}__webpack_require__.t = function(value){value = __webpack_require__(value)return {default: value}}window.webpackJsonp = (chunkId,moreModules) => {for(moduleId in moreModules) {__webpack_modules__[moduleId] = moreModules[moduleId]installedChunks[chunkId]()installedChunks[chunkId] = 0}}__webpack_require__("<%-entryId%>");})();
