webpack实战——打包优化【中】

作者: 流眸Tel | 来源:发表于2020-10-19 20:39 被阅读0次

前言

上篇从多线程打包缩小打包作用域两个方面入手,对webpack打包层面做出优化。本篇描述从动态链接库思想方面继续深入探究打包层面的深度优化。

动态链接库与DLLPlugin

动态链接库(Dynamic Link Library 或者 Dynamic-link Library,缩写为 DLL),是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。这些库函数的扩展名是 ”.dll"、".ocx"(包含ActiveX控制的库)或者 ".drv"(旧式的系统驱动程序)。

当一段相同的子程序被多个程序调用时,为了减少内存消耗,可以将这段子程序存储为一个可执行文件,当被多个程序调用时只在内存中生成和使用同一个实例。

今天要介绍的主角“DLLPlugin”则借鉴了动态链接库的思路,对于第三方模块或者一些不常变化的模块预先进行编译和打包,然后再项目实际构建过程中直接取用。不过区别还是有的,DLLPlugin实际生成的文件是JS文件而不是动态链接库。在打包vendor的时候还会附加生成一份vendor的模块清单,这份清单将会在工程业务模块打包时起到链接和索引的作用。

1 vendor配置

首先需要为动态链接库单独创建一个Webpack配置文件,例如:webpack.vendor.config.js,注意要与webpack.config.js区分开来。

例:

// webpack.vendor.config.js
const path = require('path');
const webpack = require('webpack');
const dllAssetPath = path.join(__dirname, 'dll');
const dllLibraryName = 'dllExample';

module.exports = {
    entry: ['react'],
    output: {
        path: dllAssetPath,
        filename: 'vendor.js',
        library: dllLibraryName
    },
    plugins: [
        new webpack.DllPlugin({
            name: dllLibraryName,
            path: path.join(dllAssetPath, 'manifest.json')
        })
    ]
}

其中,entry指定了将哪些模块打包为vendor,plugins的部分引入了DLLPlugin,并有如下配置:

  • name: 导出的dll library的名字,需要与output.library的值对应;
  • path: 资源清单的绝对路径,业务打包时将会使用这个清单进行模块索引;

2 vendor打包

接下来就要打包vendor并且生成资源清单。为后续方便操作,可以在package.json中配置一条运行指令:

// pachage.json
{
    ...
    "scripts": {
        ...
        "dll": "webpack --config webpack.vendor.config.js"
    }
}

然后执行npm run dll,会发现生成了一个dll目录,里面对应有两个文件:

  • vendor.js: 库的代码
  • manifest.json: 资源清单

感兴趣的可以打开这两个文件阅读一下。

3 链接到业务代码

试过之后,我们就要考虑将vendor链接到项目中去了。这里推荐与DLLPlugin配套的插件“DLLReferencePlugin”,它起到索引和链接作用。在工程的webpack配置文件中(注意是webpack.config.js,不是vendor的配置文件),通过DLLReferencePlugin来获取刚才打包好的资源清单,然后在页面中添加vendor.js就可以引用。

// webpack.cinfig.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
    ...
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: require(path.join(__dirname, 'dll/manifest.json')
        })
    ]
}

那么 index.html 引入会即可:

...

<body>
...

<script src="dll/vendor.js"></script>
<script src="dist/app.js"></script>
</body>

设置完毕后,当页面执行到vendor.js时,会声明全局变量dllExample,而manifest相当于注入app.js的资源地图,app.js会通过name字段找到名为DLLExample的library,再进一步获取其内部模块。

4 潜在问题

细心的小伙伴或许已经发现了,在当前配置中会存在一个问题:当打开manifest.json文件后,可以发现每个模块都会有一个id,其值是按照数字顺序递增的,而业务代码在引用vendor中模块时也是引用这个数字id,当我们更vendor时这个数字id也会随之发生改变。

现假设我们工程目录中有如下资源文件,并每个资源都加上了chunk hash:

  • vendor@[hasn].js
  • pageUser@[hasn].js
  • pageIndex@[hasn].js
  • util@[hasn].js

现在vendor中you一些模块,例如包含了react,其id为5.当尝试添加更多模块到vendor中的时候,那么重新进行Dll构建时,moment.js可能出现在react之前,此时react的id会变为6.而pageUser和pageIndex是通过id进行引用的,因此他们的文件内容也发生了改变。此时我们会面临如下情况:

  1. 两个页面的chunk hash均发生了改变。这是我们不希望看到的,因为他们本身并无变化,但是vendor的改变却驱使用户不得不重新下载所有资源。
  2. 两个页面chunk hash没有改变,但是这种情况更为糟糕:vendor中的模块id改变了,但是用户没有更新缓存,使用的还是旧版本的内容,而引用不到新的vendor模块,导致页面发生错误。并且对于开发者而言,这个错误却难以排查,因为开发环境下一切正常!

针对上述的问题2,解决方法是在打包vendor时添加上HashedModuleIdsPlugin,如下:

// webpack.vendor.config.js
module.exports = {
    ...
    plugins: [
        new webpack.DllPlugin({
            name: dllLibraryName,
            path: path.join(dllAssetPath, 'manifest.json')
        }),
        // 添加HashedModuleIdsPlugin
        new webpack.HashedModuleIdsPlugin();
    ]
}

HashedModuleIdsPlugin是webpack3中被引入进来的,主要就是为了解决数字id的问题。HashedModuleIdsPlugin可以把id的生成算法修改为根据模块的引用路径生成一个字符串hash。

注:从webpack3开始,模块id不仅可以是数字,也可以是字符串。

小结

本篇从动态链接库思想着手,介绍了DLLPlugin与其配套插件DLLReferencePlugin使用,将第三方库与一些不常改动的模块编译打包,处理为类似于动态链接库的JS文件,以此来节约服务器资源。
下一篇介绍打包优化最后一个环节:死代码检测与去除。

相关文章

  • webpack实战——打包优化【中】

    前言 上篇从多线程打包和缩小打包作用域两个方面入手,对webpack打包层面做出优化。本篇描述从动态链接库思想方面...

  • Webpack 打包优化之速度篇

    在前文 Webpack 打包优化之体积篇中,对如何减小 Webpack 打包体积,做了些探讨;当然,那些法子对于打...

  • 基于webpack 3 打包性能优化

    基于webpack 3 打包性能优化 source Scope Hoisting. 过去 webpack 打包时的...

  • webpack实战——打包优化【上】

    前言 本篇介绍一些webpack优化的配置方法,目的有二: 打包速度更快 输出资源更小 注意:在软件工程领域有一条...

  • webpack实战——打包优化【下】

    前言 这是webpack打包优化【下】篇。前几篇针对性能要求高的项目从加快打包速度、减小资源体积方面入手,提出了一...

  • webpack打包优化

    实现webpack打包优化,有两个优化点: 如何减少打包时间 如何减少打包大小 减少打包时间 优化Loader对于...

  • React单页面应用项目 性能优化 实践

    react 单页面应用项目在加载优化这一块就得依赖webpack的打包方式。webpack的打包优化的本质就是将 ...

  • 浅谈webpack打包原理

    近来想要对旧项目进行优化,所以了解下webpack打包原理为优化做准备 webpack 4.x 打包文件 inde...

  • webpack实战——模块打包

    写在前面 这是webpack实战系列的第二篇:模块和模块打包。上一篇:webpack实战——打包第一个应用 记录了...

  • Webpack极限打包优化

    今天为了更好地了解一下Webpack打包优化的一些内容,看了一下NEXT公开课,Webpack打包极限优化,感兴趣...

网友评论

    本文标题:webpack实战——打包优化【中】

    本文链接:https://www.haomeiwen.com/subject/aeulmktx.html