你要问我为啥突然搞uni-app了?这……我不造啊……这不临时接手捯饬捯饬么。
说实话啊,在这之前,我只在很久以前摸过一次hbuilder,并没有怎么使用,对于uni-app也没接触过,这才造成了这次苦难。
0. 由来
这是一个历史传承的小程序项目,基于 uni-app vue2。项目中使用了 lime-echart来画图,这是一个基于echarts的组件包。
后来由于资源零零总总多了些,便进行分包,与echarts依赖的页面被放进了子包中。
但是在项目运行或发行后,你会发现,主包下会出现一系列“无依赖代码文件”,大小比较可观,这主要是因为echarts本身就是个大家伙。
这几个“无依赖代码文件”不会对开发及调试产生什么影响,但是如果你计划上传或发布,就会带来问题:因为它会导致主包变大,一方面是微信对主包要求不得大于2MB,因此很容易导致超限无法上传,再就是这个文件本身很大,却是无依赖的,就很让人膈应。而这几个文件,实际上已经被打包到 vendor.js
里去了,因此不应该独立出现。
如果你在微信开发者工具中手动删除这几个文件,会发现并不影响开发运行,却可能会导致主包超过2MB大小引发拒绝上传。
所以之前在问负责这个小程序的前端开发是咋回事时,他给我的答复是,用了lime-echart
这个库就会这样,自动生成这几个文件,手动删一下就行。
我说每次手动删吗,很膈应啊。他说解决不了。
也就这么将就了。
那么现在为什么我又开始搞这个东西了呢,是因为我想把小程序的打包及发布搞到CI里面去。搞到CI里面的话,不好还要手动删了吧?看了些hbuilder的文档,觉得可以用hbuilder cli来做这个事情,编译打包上传一气呵成,那么这个多余的文件还是得想办法解决一下的。毕竟打包后的这个小程序就3.4M不到,而这几个文件直接就占了接近20%,就算你不会导致我无法上传也会让我很难受啊。
既然如此,那就收拾它。
1. 先看看人家怎么做的
我觉得这个问题肯定不是只有我会觉得是个问题。
因此第一件事就是进行了搜索。果然找到了一些帖子提供解决方案。
第一个帖子来自于 《uni-app小程序分包中使用 Echarts, 并在分包里加载依赖》。这个帖子看了下,大概思路就是把lime-echart这个包从主包的根目录移到子包的目录下,然后还需要改动一部分代码,否则可能无法使用。我开始时还试了试,但试到一半感觉味道不太对,这样移动到子目录里的包实际上已经不会被认为是项目的引入包了,总觉得怪怪的,且没有最终确认是去掉了这几个多余的文件还是只是换了个地方,便放弃这条思路。
第二个帖子来自于《uniapp小程序分包异步化实践—-echarts分包实战》,这个帖子说的更复杂,不仅用了webpack的CopyWebpackPlugin
,还要修改引用echarts组件的方式,大工又大料,也许有独特的优势,但对于我这个场景以及只是想不让这几个冗余文件出现的需求,似乎有点过于沉重了。
搜索了很多资料,也问了AI,好像都没什么有价值的信息,不得不吐槽一下uni-app的文档资料真的是少得可怜。
想了想,自力更生吧。
2. 第一次尝试
简单看了下uni-app的构成,就可以知道这个底层是webpack进行打包。那么我的第一个思路是,在打包完成后手动删一下这几个文件。
不过在探索了一番hubuilder后,发现这个思路行不通,它没有类似Visual Studio的Post Build事件,似乎CLI也是基于它自身的,没有入手点。
那么,就从webpack下手。
基于webpack,最先想到的是,从webpack配置出发,看看有没有钩子函数之类的,可以手动处理。
通过uni-app官方文档中的vue.config.js
介绍可以看出,可以通过这个文件对webpack进行定制。
兜兜转转,我觉得没准 IgnorePlugin 可以解决这个问题。
于是先在hbuilder里写下这样的代码。
但是以上代码直接无法编译。
[HBuilder] 19:20:57.507 ERROR Error loading vue.config.js:
[HBuilder] 19:20:57.507 ERROR Error: Cannot find module 'webpack'
[HBuilder] 19:20:57.507 Require stack:
[HBuilder] 19:20:57.507 - ======\vue.config.js
[HBuilder] 19:20:57.507 - ======\HBuilderX\plugins\uniapp-cli\node_modules\@vue\cli-service\lib\Service.js
[HBuilder] 19:20:57.507 - ======\HBuilderX\plugins\uniapp-cli\bin\uniapp-cli.js
[HBuilder] 19:20:57.507 Error: Cannot find module 'webpack'
[HBuilder] 19:20:57.507 Require stack:
[HBuilder] 19:20:57.507 - ======\vue.config.js
[HBuilder] 19:20:57.508 - ======\HBuilderX\plugins\uniapp-cli\node_modules\@vue\cli-service\lib\Service.js
[HBuilder] 19:20:57.508 - ======\HBuilderX\plugins\uniapp-cli\bin\uniapp-cli.js
[HBuilder] 19:20:57.508 at Module._resolveFilename (node:internal/modules/cjs/loader:1140:15)
[HBuilder] 19:20:57.508 at Module._resolveFilename (======\HBuilderX\plugins\uniapp-cli\node_modules\module-alias\index.js:49:29)
[HBuilder] 19:20:57.508 at Module._load (node:internal/modules/cjs/loader:981:27)
[HBuilder] 19:20:57.508 at Module.require (node:internal/modules/cjs/loader:1231:19)
[HBuilder] 19:20:57.508 at require (node:internal/modules/helpers:177:18)
[HBuilder] 19:20:57.508 at Object.<anonymous> (======\vue.config.js:1:17)
这里可以看出。编译的工具包是在HbuilderX的安装目录下的,这个目录下确实也有 webpack 的包,但是require会直接报错,无法找到webpack包。
试了试倒是可以本地 npm install
一下,但……明明构建服务里已经有一个webpack了,我这里还要为了require再去装一个webpack以及它的七大姑八大姨,这怎么听怎么别扭,你这跟让我去装一下360 鲁大师之类的有啥区别。
再就是,现在明明有webpack了只是无法require,应该是哪里存在问题,再去装一个双份的明显不是合理的思路。
到这时已经很晚了,于是我决定先到dcloud的社区里提个问,下次再说。
3. 解决require的问题
第二天我继续来解决这个webpack的问题。
群里有人require成功了,不过他是项目里npm install了一下。我始终觉得这不是正确的思路。
问了下AI,基本上也就是让你安装之类的回复。
这时候我在琢磨一个问题,就是明明构建服务里有webpack但是打包的配置文件里require无法加载,应该是路径有问题,或者模块加载器哪里解析有问题,但苦于对hbuilderx这套东西不熟,还真的有点一筹莫展。
忽然dcloud社区里有人回复了,定睛一看……好嘛,手动拼出来完整的webpack地址去加载……
const { resolve } = require('path')
const rootModulePath = resolve(process.cwd(), 'node_modules')
function resolveModulePath(dir) {
return resolve(rootModulePath, dir)
}
const webpack = require(resolveModulePath('webpack'))
这……怎么说呢,就是……可以工作,但真的丑,应该是构建系统里的模块加载机制不完善,导致配置文件中require时只能加载项目目录里的,并不能fallback到内置的包目录。
但,总归是能解决加载的问题,你就说能不能用吧。
4. 自己写插件吧
在解决webpack的加载问题后,继续尝试了一下 IgnorePlugin
,发现没有按预期的方式工作,生成的目录里还是有对应的几个文件。
到这里我不太想继续研究这东西到底应该怎么弄了,因为……我觉得我这是个非常简单的诉求,没必要把简单的事情复杂化。
于是我就把目光转向了自己写个简单的webpack插件。
const removeAssets = [
/^uni_modules\/lime-echart\/static\/(echarts\.min|ecStat\.min|uni\.webview\..*?)\.js$/,
/^project\.private\.config\.json$/,
];
class RemoveAssetsPlugin {
constructor(options) {
this.options = options || {}
}
apply(compiler) {
compiler.hooks.emit.tapAsync('RemoveAssetsPlugin', (compilation, callback) => {
for (const name of Object.keys(compilation.assets)) {
if (removeAssets.find(r => r.test(name))) {
console.info(`[RemoveAssetsPlugin] 强制删除多余的资源文件:${name}`);
delete compilation.assets[name];
}
}
callback();
});
}
}
module.exports = {
configureWebpack: {
plugins: [
new RemoveAssetsPlugin()
]
}
}
写完后build一下。
17:40:38.062 [RemoveAssetsPlugin] 强制删除多余的资源文件:uni_modules/lime-echart/static/echarts.min.js
17:40:38.062 [RemoveAssetsPlugin] 强制删除多余的资源文件:project.private.config.json
17:40:38.062 [RemoveAssetsPlugin] 强制删除多余的资源文件:uni_modules/lime-echart/static/ecStat.min.js
17:40:38.062 [RemoveAssetsPlugin] 强制删除多余的资源文件:uni_modules/lime-echart/static/uni.webview.1.5.5.js
17:40:38.062 [RemoveAssetsPlugin] 强制删除多余的资源文件:uni_modules/lime-echart/static/uni.webview.1.5.3.js
17:40:38.149 项目 **** 编译成功。前端运行日志,请另行在小程序开发工具的控制台查看。
看起来完美解决了需求,也没有引入第三方依赖。
嗯哼,收工。