
做微信小程序第三方平台开发的朋友应该都跟ext.json打过交道。这个文件本身不大但配置起来坑是真的多尤其在uni-app项目里很多人第一步就卡在“文件到底放哪个目录”上放错了编译一百遍也读不到。正好最近在做一个基于第三方平台的小程序项目把ext.json完整踩了一遍这篇就专门把配置流程和思路掰开揉碎讲清楚给后面要踩这条路的朋友当个参考。先自我介绍下背景我这边是用uni-app做小程序模板开发平台侧走的是微信小程序第三方平台模式。之前一直用原生小程序写配置ext.json轻车熟路换到uni-app之后突然发现事情没那么简单uni-app有自己的编译机制ext.json不能像原生项目一样随便丢到根目录就完事它和构建产物之间隔着一层编译关系。很多教程只贴出ext.json的内容但“文件放哪、什么时候放、怎么自动放”这三个问题统统没说结果就是照着抄了工具里依然报错。这篇文章会覆盖完整流程第三方平台模式的基本概念、ext.json每个字段的用途、uni-app里合适的存放位置、自动化复制方案以及我实际开发中遇到的坑和排查思路。内容以CLI创建的uni-app项目为主HBuilderX项目我也会单独说一句因为两种工程结构在配置方式上确实有差别。1. 先搞明白ext.json到底解决什么问题1.1 第三方平台代开发的基本逻辑小程序第三方平台说白了就是给没有开发能力的个人或企业提供小程序开发、托管、代运营服务的。如果你做SaaS或者代开发服务手上可能会有几十上百个商户每个商户都需要一个小程序而且每个小程序之间只有少量差异比如商户ID、接口域名、主题色这些。如果每个商户单独搞一套代码维护成本会直接失控。所以微信提供了第三方平台机制服务商在平台侧注册一个“模板程序”再通过“模板参数”的方式把一套代码复制给多个授权方使用。问题来了同一个模板程序怎么让不同商户的小程序启动时加载不同配置答案就是ext.json。这个文件在开发者工具里指明当前小程序运行在“第三方平台”模式下并告诉微信基础库当前这个程序到底属于哪个第三方平台、需要额外注入哪些页面、需要读取哪些自定义参数。微信工具在加载代码时先找到ext.json把它解析合并到启动配置里整个小程序才会按预期运行。1.2 ext.json的核心字段与作用ext.json不是随便写几个键就行里面每个字段都对应一套逻辑。我直接拿一份实际用过的模板来说明{ extAppid: wx1234567890abcdef, extEnable: true, extPages: { pages/order/detail: { navigationBarTitleText: 订单详情, enablePullDownRefresh: true } }, ext: { merchantId: 1001, env: prod, apiBaseUrl: https://api.example.com }, window: { navigationBarBackgroundColor: #4C8EF3 } }extAppid必填。这是第三方平台自身的AppID不是某个商户小程序的AppID。开发者工具靠它来识别属于哪个第三方平台并拉取对应的权限配置。extEnable是否允许小程序读取ext中的自定义数据。默认情况下是不允许的必须显式设为truewx.getExtConfigSync()才能拿到数据。extPages动态页面配置。键是页面路径值是该页面的window配置。它的作用是在模板本身不声明这些页面的情况下通过ext.json动态把页面挂载进来同时覆盖页面的标题、下拉刷新等属性。这是第三方平台扩展小程序能力的关键手段。ext自定义数据。这是全项目最自由的部分可以放商户ID、接口地址、环境标识等等开发者在代码里通过uni.getExtConfigSync()读取。window全局窗口配置。在ext.json里写window会覆盖模板app.json里的对应配置方便按不同商户定制外观。这里有个容易混淆的点ext.json虽然叫“配置”但它不是纯静态文件。它更像“运行时的入口参数”基础库在启动小程序时会先读它再决定加载哪些页面、读取哪些数据。所以配置不对表现出来的问题往往很诡异比如明明代码里写了某个页面打开却提示“页面不存在”可能就是extPages没配对。2. uni-app里最头疼的事ext.json放哪才对2.1 为什么static目录不行源码根目录也不行原生微信小程序项目里ext.json放项目根目录开发者工具直接识别。但uni-app不一样你写的源码在srcCLI项目或项目根目录HBuilderX项目真正运行的是编译后的dist产物。微信开发者工具打开的永远是dist/mp-weixin目录而不是你的源码目录。这就导致一个结果你把ext.json放在源码根目录编译后文件不会自动出现在dist里工具自然找不到。那放到static目录呢uni-app的static目录确实会原样拷贝进产物但拷贝时会把目录结构也保留下来也就是dist/mp-weixin/static/ext.json。微信工具只在项目根目录寻找ext.json放到static子目录里等于白放。所以结论很直接ext.json必须出现在编译产物的根目录即dist/dev/mp-weixin/ext.json开发调试或dist/build/mp-weixin/ext.json打包构建。要想达到这个效果要么手动复制要么写个脚本自动复制要么用构建插件搞定。2.2 两类工程结构的配置思路对比uni-app项目主要有两种工程结构配置策略不太一样CLI工程uniapp cli创建vue3 vite。推荐方式是在vite.config.js里注册一个自定义插件在构建完成时把ext.json从源码根目录复制到输出目录。这个方案最省心每次构建自动完成不会漏。HBuilderX工程。没有vite.config.js也没有npm run dev这种标准脚本虽然现在HBuilderX也支持npm了但默认流程不同最直接的办法是先跑一次构建打开unpackage/dist/dev/mp-weixin目录把ext.json手动扔进去。后续只要编译器没清空整个目录ext.json会一直在但如果改了源码导致重新全量编译又得重放一次。说实话如果你的项目涉及第三方平台模式我建议尽量用CLI工程。手动复制来回几次太容易忘而且报错之后又会怀疑是自己的配置问题排查成本很高。自动化是唯一正确姿势。3. 超详细实操从零配置ext.json3.1 第一步准备第三方平台账号配置前需要先有一个第三方平台账号。你需要去微信开放平台注册“第三方平台”创建完成后获得一个AppID不是小程序的AppID。这个AppID就是extAppid要填的值。另外还有一个环节很容易漏开发者工具要以第三方平台模式打开项目你当前的微信号必须被加入第三方平台的“项目成员”或“体验成员”否则导入时会直接提示没有权限。我当时第一次操作就卡在这一直以为是ext.json出了问题折腾半天才发现压根没加成员。如果你还没有账号可以用测试号临时体验流程但生产环境还是建议用正式账号。因为测试号功能受限部分页面配置和动态路由的表现会和正式环境有差异。3.2 第二步编写ext.json模板在CLI项目的源码根目录和package.json同级新建ext.json内容参考上文的模板。注意几点第一extAppid不要复制粘贴错尤其注意大小写和特殊字符填错后工具会提示“无效的extAppid”。 第二extPages的键不要带开头的斜杠pages/order/detail正确/pages/order/detail会匹配不到页面。 第三ext里建议放上一个默认环境标识方便在代码里判断当前是开发、体验还是生产环境。用uni.getExtConfigSync()读取时如果工具里读取不到ext数据至少能从默认值上看出问题。3.3 第三步uni-app页面注册与ext数据获取extPages能动态挂载页面但有个前提页面文件必须真实存在于编译产物里。uni-app的编译机制是pages.json里注册了哪些页面才编译哪些页面对应的代码。如果你只写了extPages但pages.json里没有对应路径那构建产物里压根没有这个页面的文件运行时就会白屏或者报“页面未找到”。所以正确做法是先把所有可能被extPages挂载的页面都注册到pages.json里哪怕某些页面只在特定商户端才会展示也要先注册进来。之后在ext.json里再用extPages覆盖这些页面的窗口配置或者动态挂载到部分商户小程序中。这一步看起来有点绕我打个比方pages.json像是“后台员工名册”extPages像是“排班表”。员工名册里必须有这个人排班表才能把他排进某个岗位。uni-app编译只认名册extPages只是影响运行时的路由表现。数据读取方面在App.vue的onLaunch里这样拿onLaunch() { let extConfig {}; try { extConfig uni.getExtConfigSync ? uni.getExtConfigSync() : {}; } catch (e) { extConfig {}; } this.globalData.extConfig extConfig; }这里用try catch包一下比较稳。如果你在普通小程序模式非第三方平台下跑同一套代码uni.getExtConfigSync()行为可能不确定包一层可以避免单点崩溃。3.4 第四步让ext.json自动进入编译产物这是最关键的环节。以CLI工程为例打开vite.config.js在uni插件后面注册一个自定义插件import { defineConfig } from vite; import uni from dcloudio/vite-plugin-uni; import fs from fs; import path from path; export default defineConfig({ plugins: [ uni(), { name: copy-ext-json, closeBundle() { const outDir process.env.UNI_OUTPUT_DIR || dist/dev/mp-weixin; const outputPath path.resolve(process.cwd(), outDir); const srcFile path.resolve(process.cwd(), ext.json); const destFile path.join(outputPath, ext.json); if (fs.existsSync(srcFile)) { fs.mkdirSync(outputPath, { recursive: true }); fs.copyFileSync(srcFile, destFile); console.log([ext.json] copied to, destFile); } else { console.warn([ext.json] source file not found); } } } ] });这里的closeBundle钩子在构建结束时会执行刚好把ext.json复制到输出目录根部。使用CLI项目后npm run dev:mp-weixin和npm run build:mp-weixin两条命令都会触发这个钩子全程不用手动干预。有朋友会问为什么不用fs.copyFileSync直接放到dist里就完事因为uni-app在不同模式下输出目录不一样开发是dist/dev/mp-weixin打包是dist/build/mp-weixin。错误目录会导致工具开启后提示找不到ext.json所以别偷懒用UNI_OUTPUT_DIR这个环境变量定位最准确。上面实现的代码只是其中一种思路如果你的项目路径结构不同也可以调整成读取环境变量再拼目录效果一样。如果你是HBuilderX工程没有vite.config.js那就在根目录创建一个简单的node脚本比如scripts/copy-ext.jsconst fs require(fs); const path require(path); const src path.resolve(__dirname, ../ext.json); const destDir path.resolve(__dirname, ../unpackage/dist/dev/mp-weixin); const dest path.join(destDir, ext.json); if (!fs.existsSync(destDir)) { fs.mkdirSync(destDir, { recursive: true }); } if (fs.existsSync(src)) { fs.copyFileSync(src, dest); console.log([ext.json] copied to, dest); } else { console.warn([ext.json] not found); }然后用node scripts/copy-ext.js手动执行。这个方案只能算半自动适合开发阶段临时用如果项目规模变大建议迁移到CLI工程。3.5 第五步微信开发者工具导入调试这一步也有讲究。打开微信开发者工具新建项目时开发模式要选择“第三方平台”而不是默认的“小程序”。AppID填写第三方平台的AppID目录选择编译输出目录。正常情况下工具读取到ext.json后会以第三方平台模式正常编译运行。如果工具一直提示找不到AppID或者项目类型错误先再检查一下开发模式是不是真的选到了第三方平台。进入调试后在代码里测试uni.getExtConfigSync()能正确打印ext字段就说明整条链路已经通了。如果返回空对象优先检查extEnable是否置为true还有开发者工具里是否真的以第三方平台模式打开。4. 常见问题与排查实录4.1 报错信息速查表这段时间实操下来我把最常见的几类问题整理成了一张表按“报错信息、原因、解决办法”三列对照排查时可以直接照着处理。报错信息或现象可能原因解决办法打开项目提示“无法识别extAppid”extAppid填错或未在后台开通第三方平台权限核对extAppid是否准确到开放平台确认当前账号已创建第三方平台工具里ext.json根本没生效getExtConfigSync返回空开发者工具没有选择第三方平台模式新建项目时开发模式选“第三方平台”并填写正确的AppID开发者工具提示没有权限当前微信号未加入第三方平台成员到开放平台把微信号添加为项目成员或体验成员页面路径明明配置了但打开提示找不到页面页面未在uni-app的pages.json中注册编译产物缺少页面文件在pages.json中先注册页面再通过extPages覆盖配置页面配置正确但标题、下拉刷新等属性没生效extPages的路径格式错误比如多写了斜杠检查路径是否以pages/开头且无前导斜杠构建后ext.json又消失了编译器重新全量构建清空了dist目录改用vite插件或脚本自动复制不要手动放文件4.2 隐藏较深的三四个坑第一个坑manifest.json里的mp-weixin.appid配置。很多人习惯在manifest里填一个小程序AppID结果导入第三方平台模式时开发者工具读取到project.config.json里的appid和第三方平台AppID对不上就会产生各种奇怪的问题。建议在第三方平台项目里把manifest的mp-weixin.appid留空或者统一不写。让工具完全通过ext.json和对话框里填写的AppID来决定身份。第二个坑动态页面如果也配置了tabBar情况会比较复杂。微信官方允许ext.json里配置tabBar但tabBar的list页面必须已经注册且在模板中存在否则工具会报错。如果你只是给某个商户换一下tabBar图标颜色直接在ext.json里改iconPath字段就行但图标文件必须以静态资源形式提前打包进模板工程比如放到static目录路径在编译后会存在不能引用不存在的文件。第三个坑ext.json里的window配置优先级。ext里的window会覆盖app.json的window但不会覆盖页面级别的window配置。如果你在具体页面的json里写死了某个背景色想用ext.json全局改成另一个色是改不动的。这一点和app.json的覆盖规则完全一致别指望ext.json是“万能覆盖”微信的配置合并顺序是页面配置 全局配置ext.json的window也在全局层面生效 默认值。第四个坑第三方平台模式下代码上传入口和普通小程序不一样。开发者工具里可能找不到“上传”按钮这很正常因为第三方平台模板代码是通过服务端接口上传的。调试阶段不用纠结上传问题只要本地编译运行正常即可。如果你需要真机预览就用开发工具的“预览”功能但前提还是那句微信号要加入项目成员。4.3 我的调试经验总结最后分享一点我个人的调试习惯。我拿到一个新的第三方平台项目时不会一上来就塞一大堆ext配置而是先写一个最小化ext.json只包含extAppid和extEnable跑通了再逐步加extPages、ext数据。这样做的好处是一旦出问题你能立刻知道是基础配置的问题还是新增字段导致的排查范围小很多。在这个最小化配置阶段我会配合code里打一个通用日志在第一次渲染的页面onLoad里打印uni.getExtConfigSync()的结果。只要控制台能打出我们预期的对象说明工具、项目类型、ext.json路径全链路都没问题后面的工作完全不同再怀疑这一层配置。还有一个很实用的小技巧如果团队里多个人都在同一台电脑上调试尽量让每个人都在自己的开发者工具里保存一份独立的第三方平台项目记录不要反复导入同一个目录。工具偶尔会因为缓存原因在切换账号后还保留上一个账号的授权状态导致看起来就像ext.json失效了。清一下工具缓存或者把项目关闭重新导入往往就恢复正常了。整体而言uni-app里配置ext.json最核心的是搞清楚“文件最终必须在dist/mp-weixin根目录”这件事。有了这个认知后面所有方案基本都是围绕“如何把文件准确送到那个位置”来展开。自动化复制是必须的手动操作永远会坑自己。如果后续你的模板需要支持更复杂的差异化能力也可以再研究Subpackages分包在ext.json中的动态配置原理和extPages类似都是先在pages.json注册分包再通过extPages或分包字段控制运行时行为。这一部分微信文档写得比较精简有机会我再单独写一篇把我在真实项目中踩过的坑也一并整理出来。