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

  1. let path = require("path");
  2. module.exports = {
  3. mode: "development",
  4. entry: {
  5. index:"./src/index.js",
  6. login:"./src/login.js",
  7. },
  8. devtool:'source-map',
  9. output: {
  10. filename: "[name].js",
  11. path: path.resolve(__dirname, "dist"),
  12. },
  13. };

6、按需加载

import 动态导入(在需要的时机触发,异步执行才是按需加载), webpack当遇到import时,会把这个import的模块单独独立到一个代码快里,这个代码块会单独生成一个文件

首次加载值加载main.js 当遇到import语句时,会想服务器发送一个jsonp请求,请求被分割出去异步代码,然后合并到原来modules,然后加载这个新的模块,把模块的exports导出对象向后传递

image-20210429201817475

image-20210429201830921

6-1、webpack.config.js

  1. let path = require("path");
  2. module.exports = {
  3. mode: "development",
  4. entry: "./src/index.js",
  5. devtool:'source-map',
  6. output: {
  7. filename: "bundle.js",
  8. path: path.resolve(__dirname, "dist"),
  9. },
  10. };

6-2、index.js

  1. let button = document.createElement("button");
  2. button.innerHTML = "按钮";
  3. button.addEventListener("click", function () {
  4. import("./hello").then((result) => {
  5. console.log(result.default);
  6. });
  7. });
  8. console.log("index");
  9. document.body.appendChild(button)

6-3、 hello.js

  1. console.log('hello')

7、splitChunks

  • webpack奖给予以下条件自动分割代码块
    • 新的代码被共享或者来自node_modules文件夹
    • 新的代码块大雨30kb
    • 按需加载代码块的请求数量应该<=5
    • 页面初始化时加载代码块的请求数量<=3

webpack.config.js

  1. let path = require("path");
  2. module.exports = {
  3. mode: "development",
  4. entry: {
  5. page1: "./src/page1.js",
  6. page2: "./src/page2.js",
  7. page3: "./src/page3.js",
  8. },
  9. output: {
  10. filename: "[name].js",
  11. path: path.resolve(__dirname, "dist"),
  12. },
  13. optimization: {
  14. splitChunks: {
  15. cacheGroups: {
  16. vendors: {
  17. chunks: "initial", // 指定分割类型, 3种类型 all 、 async 异步(默认)、initial 同步
  18. name: "vendors", // 给分割出去的代码块起一个名字叫vendors
  19. test: /node_modules/, // 如果模块路径匹配这个正则的话,就会添加一个vendors代码块
  20. priority: -10, // 优先级
  21. },
  22. commons: {
  23. chunks: "initial",
  24. name: "commons",
  25. minSize: 0, // 最小提取字节 大于多少才用提取
  26. minChunks: 2, //最少被几个chunk引用提取
  27. priority: -20,
  28. },
  29. },
  30. },
  31. },
  32. };

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
  1. let button = document.createElement("button");
  2. button.innerHTML = "按钮";
  3. button.addEventListener("click", function () {
  4. debugger;
  5. import("./hello").then((result) => {
  6. console.log(result.default);
  7. });
  8. });
  9. console.log("index");
  10. document.body.appendChild(button);
  • Compiler 模块

    改造的点 按照 import 区分 成两个 chunk main 模块 和 其他模块
    替换 import 节点 改为 replaceWithSourceString

  1. // 整个替换上面代码 异步加载我们的代码 封装一下 通过.default 拿到值
  2. nodePath.replaceWithSourceString(`
  3. __webpack_require__.e("${dependencyChunkId}").then(__webpack_require__.t.bind(__webpack_require__, "${dependencyModuleId}"))
  4. `);
  5. // ... 发射代码阶段 循环chunks 按照不同的chunId 发射不同的ejs模版
  6. emitFiles() {
  7. // 发射文件 用数据 渲染模版
  8. // 用数据渲染
  9. // 拿到输出到哪个目录下
  10. console.log(this.chunks,'chunks');
  11. // 现在 增加 懒加载写法
  12. Object.keys(this.chunks).forEach(chunkId=>{
  13. if(chunkId == 'main'){
  14. let outputFile = path.join(this.config.output.path, this.config.output.filename);
  15. let moduleTemplateStr = this.getSource(path.join(__dirname, "main.ejs"));
  16. let bundle = ejs.compile(moduleTemplateStr)({ entryId: this.entryId,modules:this.chunks[chunkId]})
  17. fs.writeFileSync(outputFile, bundle , 'utf8');
  18. }else{
  19. let outputFile = path.join(this.config.output.path, chunkId);
  20. let chunkTemplateStr = this.getSource(path.join(__dirname, "chunk.ejs"));
  21. let bundle = ejs.compile(chunkTemplateStr)({ chunkId, modules:this.chunks[chunkId]})
  22. fs.writeFileSync(outputFile, bundle , 'utf8');
  23. }
  24. })
  • chunk.ejs
  1. window.webpackJsonp("<%-chunkId%>",{
  2. <%for(moduleId in modules){%>
  3. "<%-moduleId%>":(function(module,exports,__webpack_require__){
  4. <%-modules[moduleId]%>
  5. }),
  6. <%}%>
  7. });
  • main.ejs
  1. (() => {
  2. var installedModules = []
  3. var installedChunks = {
  4. main: 0
  5. }
  6. var __webpack_modules__ = {
  7. <%for(let key in modules){%>
  8. "<%-key%>": (module, __unused_webpack_exports, __webpack_require__) => {eval(`<%-modules[key]%>`) },
  9. <%}%>
  10. };
  11. function __webpack_require__(moduleId) {
  12. if(installedModules[moduleId]) {
  13. return installedModules[moduleId].exports;
  14. }
  15. var module = installedModules[moduleId] = {
  16. i: moduleId,
  17. l: false,
  18. exports: {}
  19. }
  20. __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
  21. return module.exports;
  22. }
  23. __webpack_require__.o = (obj, chunkId) => obj.hasOwnProperty(chunkId);
  24. __webpack_require__.e = function(chunkId){
  25. var installedChunkData = __webpack_require__.o(installedChunks, chunkId)
  26. ? installedChunks[chunkId]
  27. : undefined;
  28. if(installedChunkData !== 0) {
  29. return new Promise((resolve,reject)=>{
  30. installedChunks[chunkId] = resolve
  31. let script = document.createElement('script')
  32. script.src = chunkId;
  33. document.body.appendChild(script)
  34. })
  35. }else{
  36. return new Promise(res=> {
  37. res()
  38. installedChunks[chunkId] = 0;
  39. })
  40. }
  41. }
  42. __webpack_require__.t = function(value){
  43. value = __webpack_require__(value)
  44. return {
  45. default: value
  46. }
  47. }
  48. window.webpackJsonp = (chunkId,moreModules) => {
  49. for(moduleId in moreModules) {
  50. __webpack_modules__[moduleId] = moreModules[moduleId]
  51. installedChunks[chunkId]()
  52. installedChunks[chunkId] = 0
  53. }
  54. }
  55. __webpack_require__("<%-entryId%>");
  56. })();