Webpack dll优化实战

DLL是什么,用它来干啥?

  DLL(Dynamic Link Libray)原来特指windows系统中实现共享函数库的一种方式,扩展名通常为.dll。玩过老windows游戏的同学应该对这种文件不陌生,很多游戏的安装盘下就有很多.dll的文件。DLL通常是已经编译、链接的二进制文件,方便程序直接调用。

前端应用场景

  在大型项目的开发过程中,往往会用到很多公共库,公共库的内容不同于业务代码,在很长的一个时间周期内都不会有改动。这部分公共库通常会被打包在commonChunk中,webpack配置节选如下:

    optimization: {
        splitChunks: {
            minSize: 1000000,
            cacheGroups: {
                vendor: {
                    name: "common",
                    test: /[\\/]node_modules[\\/]/,
                    chunks: "initial",
                    minSize: 30000,
                    minChunks: 1,
                    priority: 8
                }
            },
        }
    },

  这样可以把长时间不变的公共包内容打包进一个名为common的chunk中,同业务代码进行隔离,生成稳定的缓存key,以便浏览器端实现缓存。但是这也带来一个问题:尽管用户端实现了缓存,但是我们在打包的时候,webpack依然会对所有用到的公共包进行遍历,解析,处理。严重影响打包速度。
  webpack中的DLL打包优化,同windows中的DLL的原理类似。通过另写一份打包配置,把长期不变的公共包内容单独打包,然后在业务代码打包时,通用webpack内置插件DllReferencePlugin引用之前打包的动态链接内容,而不是每次都打包同样的公共包内容,从而加快打包速度。

项目实战

接下来笔者将以自己项目为例,进行webpack dll优化。
使用原来的打包配置,结果如下:

image

打包耗时:
image

接下来,在项目中添加webpack.dll.config.js文件,对一些固定不变的内容提前打成dll资源:

const webpack = require('webpack')
const library = '[name]_dll'
const path = require('path');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  entry: {
    vendors: ['react', 'mobx', 'mobx-react', 'antd', 'socket.io-client']
  },

  output: {
    filename: '[name]_dll.js',
    path: path.resolve(__dirname, 'dist'),
    //publicPath: path.resolve(__dirname, 'dist'),
    publicPath: './',
    library
  },

  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, 'dist/[name]-manifest.json'),
      // This must match the output.library option above
      name: library
    }),
    new BundleAnalyzerPlugin({
        analyzerPort: 8899
    })
  ],
}

打包结果如下(vendors_dll.js: 685kb):

image

这个dll包压缩后还有600多kb!
接下来我们再对项目代码进行打包,在webpack配置文件中添加如下插件:

    plugins: [
        ......
        //  动态链接库
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dist/vendors-manifest.json')
        }),
        ......
    ]    

打包大小:

image

打包时间:
image

  通过如上实验我们可以发现,使用DllReferencePlugin插件后,打包时间大幅缩小,从原来的16s缩减到10秒左右,这是由于相当部分的资源已经提前构建好,在业务代码改变的时候,自然就不用重复打包浪费时间了。但同时暴露出一个问题:原先的打包方式打包文件的体积大概是378kb,使用dll之后,打包体积是dll资源与业务代码之和(685kb+92kb),整整大了两倍。要知道,dll文件浏览器也是需要下载的,这样完全是无法接受的。通过对打包内容的分析,我们可以发现webpack.dll.config.js文件的入口不是业务代码,直接是各个公共包,这就意味着dll显然是无法使用treeShake等等优化特性的,比如antd这个包,优化前使用treeShake按需加载,打包体积只有168kb左右,而在dll包中,antd整个包都进行了引入,体积膨胀到了250kb左右。由此我们可以得到一个重要的实战经验:针对antd,lodash等等可以使用按需加载的公共库,不能提前打包在dll中,像react,mobx等等必然会全盘引入的公共库才是最适合放入dll中的。
  接下来我们修改一下webpack.dll.config.js的entry配置(删掉antd):

  entry: {
    vendors: ['react', 'mobx', 'mobx-react', 'socket.io-client']
  },

dll打包结果(vendors_dll:84kb):

image

业务代码打包结果:
image

二者相加,打包文件体积与使用DllReferencePlugin前相仿。打包时间缩减了两秒,在不增加打包文件体积的前提下,减少了打包时间。

如何将vendors_dll.js插入html?

  笔者在实践过程中还发现一个问题,打包后的vendors_dll.js并没有被HtmlWebpackPlugin所识别,导致输出的HTML文件里没有这个资源,解决方法如下,在html模板文件的body添加一个script标签,并且附带内容模板:

<body>
    <div id='main'></div>
    <script type="text/javascript" src="<%= htmlWebpackPlugin.options.vendor %>"></script>
</body>

然后在webpack文件中作如下更改:

//  头部引入打包好的文件
const manifest = require('./dist/vendors-manifest.json'); 
......

    plugins: [
        ......
        new HtmlWebpackPlugin({
            filename: '../dist/index.html',
            template: './views/template.html',
            inject: 'body',
            //  添加vendor,替换html模板中的内容
            vendor: './' + manifest.name + '.js' //manifest就是dll生成的json
        }),
        ......
    ]

最后在输出的html中将会带上我们的dll文件。


上文中用到的例子:https://github.com/dianluyuanli-wp/chat

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容