• Webpack SplitChunks插件用法详解 广东靓仔


    欢迎关注前端早茶,与广东靓仔携手共同进阶
    前端早茶专注前端,一起结伴同行,紧跟业界发展步伐~

    前言

    SplitChunks插件是什么呢,简单的来说就是Webpack中一个提取或分离代码的插件,主要作用是提取公共代码,防止代码被重复打包,拆分过大的js文件,合并零散的js文件。

    提到前端优化,提取公共代码是必不可少的手段。在Webpack出现前,提取公共代码是人为去处理,而SplitChunks插件的作用就是通过配置让Webpack去帮你提取公共代码。Webpack创始人的初衷也是希望能有更多时间写更多代码,所以这种纯体力的劳动交给Webpack去完成。

    所以SplitChunks插件是前端进阶的一项必备技能,下面就详细介绍SplitChunks插件的用法。

    一、工欲利其事必先利其器

    因为SplitChunks插件会提取模块并打包生成js文件。先学会给打包生成的js文件命名,不然不好确认打包生成的js文件是不是你想要的。Webpack中给打包生成的js文件命名有以下几种方法。

    1、output.filename

    此选项给打包后的入口js文件命名,下图中的app.js文件就是打包后的入口js文件。

    • 在单入口的Vue项目中,打包前的入口文件,在vue.config.js中配置

      module.exports = {
          configureWebpack:{
              entry:'./src/main.js',
          }
      }
      

      打包后的入口文件,在vue.config.js中配置

      module.exports = {
          configureWebpack:{
             output:{
                  filename: 'js/appCS.js'
              }, 
          }
      }
      

      为什么前面要加个js/,因为该js文件是output:path选项指定的目录下生成的,其默认为根目录/。打包结果如下所示

    • 在多入口的Vue项目中,打包前的入口文件,在vue.config.js中配置

        module.exports = {
            configureWebpack:{
                entry: {
                    appCS: './src/main.js'
                },
            }
        }
      

      打包后的入口文件,在vue.config.js中配置

      module.exports = {
          configureWebpack:{
              output:{
                  filename: 'js/[name].js'
              },
          }
      }
      

      [name]是入口文件模块名称。打包结果如下所示

      会发现多了个app.js,这是因为configureWebpack的值为对象时,则会通过webpack-merge 合并到最终的配置中。要去掉app.js可以用chainWebpack来配置。配置如下

      module.exports = {
          chainWebpack: config =>{
              config.entryPoints.delete('app').end().entry('appCS').add('./src/main.js')
          }
      }
      

      打包结果如下所示

    2、output.chunkFilename

    此选项给打包后的非入口js文件命名,那下图红框中所示就是非入口js文件

    在vue.config.js中配置

    module.exports = {
        configureWebpack:{
            output:{
                chunkFilename: 'CS.[name].js'
            },
        }
    }
    

    打包结果如下所示

    然而output.chunkFilename并不能改变chunk-0a4e15c9这样名字字段。

    3、webpackChunkName

    webpackChunkName:块的名称,[request]可解释为实际的解析文件名。可在路由懒加载中使用

    function load(component) {
        return () => import(/* webpackChunkName: "[request]" */ `views/${component}`)
    }
    const routes = [
        {
            {
                path: '/apiApply',
                name: 'apiApply',
                component: load('api_manage/api_apply'),
            }
        }
    ]
    

    打包结果如下所示

    src/views/api_manage/api_apply/index.vue这个组件打包后生成的js文件是api_manage-api_apply.48227bf7.js

    如果不用[request]也可以这么写。

    component: () =>import(/* webpackChunkName: "api_manage-api_apply"*/ 'src/views/api_manage/api_apply'),
    

    再看一下其它js文件,发现还有chunk-xxx.js类的文件,如下图红框中所示

    这些js文件的命名要在SplitChunks插件中设置了。

    二、插件配置选项

    • chunks选项,决定要提取那些模块。
      • 默认是async:只提取异步加载的模块出来打包到一个文件中。
        • 异步加载的模块:通过import('xxx')require(['xxx'],() =>{})加载的模块。
      • initial:提取同步加载和异步加载模块,如果xxx在项目中异步加载了,也同步加载了,那么xxx这个模块会被提取两次,分别打包到不同的文件中。
        • 同步加载的模块:通过 import xxxrequire('xxx')加载的模块。
      • all:不管异步加载还是同步加载的模块都提取出来,打包到一个文件中。
    • minSize选项:规定被提取的模块在压缩前的大小最小值,单位为字节,默认为30000,只有超过了30000字节才会被提取。
    • maxSize选项:把提取出来的模块打包生成的文件大小不能超过maxSize值,如果超过了,要对其进行分割并打包生成新的文件。单位为字节,默认为0,表示不限制大小。
    • minChunks选项:表示要被提取的模块最小被引用次数,引用次数超过或等于minChunks值,才能被提取。
    • maxAsyncRequests选项:最大的按需(异步)加载次数,默认为 6。
    • maxInitialRequests选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。
    • 先说一下优先级 maxInitialRequests / maxAsyncRequests <maxSize <minSize
    • automaticNameDelimiter选项:打包生成的js文件名的分割符,默认为~
    • name选项:打包生成js文件的名称。
    • cacheGroups选项,核心重点,配置提取模块的方案。里面每一项代表一个提取模块的方案。下面是cacheGroups每项中特有的选项,其余选项和外面一致,若cacheGroups每项中有,就按配置的,没有就使用外面配置的。
      • test选项:用来匹配要提取的模块的资源路径或名称。值是正则或函数。
      • priority选项:方案的优先级,值越大表示提取模块时优先采用此方案。默认值为0。
      • reuseExistingChunk选项:true/false。为true时,如果当前要提取的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提取的模块打包生成新的js文件。
      • enforce选项:true/false。为true时,忽略minSizeminChunksmaxAsyncRequestsmaxInitialRequests外面配置选项。

    配置选项很多,下面在实际项目中使用SplitChunks,让你更深刻理解这些配置选项。

    首先了解一下SplitChunks在Vue Cli3中的默认配置。在Vue Cli3源码中这样配置

    整理后默认配置如下所示:

    module.exports = {
        configureWebpack:config =>{
            return {
                optimization: {
                    splitChunks: {
                        chunks: 'async',
                        minSize: 30000,
                        maxSize: 0,
                        minChunks: 1,
                        maxAsyncRequests: 6,
                        maxInitialRequests: 4,
                        automaticNameDelimiter: '~',
                        cacheGroups: {
                            vendors: {
                                name: `chunk-vendors`,
                                test: /[\\/]node_modules[\\/]/,
                                priority: -10,
                                chunks: 'initial'
                            },
                            common: {
                                name: `chunk-common`,
                                minChunks: 2,
                                priority: -20,
                                chunks: 'initial',
                                reuseExistingChunk: true
                            }
                        }
                    }
                }
            }
        }
    };
    

    先安装个webpack-bundle-analyzer插件,可以可视化分析打包后的文件。

    npm install webpack-bundle-analyzer --save-dev
    

    vue.config.js中引入插件

    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
    module.exports={
        configureWebpack:config =>{
            return {
                plugins:[
                    new BundleAnalyzerPlugin()
                ]
            }
        }
    }
    

    打包后,会在浏览器自动打开http://127.0.0.1:8888/,内容如下所示

    会发现里面有个chunk-vendors.js文件。是cacheGroups中vendors这个方案打包出来的js文件。

    可以用name选项来修改chunk-vendors.js文件的名字,,代码如下

    vendors: {
        name: `app-chunk-vendors`,
        test: /[\\/]node_modules[\\/]/,
        priority: -10,
        chunks: 'initial'
    },
    

    打包后,会发现chunk-vendors.js文件已经变成了app-chunk-vendors.js文件,里面内容不变。

    三、入口文件的拆分

    先去掉cacheGroups里面的方案,再打包一下。

    cacheGroups: {
        vendors: false,
        common: false
    }
    

    app.a502ce9a.jschunk-be34ce9a.ceff3b64.js这两个js文件是由项目中main.js这个入口文件打包生成的。

    例如app.js文件中有element-ui、moment、jquery、vue、router、store、jsencrypt等内容。这些都是在main.js中引入

    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    import JsEncrypt from 'jsencrypt';
    import $ from 'jquery';
    import ElementUI from 'element-ui';
    Vue.use(ElementUI);
    import treeSelect from 'fxft-tree-select';
    Vue.use(treeSelect);
    import moment from 'moment';
    Vue.prototype.moment = moment;
    //注册全局变量、全局函数
    import base from 'service/base';
    Vue.use(base);
    //注册打印插件
    import print from  'service/print';
    Vue.use(print);
    const vm = new Vue({
        router,
        store,
        render: h => h(App)
    }).$mount('#app')
    window.vm = vm;
    

    打包生成的index.html中有段代码是这么写。

    <body>
        <div id=app></div>
        <script src=/js/app.a502ce9a.js></script>
    </body>
    

    表明app.js是项目一开始就要加载的,会影响首屏的加载时间。那么可以在main.js中去掉一些在首屏中暂时用不到的引入,比如这些都可以暂时去掉。

    import JsEncrypt from 'jsencrypt';
    import treeSelect from 'fxft-tree-select';
    Vue.use(treeSelect);
    //注册全局变量、全局函数
    import base from 'service/base';
    Vue.use(base);
    //注册打印插件
    import print from  'service/print';
    Vue.use(print);
    

    打包后再看分析图,会发现jsencrypt等内容都在app.js中都消失了。

    为什么说chunk-be34ce9a.js也是从main.js打包生成的?因为在main.js中有段代码:

    //注册全局变量、全局函数
    import base from 'service/base';
    Vue.use(base);
    

    在看service/base.js文件中有

    import('./Export2Excel').then(res => {
        res.export_json_to_excel(defaultOpition);
    })
    

    Export2Excel.js是异步加载,在service/Export2Excel.js

    import { saveAs } from 'file-saver'
    import XLSX from 'xlsx'
    

    相当,file-saver、xlsx也是异步加载,所以file-saver、xlsx会被提取出来打包生成chunk-be34ce9a.js文件。

    在默认配置下,main.js中异步加载或间接异步加载的模块,都会被另外打包生成一个js文件。

    如果要把从node_modules中加载的模块全部打包到一个js文件中,要怎么做呢?Vue Cli3中的已经帮我们做了。

    cacheGroups: {
        vendors: {
            name: `chunk-vendors`,
            test: /[\\/]node_modules[\\/]/,
            priority: -10,
            chunks: 'initial'
        },
    }
    

    其核心是test选项,匹配项目从node_modules中加载的模块并提取打包生成chunk-vendors.js文件。 打包后再从分析图中搜索node_modules,发现还是由很多文件中含有从node_modules中加载的模块,和预期的不一样。

    这是chunks选项在作怪,值initial表示如果xxx在项目中异步加载或同步加载多少次,那么xxx这个模块也会被提取多少次,分别打包到不同的文件中。core-js库在项目中每个文件都会加载到,故它会提取多次。

    只要把chunks选项的值改成all(不管异步加载还是同步加载的模块都是提取打包到一个文件中),就可以把从node_modules中加载的模块全部打包到一个js文件中。

    发现chunk-vendors.js的大小有点大了,有1.91MB,还是项目初始化时需要加载的js文件,大小过大会导致首屏加载时间过长。要优化一下,由两种方法

    第一种用externals来优化,看我的另一篇文章Webpack之externals用法详解

    第二种用SplitChunks优化。例如要把elementchunk-vendors.js提取出来,要在cacheGroups中配置:

    element: {
        chunks: 'all',
        name: `element-ui`,
        test: /[\\/]element-ui[\\/]/,
        priority: 0,
    },
    

    其中要注意priority选项,要把element单独提取出来,priority的值必须比vendors方案中的priority的值大,不然提取不出来。

    打包后可看到element被打包生成新的element-ui.js文件,chunk-vendors.js大小变成1.27MB,比原来的1.91MB有减小。此外可以自己提取xlsx、moment、jquery等第三方依赖。

    四、非入口文件的拆分和合并

    在分析图中,除了入口文件,还有很多js文件,这些文件中有一大部分是项目中组件打包生成的。

    如果在实现路由懒加载时,用到/*webpackChunkName:"[request]"*/,那么在由组件打包生成的js文件名上,可以得知这个js文件是哪个组件打包生成的。

    图中的base_info_manage-group_info_set-ability_bind_set.85b419a1.js是项目中views/base_info_manage/group_info_set/bility_bind_set/index.vue这个组件打包生成的。

    base_info_manage-group_info_set-ability_bind_set-edit.08f91768.js是项目中views/base_info_manage/group_info_set/bility_bind_set/edit.vue这个组件打包生成的。

    另外会发现chunk-5c1416e3.1cbcb0ec.js的内容怎么跟base_info_manage-group_info_set-ability_bind_set-edit.08f91768.js的内容相似,只少了src/api的内容。而且api/common.jsapi/ability_bind_set.jsedit.vuemixins等模块被重复打包了好几次。

    可以用SplitChunks提取一下这些模块。避免重复打包,减少打包生成的文件总体大小。

    cacheGroups中配置

    api: {
        name: 'api',
        test: /[\\/]api[\\/]/,
        priority: 0,
    },
    

    当提取多个模块打包生成文件时,name选项为必填。

    打包后再看分析图会发现api/common.jsapi/ability_bind_set.js已经被提取到api.05ad5193.js中了

    接着提取mixins模块,在cacheGroups中配置

    mixins: {
        name: 'mixins',
        test: /[\\/]mixins[\\/]/,
        priority: 0,
    },
    

    打包后再看分析图发现mixins模块已经被提取到mixins.8d1d6f50.js中。

    接着提取edit.vue模块,在cacheGroups中配置

    base_info_manage: {
        name: 'base_info_manage',
        test: /[\\/]base_info_manage[\\/]/,
        minChunks: 2,
        priority: 0,
    },
    

    其中minChunks选项,必须为2,因为从上面分析图来看,edit.vue被引用了2次,而index.vue只被引用了1次。如果为1则index.vue也会被提取出来。如果为2以上的值则edit.vue不会被提取出来。

    打包后再看分析图发现edit.vue模块已经被提取到base_info_manage.d5c14c01.js中。

    如果觉得base_info_manage.d5c14c01.js文件太大,有两种方法可以处理。

    第一种是利用maxSize选项,提取模块后打包生成的文件大小不能超过maxSize值,如果超过了,要再提取并打包生成新的文件。

    base_info_manage: {
        name: 'base_info_manage',
        test: /[\\/]base_info_manage[\\/]/,
        minChunks: 2,
        priority: 0,
        maxSize: 102400
    },
    

    打包后再看分析图会发现base_info_manage.js已经被分割成五个小js文件了。

    第二种是按base_info_manage文件夹下的子文件夹来继续提取,例如base_info_manage文件夹下有个子文件叫group_info_set。在cacheGroups中配置

    group_info_set: {
        name: 'group_info_set',
        test: /[\\/]base_info_manage[\\/]group_info_set[\\/]/,
        minChunks: 2,
        priority: 10,
    },
    

    打包后再看分析图会发现base_info_manage.js中的group_info_set模块集合已经被提取到group_info_set.js文件中。base_info_manage.d5c14c01.js文件也有相应的减小。

    另外可以把src/apisrc/mixinssrc/service中的内容合并打包生成一个js文件,替代之前打包生成的mixins.8d1d6f50.jsapi.05ad5193.js

    common: {
        test: /[\\/]api[\\/]|[\\/]mixins[\\/]|[\\/]src[\\/]service[\\/]/,
        priority: 0,
        name: 'common',
    },
    

    其他的就不多说,可以按照上面所讲在自己的项目用SplitChunks插件来控制Webpack打包生成自己想要的js文件,直到dist/js文件夹中每个js都是你所预想的生成的js文件为止。

    五、总结

    用SplitChunks插件来控制Webpack打包生成的js文件的内容的精髓就在于,防止模块被重复打包,拆分过大的js文件,合并零散的js文件。最终的目的就是减少请求资源的大小和请求次数。因这两者是互相矛盾的,故要以项目实际的情况去使用SplitChunks插件,需切记中庸之道。

     
     
    欢迎关注前端早茶,与广东靓仔携手共同进阶
    前端早茶专注前端,一起结伴同行,紧跟业界发展步伐~
     
  • 相关阅读:
    .net core 基于IIS发布
    所有开源播放器
    JWT的相关讲解
    .NET Core2.0 使用EF做数据操作
    任意的组合,数组或数组,数组或list,list或list不用循环得出匹配的总数和需要的字段列
    linq to sql学习
    winform文本框怎么实现html的placeholder效果
    [数据结构]-单链表实现栈
    [数据结构]-数组实现栈
    [数据结构]-单链表
  • 原文地址:https://www.cnblogs.com/cczlovexw/p/16549981.html
Copyright © 2020-2023  润新知