logo

鱼肚的博客

Don't Repeat Yourself

WebpackDllPlugin中添加例外

TL; DR

要使得一个在Dll中的包在webpack构建时不被跳过,可以使用别名。

背景

在提升webpack构建性能时,很多人都会选择使用webpackDllPlugin,将不常更新的第三方依赖(如react、lodash等)打包到一个固定的vendor之中,并在之后的构建中直接跳过它们。

一般来说,在需要更新其中的依赖版本时,重新构建一次dll,生成一个新的vendor和manifest即可。然而,最近我遇到一个场景,一个vendor同时被多个项目共同引用,要更新就必须同时更新,所以很难更新它的内容。

在这种情况下,如果个别项目需要升级其中一个依赖的版本,就有点麻烦了。

下面我介绍下我的解决思路:

别名

最开始接到这个问题时(不动dll的情况下,更新个别项目的echarts版本),第一反应就是利用别名处理。

别名分为几个不同的层次,比如有webpack的alias,有require的cache等,甚至还有最暴力的手段,直接把echarts包重新发一个,改名叫myecharts。

npm别名

直接clone echarts,然后重新发个myecharts,应当算是下下之选,一方面多了一个维护的成本,另外一方面即使echarts被改名为myecharts,它的依赖项并不会跟着改名。这样就有可能出现虽然本地安装了更新版本的依赖,但是打包之后还是旧版本的情况。还是会有可能出bug。

因此,还是应该首先考虑下工具层别名的处理。

webpack 别名

webpack的resolve里面,可以设置包的别名。要确定这个别名能否发挥作用,就需要先确定下 webpack 别名与 dllPlugin 的先后关系。

如果是先DllPlugin处理,再解析webpack alias,那么这种方法可行,反之则不行。

为了确定这个顺序,需要看下dllPlugin输出文件中的 manifest.json:

1{
2  "name": "vendorLibrary",
3  "content": {
4    "./node_modules/prop-types/index.js": {
5      "id": 0,
6      "buildMeta": { "providedExports": true }
7    },
8    "./node_modules/react/index.js": {
9      "id": 1,
10      "buildMeta": { "providedExports": true }
11    },
12    "./node_modules/ramda/es/internal/_curry2.js": {
13      "id": 2,
14      "buildMeta": {
15        "exportsType": "namespace",
16        "providedExports": ["default"]
17      }
18    },
19    "__others__": "省略其它内容"
20  }
21}

上面是截取的manifest.json中的部分内容,从它的结构中可以看出,key是node_modules下的具体文件名。

这样也就能判断出,修改webpack alias是没有用的了,因为无论再怎么改,最终映射到node_modules中的具体文件名是不一样的。

因此这种方案不可行。

node别名

另一种方式,是修改node中的require。

比较常见的使用方式,是引用一个npm包 module-alias

它通过修改require.cache等操作,影响了nodejs中的路径解析过程。所以一般会被用来简化引用路径。

但是考虑到无论怎样简化,最终对应到node_modules中的文件名是不变的,所以理论上来说这种方式也起不到相应的效果。

npm别名 - 2

npm从v6.9.0版开始,支持将一个包安装到不同的两个位置。

比如说通过如下的命令:

1npm i echarts-410@npm:echarts@4.1.0

就可以在node_modules中生成一个 `echarts-410 的目录,与之前安装的旧版本所处目录不同。

然后设置webpack别名,将echarts解析到echarts-410

1const config = {
2  resolve: {
3    alias: {
4      'echarts': 'echarts-410'
5    }
6  }
7}

真正解析到的位置就会是 node_modules/echarts-410/xxx 这样的文件,也就匹配不到manifest.json中的key了。

只要匹配不到manifest.json中的key,自然在webpack打包过程中就不会被略过。

如此一来,就可以实现在不动dll的情况下,使用echarts的另一个版本。

注意如果有内部依赖,如依赖了 echarts/map/json/china.json,那么就需要保证新版和旧版的路径一致,不然的话就需要修改引用语句,适配新版本。

总结

在解决webpackDllPlugin中添加例外这个问题时,尝试了几种方法。

其中 webpack 别名和node别名不能正常工作,重发npm包的方式代价比较大,相对最轻量的方案,是通过npm的别名机制,将同一个包安装到两个不同的位置上。从而绕过dllPlugin的处理。