手写小程序摇树优化工具(九)——删除业务组代码
迪丽瓦拉
2024-02-08 07:45:25
0

善为士者不武;善战者不怒;善胜敌者不与;善用人者为之下。是谓不争之德,是谓用人之力,是谓配天,古之极。

github: miniapp-shaking

1. 背景介绍

第三章我们遍历json的时候有介绍过通过groupNamereplaceComponents来取代对应的业务组件来解决大公司复杂逻辑共用导致的超包问题。这很好,现在我要实现一个更加高级的功能,来达到精细化的控制业务代码的程度。这主要是为了解决超级复杂项目的问题,一般的小项目用不到。

举一个例子,不同的业务组的组件可能不光光是在组件层面,他们可能在一个单一个的jswxswxml文件里面分别写了自己的业务逻辑,例如以下例子:
index.js

const app = getApp()Page({/*** groupStart:groupA,groupC* @params: ss*/sum() {return 1 + 1 + 1;},/** groupEnd *//*** groupStart:groupB* @pssfsf*/sum2() {return 2 + 2;},/** groupEnd */sum3() {return 3 + 3;},
})

index.wxml

主包



index.json

{"usingComponents": {"componentOne": "../../components/component-1"}
}

方法sum2和组件componentOne是业务组groupB的,那么对于业务组groupA来说摇树的时候如果能把groupB的代码干掉将会节省很多的空间,并且不仅是componentOne组件,连它的所有依赖都将被忽略掉。被摇树之后的代码应该是这样的:

index.js

const app = getApp()Page({/*** groupStart:groupA,groupC* @params: ss*/sum() {return 1 + 1 + 1;},/** groupEnd */sum3() {return 3 + 3;},
})

index.wxml

主包

index.json

{"usingComponents": {}
}

这对于大公司的非常复杂的项目是非常有帮助的,能够解决他们超包的问题。

2. 实现

2.1 定义规则

现在我们来实现这一个过程,首先我们在config里面配置一个参数needDeleteGroupCode来表明是否需要做删除业务代码的操作,因为一些小项目不需要这样做,可以加快摇树的速度。

this.needDeleteGroupCode = options.needDeleteGroupCode || false;

接着我们新建两个正则表达式来标志哪些代码属于哪些业务组

// 业务逻辑
if (this.groupName && this.needDeleteGroupCode) {this.groupCodeJsRegexp =  new RegExp(`(?<=\\/\\*\\*[\\s\\S]*groupStart:((?!${this.groupName}).)+[\\s\\S]*\\*\\/)[\\s\\S]*(?=\\/\\*\\*[\\s\\S]*groupEnd[\\s\\S]*\\*\\/)`, 'ig');this.groupCodeWxmlRegexp = new RegExp(`(?<=this.groupName}).)+[\\s\\S]*-->)[\\s\\S]*(?=)`, 'ig');
}

我们定义一个规则,通过注释来标志js和wxml的代码所属的业务,规则是这样的:

对于js和wxs来说我们定义一个规则,由groupStart:开始,然后后面加上业务组名称,多个业务组可以通过逗号或者空格分开,之后以groupEnd结束。这中间的代码块就被标记为这些业务组的代码。非自己业务组的代码将会被摇树掉,如果没有这些标志的被视为共有的代码保留。注意只能使用/** */这样的注释。

 /*** groupStart:groupA,groupC* @params: ss*/业务代码/** groupEnd */

同理对于wxmlwxml这些标签被删除后会同步删除json的组件,并忽略被删除组件的所有依赖。




注意:业务组名称不能存在包含关系,例如goup1,group12。

2.2 过滤被删除代码的依赖

BaseDepend中新建一个变量来保存有业务代码标志的文件:

class BaseDepend {constructor(config, rootDir = '') {...// 有业务组代码的文件this.groupFile = new Set();}
}

新建一个方法遍历时忽略掉被删除业务组代码的依赖

class BaseDepend {/*** 删除业务组代码* @param codeStr* @returns {void|string|*}* @private*/_deleteGroupCode(codeStr, file) {if (this.config.needDeleteGroupCode && codeStr) {const ext = path.extname(file);const regExp = ext === '.wxml' ? this.config.groupCodeWxmlRegexp : this.config.groupCodeJsRegexp;if (regExp.test(codeStr)) {if (!this.groupFile.has(file)) {// 保存有业务组代码的文件this.groupFile.add(file);}return codeStr.replace(regExp, '');}}return codeStr;}
}jsDeps(file) {// 保存依赖const deps = [];// 文件所处的目录const dirname = path.dirname(file);// 读取js内容let content = fse.readFileSync(file, 'utf-8');content = this._deleteGroupCode(content, file);// 将代码转化为AST树.....
}wxsDeps(filePath) {const deps = [];const dirname = path.dirname(filePath);// 读取js内容let content = fse.readFileSync(filePath, 'utf-8');content = this._deleteGroupCode(content, filePath);// 将代码转化为AST....
}wxmlDeps(file) {const deps = [];const dirName = path.dirname(file);let content = fse.readFileSync(file, 'utf-8');content = this._deleteGroupCode(content, file);...
}getWxmlTags(filePath) {let needDelete = true;const tags = new Set();if (fse.existsSync(filePath)) {let content = fse.readFileSync(filePath, 'utf-8');content = this._deleteGroupCode(content, filePath);.....}
}

到此时被删除业务组的代码的依赖已经被忽略了。

2.3 真正删除业务组代码

在容器类中真正的删除这些代码,这步操作一定要在拷贝完文件之后,否则源码会被修改。

class DependContainer {async init() {this.clear();this.initMainDepend();this.initSubDepend();this.handleAsyncFile();this.splitIsolatedNpmForSubPackage();const allFiles = await this.copyAllFiles();this.deleteGroupCode();....console.log('success!');}deleteGroupCode() {if (this.config.needDeleteGroupCode) {console.log('正在删除业务组代码...');const fileSet = this.mainDepend.groupFile;this.subDepends.forEach(subDepend => {this.appendSet(fileSet, subDepend.groupFile);});Array.from(fileSet).forEach(file => {const targetPath = file.replace(this.config.sourceDir, this.config.targetDir);let content = fse.readFileSync(targetPath, 'utf-8');const ext = path.extname(file);const regExp = ext === '.wxml' ? this.config.groupCodeWxmlRegexp : this.config.groupCodeJsRegexp;content = content.replace(regExp, '');fse.outputFileSync(targetPath, content);});}}
}

现在我们已经删除了非自己业务组的代码了。为什么我不压缩混淆代码呢?因为这对于解决超包问题无用,请关注我的另一片文章:微信小程序源码压缩探索

下一章我们将介绍如何删除dead code,敬请关注。

连载文章链接:
手写小程序摇树工具(一)——依赖分析介绍
手写小程序摇树工具(二)——遍历js文件
手写小程序摇树工具(三)——遍历json文件
手写小程序摇树工具(四)——遍历wxml、wxss、wxs文件
手写小程序摇树工具(五)——从单一文件开始深度依赖收集
手写小程序摇树工具(六)——主包和子包依赖收集
手写小程序摇树工具(七)——生成依赖图
手写小程序摇树工具(八)——移动独立npm包
手写小程序摇化工具(九)——删除业务组代码

相关内容