资讯专栏INFORMATION COLUMN

代码分割与懒加载情况下(code-splitting+lazyload)抽离懒加载模块的公用模块代码

zebrayoung / 1670人阅读

摘要:但是同时,抽离到父模块,也意味着如果有一个懒加载的路由没有用到模块,但是实际上引入了父模块,也为这也引入了的代码。

前言

我们清楚,在 webpack 中通过CommonsChunkPlugin 可以将 entry 的入口文件中引用多次的文件抽离打包成一个公用文件,从而减少代码重复冗余

    entry: {
        main: "./src/main.js",
        user: "./src/user.js"
    },
    ......
    new webpack.optimize.CommonsChunkPlugin({
        name: "commons",
        filename: "common.js",
        minChunks: 2,
    })
    
    // 打包生成一个文件common.js ,包含main.js 和 user.js 中引用两次及以上的模块代码

那么问题来了,当使用了类似 vue-router 的代码分割+懒加载功能的时候,每个路由对应的.vue文件中,共同引用了多次的模块,要怎么抽离出代码分割模块的公用模块代码出来呢?

问题实际场景

举个栗子

// 懒加载路由文件 routes.js
const
    Index = () => import(/* webpackChunkName: "index" */ "page/index/Index.vue"),
    User = () => import(/* webpackChunkName: "userIndex" */ "page/user/Index.vue"),
    UserDetail = () => import(/* webpackChunkName: "userDetail" */ "page/user/Detail.vue"),
    ...
// page/index/Index.vue 首页路由文件

// page/index/Index.vue 用户页路由文件

上述使用了vue-router懒加载打包出来的 首页路由文件index.js 和 用户页文件userIndex.js 都会包含一份 public.js的代码,重复了。

那么问题就是,在代码分割的代码中,怎么自动抽离公共代码? 就像CommonsChunkPlugin的效果一样,CommonsChunkPlugin怎么在 code-splitting 的场景上使用呢 ?

解决方案

如问题所示,存在两个使用了webpack code-splitting 和 懒加载的路由文件,路由文件都使用了公用的public.js模块。

// page/index/Index.vue 首页路由文件

// 用户页
// page/index/Index.vue 用户页路由文件

要将 public.js公用模块抽离,有三种解决方案

方案一,CommonsChunkPlugin 具名模块

手动将所有共用的模块抽离在一个文件。
创建文件commons.js

// commons.js
import pub from "public"

webpack.config.jsCommonsChunkPlugin插件指定commons 的entry

// webpack.config.js
entry:{
    main: "src/main.js",
    commons: "src/commons.js"
},
...
    new webpack.optimize.CommonsChunkPlugin({
        name: "commons",   // 和 entry的commons对应,
        filename: "common.bundle.js", // 抽离公共文件
        minChunks: Infinity,
    })

这样,如果路由文件或其他模块使用到了 commons.js中的模块,都不会重复加载代码,而是在common.bundle.js中获取。

方案二,CommonsChunkPlugin 设置 children 属性

官方文档CommonsChunkPlugin 中 children属性解释

Move common modules into the parent chunk

With Code Splitting, multiple child chunks of an entry chunk can have common dependencies. To prevent duplication these can be moved into the parent. This reduces overall size, but does have a negative effect on the initial load time. If it is expected that users will need to download many sibling chunks, i.e. children of the entry chunk, then this should improve load time overall.

可知,设置 children 为 true 可以将code-splitting的模块的依赖模块抽离到父模块,这样做的后果就是,确实抽离公用模块,降低了代码重复,减少了代码体积。但是同时,抽离到父模块,也意味着如果有一个懒加载的路由 ShopList.vue 没有用到public.js 模块,但是实际上引入了父模块,也为这ShopList.vue也引入了public.js的代码。

这就需要CommonsChunkPluginasync 属性。

方案三(最佳实践),childrenasync 双管齐下
Extra async commons chunk

Similar to the above one, but instead of moving common modules into the parent (which increases initial load time) a new async-loaded additional commons chunk is used. This is automatically downloaded in parallel when the additional chunk is downloaded.

设置了async, 会将上述懒加载的路由文件公用的模块代码,抽离打包成一个多带带的文件,并且该文件是按需加载的,如果某个路由没有使用到这些公用模块,是不会加载进来的。

举个例子:
首页路由模块(访问路径/index),引用了 public模块
用户路由模块(访问路径/user),引用了 public模块
购物车模块(访问路径/shop),没有引用 public模块

那么,打包生成的文件大概是

main.js - 根入口文件
index.js - 首页路由文件
user.js - 用户路由文件
shop.js - 购物车路由文件
0.js - 抽离路由的公用模块文件

访问url/index,加载的依赖文件是main.js + index.js + 0.js
访问url/user,加载的依赖文件是main.js + user.js + 0.js
访问url/shop,加载的依赖文件是main.js + shop.js
基本解决了 lazy load + code-splitting 情况下的公用模块抽离。

以下附上简单的webpack.config.js配置代码

entry: {
    main: "./src/main.js"
},
...
plugins: [
    ...
    new webpack.optimize.CommonsChunkPlugin({
        name: "main",
        minChunks: 2,
        children: true,
        // deepChildren: true,
        async: true,
    })
]
The CommonsChunkPlugin has been removed in webpack v4 legato. To learn how chunks are treated in the latest version, check out the SplitChunksPlugin.

PS: webpack 4 已经将CommonsChunkPlugin废弃,解决方案仅能在webpack 3 以下使用。

参考资料

commons-chunk-plugin
CommonChunkPlugin: Feature - Select statically imported modules from chunks that were created from a dynamic import (require.ensure / System.import / import(".."))

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94023.html

相关文章

  • 前端笔记(二) 对象深浅拷贝,函数防抖与节流,函数柯里化 ,图片加载与懒加载

    摘要:对象是无法通过这种方式深拷贝。这就是函数防抖和节流要做的事情。函数防抖当触发频率过高时函数基本停止执行而函数节流则是按照一定的频率执行事件。 对象的深浅拷贝 对象的深拷贝与浅拷贝的区别: 浅拷贝:仅仅复制对象的引用, 而不是对象本身。 深拷贝:把复制的对象所引用的全部对象都复制一遍 浅拷贝的实现: var obj = { age : 18, person : { ...

    dongxiawu 评论0 收藏0
  • webpack4 SplitChunks实现代码分隔详解

    摘要:代码均放在仓库给我们带来了一些改变。插件以前使用允许我们将公共依赖项提取到现有的或全新的代码块中。代码详情见文章开头仓库。这才是配置的关键缓存组会继承的配置,但是和只能用于配置缓存组。可以通过禁用缓存组。代码均放在git仓库 Webpack 4给我们带来了一些改变。包括更快的打包速度,引入了SplitChunksPlugin插件来取代(之前版本里的)CommonsChunksPlugin插件...

    Zachary 评论0 收藏0
  • 如何优化你超大型React应用 【原创精读】

    摘要:往往纯的单页面应用一般不会太复杂,所以这里不引入和等等,在后面复杂的跨平台应用中我会将那些技术一拥而上。构建极度复杂,超大数据的应用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React为了大型应用而生,Electron和React-native赋予了它构建移动端跨平台App和桌面应用的能力,Taro则赋...

    cfanr 评论0 收藏0
  • 如何优化你超大型React应用 【原创精读】

    摘要:往往纯的单页面应用一般不会太复杂,所以这里不引入和等等,在后面复杂的跨平台应用中我会将那些技术一拥而上。构建极度复杂,超大数据的应用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React为了大型应用而生,Electron和React-native赋予了它构建移动端跨平台App和桌面应用的能力,Taro则赋...

    codecook 评论0 收藏0
  • 如何优化你超大型React应用 【原创精读】

    摘要:往往纯的单页面应用一般不会太复杂,所以这里不引入和等等,在后面复杂的跨平台应用中我会将那些技术一拥而上。构建极度复杂,超大数据的应用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React为了大型应用而生,Electron和React-native赋予了它构建移动端跨平台App和桌面应用的能力,Taro则赋...

    xiguadada 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<