善为士者不武;善战者不怒;善胜敌者不与;善用人者为之下。是谓不争之德,是谓用人之力,是谓配天,古之极。
github: miniapp-shaking
第三章我们遍历json的时候有介绍过通过groupName
和replaceComponents
来取代对应的业务组件来解决大公司复杂逻辑共用导致的超包问题。这很好,现在我要实现一个更加高级的功能,来达到精细化的控制业务代码的程度。这主要是为了解决超级复杂项目的问题,一般的小项目用不到。
举一个例子,不同的业务组的组件可能不光光是在组件层面,他们可能在一个单一个的js
、wxs
和wxml
文件里面分别写了自己的业务逻辑,例如以下例子:
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": {}
}
这对于大公司的非常复杂的项目是非常有帮助的,能够解决他们超包的问题。
现在我们来实现这一个过程,首先我们在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 */
同理对于wxml
,wxml
这些标签被删除后会同步删除json
的组件,并忽略被删除组件的所有依赖。
注意:业务组名称不能存在包含关系,例如goup1,group12。
在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);.....}
}
到此时被删除业务组的代码的依赖已经被忽略了。
在容器类中真正的删除这些代码,这步操作一定要在拷贝完文件之后,否则源码会被修改。
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包
手写小程序摇化工具(九)——删除业务组代码