0
点赞
收藏
分享

微信扫一扫

uni-app的splitChunks分包历险记

前言

构建结果从64.41M缩减为26.8M,优化率58.4%,同时构建时间也从10分钟缩减为4分钟,优化率也差不多60%,这个数据只是针对我们这个项目,没有什么参考意义。但是通过这个例子学到uni-app怎么分包,以及分包后出现问题对应的解决思路。注:vue2 + webpack

起因

业务端突然出现一个项目(uni-app开发)在k8s构建为h5的时候提示内存溢出,使用webpack-bundle-analyzer插件对其构建结果分析,发现存在很多冗余代码,构建结果变大占用过多的内存。比如很多页面使用了echarts图表,构建结果中就存在多份echarts,其他的公共代码也是如此。这边先以echarts作为示例,如下,我在两个页面pages/index/indexpages/me/index中使用了echarts,这两个页面被打包为两个独立的chunk,并且都包含了echarts代码,而不是抽离为公共代码进行复用,所以造成了代码冗余。

uni-app的splitChunks分包历险记_分包

uni-app打包为h5时,有多少个页面,就会打包为多少个chunk,这是由于uni-apppage.json进行解析转换,构建了一份路由,路由中组件是通过懒加载的形式,chunk的名称为路由转换连接符生成的,大概如下这个样子,这个只是个简化版,后续会出一篇page.json具体怎么转化为路由的文章。

{
    path: '/pages/index/index',
    component: () => import(/* webpackChunkName: 'pages-index-index' */ 'C:/uni-demo/src/pages/index/index.vue'),
},

从路由的结构可以看出,组件是异步加载的形式。我们再来看一下uni-app的基础分包配置,这边chunks的配置都是initial,而initial选项没办法抽离异步加载的公共模块,从而导致了公共的模块的重复引入到了构建结果中。

splitChunks: {
  cacheGroups: {
    vendors: {
      name: 'chunk-vendors',
      test: /[\\/]node_modules[\\/]/,
      priority: -10,
      chunks: 'initial'
    },
    common: {
      name: 'chunk-common',
      minChunks: 2,
      priority: -20,
      chunks: 'initial',
      reuseExistingChunk: true
    }
  }
},

解决方式

分包实现

再来回顾一下uni-app默认的splitChunks,它chunks的值为initial无法对于抽离异步加载的公共模块,所以我们的分包方式也很简单,让对应配置可以分离包含异步加载方式的公共模块就可以了,以下以uview-ui为示例,priority这个值是权重,值越大,权重越高,我们这边改为-9优先于默认配置权重就可以,最主要的是把chunks值设置为all,这样子就可以抽离同步和异步的公共模块了。chunks文档

module.exports = {
  configureWebpack: {
    optimization: {
      splitChunks: {
        cacheGroups: {
          'uview-ui': {
            name: 'chunk-uview-ui',
            test: /[\\/]node_modules[\\/]uview-ui[\\/]/,
            priority: -9,
            chunks: 'all',
          },
        },
      },
    },
  },
};

页面示例

这个是示例项目路由,将index页面放在主包,testpassion页面放在子包pages/child

"pages": [
    {
      "path": "pages/index/index",
    }
],
"subPackages": [
    {
      "root": "pages/child",
      "pages": [
        {
          "path": "test/index",
        },
        {
          "path": "passion/index",
        }
      ]
    }
],

分包结果,输出对应文件

这个是打包后的结果,可以得出两个结论:

  • uview-ui被分离出来了,说明我们配置的分包生效了
  • 每一个页面,都会打包出一个chunk,chunk的名称就是路由转换连接符生成的,印证了我上面所说的uni-app构造的路由结构

uni-app的splitChunks分包历险记_html_02

页面白屏啦

查看一下浏览器资源加载,发现chunk-uview-ui文件没有被加载,打开index.html也没看到chunk-uview-ui这个js文件。这个js文件是首页渲染必须的,现在没有注入到index.html,导致这个js没有被加载,所以页面白屏了。我们来分析一下原因,构建后的index.html这个文件是由html-webpack-plugin这个插件根据项目的index.html生成的,所以首先就是看一下项目内html-webpack-plugin是怎么配置的。

uni-app的splitChunks分包历险记_uni-app_03

查看webpack配置

webpack配置都是被vue-cli封装好的,没有直接在项目中显示,但vue有提供一个命令可以查看webpack的所有配置,vue inspect将配置在命令行中显示,但平常为了更好查看,都是将配置输出到一个文件中,所以可以这样子使用,vue inspect > config.js,这样子我们就可以看到html-webpack-plugin对应配置

uni-app的splitChunks分包历险记_html_04

对比vue配置

因为正常分包只需要在vue.config.js中做好分包配置就可以,所以我对于vue项目也输出了一份html-webpack-plugin配置,发现在vue中html-webpack-plugin配置没有具体写导入的chunks,所以我们的解决方式就是将html-webpack-plugin配置中多余的chunks删除掉。

uni-app的splitChunks分包历险记_分包_05

为什么删掉chunks配置就可以?

这个是html-webpack-plugin中的一段代码,生成引入html中的chunk之前,会执行这个方法,过滤掉不需要在首页加载的chunks,也就是js。filterChunks包含三个参数,chunks——项目的所有chunk,includedChunks——需要首页加载的所有chunk,没有配置的话,默认为allexcludedChunks——需要排除的chunk,默认为空数组。同样也会过滤!chunk.initial也就是async异步类型的chunk,我们在splitChunks配置chunks值为initialall都满足需求。

uni-app的splitChunks分包历险记_分包_06

接下来通过断点的方式查看一下filterChunks三个参数的具体值,如下:

uni-app的splitChunks分包历险记_分包_07

这样子就能得到我们想要的结论,虽然chunk-uview-uiinitial值为true,本来应该在index.html中被引入,但是uni-app配置了includedChunks的值为chunk-vendors、chunk-common、indexchunk-uview-ui不包含在includedChunks中,导致了它被过滤掉了,如果我们把html-webpack-plugin插件配置的chunks删掉,那includedChunks的值就会变为默认的allArray.isArray(includedChunks)为false,就不会过滤本来应该被引用的chunk了,这样子就解释了为什么删掉chunks配置,我们的分包结果就能被正常加载。

解决对应问题,怎么删掉chunks配置

chainWebpack: (config) => {
  config.plugin('html-index').tap((options) => {
    delete options[0].chunks;
    return options;
  });
}

  • 我们其实也不知道options对应的接口,可以通过console.log或者debugger得到对应内容,可以看到options是个数组,包含了html-webpack-plugin配置。

uni-app的splitChunks分包历险记_uni-app_08

怎么知道是怎么修改呢,可以查看vue-cli文档,对象的修改插件选项方式

uni-app的splitChunks分包历险记_html_09

至于为什么在uni-app中是使用的插件名称是html-index,而不是html,我们这边本质修改的html-webpack-plugin的配置,通过vue inspect输出的结果可以看到怎么使用插件的备注config.plugin('html-index')

uni-app的splitChunks分包历险记_uni-app_10

这边是html-index而不是html的原因,是因为uni-app是使用多页面配置的方式,虽然平常只配置了一个页面,可以在@dcloudio/vue-cli-plugin-uni/lib/h5/index.html查看配置

uni-app的splitChunks分包历险记_uni-app_11

举报

相关推荐

0 条评论