
做跨平台项目做得久了难免会碰到同一个局面新业务要立一个 AppPM 说明天要看到能跑的东西你打开上一代项目心里清楚里面有三十多个历史补丁、两套半废弃的状态管理、还有一个没人敢删的旧构建脚本。复制它就等于把技术债打包继承。ReNative 模板系统解决的就是这个事它把“起一个新项目”从复制目录变成了一次声明式的组合你需要的是一套可版本化、可分发、可继承的工程骨架。这篇内容我会把 ReNative 模板系统从目录结构、声明字段、注册机制到企业级场景下的多环境注入、私有源托管、流水线衔接再到我踩过的坑一次性拆开讲。适合两类人看一是刚接触这套框架、想把官方示例跑通并改成自己骨架的开发者二是团队里负责工程化基建、要给多条业务线统一交付标准的同学。文中涉及的具体参数和命令凡是官方文档没有明说的部分我都会标明这是基于常见实践的推断你可以按自己项目的实际情况调整不必照搬。1. 模板系统到底在解决什么工程问题在聊文件结构之前先把“为什么要有模板”这件事说透。很多团队的做法是把模板等同于“初始化的脚手架”跑一次就丢掉后面全靠人肉维护。ReNative 的模板不是一次性脚本它是一份持续生效的工程契约理解这一点后面的设计选择才站得住。1.1 从复制粘贴脚手架到配置驱动传统脚手架的问题在于“快照语义”脚手架生成的那一刻是好的之后业务代码和工程配置混在一起模板就再也回不去了。第二年想做第三个 App你只能从第一个 App 里再扒一次扒的时候还得分清楚哪些是通用的、哪些是业务耦合的这个分拣成本通常比重新写还高。ReNative 走的是另一条路把工程骨架抽成独立的包里面只放目录约定、平台配置基线、插件声明和依赖清单业务代码全部通过后续的页面插件或业务模块挂进来。这么设计的好处是可以被包管理器管理。模板有名字、有版本号、有依赖关系能被安装、被升级、被锁定。你在项目里写下的模板版本号等同于给“工程基线”打了一个提交点半年后回来看能精确还原当时用的是哪一版骨架。这一点在多人协作里价值极大因为工程配置文件的冲突是最难 review 的而版本号是最好 review 的。代价也要说清楚模板一旦成为依赖就意味着它不能随便改。任何一次改动都要走发布流程都要考虑存量项目会不会被影响。所以模板的粒度和边界必须提前想好把稳定不变的东西放进去把容易变的东西留给项目本地配置去覆盖。这个分层思路后面会反复出现是整个系统的核心设计。1.2 模板、插件、应用配置三者的职责边界新手最容易混淆的就是这三者。我见过有人把业务页面写进模板结果模板一升级全公司的业务代码都被覆盖了也见过有人把平台签名配置塞进模板导致每次发布都要手动改回来。用一句话概括三者的分工模板定义骨架插件提供能力应用配置决定取值。模板负责的是“有哪些目录、哪些平台基线配置、默认需要哪些依赖和插件”。它关心的是结构不关心具体值。插件负责的是“这个能力怎么接入各个平台”比如一个推送能力它要知道 iOS 怎么配置、Android 怎么配置、Web 端降级成什么。应用配置负责的是“在这个环境、这个 App 里这些参数取什么值”比如不同环境的接口地址、不同 App 的包名和应用名。理解了这层很多决策就变得理所当然了。为什么模板里不该写死接口地址因为它属于应用配置。为什么模板里不该出现具体页面的业务逻辑因为它属于插件或者业务代码。为什么模板需要声明插件依赖因为骨架决定了这颗树需要挂哪些枝。我个人的经验是在团队里把这三分法贴在文档第一页能省掉后面百分之八十的争吵。还有一个容易被忽略的点模板是可以叠加的。基础模板提供最小可用骨架业务模板继承它再往上做行业定制。这种继承关系不是文件复制而是配置的深度合并所以顺序和优先级非常关键。下一节拆目录结构时我会把合并优先级单独拿出来讲因为这是实际项目里最容易出问题的地方。2. 拆解一个模板的目录结构与声明字段看一个东西最快的方式是看它的最小形态。模板目录本身并不神秘本质上就是一棵普通目录树加一个描述文件。真正需要花时间理解的是描述文件里每个字段在生成项目时会被如何处理这决定了你能控制到什么粒度。2.1 最小可用模板的目录构成一个能跑通的最简模板我一般会整理成这样的结构my-template/ ├── renative.template.json # 模板描述文件核心 ├── package.json # 模板自身的依赖与元信息 ├── appConfigs/ │ └── base/ │ └── renative.json # 基线应用配置会合并进生成的项目 ├── src/ │ └── index.js # 入口占位业务侧会覆盖 └── assets/ └── icon.png # 各平台图标基线renative.template.json是整个模板的身份证没有它工具链就不会把这个目录识别成模板。appConfigs/base/renative.json是最容易被低估的文件它承载的是“基线配置”生成项目时会和用户自己的配置做深度合并而不是简单覆盖。src和assets属于约定目录工具链本身不强制内容但你的构建脚本、插件、页面入口都会默认从这里找东西。我建议模板里不要预置平台原生工程目录也就是别把android/、ios/这些生成产物塞进去。原因很实在不同机器的构建工具版本、签名证书、本地属性都不一样一旦这些目录被模板带进版本库后面每次构建都可能在本地产生 diff团队里天天处理无关冲突。正确做法是让构建过程自己生成模板只提供生成后的配置补丁这条经验我后面在踩坑那节还会再提一次。package.json在模板里的作用有两重一是描述模板自身的元信息方便发布到包仓库二是声明模板运行时需要的工具依赖。这里有个小坑模板的依赖和项目的依赖是两套东西如果版本策略不一致生成出来的项目可能出现依赖树悬空的情况具体表现和排查方法我会放在第五节细讲。2.2 声明文件里的关键字段与实际含义描述文件里字段不算多但每一个都影响生成结果我用表格整理一下我实际会用到的那部分并说明我为什么这么用字段作用我的使用建议模板名称与版本标识模板身份供引用与锁定名称带业务域前缀版本严格语义化模板描述人读的说明出现在交互式选择里写清楚适用场景别写“测试模板”模板依赖生成项目时要装进项目的包只放骨架必需项业务依赖交给插件模板插件声明默认挂载的能力插件按最小集给多一个都是负担模板配置会合并进项目配置的片段只放基线值环境相关一律留空模板标签用于分类与筛选按平台矩阵、行业、复杂度打标这里最需要展开的是“模板配置”的合并行为。合并通常是递归的对象逐层合并数组多数情况下是覆盖而不是拼接。这意味着如果你在模板里给了一个默认的插件数组项目里再写一个插件数组很可能模板里的那一份就没了。我一般的做法是模板里只提供对象型基线配置数组型配置明确写在文档里要求项目侧补全。这一点官方文档不一定写得很清楚属于实践推断你用之前最好自己验一遍验的方法很简单生成一个项目看合并后的配置文件长什么样。另一个经常被忽视的是模板标签。它看起来只是分类用的元数据但在模板多起来之后标签直接决定了交互式选择界面的可用性。团队里模板超过十个没有标签体系选模板就跟抽盲盒一样。我的习惯是按“平台组合”和“业务复杂度”两个维度打标前者让工具链能筛出多端模板后者让人能一眼判断这是五分钟能用的还是需要配置半天的。2.3 配置合并的优先级顺序合并优先级这件事值得单独占一小节因为它决定了“在哪儿改配置才生效”。我实测下来从高到低大致是这样的顺序项目本地显式写的配置优先级最高可以覆盖模板带来的任何基线值。环境专属配置比如按环境划分的配置目录里的内容覆盖基线但不覆盖项目级显式配置。模板带来的基线配置作为兜底存在。各平台的框架默认值最底层。这个顺序背后的逻辑是“越靠近具体项目越应该有最终话语权”。模板是全局的、跨项目的它的值必须能被单项目推翻否则共享模板就变成了枷锁。环境配置高于基线是因为同一份基线在不同环境下的取值本来就该不同把它放在基线之上符合直觉。实操中我踩过的坑是有人把环境相关的东西写进了模板基线然后在项目里怎么改都不生效因为改的地方优先级不对。排查这类问题有个笨但有效的办法就是把生成后的完整配置文件打印出来对照上面这个顺序看目标字段最终落在哪一层。工具链一般有配置查看类的命令没有的话就自己在构建脚本里打一行日志成本很低收益很高。3. 五分钟做出第一个能跑的模板前面讲的都是结构这一节进入动作层面。我会按我自己实际的顺序走一遍生成骨架、改声明、注册到本地、拉起验证。整个流程熟练之后确实能在五分钟内走完但第一次做建议留出半小时把每一步生成的结果都看一遍。3.1 从官方模板生成骨架再改造最快的起点不是从空目录手写而是拿一个官方最小模板当底子改造成自己的。流程大概是先用命令行创建一个新项目交互式选择最简模板然后把这个项目里与工程骨架相关的部分抽出来剔掉业务代码整理成模板目录最后补上描述文件。这里有个判断标准值得说什么该抽进模板什么该留在业务项目。我的判断是“三条以上业务线都会用到的东西”才进模板。举个反例鉴权流程看起来通用但每个业务的登录方式、Token 策略、埋点要求都不一样硬塞进模板的结果就是每个项目进来第一件事是先删掉模板里的鉴权代码。反例之外像目录约定、代码规范配置、构建脚本封装、基础的主题变量这些才是真正三条线都一样的适合进模板。抽取过程中我会刻意做两件事。第一件是把所有硬编码的字符串抽成占位符或配置项包括应用名、包名、接口前缀、图标路径。第二件是删掉所有跟具体业务相关的资源文件只留一份占位资源避免模板包体积膨胀。这两件事看起来琐碎但决定了这个模板半年后还能不能用。我见过一个模板里带着几十兆的演示图片结果每次拉模板都要多等十几秒团队里怨声载道。改造完成之后用描述文件把模板身份登记上去。名称建议带上组织或业务域前缀避免和公共模板重名版本从 1.0.0 起严格遵守语义化版本因为下游项目会按版本锁定。描述写清楚适用场景和平台覆盖范围这行文案以后会被几十个人看到值得多花两分钟。3.2 本地注册与调试的正确姿势模板写完了怎么让工具链认识它几种常见的来源形式本地目录、包仓库里的包、代码仓库地址。开发阶段毫无疑问用本地目录改一次立刻生效不用打包发布省时间。本地注册的方式通常是在项目里添加模板来源工具链会记录这个来源并在生成时解析。我一般会把本地路径写进一个单独的开发配置里不提交到版本库避免污染团队其他成员的配置。这一点很重要本地绝对路径是最典型的“只在我机器上能跑”的来源。调试阶段我关注三件事模板能不能被正确识别也就是出现在模板列表里名称和描述显示正常。生成的项目里配置文件是不是按预期合并的重点看数组字段有没有被意外覆盖。生成的项目能不能直接启动先跑 Web 端最快因为它对本地环境要求最低。关于验证顺序我强烈建议先 Web 后移动端。Web 端构建链路短、报错信息友好能快速暴露依赖缺失、入口路径写错这类基础问题。移动端构建链路长一个依赖冲突可能要等五分钟才报错用它来做第一轮验证是浪费时间。等 Web 端跑通了再上移动端问题范围就小很多。3.3 生成一个项目并逐项验证生成项目的命令执行完之后别急着写业务代码先做一轮系统性验证。我自己的验证清单是这样的你也可以直接拿去改验证项检查内容不通过的典型原因目录结构约定的目录是否齐全模板目录被误删或命名不一致配置文件基线配置是否成功合并描述文件里的配置字段拼写错误依赖安装依赖是否装全、有无警告模板依赖与项目依赖版本冲突入口启动Web 端能否正常渲染入口文件路径与实际不符平台构建至少一个移动端能出包平台工具链版本不匹配资源引用图标、字体是否可加载资源路径写成了绝对路径这份清单里我踩得最多的是第四项和第五项。入口路径的问题是模板里写了相对路径但生成后目录层级变了相对路径就失效了。平台构建失败的原因通常和模板无关是本地环境的问题所以遇到这一类先确认环境再怀疑模板。还有一个细节生成完之后把项目里最终生效的完整配置导出来看一眼。这一步能发现大量隐性问题比如某个基线值被更高优先级的空值覆盖了或者某个插件声明被数组覆盖规则吃掉了。花两分钟看这一眼比后面花两小时排查要划算得多。4. 把模板做成企业级基建的关键改造能跑通是及格线能支撑多条业务线长期演进才算合格。这一节讲的是我在实际项目里做的几项改造每项改造都对应一个真实存在的痛点不是为了架构好看而架构。4.1 多环境配置与参数注入方案企业级项目和玩具项目的最大差别就是环境多。开发、测试、预发、生产可能还有内部体验环境。如果模板里写死一套配置每个环境都要手动改出错是必然的。我的做法是把配置按环境拆开模板只提供基线环境和差异全部下沉到项目侧。目录上大致是这样的结构appConfigs/ ├── base/ │ └── renative.json # 基线来自模板 ├── develop/ │ └── renative.json # 开发环境覆盖 ├── staging/ │ └── renative.json # 预发覆盖 └── production/ └── renative.json # 生产覆盖环境目录里只写与基线不同的部分相同的继承下来。这样做的直接好处是 diff 干净review 的时候一眼能看出这个环境改了什么。间接好处是环境新增成本极低加一个新环境就是加一个目录写三五个字段的事。敏感参数的注入要单独处理。签名证书、密钥这类东西绝对不能进配置文件和模板我的做法是通过构建时的环境变量注入配置里只留占位符引用。这里要注意一点占位符的解析发生在构建期还是运行期不同工具链行为不一样需要实测确认。如果是运行期解析那构建产物里可能还留着占位符字符串得在启动时做兜底校验否则线上会拿到一个字面量路径排查起来很费劲。参数注入还有一个实践细节给所有可注入参数一个明确的默认值。默认值不是为了生产用是为了让项目在缺少注入时能启动起来、能跑本地调试而不是直接崩掉。这个习惯能省掉大量“新同学拉下代码跑不起来”的沟通成本。4.2 私有模板源与版本策略模板要共享就得有地方存。小团队可以直接放在代码仓库里用目录或子模块的方式引用。团队规模上去之后我更推荐走私有包仓库因为包仓库自带版本管理、权限控制、发布记录这三点是代码仓库给不了的。版本策略上我的建议是模板版本和项目版本解耦但要有明确的兼容承诺。具体来说模板遵循语义化版本补丁版本只改文档和注释次版本加配置项且保持向后兼容主版本才允许破坏性变更。项目侧锁定到次版本范围既能拿到修复又不会被破坏性变更打到。升级节奏也需要约定。我的经验是模板的主版本升级必须配迁移说明写明哪些字段被删了、哪些默认值变了、存量项目该怎么改。没有迁移说明的主版本升级基本等于逼着下游项目放弃升级模板就僵化了。迁移说明不用写得多正式一个变更清单加几段示例配置就够关键是别省略。另外一个容易被忽略的点是模板的废弃策略。模板也会过时会有一批模板没人用但还挂在列表里新同学选模板的时候会被干扰。我一般会定期做一次盘点把没人引用的模板归档归档之前先在文档里标注废弃原因和推荐替代保留半年再删。这个流程听起来麻烦但比让十个模板自生自灭要好。4.3 与构建流水线的衔接方式模板最终要落到流水线里才能体现价值。我理解的衔接分两条链路模板自身的发布链路和项目侧的构建链路。模板发布链路要做的事是模板变更后自动跑一遍校验包括文件结构检查、描述文件字段校验、以及最关键的“冒烟生成”——自动生成一个项目并跑一遍 Web 端构建。这一步能拦住绝大多数低级错误比如描述文件写错导致模板不可用。我见过有团队模板直接手改手发布结果某次改完所有新项目都起不来回滚花了半小时而这道冒烟检查的成本不到两分钟。项目侧构建链路要做的事是构建开始时确认模板版本符合预期配置合并结果能被审计。做法通常是在构建脚本开头打一行日志输出模板名称、版本、配置来源这行日志在排查线上问题时价值极高能立刻确认“这次构建到底用的是哪套骨架”。还有一处值得做的是变更感知。模板更新后存量项目不会自动跟着变这是设计使然但团队需要一个机制知道“我的项目落后了”。简单做法是定期跑一个脚本扫描所有项目的模板版本生成一份落后清单按业务重要度排序安排升级。复杂做法是接进流水线做强制检查但我不建议一上来就强制先让大家看到清单形成习惯之后再收紧阻力会小很多。5. 踩坑实录与问题排查速查这一节全是实打实踩出来的。模板系统的问题有个特点报错信息往往指向业务代码但根因在模板配置所以排查方向很容易跑偏。下面这几个是我遇到频率最高的附上我的定位思路。5.1 依赖版本错位导致构建失败最典型的表现是模板单独看没问题生成项目也没报错但一构建就出现模块找不到或者重复实例化的错误。根因通常是模板声明了一套依赖版本项目侧又装了另一套包管理器在提升依赖的时候产生了两个版本共存。定位方法是把依赖树打出来搜关键包名看有没有出现两个不同版本。有的话先确认是谁引入的通常能顺着依赖链找到模板声明的那一条。解决方式有两种一是在项目侧显式锁定版本把冲突压平二是修改模板的版本范围放宽或收紧。我倾向第二种因为问题根源在模板压平只是掩盖。这里有个预防措施很有效模板的依赖声明尽量用宽松范围把具体版本锁定交给项目侧的锁文件。模板管“需要什么”项目管“用哪一版”职责清晰冲突自然少。反过来如果模板里写死精确版本项目侧想升某个包就必须改模板非常别扭。注意依赖冲突的报错信息经常具有误导性指向的是一个完全无关的模块。遇到这类报错第一反应应该是打依赖树而不是去看报错指向的那个文件。5.2 平台工程目录被覆盖的现场这个坑我踩过两次损失都不小。场景是这样的模板里不小心带上了平台原生工程目录生成项目的时候把本地的签名配置、本地属性文件覆盖掉了。表现是构建突然要求重新配置签名或者本地调试的接口地址被重置。根因是模板把“生成产物”当成了“源文件”。平台原生工程目录本质上是构建工具生成的中间产物虽然有时候需要手动改但它不适合作为模板内容分发。正确的处理方式是只分发配置补丁让构建过程自己生成完整目录。已经出了这个问题怎么救如果项目在版本库里直接回滚被覆盖的文件最快。如果没在版本库里那就得靠本地备份或者重新配置。这也是我坚持“平台目录不进版本库、但关键本地配置要有备份”的原因。听起来矛盾其实是分层的生成产物不进版本库但生成产物依赖的本地私密配置要单独备份。提示模板里凡是会被构建工具重新生成的目录一律不要纳入。判断标准很简单删掉它还能通过构建重新生成那就是生成产物。5.3 常见问题速查表把上面这些和另外几类高频问题整理成一张表出问题的时候按表扫一遍能覆盖八成场景现象高概率原因处理动作模板列表里看不到自定义模板描述文件缺失或字段非法校验描述文件结构与必填字段生成后配置项不生效优先级被更高层覆盖导出完整配置逐层比对数组配置丢失数组合并策略是覆盖项目侧完整声明数组本地能跑、流水线失败本地路径或环境变量未注入检查构建脚本的变量来源模板升级后项目报错主版本破坏性变更按迁移说明逐项调整构建产物异常变大模板带了演示资源或二进制清理模板资源目录跨系统构建失败路径分隔符或大小写敏感差异统一用路径拼接 API检查文件名这张表里我最想强调“本地能跑、流水线失败”这一条因为它最难排查。本地有环境变量、有缓存、有全局安装的工具流水线里什么都没有。我的习惯是每次改完模板都想办法在一个干净环境里跑一次生成和构建哪怕只是临时起个容器。这一步能提前暴露九成的环境依赖问题。6. 关于模板粒度的一点个人取舍写到这里前面把结构、流程、改造、排查都过了一遍最后我想聊一个没有标准答案的问题模板到底该做多细。我试过两个极端一个是极简模板只给目录结构和基础配置另一个是重模板把代码规范、构建脚本、主题、甚至常用页面骨架全塞进去。极简模板的好处是灵活坏处是每条业务线都要自己搭一遍半年后各条线的工程结构差异很大想统一做点基建改造就得挨个适配。重模板反过来起步统一但升级成本高任何一处变更都要考虑所有下游而且容易把不该通用的东西固化下来。我现在的位置在中间偏极简模板只固化三样东西目录约定、构建脚本封装、平台配置基线其余全部交给插件和项目本地。判断某个东西要不要进模板我会问自己一句如果一年后有一个业务线想改掉它改起来是改模板容易还是改项目配置容易。答案通常是后者那就别进模板。还有一个实操小技巧值得分享。模板的改动我一般不直接改而是先在一个真实项目里改跑通两周确认没问题再反哺回模板。这样能避免把试验性的东西直接推给所有下游。模板越是被依赖变更就越要谨慎这个节奏感需要时间培养急不来。另外建议给每个模板配一份极简的使用说明不超过一页写清楚适用场景、需要注入哪些参数、生成后第一步该做什么。这份说明的阅读量远超正式文档因为大家都是在赶时间的时候才会去翻它。