手写小程序摇树优化工具(九)——删除业务组代码

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

github: miniapp-shaking

1. 背景介绍

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

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

const app = getApp()

Page({
  /** groupStart:groupA, groupC */
  sum() {
    return 1 + 1 + 1;
  },
  /** groupEnd */

  /** groupStart:groupB */
  sum2() {
    return 2 + 2;
  },
  /** groupEnd */

  sum3() {
    return 3 + 3;
  },
})

index.wxml

<view>主包</view>
<!--groupStart:groupB-->
<componentOne></componentOne>
<!--groupEnd-->

index.json

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

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

index.js

const app = getApp()

Page({
  /** groupStart:groupA, groupC */
  sum() {
    return 1 + 1 + 1;
  },
  /** groupEnd */

  /** groupStart:groupB *//** groupEnd */

  sum3() {
    return 3 + 3;
  },
})

index.wxml

<view>主包</view>

index.json

{
  "usingComponents": {}
}

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

2. 实现

2.1 定义规则

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

this.needDeleteGroupCode = options.needDeleteGroupCode || false;

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

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

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

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

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

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

<!--groupStart:groupB-->
<componentOne></componentOne>
<!--groupEnd-->

注意:业务组名称不能存在包含关系,例如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包
手写小程序摇化工具(九)——删除业务组代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值