webpack dll打包重复问题优化

关于webpack dll的使用,我这里不作过多介绍,网上都有,一撸一大把,今天我要说的是在使用dll plugin过程当中出现的一个包依赖问题,这个问题致使打出来的包会包含重复的代码。vue

优化背景

最近在给公司项目优化的时候,因为内部CDN上传文件大小限制了500K,因此用了webpack dll来进行拆分打包,我将拆分的包分为三部分:webpack

  • vue生态包(vuevuexvue-routervuex-classvue-class-component等周边生态的库)
  • vue插件包(vee-validate、内部UI库,图片预览等vue插件库)
  • 第三方包(axios、内部一些错误统计、上报,员工水印等这些脱离于vue的第三方库)

三部分的包名分别是vue.dll.jsplugin.dll.jslib.dll.js,这样的好处是结构清晰,最重要的缘由仍是分解包的大小,下降到500K之内ios

可是在进行dll打包后,我惊奇地发现vue.dll.jsplugin.dll.js中会包含重复的vue的dist代码web

下面是分别是前两部分的bundle分析图vue-router

vue.dll.js

plugin.dll.js

能够看到这俩dll都包含了vuevuex

那么要分析问题缘由,先说一下个人DLL的配置吧axios

DLL配置

由于webpack支持多entry,因此通常多入口dll打包的话,首先会考虑一个webpack配置,多个entry入口,因此可能会出现优化

// webpack.dll.conf.js

module.exports = {
    // 其余配置先省略
        entry: {
             vue: ['vue', 'vuex', 'vue-router', ...],
             plugin: ['vee-validate', '内部UI库', ...],
             lib: ['axios', 'dayjs', ...]
        },
    plugins: [
        new webpack.DllPlugin({
            // dll.配置
        })
    ]
}

可是亲测这样打包出来的文件依然有上述问题ui

因此结合我在以前公司所实践的webpack multi compiler方式,参考webpack multi compiler,我把webpack的配置一分为三,每个dll包都有一个webpack配置,即spa

// config.js

exports.dll = [
    {
        name: 'vue',
        libs: ['vue', 'vuex', 'vue-router', 'vuex-class', 'vue-class-component']
    },
    {
        name: 'lib',
        libs: [axios', 'dayjs', '第三方库']
    },
    {
        name: 'plugin',
        libs: ['vee-validate', 'v-viewer', 'vue插件库']
    }
]
// webpack.dll.conf.js

module.exports = config.dll.map(function (vendor) {
    return {
        // 省略其余配置
        entry: {
            [vendor.name]: vendor.libs
        },
        plugins: [
            new webpack.DllPlugin({
                // dll.配置
            })
        ]
    }
})
// dll.js

const dllConfig = require('./webpack.dll.conf')

webpack(dllConfig, function (err, stats) {
    if (err) throw err
    // 处理stats相关信息
})

本觉得这样能够解决问题,可是现实倒是不能,因此得先分析一下问题所在

分析问题

通过仔细的排查,发现是因为内部UI库中单独引用了vue,即在库中有

import Vue from 'vue'

// ...
// Vue相关操做
// Vue.prototype.$isServer等

这样不论是多入口打包仍是multi compiler方式下都会出现重复的包

解决方法

分析dll的原理,其实dll在打包的时候会将全部包含的库作一个索引,写在一个manifest文件中,而后在引用dll的时候只须要引用这个manifest文件便可

因此我就在想,若是plugin.dll.js依赖于vue.dll.js中的vue,那么是否能够先打包vue.dll.js,而后在打包plugin.dll.js的时候引用vue.dll.js呢?

心动不如行动,赶忙尝试一下,作出以下修改

// config.js

exports.dll = [
    {
        name: 'vue',
        libs: ['vue', 'vuex', 'vue-router', 'vuex-class', 'vue-class-component']
    },
    {
        name: 'lib',
        libs: [axios', 'dayjs', '第三方库']
    },
    {
        name: 'plugin',
        libs: ['vee-validate', 'v-viewer', 'vue插件库'],
        ref: 'vue'
    }
]
// webpack.dll.conf.js

// generate config
const gen = function (vendors) {
    return vendors.map(function (item) {
        const base = {
            entry: {
                [item.name]: item.libs
            },
            plugins: [
                new webpack.DllPlugin({
                    // dll配置
                })
            ]
        }
        
        if (item.ref) {
            // 重点在这
            // 在有ref的dll配置中,插入dll reference的plugin,内容是所依赖的dll包的manifest
            base.plugins.push(new webpack.DllReferencePlugin({
                // dll reference其余配置
                manifest: '所依赖的dll包的manifest文件路径'
            }))
        }
        
        return base
    })
}

// 根据是否有ref依赖项,区分base config和ref config
const [baseVendors, refVendors] = config.dll.vendors.reduce((config, v) => {
    config[v.ref ? 1 : 0].push(v)
    return config
}, [
    [],
    []
])

// 生成base config
const getConfig = function () {
    return gen(baseVendors)
}

// 生成ref config
const getRefConfig = function () {
    return gen(refVendors)
}

module.exports = {
    getConfig,
    getRefConfig
}
// dll.js

const dllConfig = require('./webpack.dll.conf')

// 由于ref config依赖于base config,因此要保证base config先打包出来
const runWebpack = function (config) {
    return new Promise(function (resolve) {
        webpack(config, function (err, stats) {
            if (err) throw err
            // ...
            resolve()
        })
    })
}

module.exports = function run () {
    runWebpack(dllConfig.getConfig())
        .then(() => runWebpack(dllConfig.getRefConfig()))
}

总体变成了以下结构

dll structure

最关键的一步就是plugin.dl.js会引用vue.dll.js的manifest文件,这样公共部分vue,就只会出如今vue.dll.js中了,plugin.dll.js打包后的bundle分析图以下

plugin.dll.js

能够很明显地看到plugin.dll.js中已经没有vue dist的身影了,包的体积获得了优化✌️

可优化项

上述优化其实只考虑了一个依赖项,那么若是plugin.dll.js同时依赖于vue.dll.js和lib.dll.js呢?若是此时vue.dll.js也依赖于lib.dll.js呢?

若是出现上述状况,那么请先考虑dll包是否须要拆分?拆分是否合理?

而后再思考如何根据依赖顺序思考打包顺序,以及若是出现循环依赖,该怎么办?

因为目前优化需求中还未出现这种状况(这种状况应该不多不多不多见),因此我这边就没有解决这些问题了

总结

参考日常打包经过dll reference plugin来引用dll包的manifest的方式,若是多个dll包内出现了依赖,致使打包重复,那么是能够在依赖包中运用dll reference plugin来引用被依赖包的dll manifest,不过这样的话,须要注意dll包的打包顺序,被依赖包的dll要先于依赖包dll进行打包

相关文章
相关标签/搜索