最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • webpack4流程分析6

    正文概述 掘金(dream_99517)   2021-07-10   392

    1.Compilation

    // 经过之前的步骤我们得到了创建出来的modules 执行回调会到compilation.seal封装代码
    function seal() {
      this.hooks.seal.call();
      // 触发各种hook 为我们构建提供很多的钩子
      this.hooks.beforeChunks.call();
      // _preparedEntrypoints 在addEntry的时候添加的 为每个入口生成一个chunk
      for (const preparedEntrypoint of this._preparedEntrypoints) {
        const module = preparedEntrypoint.module; // 入口模块
        const name = preparedEntrypoint.name; // main
        //  new Chunk(name)
        const chunk = this.addChunk(name); // 新建chunk将module添加到chunk中
        // extend ChunkGroup 主要用来优化 chunk graph
        const entrypoint = new Entrypoint(name); // 生成入口点
        entrypoint.setRuntimeChunk(chunk); // 运行时chunk
        entrypoint.addOrigin(null, name, preparedEntrypoint.request); // 增加来源
        this.namedChunkGroups.set(name, entrypoint); // key为chunk的名称 值为chunkGroup
        this.entrypoints.set(name, entrypoint); // key为chunk的名称 值为chunkGroup
        this.chunkGroups.push(entrypoint); // 添加一个新的chunkGroup
    
        // 建立chunkGroup和chunk的关系 	chunk.addGroup(chunkGroup);
        // this._groups.add(chunkGroup)
        GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk);
        // 建立chunk和module的关系 	chunk.addModule(module);
        // this._modules.add(module);
        GraphHelpers.connectChunkAndModule(chunk, module);
        chunk.entryModule = module; // 代码块的入口模块
        chunk.name = name; // 代码块的名称
        // 依赖的深度??
        // this.assignDepth(module);
      }
      // [{Entrypoint: chunks: [ chunk: [{_modules, _groups}] ]}]
      // 构建chunkGraph 用来生成优化chunk依赖图
      buildChunkGraph(this, this.chunkGroups.slice());
    }
    

    2. buildChunkGraph

    // seal的流程很多 先分析 buildChunkGraph 主要分为三步
    const buildChunkGraph = (compilation, inputChunkGroups) => {
      // 1. 建立chunkGroup chunk module之间的关系 使用 blockInfoMap 保存信息
      visitModules();
      // 2. 建立不同chunkGroup之间的父子关系 优化chunkGroup
      connectChunkGroups();
      // 3. 清理无用的chunk 清理相关的联系
      cleanupUnconnectedGroups();
    };
    

    2.1 demo

    // 为了方便看里面的逻辑 建立一个demo
    // index.js
    import common from "./common.js";
    import("./async.js").then((result) => console.log(result));
    // async.js
    import title from "./title.js";
    import("./common.js").then((result) => console.log(result));
    export const lazy = "lazy";
    // common.js
    import title from "./title.js";
    // title.js
    export const title = "title";
    

    webpack4流程分析6

    2.2 visitModules

    // 1. 建立关联
    const visitModules = (
      compilation,
      inputChunkGroups,
      chunkGroupInfoMap,
      blockConnections,
      blocksWithNestedBlocks,
      allCreatedChunkGroups
    ) => {
      // 1.使用blockInfo用来存储模块关系 同步存入modules 异步blocks module graph
    
      // 经过处理 blockInfoMap 中共有6条记录 特别注意ImportDependenciesBlock_async的两个
      // const obj = {
      //   NormalModule_index: { modules: [common], blocks: [async] },
      //   ImportDependenciesBlock_async: { modules: [async], blocks: [] },
      //   NormalModule_common: { modules: [title], blocks: [] },
      //   NormalModule_async: { blocks: [common], modules: [title] },
      //   ImportDependenciesBlock_common: { modules: [common] },
      //   NormalModule_title: { modules: [] },
      // };
    
      const blockInfoMap = extraceBlockInfoMap(compilation);
    
      // 2.处理模块之见的关系 chunk graph
      let nextFreeModuleIndex = 0; // 下一个模块的空闲索引 每个模块是有两个index的默认是0
      let nextFreeModuleIndex2 = 0; // 通过下表去添加值
      const ADD_AND_ENTER_MODULE = 0; // 增加进入模块
      const ENTER_MODULE = 1; // 进入模块
      const PROCESS_BLOCK = 2; // 处理代码块
      const LEAVE_MODULE = 3; // 离开模块
    
      const reduceChunkGroupToQueueItem = (queue, chunkGroup) => {
        for (const chunk of chunkGroup.chunks) {
          queue.push({
            action: ENTER_MODULE,
            block: module,
            module: chunk.entryModule,
            chunk,
            chunkGroup,
          });
        }
        // 设置 chunkGroupInfoMap 映射 chunkGroup 和相关的信息对象
        chunkGroupInfoMap.set(chunkGroup, { chunkGroup, children: undefined });
        return queue;
      };
      // 将 entryPoint(chunkGroup) 变成一个queue {block, chunk, chunkGroup, module}
      let queue = inputChunkGroups
        .reduce(reduceChunkGroupToQueueItem, [])
        .reverse();
      const iteratorBlock = (b) => {
        // 1. 为block创建一个chunk import动态引入的会单独生成一个chunk(这个时候chunk中还没有依赖的module)
        // const chunkGroup = new ChunkGroup(groupOptions);
        // if (module) chunkGroup.addOrigin(module, loc, request); this.origins.push({})
        // const chunk = this.addChunk(name);
        // this.chunkGroups.push(chunkGroup);
        c = compilation.addChunkInGroup(b.chunkName, module);
        blockChunkGroups.set(b, c); // c是一个chunkGroup
        allCreatedChunkGroups.add(c); // 已经创建的chunksGroup
        blockConnections.set(b, []); // chunk Group的依赖
        // 2. 建立 module 所属的 chunkGroup 和 block 和 block 属于的chunkGroup的依赖关系
        // 主要用于优化 chunk graph
        blockConnections.get(b).push({
          originChunkGroupInfo: chunkGroupInfo, // chunkGroupInfoMap对应的信息
          chunkGroup: c,
        });
        // 3. 创建/跟新 chunk Group 的信息
        queueConnect.set(chunkGroup, connectList);
        connectList.add(c);
        // 4. 代码块 用作外层的遍历
        queueDelayed.push({
          action: PROCESS_BLOCK,
          block: b,
          module: module,
          chunk: c.chunks[0],
          chunkGroup: c,
        });
      };
    
      /**
       * 第一次外层while是入口 entry: {block, chunk, module, chunkGroup}
       * 第一次内层循环
       *  进入ENTER_MODULE 处理 完成后(没有break)进入 PROCESS_BLOCK
       *  PROCESS_BLOCK中会处理 modules 和 blocks
       *  index中的
       *      modules 是 common queue.push()
       *      blocks 是 async iteratorBlock是 新建chunk queueDelayed.push() 外层循环
       *
       * 第二次内层循环
       *  处理 ./common.js 调用 module.addChunk(chunk);
       *  在处理 common 的 modules(./title.js) 和 blocks(无)
       *
       * 第三次内层循环
       *  处理 title.js 没有 modules 和 blocks
       *
       *  处理queueConnect 在处理blocks的时候会处理
       *
       * 在处理 queueDelayed 开始外层的循环
       * // 这里使用了reverse的
       * queue = queueDelayed.reverse()
       *
       * 开始第二次外层循环 
       * 开始一次内循环
       *  queueDelayed的参数 {
       *    block: b, // index中blocks的block async.js
    			  module: module, // index的module index.js
       *  }
          从bockInfoMap中找到 async 对应的 blockInfo
          block为  './async.js'  modules为空
       *
       * 。。。 通过while有一直处理下去 建立 graph图
       * 
       * 我们得到三个 chunkGroup entry async common
       */
      // 外层循环 会对queueDelayed的数据集进行处理 第一次的queue是entry开始
      while (queue.length) {
        // 每一轮的内层都对应同一个chunkGroup 对chunkGroup中所有的module进行处理
        while (queue.length) {
          const queueItem = queue.pop();
          module = queueItem.module;
          block = queueItem.block;
          chunk = queueItem.chunk;
          chunkGroup = queueItem.chunkGroup;
          chunkGroupInfo = chunkGroupInfoMap.get(chunkGroup);
          // 判断不同的同作
          switch (queueItem.action) {
            case ADD_AND_ENTER_MODULE: {
              if (chunk.addModule(module)) {
                module.addChunk(chunk);
              }
              break;
            }
            // 入口出的就是进入模块
            case ENTER_MODULE: {
              chunkGroup.setModuleIndex(
                module,
                chunkGroupCounters.get(chunkGroup).index++
              );
              // 添加一个离开的动作
              queue.push({ action: LEAVE_MODULE });
            }
            // 上面没有break 执行完ENTER_MODULE就会执行PROCESS_BLOCK处理代码快
            case PROCESS_BLOCK: {
              // 获取这个module的同步依赖modules和异步依赖blocks
              // 入口出的block就是chunk.entryModule
              const blockInfo = blockInfoMap.get(block);
              // 同步的依赖
              for (const refModule of blockInfo.modules) {
                if (chunk.containsModule(refModule)) {
                  continue; // 有就跳过 否则添加 继续内层的遍历
                } else {
                  // 将依赖的module添加到这个chunkGroup中了
                  queueBuffer.push({
                    action: ADD_AND_ENTER_MODULE,
                    block: refModule,
                    module: refModule,
                    chunk,
                    chunkGroup,
                  });
                }
              }
              // 处理blocks
              for (const block of blockInfo.blocks) iteratorBlock(block);
              break;
            }
            case LEAVE_MODULE: {
              // 将这个module添加都了chunkGroup中
              chunkGroup.setModuleIndex(module);
              break;
            }
          }
        }
        // block的处理会set值
        while (queueConnect.size > 0) {
          for (const [chunkGroup, targets] of queueConnect) {
            const info = chunkGroupInfoMap.get(chunkGroup);
            // 1. 添加i个新的模块数据集 添加chunkGroup中chunk的模块
            const resultingAvailableModules = new Set(minAvailableModules);
            resultingAvailableModules.add(m);
            info.children = targets;
            // 2. 更新chunkGroup的信息
            chunkGroupInfoMap.set(target, chunkGroupInfo);
            if (outdatedChunkGroupInfo.size > 0) {
              // 合并模块
              for (const info of outdatedChunkGroupInfo) {
              }
            }
          }
        }
    
        // 内层遍历完成表示一个chunkGroup完了
        // 处理queueDelayed 就是 用来处理block 异步的是在说有的同步处理了再处理的
        if (queue.length === 0) {
          const tempQueue = queue;
          queue = queueDelayed.reverse();
          queueDelayed = tempQueue;
        }
      }
    };
    

    2.3 extraceBlockInfoMap

    // 经过之前的流程 我们可以得到4个模块
    
    // 1. 处理index.js对应的模块 因为是pop会先处理block push的
    // {NormalModule => Object}   // 1.index.js
    // {ImportDependenciesBlock => Object}  // 2.async
    // 2.处理common模块
    // {NormalModule => Object} // 3.common
    
    // 3.处理async模块
    // {NormalModule => Object} // 4.async模块 里面 import 了 common
    // {ImportDependenciesBlock => Object} // 5.async 模块中的 common
    // 4.处理title的module
    // {NormalModule => Object} // 6.title模块
    const extraceBlockInfoMap = () => {
      const blockInfoMap = new Map();
      const iteratorDependency = (d) => {
        // module
        blockInfoModules.add(refModule);
      };
      const iteratorBlockPrepare = (b) => {
        blockInfoBlocks.push(b);
        // index和async中有block的依赖会再次遍历
        blockQueue.push(b); // 将block加入到blockQueue中 下一次遍历
      };
    
      // 遍历执行所有的模块 开始我们是有四个模块的
      // {"~/src/index.js" => NormalModule} 里面有 import('./async')
      // {"~/src/common.js" => NormalModule}
      // {"~/src/async.js" => NormalModule} 里面有 import('./common')
      // {"~/src/title.js" => NormalModule}
      for (const module of compilation.modules) {
        blockQueue = [module]; // blockQueue
        currentModule = module; // 当前模块
        // block的需要加入到队列中再次处理
        while (blockQueue.length > 0) {
          block = blockQueue.pop(); // 这里用的是pop 所有会先处理block加进来的
          // 分别处理 variables dependencies blocks
          if (block.variables) {
            // 变量
            iteratorDependency();
          }
          if (block.dependencies) {
            // 依赖
            iteratorDependency();
          }
          if (block.blocks) {
            // import的动态模块依赖
            iteratorBlockPrepare();
          }
          const blockInfo = {
            modules: blockInfoModules,
            blocks: blockInfoBlocks,
          };
          // compilation.modules module
          blockInfoMap.set(block, blockInfo);
        }
      }
      return blockInfoMap;
    };
    

    2.4 connectChunkGroups

    // 经过上面的处理 我们得到三个chunkGroup 连接chunkGroup 建立父子关系
    const connectChunkGroups = (
      blocksWithNestedBlocks,
      blockConnections,
      chunkGroupInfoMap
    ) => {
      // 检查代码块中是否已经有了所有的模块
      const areModulesAvailable = () => {};
      // 遍历所有的connections chunk group的依赖
      // 在处理blocks的时候 async和common的时候 会被加到里面 blockConnections.set()
      // [ImportDependenciesBlock => Array(1), ImportDependenciesBlock => Array(1)]
      const map = {
        "./sync.js": {
          // const chunkGroup = new ChunkGroup(groupOptions);
          chunkGroup: compilation.addChunkInGroup(b.chunkName, module),
        },
      };
      // block是blocks中的block
      // blockConnections.set('./async', [{originChunkGroupInfo, chunkGroup}])
      for (const [block, connections] of blockConnections) {
        // 1. 检查连接是否有必要
        // 2. Foreach edge
        for (let i = 0; i < connections.length; i++) {
          // chunkGroup是new 出来的c  originChunkGroupInfo是map对象中值
          const { chunkGroup, originChunkGroupInfo } = connections[i];
          // 之前是通过 chunkGroup.setModuleIndex(module); 往里面添加了module
          // 3. Connect block with chunk 把block添加到chunkGroup中
          GraphHelpers.connectDependenciesBlockAndChunkGroup(block, chunkGroup);
          //  4. Connect chunk with parent建立父子关系  blockInfoMap中的属性
          GraphHelpers.connectChunkGroupParentAndChild(
            originChunkGroupInfo.chunkGroup,
            chunkGroup
          );
        }
      }
    };
    
    const connectChunkGroupParentAndChild = (parent, child) => {
      // chunkGroup
      if (parent.addChild(child)) {
        // this._parents.add(parentChunk);
        child.addParent(parent);
      }
    };
    
    // async.js: {chunkGroup: chunkGroup}
    const connectDependenciesBlockAndChunkGroup = (depBlock, chunkGroup) => {
      if (chunkGroup.addBlock(depBlock)) {
        depBlock.chunkGroup = chunkGroup;
      }
    };
    

    2.5 cleanupUnconnectedGroups

    const cleanupUnconnectedGroups = (compilation, allCreatedChunkGroups) => {
      // async和common建立的chunkGroup
      for (const chunkGroup of allCreatedChunkGroups) {
        if (chunkGroup.getNumberOfParents() === 0) {
          // 如果有父亲 就将chunkGroup中所有的chunk删除
          for (const chunk of chunkGroup.chunks) {
            const idx = compilation.chunks.indexOf(chunk);
            if (idx >= 0) compilation.chunks.splice(idx, 1);
            chunk.remove("unconnected");
          }
          chunkGroup.remove("unconnected");
        }
      }
    };
    

    3. optimizeChunk

    // 生成chunk之后我们对进行一些优化的处理
    this.hooks.optimize.call();
    // module
    this.hooks.optimizeModulesBasic.call(this.modules)
    this.hooks.optimizeModules.call(this.modules)
    this.hooks.optimizeModulesAdvanced.call(this.modules)
    // chunks
    this.hooks.optimizeChunksBasic.call(this.chunks, this.chunkGroups)
    this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups)
    this.hooks.optimizeChunksAdvanced.call(this.chunks, this.chunkGroups)
    this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
      this.applyModuleIds(); // 设置模块id
      this.applyChunkIds(); // 设置 chunk.id
      this.createHash(); // hash
      // 生成资源
      this.createModuleAssets();
      // 生成chunk资源
      this.hooks.beforeChunkAssets.call();
    	this.createChunkAssets();
    })
    

    4. SplitChunksPlugin

    // 这个特性非常重要 单独说明下 当我们触发optimizeChunksAdvanced的时候
    // 面试被问过 splitChunks的原理是什么 不知道
    // 也是在webpackOptionsApply中处理的
    if (options.optimization.splitChunks) {
      // 配置的splitChunks
      const SplitChunksPlugin = require("./optimize/SplitChunksPlugin");
      new SplitChunksPlugin(options.optimization.splitChunks).apply(compiler);
    }
    // 配置的runtimeChunk
    if (options.optimization.runtimeChunk) {
      const RuntimeChunkPlugin = require("./optimize/RuntimeChunkPlugin");
      new RuntimeChunkPlugin(options.optimization.runtimeChunk).apply(compiler);
    }
    
    class SplitChunksPlugin {
      constructor(options) {
        // 格式化
        this.options = SplitChunksPlugin.normalizeOptions(options);
      }
      apply(compiler) {
        compiler.hooks.thisCompilation.tap("SplitChunksPlugin", (compilation) => {
          // 监听这个钩子 在生成了chunkGroup之后 优化的时候触发的代码分割
          compilation.hooks.optimizeChunksAdvanced.tap(
            "SplitChunksPlugin",
            (chunks) => {}
          )
        })
      }
    }
    

    4.1 config

    // 以element-admin的配置为例子 加一些配置的说明
    config.optimization.splitChunks({
       // initial async all function(return chunks) 要提取的模块
      chunks: 'all',
      // minSize: 10000, // 最小的体积 10k的才会分出来
      // maxSize: 0, // 代表能分则分 分不了就算了 保证尽量的小
      // minChunks: 1, //  最小的被引用次数
      // maxAsyncRequests: 5, // 按需加载的代码块最多允许的并行请求数 在webpack5里默认值变为6
      // maxInitialRequests: 3, // 入口代码块最多允许的并行请求数,在webpack5里默认值变为4
      // automaticNameDelimiter: "~", // 代码块命名分割符
      // name: true, // 每个缓存组打包得到的代码块的名称
      cacheGroups: {
        libs: {
          name: 'chunk-libs', // 打包的名字
          test: /[\\/]node_modules[\\/]/, // 正则
          priority: 10, // 优先级
          chunks: 'initial'
        },
        elementUI: {
          name: 'chunk-elementUI',
          priority: 20, 
          test: /[\\/]node_modules[\\/]_?element-ui(.*)/ 
        },
        // 之前一直打包不出来是因为没有设置 minSize
        commons: {
          name: 'chunk-commons',
          test: resolve('src/components'), 
          minSize: 0, // 最小提取字节数
          minChunks: 2, // 最小的被引用次数
          priority: 5,
          reuseExistingChunk: true // 重用模块
        }
      }
    })
    

    4.2 demo

    // 为了演示效果 我们安装element-ui和jquery
    
    // index.js
    import $ from "jquery";
    import ElementUI from "element-ui";
    import("./async.js").then((result) => console.log(result));
    import("./async1.js").then((result) => console.log(result));
    import("./async2.js").then((result) => console.log(result));
    // async async1 async2 设置了minChunks表示至少被几个chunk引用
    import title from "./title.js";
    

    webpack4流程分析6

    4.3

    // splitChunk就是将每个模块按照规则分配到不同的缓存组中
    // 每个缓存组对应最终分割出来的新代码块
    // chunksInfoMap 
    // addModuleToChunksInfoMap
    // newChunk = compilation.addChunk(chunkName);
    function  apply(compiler) {
      compiler.hooks.thisCompilation.tap("SplitChunksPlugin", (compilation) => {
        // 监听这个钩子 在生成了chunkGroup之后 优化的时候触发的代码分割
        compilation.hooks.optimizeChunksAdvanced.tap(
          "SplitChunksPlugin",
          (chunks) => {
            // 生成一个index 给每个选中的chunk一个index  create strings from chunks
            indexMap.set(chunk, index++);
            // key和chunkSet的关系
            const chunkSetsInGraph = new Map();
            // 遍历modules为每个module的chunk生成一个key
            // 经过上面的处理 我们得到四个chunk n个modules(element-ui和jquery中有很多个module)
            // {1: {}, 2: {}, 3: {}, 4: {}, '2, 3, 4': {set(3)}} title.js是在三个chunks中的
            for (const module of compilation.modules) {
              // chunksIterable return this._chunks
              const chunksKey = getKey(module.chunksIterable);
              if (!chunkSetsInGraph.has(chunksKey)) {
                chunkSetsInGraph.set(chunksKey, new Set(module.chunksIterable));
              }
            }
    
            // 按照count对这些chunk进行分组
            const chunkSetsByCount = new Map();
            // {1: [4], 3: [3]} 数量为1的是四个chunk 3个的是 set(3)
            for (const chunksSet of chunkSetsInGraph.values()) {
              // 每个module对应一个chunksSet 一个chunkSet中有多个module就是有多个count
              const count = chunksSet.size;
              let array = chunkSetsByCount.get(count);
              if (array === undefined) {
                array = [];
                chunkSetsByCount.set(count, array);
              }
              array.push(chunksSet);
            }
    
            // 创建一个可能组合的列表  Map<string, Set<Chunk>[]>
            const combinationsCache = new Map();
            // 根据key得到module中对应的chunks集合 set
            const chunksSet = chunkSetsInGraph.get(key);
            const getCombinations = (key) => {
              const chunksSet = chunkSetsInGraph.get(key);
              var array = [chunksSet];
              if (chunksSet.size > 1) {
                for (const [count, setArray] of chunkSetsByCount) {
                  // "equal" is not needed because they would have been merge in the first step
                  if (count < chunksSet.size) {
                    for (const set of setArray) {
                      if (isSubset(chunksSet, set)) {
                        array.push(set);
                      }
                    }
                  }
                }
              }
              return array;
            };
            // 处理缓存
            // const selectedChunksCacheByChunksSet = new WeakMap();
            // // 通过将过滤器功能应用于列表来获取列表和键 出于性能原因,它被缓存
            // const getSelectedChunks = (chunks, chunkFilter) => {};
            // Map a list of chunks to a list of modules 代码分割的信息
            const chunksInfoMap = new Map();
            const addModuleToChunksInfoMap = (
              cacheGroup, // the current cache group 当前的cache组
              cacheGroupIndex, // the index of the cache group of ordering
              selectedChunks, // chunks selected for this module
              selectedChunksKey, // a key of selectedChunks
              module // the current module
            ) => {
              // minChunks属性
              if (selectedChunks.length < cacheGroup.minChunks) return;
              // name
              const name = cacheGroup.getName();
              // key
              const key =
                cacheGroup.key +
                (name ? ` name:${name}` : ` chunks:${selectedChunksKey}`);
              let info = chunksInfoMap.get(key);
              // 添加新的缓存组信息
              chunksInfoMap.set(
                key,
                (info = {
                  modules: new SortableSet(undefined, sortByIdentifier),
                  cacheGroup,
                  cacheGroupIndex,
                  name,
                  size: 0,
                  chunks: new Set(),
                  reuseableChunks: new Set(),
                  chunksKeys: new Set(),
                })
              );
              info.modules.add(module);
              info.size += module.size(); // 判断我们设置的miniSize
              info.chunksKeys.add(selectedChunksKey);
              for (const chunk of selectedChunks) {
                info.chunks.add(chunk); // 最后打包的是chunk
              }
            };
            // 遍历模块 分组
            for (const module of compilation.modules) {
              // 一个module可能符合多个条件 我们会根据 优先级来判断
              let cacheGroups = this.options.getCacheGroups(module, context);
              const chunksKey = getKey(module.chunksIterable);
              for (const cacheGroupSource of cacheGroups) {
                const minSize = cacheGroupSource.minSize;
                const cacheGroup = {
                  // 一系列的属性 配置
                  key: cacheGroupSource.key,
                  // 默认优先级是0
                  priority: cacheGroupSource.priority || 0,
                  minSize,
                  minChunks: cacheGroupSource.minChunks,
                };
              }
              // 根据webpack的配置 选出符合添加的chunk
              for (const chunkCombination of combs) {
                addModuleToChunksInfoMap();
              }
            }
            // 处理size < minSize
            for (const pair of chunksInfoMap) {
              chunksInfoMap.delete(pair[0]);
            }
            // maxSize
            const maxSizeQueueMap = new Map();
            while (chunksInfoMap.size > 0) {
              let bestEntryKey; // 最匹配的cacheGroup分组信息
              let bestEntry;
              for (const pair of chunksInfoMap) {
                bestEntry = pair[1];
                bestEntryKey = pair[0];
              }
              const item = bestEntry;
              chunksInfoMap.delete(bestEntryKey);
              let chunkName = item.name;
              // 新的chunk
              let newChunk;
              // 重用模块 如果没有name 看是否能复用
              if (item.cacheGroup.reuseExistingChunk) {
              }
              // 创建新的代码块
              newChunk = compilation.addChunk(chunkName);
              for (const chunk of usedChunks) {
                // Add graph connections for splitted chunk 建立关系
                chunk.split(newChunk);
              }
              if (chunkName) {
                const entrypoint = compilation.entrypoints.get(chunkName);
              }
              // 删除提取出来的模块
              for (const [key, info] of chunksInfoMap) {
              }
              // Make sure that maxSize is fulfilled
              for (const chunk of compilation.chunks.slice()) {
              }
            }
          }
        );
      });
    }
    

    5. hash

    // 各种hash值 content hash chunk hash full hash module hash
    function createHash() {
      const hashFunction = outputOptions.hashFunction;
      const hash = createHash(hashFunction);
      // new MainTemplate(this.outputOptions);
      this.mainTemplate.updateHash(hash);
      this.chunkTemplate.updateHash(hash);
      for (const key of Object.keys(this.moduleTemplates).sort()) {
        this.moduleTemplates[key].updateHash(hash);
      }
      // module hash
      for (let i = 0; i < this.modules.length; i++) {
        const module = modules[i];
        const moduleHash = createHash(hashFunction);
        module.updateHash(moduleHash);
      }
      const chunks = this.chunks.slice();
      chunks.sort((a, b) => {});
      // chunk hash
      for (let i = 0; i < chunks.length; i++) {
        const chunk = chunks[i];
        const chunkHash = createHash(hashFunction);
        // hasRuntime
        this.hooks.contentHash.call(chunk);
      }
      this.fullHash = /** @type {string} */ (hash.digest(hashDigest));
      this.hash = this.fullHash.substr(0, hashDigestLength);
    }
    

    起源地下载网 » webpack4流程分析6

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元