zoukankan      html  css  js  c++  java
  • 基于CommonsChunkPlugin,webpack打包优化

    前段时间一直在基于webpack进行前端资源包的瘦身。在项目中基于路由进行代码分离,http://www.cnblogs.com/legu/p/7251562.html。但是打包的文件还是很大,特别是通过CommonsChunkPlugin的async:true打包的chunk的公共包不可控。今天就通过CommonsChunkPlugin插件的理解,来优化这个问题

      问题描述详细些,我们的打包是基于router进行的chunk分割,比如router有10个,router1,router2用到了echart,所以echart打包到了公共文件async中。但是如果用户通过链接,

      第一次直接访问的router3,这样就会先加载公共文件async,可是echart代码其实是多余的,影响到了router3的展现。

      一开始遇到这个问题,没想到太好的方法,让echart基于chunk进行按需打包~~实在没办法,最后只能从CommonsChunkPlugin插件的源代码入手,看看有什么启发。

      

     1     apply(compiler) {
     2         compiler.plugin("this-compilation", (compilation) => {
     3             compilation.plugin(["optimize-chunks", "optimize-extracted-chunks"], (chunks) => {
     4                 /**
     5                  * 根据chunkNames[options.name, options.names],从chunks中筛选出targetChunks, 没有则使用compilation.addChunk新增
     6                  * 如果children || async,返回targetChunks = chunks
     7                  */
     8                 const targetChunks = this.getTargetChunks(chunks, compilation, this.chunkNames, this.children, this.async);
     9                 targetChunks.forEach((targetChunk, idx) => {
    10                     /**
    11                      * 根据selectedChunks【options.chunks】,从chunks筛选出affectedChunks
    12                      * async || children,返回 affectedChunks = targetChunk.chunks, 如果children = true,进行深度遍历  
    13                      */
    14                     const affectedChunks = this.getAffectedChunks(compilation, chunks, targetChunk, targetChunks, idx, this.selectedChunks, this.async, this.children, this.deepChildren); 
    15                     let asyncChunk;
    16                     if(this.async) {
    17                         //如果async==string,进行name筛选
    18                         asyncChunk = affectedChunks.filter(c => c.name === this.async)[0];
    19                         if(!asyncChunk) {
    20                             /**
    21                              * 根据async创建一个新的chunk,和targetChunk绑定关系
    22                              * asyncChunk.addParent(targetChunk); targetChunk.addChunk(asyncChunk);  
    23                              */
    24                             asyncChunk = this.createAsyncChunk(
    25                                 compilation,
    26                                 targetChunks.length <= 1 || typeof this.async !== "string" ? this.async :
    27                                 targetChunk.name ? `${this.async}-${targetChunk.name}` :
    28                                 true,
    29                                 targetChunk
    30                             );
    31                         }
    32                         targetChunk = asyncChunk;
    33                     }
    34                     // 根据minChunks的设置,遍历affectedChunks的modules,返回符合条件的公共modules集合
    35                     const extractableModules = this.getExtractableModules(this.minChunks, affectedChunks, targetChunk);
    36                     if(this.minSize) {// minSize限制逻辑
    37                         const modulesSize = this.calculateModulesSize(extractableModules);
    38                         if(modulesSize < this.minSize)
    39                             return;
    40                     }
    41                     // affectedChunks中移除extractableModules中modules的关系,只返回存在公共modules的Chunks集合(removeChunk返回true)
    42                     const chunksWithExtractedModules = this.extractModulesAndReturnAffectedChunks(extractableModules, affectedChunks);
    43                     // 公共的modules 和 targetChunk 绑定关联关系
    44                     // chunk.addModule(module); module.addChunk(chunk);
    45                     this.addExtractedModulesToTargetChunk(targetChunk, extractableModules);
    46                     if(this.filenameTemplate)
    47                         targetChunk.filenameTemplate = this.filenameTemplate;
    48                     if(this.async) {
    49                         //被移除modules的Chunk,设置和targetChunk的关系,需要第一个加载targetChunk才能加载chunksWithExtractedModules
    50                         this.moveExtractedChunkBlocksToTargetChunk(chunksWithExtractedModules, targetChunk);
    51                         asyncChunk.origins = this.extractOriginsOfChunksWithExtractedModules(chunksWithExtractedModules);
    52                         return;
    53                     }
    54                     //设置affectedChunks和targetChunk的parent关系
    55                     this.makeTargetChunkParentOfAffectedChunks(affectedChunks, targetChunk);                    
    56                 });
    57                 return true;
    58             });
    59         });
    60     }

      代码逻辑不是很复杂,主要是chunks之间的关系和chunks与modules之间的关系该怎么去维护,对于不清楚webpack打包机制的人,很难一时间了解。其实我也不很了解。

      根据上面我的中文注释,对大家的了解有一些帮助。我们会发现,对我们的问题没有什么直接关系。

      回到我们的问题,异步的模块中,共用模块怎么能再进行拆分,把大模块echarts,ace编辑器等进行分开打包,并且能自己处理关系,需要的时候才异步加载进来?

      其实最后问题的答案很简单,需要实现自动异步加载,那肯定还是要借助CommonsChunkPlugin的async,我们可以根据实际情况,通过minChunks,把echarts,ace这种大库先进行一次async打包,这样再进行根据router的async打包的时候,自然不会再有echarts,ace了,看下现在的配置

     1         new webpack.optimize.CommonsChunkPlugin({
     2             names: ['vendor', 'libs', 'manifest']
     3         }),
     4         new webpack.optimize.CommonsChunkPlugin({
     5             async: 'brace',
     6             minChunks: function(module, count) {
     7                 var path = `/public/node_modules`;
     8                 var resource = module.resource;
     9                 if ( resource && 
    10                         (
    11                             resource.indexOf(`${path}/_brace`) !== -1 ||
    12                             resource.indexOf(`${path}/brace`) !== -1
    13                         )
    14                 ) {
    15                     return true
    16                 }
    17                 return false;
    18             }
    19         }),
    20         new webpack.optimize.CommonsChunkPlugin({
    21             async: 'echarts',
    22             minChunks: function(module, count) {
    23                 var path = `/public/node_modules`;
    24                 var resource = module.resource;
    25                 if ( resource && 
    26                         (
    27                             module.resource.indexOf(`${path}/_echarts`) !== -1 ||
    28                             module.resource.indexOf(`${path}/echarts`) !== -1 ||
    29                             module.resource.indexOf(`${path}/zrender`) !== -1 ||
    30                             module.resource.indexOf(`${path}/_zrender`) !== -1 
    31                         )
    32                 ) {
    33                     return true
    34                 }
    35                 return false;
    36             }
    37         }),
    38         new webpack.optimize.CommonsChunkPlugin({
    39             async: 'async',
    40             minChunks: 2
    41         }),

      

  • 相关阅读:
    jQuery.delegate() 函数详解
    java中的Class.forName的作用
    java Map及Map.Entry详解
    SQL Case when 的使用方法
    SpringBoot入门篇--对于JSON数据的返回以及处理二
    SpringBoot入门篇--对于JSON数据的返回以及处理一
    SpringBoot入门篇--使用IDEA创建一个SpringBoot项目
    数据结构和算法之栈和队列三:自定义一个栈包含min函数
    数据结构和算法之栈和队列二:栈的压入,弹出序列
    数据结构和算法之栈和队列一:两个栈模拟一个队列以及两个队列模拟一个栈
  • 原文地址:https://www.cnblogs.com/legu/p/8359898.html
Copyright © 2011-2022 走看看