一、Turborepo在移动应用开发中的应用背景
很多做移动应用开发的团队,都会遇到一个共性问题:随着业务扩张,团队里可能同时做着同一款应用的iOS版、Android版,还有基于Flutter或React Native的跨平台版本,甚至还有配套的管理后台、用户端小程序。这么多项目堆在一起,大家慢慢就会发现,好多重复的工作在做——比如每个项目都要写一遍用户登录的逻辑、统一的弹窗组件、还有埋点统计的代码,改一个公共规则,所有项目都得挨个改一遍,效率特别低。
之前很多团队的解决办法是把公共代码打包成npm包或者Pub包,单独维护,然后各个项目再依赖这个包。但这种方式有个大麻烦:改公共代码的时候,得先等公共包打包发布,再到各个项目里升级版本,改完还要挨个测试,一旦改的地方出问题,还得挨个回滚,折腾半天。
Turborepo就是为了解决这类多项目协同的痛点出现的,它本质是一个针对JavaScript/TypeScript技术栈的单仓库(Monorepo)管理工具,不需要把公共代码单独打包发布,就能让多个项目共享同一份公共代码,改完直接生效,还能自动帮你处理依赖关系、缓存构建结果,大大提升开发效率。
二、核心应用场景
2.1 跨平台移动应用的公共逻辑复用
这是Turborepo在移动开发里最常用的场景。比如一个团队同时做React Native的跨平台应用、配套的Web管理后台,两者都需要用户登录、权限校验、埋点统计的逻辑,这些逻辑可以放在一个公共的子仓库里,React Native和Web项目直接依赖这份代码,不用单独维护。
2.2 同应用多端版本的协同开发
比如团队先做了React Native版本的应用,后来又要做纯原生的iOS和Android版本,或者基于Flutter的新版本,这时候可以把公共的业务规则(比如会员等级计算、商品折扣逻辑)放在公共子仓库,不同端的项目直接引用,保证所有端的规则完全一致,不会出现同一个商品在React Native端打9折,在Android端打8折的问题。
2.3 组件库的快速迭代
如果团队有自己的UI组件库,比如统一的按钮、输入框、弹窗,这些组件需要在多个项目里用,用Turborepo可以直接在本地修改组件库的代码,所有依赖它的项目马上就能看到修改效果,不用等组件库发布新版本,测试完再统一打包就行。
三、完整应用示例
3.1 示例技术栈声明
本示例统一使用React Native + TypeScript技术栈,包含三个子项目:React Native用户端应用、React Native管理端应用、公共业务逻辑子仓库。
3.2 项目结构搭建
首先我们需要搭建一个基于Turborepo的单仓库项目,先创建根目录,然后初始化Turborepo:
# 创建根目录
mkdir turbo-mobile-demo && cd turbo-mobile-demo
# 初始化Turborepo项目,选择默认的TypeScript模板
npx create-turbo@latest
初始化完成后,项目结构大概是这样的:
turbo-mobile-demo/
├── apps/ # 存放应用项目
│ ├── user-app/ # 用户端React Native应用
│ └── admin-app/ # 管理端React Native应用
├── packages/ # 存放公共子仓库
│ └── common/ # 公共业务逻辑
├── package.json # 根目录的依赖和脚本
└── turbo.json # Turborepo的配置文件
3.3 公共子仓库开发
公共子仓库packages/common用来放所有项目都要用的代码,比如用户登录逻辑、会员等级计算逻辑、埋点统计逻辑。我们先给公共子仓库加个会员等级计算的函数,代码放在packages/common/src/member.ts:
// packages/common/src/member.ts
// 会员等级枚举:0=普通会员,1=银卡,2=金卡,3=钻石
export enum MemberLevel {
NORMAL = 0,
SILVER = 1,
GOLD = 2,
DIAMOND = 3
}
/**
* 根据用户积分计算会员等级
* @param points 用户当前积分
* @returns 会员等级
*/
export function getMemberLevel(points: number): MemberLevel {
if (points >= 10000) return MemberLevel.DIAMOND;
if (points >= 5000) return MemberLevel.GOLD;
if (points >= 1000) return MemberLevel.SILVER;
return MemberLevel.NORMAL;
}
然后给公共子仓库加个埋点统计的函数,代码放在packages/common/src/track.ts:
// packages/common/src/track.ts
/**
* 埋点统计函数:记录用户行为
* @param eventName 事件名称,比如'click_buy_button'
* @param params 事件参数,比如{ productId: '123' }
*/
export function trackEvent(eventName: string, params?: Record<string, any>) {
// 实际项目里这里会调用埋点SDK,比如友盟、神策的接口
console.log('埋点上报:', eventName, params);
// 这里可以加实际的埋点逻辑,比如:
// UmengAnalytics.track(eventName, params);
}
公共子仓库开发完后,需要在它的package.json里配置导出路径,方便其他项目引用:
{
"name": "@turbo-mobile/common",
"version": "0.0.0",
"main": "./src/index.ts",
"types": "./src/index.ts",
"dependencies": {}
}
我们再给公共子仓库加个index.ts,把所有公共代码统一导出:
// packages/common/src/index.ts
export * from './member';
export * from './track';
3.4 应用项目引用公共代码
现在我们在用户端应用apps/user-app里引用公共代码,比如在apps/user-app/src/MemberPage.tsx里,展示用户的会员等级:
// apps/user-app/src/MemberPage.tsx
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
// 直接引用公共子仓库的代码,不用安装任何包
import { getMemberLevel, MemberLevel, trackEvent } from '@turbo-mobile/common';
const MemberPage = () => {
// 模拟用户积分
const [userPoints, setUserPoints] = useState(6000);
// 计算会员等级
const memberLevel = getMemberLevel(userPoints);
// 点击升级按钮的逻辑
const handleUpgrade = () => {
// 调用公共的埋点函数,记录点击事件
trackEvent('click_upgrade_member', { currentPoints: userPoints });
// 模拟升级逻辑,积分加2000
setUserPoints(prev => prev + 2000);
};
// 会员等级对应的显示名称
const levelNames = {
[MemberLevel.NORMAL]: '普通会员',
[MemberLevel.SILVER]: '银卡会员',
[MemberLevel.GOLD]: '金卡会员',
[MemberLevel.DIAMOND]: '钻石会员'
};
return (
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 20, marginBottom: 20 }}>
您的会员等级:{levelNames[memberLevel]}
</Text>
<Text style={{ fontSize: 16, marginBottom: 30 }}>
当前积分:{userPoints}
</Text>
<Button title="升级会员" onPress={handleUpgrade} />
</View>
);
};
export default MemberPage;
同样的,管理端应用apps/admin-app里也可以直接引用这份公共代码,比如在管理端的会员统计页面里,用同样的getMemberLevel函数计算会员等级,保证两个项目的会员规则完全一致。
3.5 Turborepo的核心配置
根目录的turbo.json是Turborepo的核心配置,用来定义任务、缓存规则等,我们可以给它加个构建和启动的配置:
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
这个配置的意思是:如果我们执行turbo build,Turborepo会先构建所有依赖的子仓库(比如先构建common),再构建应用项目;执行turbo dev的时候,会启动所有项目的开发服务,并且缓存会关闭(因为开发模式下需要实时更新)。
3.6 开发和运行
现在我们可以用Turborepo的命令来启动项目了:
# 启动所有项目的开发服务
turbo dev
如果我们修改了公共子仓库的代码,比如把getMemberLevel里的金卡积分阈值从5000改成6000,不需要发布任何包,用户端和管理端的应用马上就能看到修改效果,开发模式下的热更新会自动生效,测试完没问题后,再统一执行turbo build构建所有项目就行。
四、技术优缺点分析
4.1 优点
第一,代码复用效率高,不用单独维护公共包,改完直接生效,避免了包版本不一致的问题。比如之前改公共代码要走“修改公共包→发布→升级所有项目→测试”的流程,现在只要改完公共代码,所有项目都能马上用,至少能省一半的时间。
第二,规则一致性强,所有端的业务规则、组件样式都用同一份代码,不会出现同一个逻辑在不同端表现不一样的问题。比如会员等级计算、商品折扣、支付逻辑这些核心规则,只要改一次,所有端都同步更新,避免了跨端不一致的bug。
第三,构建速度快,Turborepo的缓存机制会把之前构建过的结果存起来,下次构建的时候直接复用,不用重新构建所有项目。比如如果只改了用户端的代码,Turborepo会发现公共子仓库和管理端的代码没改,直接用之前的构建结果,构建速度能快好几倍。
第四,依赖管理简单,所有项目的依赖都放在根目录的package.json里统一管理,不用每个项目单独装依赖,避免了不同项目依赖版本不一致的问题。比如React Native的版本,所有项目都用同一个版本,不会出现用户端用0.72,管理端用0.71的情况。
4.2 缺点
第一,技术栈限制,Turborepo主要针对JavaScript/TypeScript技术栈,原生的iOS(Swift/Objective-C)、Android(Java/Kotlin)项目不能直接用,得单独适配,或者用Bridge的方式调用公共代码,会增加复杂度。
第二,项目体积变大,所有项目都放在同一个仓库里,仓库的体积会越来越大,拉取代码的时候会比较慢,需要配置合理的.gitignore规则,排除不需要的文件,比如node_modules、构建产物等。
第三,权限管理复杂,如果团队里有人只负责用户端,有人只负责管理端,同一个仓库里的代码所有人都能改,容易出现误改的情况,需要配合代码权限工具,或者把公共子仓库单独拆分出来。
第四,学习成本,团队成员需要熟悉Turborepo的配置和使用,比如任务依赖、缓存规则、单仓库的项目结构,需要花时间学习和适应。
五、注意事项
第一,合理划分项目结构,公共子仓库和应用项目要分清楚,不要把应用项目的代码放到公共子仓库里,也不要把公共代码放到应用项目里。比如公共子仓库里只放所有项目都要用的代码,应用项目里放自己特有的代码,比如用户端的首页、管理端的订单列表等。
第二,配置合理的缓存规则,turbo.json里的缓存规则要根据项目的实际情况配置,比如开发模式下要关闭缓存,构建模式下要配置正确的输出路径,避免缓存失效或者缓存错误的结果。
第三,规范代码提交,因为所有项目都在同一个仓库里,提交代码的时候要明确说明修改的是哪个项目,比如“修改公共子仓库的会员等级逻辑”、“修改用户端的会员页面样式”,方便其他人查看和回滚。
第四,定期清理仓库,随着项目的增加,仓库的体积会越来越大,要定期清理不需要的文件,比如旧的构建产物、临时文件等,也可以用Git的压缩命令来优化仓库的体积。
第五,做好测试,虽然Turborepo能保证代码的一致性,但修改公共代码的时候还是要测试所有依赖它的项目,避免改完公共代码后,某个项目出现bug。比如改了埋点逻辑后,要测试用户端和管理端的埋点是否正常上报。
六、文章总结
Turborepo是一个非常适合多端移动应用开发的单仓库管理工具,它解决了跨端开发中公共代码复用效率低、规则不一致、构建速度慢等痛点,能大大提升团队的开发效率。
从实际应用来看,它特别适合同时开发多个JavaScript/TypeScript技术栈的移动应用项目,比如React Native跨端应用、配套的Web管理后台、小程序等,能让团队的协同开发更顺畅。
当然,它也有技术栈限制、项目体积大等缺点,需要团队根据自己的实际情况来选择。如果团队的技术栈主要是JavaScript/TypeScript,同时开发多个端的项目,Turborepo是一个非常值得尝试的工具,能帮团队节省很多开发时间,提升产品的质量。
评论
围绕“Turborepo在移动应用开发中的应用案例”参与讨论