[toc]
# 1. 开始
何为孪生项目?故名思义,就是两个项目长得很像,比如项目A
和项目A+
,项目A+
从项目A
衍生而来,二者有些功能差异,但差异度很小,低于10%。
除了业务上的这种场景,还有其他情况会产生孪生项目,比如开源项目,如果对内和对外的开源程度不同,或者功能有差异,也会形成孪生项目。
这里不去讨论产品层面的逻辑,也不去讨论拆分为两个项目是否合理,仅讨论如何更高效的开发此类的项目。
# 2. 刀耕火种
最原始的方式就是在项目A
上开发后,将代码搬运到项目A+
上,此种方式开发时十分痛苦,痛苦点如下:
- 开发效率低,一模一样的代码需要写两遍
- 存在遗漏风险,开发周期稍长时很可能发生
- 存在错误风险,有可能粘贴错位置
- 存在冲突风险,有可能与他人改动冲突
同步代码的频率也很高,包括以下场景:
- 需求迭代
- bug修复
- 重构、优化代码
另外,业务自身就比较复杂,这种开发方式更加剧了项目的复杂性,不利于后期维护。
# 3. 工具化
既然原始方式弊端如此多,如何改进呢?
# 3.1. 思路及实现
可以用工具自动化的同步代码,这里的实现思路如下:
- 条件编译解决差异部分
- 以页面为单位,将每个差异部分控制在几行内,也就是封装组件、方法
- 用脚本同步代码,从页面开始,递归分析依赖,一起同步
这里面有几个前提:
- 项目
A
和项目A+
的差异点不能太多,如果异大于同,可以另起炉灶,用新路由,公共部分封装中间层 - 公共组件、方法需保证一致,包括:
- 三方库,比如
press-ui
common
component
- 三方库,比如
对于 store
、全局mixin
、local-component
下的公共组件,比如header
,也建议保持一致,如果确实无法保持一致,也可以解决,可以用工具ignore
字段排除,或者让同步文件的粒度变细一点。
另外,这种工具是利用了uni-app
扩展平台 (opens new window)的能力,国际化也是这么做的。
# 3.2. 使用方式
在项目根目录下增加文件light-cli.config.js
,内容如下:
module.exports = {
'sync-repo': {
target: '../pro',
ignore: [
'src/common/**/*',
'src/component/**/*',
],
files: [
'src/project/user/views/message-center/message-center-index.vue',
'src/project/user/views/message-center/message-center-detail.vue',
],
},
};
然后执行:
npx light-cli
注意files
不一定是页面,如果想同步某个底层文件也可以,工具足够灵活,可以支持任意粒度的文件。
# 3.3. 可能引起的问题
此外,使用扩展条件编译发现一个坑点,在三方库ts
文件中用条件编译,包裹import
语句时,在小程序平台,顶部的#ifdef
会被去掉。
比如下面这段代码,#ifdef H5
会被去掉,导致编译出错。
// handler.ts
import Vue from 'vue';
import { dialogProps } from './computed';
// #ifdef H5
import VueDialog from './press-dialog.vue';
// #endif
# 4. 效果
使用这种方式可以大大提高生产力,提升开发效率。并且,工具比人更能减少出错的可能,减少bug。
更重要的是,关注点会更加聚焦,只关心“主”项目,“从”项目只需要看下每次脚本跑的差异即可,这里主从关系并不是固定的,二者可互换。