微信小程序项目目录结构详解:app、pages、utils 与配置文件
微信小程序目录结构看起来只是一些固定文件名,但真正容易出错的是它们的作用范围:哪些配置控制整个小程序,哪些文件只属于一个页面,哪些目录只是团队约定,以及开发者工具配置和运行时代码为什么不能混在一起。
本文以原生微信小程序为例,从项目根目录开始拆解 app.js、app.json、app.wxss、pages、components、utils 和项目配置文件。如果账号、AppID 或开发者工具还没有准备好,可以先完成微信小程序账号、AppID 与开发者工具配置,再回到这篇查看新项目。
一、先分清项目根目录和小程序源码目录
最简单的项目会把小程序源码直接放在项目根目录;稍复杂的仓库则可能把源码放进 miniprogram/,把后端、脚本或文档放在同一个仓库的其他目录。开发者工具通过 project.config.json 中的 miniprogramRoot 判断小程序源码从哪里开始。
mini-program-demo/
├── miniprogram/ # 小程序源码目录
│ ├── app.js # 小程序入口逻辑
│ ├── app.json # 全局配置
│ ├── app.wxss # 全局样式,可选
│ ├── sitemap.json # 索引规则
│ ├── pages/
│ │ ├── home/
│ │ │ ├── home.js
│ │ │ ├── home.json
│ │ │ ├── home.wxml
│ │ │ └── home.wxss
│ │ └── detail/
│ │ ├── detail.js
│ │ └── detail.wxml
│ ├── components/
│ │ └── product-card/
│ ├── services/ # 业务接口封装,团队约定
│ ├── utils/ # 通用工具,团队约定
│ └── assets/ # 本地图片等静态资源
├── project.config.json # 可共享的开发者工具配置
└── project.private.config.json # 个人工具配置
上面的 services、utils 和 assets 不是框架强制目录,只是便于维护的常见约定。不要因为模板里有一个目录,就误以为小程序会自动加载其中的文件。

二、根目录的 app 文件分别负责什么
| 文件 | 是否必需 | 作用 |
|---|---|---|
app.js | 是 | 注册小程序实例,处理全局级事件和初始化逻辑。 |
app.json | 是 | 声明页面路径、窗口、分包、组件和其他全局配置。 |
app.wxss | 否 | 保存会作用于所有页面的公共样式。 |
当前微信开放文档明确把 app.js 和 app.json 列为必需文件,把 app.wxss 列为可选文件。app.js 是整个小程序中最先执行的 JavaScript 文件,整个小程序只有一个 App 实例。适合放启动、前后台切换和全局错误等事件入口,但不适合把所有业务函数、共享变量和接口请求都堆进去。
app.json 是运行时全局配置,不是开发者工具配置。页面注册、默认窗口表现、底部 tab、分包和全局组件等都从这里读取;JSON 文件不能写注释,键名和字符串必须使用双引号。
三、pages 中一个页面由哪些文件组成
一个页面通常把同名文件放在同一个目录中。例如 pages/detail/detail 对应 detail.js、detail.wxml、detail.json 和 detail.wxss。四个文件必须保持相同路径和基本文件名,但不是四个文件都必须存在。
| 文件类型 | 是否必需 | 职责 |
|---|---|---|
.js | 是 | 页面数据、事件处理和生命周期逻辑。 |
.wxml | 是 | 页面结构和数据绑定,相当于视图模板。 |
.json | 否 | 当前页面的标题、下拉刷新、组件引用等局部配置。 |
.wxss | 否 | 只作用于当前页面的局部样式。 |
很多旧教程会把四个文件都写成“必需”,这和当前官方目录结构文档不一致。没有页面级配置时可以不创建 .json;没有局部样式时也可以不创建 .wxss。保留空文件不会让项目更规范,反而会增加维护噪声。
页面 JS 还承载加载、显示、隐藏和销毁等状态变化。代码应该放在初始化还是返回刷新阶段,可以继续参考微信小程序页面生命周期详解。
四、app.json 如何找到 pages 里的页面
app.json 的 pages 数组声明小程序由哪些页面组成。路径不写扩展名,框架会到对应位置查找同名的 .js、.wxml、.json 和 .wxss 文件。
{
"pages": [
"pages/home/home",
"pages/detail/detail"
],
"window": {
"navigationBarTitleText": "示例小程序"
},
"sitemapLocation": "sitemap.json"
}
没有设置 entryPagePath 时,pages 数组第一项就是默认启动页面。新增、移动或删除页面后,要同步修改 pages;否则常见结果是文件明明存在,编译时却提示找不到页面,或者启动后仍然进入旧首页。
五、页面配置为什么会覆盖全局配置
页面对应的 .json 可以覆盖 app.json 中允许按页面设置的同名配置。例如全局导航栏标题在 app.json 中定义,详情页可以在 pages/detail/detail.json 单独改写:
{
"navigationBarTitleText": "商品详情",
"enablePullDownRefresh": true
}
页面配置不是把完整的 window 对象复制一遍,而是直接写当前页面支持的属性。并非所有全局配置都能被页面覆盖,应以当前页面配置文档列出的选项为准。
六、components、services、utils 和 assets 怎么分
| 目录 | 适合放什么 | 不适合放什么 |
|---|---|---|
components/ | 可复用的界面单元及其逻辑、模板、样式和配置。 | 只会在一个页面出现、与页面状态强绑定的大段结构。 |
services/ | 登录、用户、商品等业务接口封装。 | 按钮点击和页面展示状态。 |
utils/ | 日期、格式化、校验等无界面依赖的通用函数。 | 所有接口、缓存和业务规则混成一个巨大文件。 |
assets/ | 需要随小程序包发布的本地图片等静态资源。 | AppSecret、生产令牌或本可放 CDN 的大体积素材。 |
自定义组件和页面一样使用同名的 JS、WXML、JSON、WXSS 文件,但需要在组件 JSON 中声明 "component": true,再通过页面或全局的 usingComponents 引用。目录叫不叫 components 并不重要,引用路径和职责边界才重要。
services 和 utils 都属于团队约定。一个实用判断是:函数如果知道“用户”“订单”或具体接口路径,它更像业务服务;函数如果只接收值并返回格式化结果,它更适合放在工具模块。无论放在哪里,都需要通过模块导入使用,小程序不会因为目录名称自动加载代码。
七、project.config.json 不属于运行时代码
project.config.json 和 project.private.config.json 主要服务于微信开发者工具,例如 AppID、源码根目录和本地调试设置。它们不会代替 app.json 注册页面,也不应被业务代码读取。
同名配置同时存在时,个人配置的优先级高于公共项目配置。团队通常提交 project.config.json,把 project.private.config.json 加入 .gitignore。遇到“同一仓库在不同电脑行为不同”,应同时比较这两个文件和开发者工具中的当前项目设置。
八、代码到底应该放在哪里
- 只影响某个页面的数据和交互:放在对应页面的 JS 中。
- 多个页面复用的界面和交互:拆成自定义组件。
- 多个页面复用的业务接口:按领域拆到
services/。 - 不依赖页面和业务对象的纯函数:放到
utils/。 - 全局启动和应用级事件:保留在
app.js。 - 全局窗口、页面列表、分包和全局组件声明:写在
app.json。
目录结构的目的不是让文件看起来整齐,而是让问题有明确归属。页面不能打开时先看 pages 和路径;样式覆盖异常时区分 app.wxss 与页面 WXSS;团队机器行为不一致时看项目配置;接口鉴权问题则不应在视图目录里反复试错。
九、七个常见目录结构错误
- 页面文件名不一致:
detail.js搭配了index.wxml,框架无法把它们识别为同一页面。 - 新增页面没有注册:目录已经创建,但
app.json的pages没有对应路径。 - 把扩展名写进 pages:页面路径应写成
pages/detail/detail,而不是detail.wxml。 - 源码根目录选错:开发者工具打开仓库根目录,却没有正确设置
miniprogramRoot。 - 页面配置放错层级:把页面标题写进错误的 JSON,或误以为所有全局配置都能被页面覆盖。
- 把所有共享代码塞进 app.js:造成全局耦合,页面和业务模块难以单独测试。
- 客户端保存密钥:把 AppSecret、数据库密码或生产令牌放入 config、utils 或任何会进入小程序包的文件。
十、整理现有项目的检查清单
- 确认开发者工具识别的源码根目录与
miniprogramRoot一致; - 核对
app.json中的页面路径与实际目录完全一致; - 确认每个页面的文件保持同路径、同基本文件名;
- 删除没有用途的空页面配置和空样式文件;
- 把重复界面拆成组件,把业务接口和纯工具函数分开;
- 确认个人工具配置未意外进入 Git;
- 确认整个客户端代码和静态资源中不存在 AppSecret 或其他服务端密钥。
看懂目录结构后,下一步不应继续添加更多空目录,而是选一个真实页面,从 app.json 的页面注册开始,沿着页面 JS、WXML、WXSS 和接口模块走一遍完整数据流。后续接入登录时,就能明确 wx.login 应由页面或服务模块发起,而 AppSecret 只能留在后端。
官方资料:微信开放文档《目录结构》、微信开放文档《小程序代码构成》、微信开放文档《全局配置》、微信开放文档《页面配置》、微信开放文档《注册小程序》(访问日期:2026 年 8 月 24 日)




