背景
以前有用过dllplugin将一些第三方库打包,在日常开发中忽略第三方的打包,来优化打包速度。
存在问题
打包出来的文件是通过var [name]_[hash]的方式,暴露在全局变量当中,但是在小程序里面,这样暴露的变量并不是全局变量,具体可以看下这篇资料。小程序有一个全局变量,App,Page,Component,global,wx这些基本变量都是挂载到这个全局变量里面。
解决方案
webpack 的配置上有一个配置,output中libraryTarget,可以将变量导出到一个变量当中,相关资料。
所以可以将变量导出到global当中,但是当我设置libraryTarget:global
的时候,导出的dll文件将变量挂载到了window下。具体原因我还没找出来,通过尝试,我发现设置globalObject:global
就可以将变量挂载到global对象当中。 webpack的output 配置如下:
output: {
filename: filename,
library: '[name]_[chunkhash]',
globalObject: 'global', //全局对象配置
libraryTarget: 'global',
}
代码引用出现问题
我们将打包出来的变量挂载到了global对象当中,但是在文件里面引用时,还是以 [name]_[hash] 全局变量的形式引用,所以小程序会报错:[name]_[hash] is undefined
解决方案
我想了一种解决方案,比较low,我在在配置dllplugin的时候,将引入的变量名改为 global.[name]_[hash],暂时解决了这个问题,由于时间关系,我没有深入寻找其他解决方案,如果有朋友有其他解决方案,欢迎随时交流。
代码片段如下:
new DllPlugin({
path: path.join(outputPath, '[name].manifest.json'),
name: 'global.[name]_[chunkhash]',
})
vendor.dll.js引入问题
熟悉dllplugin的都知道,如果在H5项目中,使用插件在html文件里面插入一个script标签即可。在小程序项目当中,需要通过require("…/…/vendor.dll.js");引入。现成的插件当中没有这样的。所以我们老大写了一个插件将vendor.dll.js加到入口文件。
插件实现方案
通过webpack的插件钩子emit,在生成到js文件之前,将require("vendor.dll.js");
加入到js文件当中。具体实现方案:
const path = require('path');
const upath = require('upath');
const relative = require('relative');
const getRelativePath = (filePath) => {
if (!/^\.(\.)?\//.test(filePath)) {
filePath = `./${filePath}`;
}
return filePath;
};
const chunksHandle = (chunks, compilation) => {
// 获取入口chunk
const entryChunk = chunks.pop();
// 遍历入口文件
entryChunk.files.forEach(filePath => {
const assetFile = compilation.assets[filePath];
const extname = path.extname(filePath);
let content = assetFile.source();
// 增加 dll js
if (extname === '.js') {
let subFile = 'vendor.dll.js';
let relativePath = upath.normalize(relative(filePath, subFile));
relativePath = getRelativePath(relativePath);
content = `require("${relativePath}");\n${content}`;
}
assetFile.source = () => content;
});
};
const emitHandle = (compilation, callback) => {
if (compilation.entrypoints instanceof Map) {
compilation.entrypoints.forEach(({ chunks }) => chunksHandle(chunks, compilation));
} else {
Object.keys(compilation.entrypoints).forEach(key => {
const { chunks } = compilation.entrypoints[key];
chunksHandle(chunks, compilation);
});
}
callback();
};
MpvuePlugin.prototype.apply = compiler => compiler.hooks.emit.tapAsync('mpvue-emit', emitHandle);
module.exports = MpvuePlugin;
推荐一个 DllPlugin 的插件
auto-dllplugin,这个插件将dllplugin 首次打包和再次打包结合在一起,通过计算hash值判断是否为首次打包,并且将打包结果缓存,如果是再次打包则直接拿缓存数据。不过这个插件不能直接使用到小程序项目打包当中,需要按照我上面所述的修改,我已经修改好一个版本,在个人GitHub项目上,已提pr。
效果
- 未使用dllplugin
- 使用dllplugin
总结
我们这个 DllPlugin 的方案同理也适用于当前主流的一些框架,例如:mpvue,wepy,taro等等。我们当前使用的环境是:mpvue+wepack4+babel7。欢迎大家一起交流~
请问taro dllpugin.output.path 是怎么填写的, 目前现在如果是weapp/dll, taro 编译会清空weapp/dll,不放在weapp 目录下, 开发工具又找不到