评论

使用 DllPlugin 优化小程序项目打包速度

本文主要介绍在使用dllplugin插件打包小程序项目的时候,遇到的问题,并提出解决方案。主要问题是:dll打包出来的文件,小程序不能使用问题,以及怎样将dll.js文件引入到打包后的js文件当中。

背景

以前有用过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。欢迎大家一起交流~

点赞 33
收藏
评论

6 个评论

登录 后发表内容