第1章 1.3 页面与路由:pages.json
📌 上一章我们创建了第一个 uniapp 项目,看到了那个"Hello World"跑了起来。但你有没有想过:这个页面是从哪来的?点击 tabBar 切换页面是什么原理?小程序里那些"页面"到底是怎么组织的?
这一章我们就来解决这些问题。学完你会发现:页面管理原来这么简单,就靠一个文件——pages.json。
🎯 开场 3 分钟:为什么要学这个?
你有没有遇到过这些情况:
- 新建了一个页面,但小程序里怎么都找不到它——因为没在
pages.json里注册 - 想加个底部 tabBar,但不知道怎么配——还是
pages.json的活儿 - 想自定义页面标题,结果改了 wxml 没用——得改
pages.json
pages.json 就像是剧院的节目单——告诉小程序:有哪些"页面"节目、每个节目长什么样、观众该怎么切换。
学完这章,你就能:
1. 自由添加/删除页面
2. 配置底部 tabBar
3. 自定\n\n
\n\n
\n\n义页面标题和样式
4. 用 easycom 自动引入组件(省去一堆 import)
🧱 基础 25 分钟:核心概念
1. pages.json 在哪?
my-project/
├── pages/
│ ├── index/
│ │ └── index.vue # 页面文件
│ └── list/
│ └── list.vue
├── static/
├── App.vue
├── main.js
└── pages.json # ⭐ 就是这个文件
pages.json 在项目根目录,和 App.vue、main.js 平级。
2. pages 数组:注册页面
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/list/list",
"style": {
"navigationBarTitleText": "列表页"
}
}
]
}
解释一下:
- path:页面路径,注意不要写文件后缀(pages/index/index 而不是 pages/index/index.vue)
- style:页面样式配置
- navigationBarTitleText:页面顶部的标题
💡 小明的购物清单类比:想象你在整理一叠便签纸。
pages数组就是便签纸清单,告诉小程序"我有这几张便签,按这个顺序排好"。
3. 新建页面的正确姿势
❌ 错误做法:直接在 pages/ 下新建文件
✅ 正确做法:两步走
Step 1:在 pages/ 下创建文件夹和 .vue 文件
pages/
└── mine/
└── mine.vue
Step 2:在 pages.json 的 pages 数组里注册
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/mine/mine",
"style": {
"navigationBarTitleText": "我的"
}
}
]
}
⚠️ 注意!
pages数组里第一个页面是小程序的启动页。小程序打开默认显示它。
4. tabBar 配置:底部导航
想加个底部 tabBar(比如微信底部那种)?在 pages.json 加上 tabBar 节点:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/list/list",
"style": {
"navigationBarTitleText": "列表页"
}
},
{
"path": "pages/mine/mine",
"style": {
"navigationBarTitleText": "我的"
}
}
],
"tabBar": {
"color": "#999999",
"selectedColor": "#1890ff",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home-active.png"
},
{
"pagePath": "pages/list/list",
"text": "列表",
"iconPath": "static/tabbar/list.png",
"selectedIconPath": "static/tabbar/list-active.png"
},
{
"pagePath": "pages/mine/mine",
"text": "我的",
"iconPath": "static/tabbar/mine.png",
"selectedIconPath": "static/tabbar/mine-active.png"
}
]
}
}
每个 tab 项说明:
| 属性 | 作用 |
|---|---|
pagePath |
点击后跳转的页面(必须在 pages 数组里) |
text |
tab 上显示的文字 |
iconPath |
未选中时的图标路径 |
selectedIconPath |
选中时的图标路径 |
💡 类比:tabBar 就像餐厅的菜单卡片,每张卡片对应一个"节目"(页面),点击哪张就上哪道菜。
5. globalStyle:全局样式
如果每个页面都要写相同的导航栏样式,太麻烦了。用 globalStyle 统一设置:
{
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "我的小程序",
"navigationBarBackgroundColor": "#1890ff",
"backgroundColor": "#f5f5f5"
}
}
效果:所有页面默认使用这个导航栏样式,除非单个页面在 style 里覆盖它。
6. easycom:自动引入组件(超实用!)
以前用组件要手动 import:
import myButton from '@/components/my-button/my-button.vue'
export default {
components: { myButton }
}
现在用 easycom,自动引入,不用手动 import!
在 pages.json 添加:
{
"easycom": {
"autoscan": true,
"custom": {
"^my-(.*)": "@/components/my-$1/my-$1.vue"
}
}
}
然后直接在 wxml 里用:
<my-button type="primary">点我</my-button>
不用 import,不用注册,直接用!
💡 类比:easycom 就像快递自动签收——以前要自己跑去驿站取,现在快递员直接放你门口。
🔥 实战 35 分钟:3 个递进的小项目
项目 1:配置一个带 tabBar 的小页面(5 分钟)
目标:搭建一个"首页 + 列表 + 我的"三 tab 小程序
Step 1:创建 3 个页面文件
pages/index/index.vue:
<template>
<view class="container">
<text>这是首页</text>
</view>
</template>
<style>
.container {
padding: 20px;
}
</style>
pages/list/list.vue:
<template>
<view class="container">
<text>这是列表页</text>
</view>
</template>
<style>
.container {
padding: 20px;
}
</style>
pages/mine/mine.vue:
<template>
<view class="container">
<text>这是我的页面</text>
</view>
</template>
<style>
.container {
padding: 20px;
}
</style>
Step 2:配置 pages.json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/list/list",
"style": {
"navigationBarTitleText": "列表页"
}
},
{
"path": "pages/mine/mine",
"style": {
"navigationBarTitleText": "我的"
}
}
],
"tabBar": {
"color": "#999999",
"selectedColor": "#1890ff",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/list/list",
"text": "列表"
},
{
"pagePath": "pages/mine/mine",
"text": "我的"
}
]
},
"globalStyle": {
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
预期效果:底部有三个 tab,点击切换,顶部导航栏显示对应标题。
项目 2:给列表页加个标题(15 分钟)
需求:从 JSON 数据读取"小明的购物清单",展示在列表页
数据准备:static/goods.json:
[
{ "id": 1, "name": "苹果", "price": 5.5 },
{ "id": 2, "name": "香蕉", "price": 3.2 },
{ "id": 3, "name": "橘子", "price": 4.0 }
]
列表页 pages/list/list.vue:
<template>
<view class="container">
<view class="title">小明的购物清单</view>
<view v-for="item in goodsList" :key="item.id" class="item">
<text class="name">{{ item.name }}</text>
<text class="price">¥{{ item.price }}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
goodsList: []
}
},
onLoad() {
uni.request({
url: '/static/goods.json',
success: (res) => {
this.goodsList = res.data
}
})
}
}
</script>
<style>
.container {
padding: 20px;
}
.title {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.item {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.name {
color: #333;
}
.price {
color: #ff6600;
}
</style>
效果:页面加载时从 JSON 文件读取数据,展示购物清单。
💡 这个例子里我们继续用"小明的购物清单"——就像上一章做的那样,现在给它加上页面展示。
项目 3:组合技——做一个带配置的待办清单(15 分钟)
需求:
1. 首页显示"待办清单"(用 easycom 自动引入组件)
2. 页面标题显示当前日期
3. 列表页用 tabBar 切换
Step 1:创建自定义组件 components/todo-item/todo-item.vue
<template>
<view class="todo-item">
<text class="checkbox">{{ done ? '✅' : '⬜' }}</text>
<text class="content">{{ text }}</text>
</view>
</template>
<script>
export default {
props: {
text: String,
done: Boolean
}
}
</script>
<style>
.todo-item {
display: flex;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #eee;
}
.checkbox {
margin-right: 10px;
}
.content {
font-size: 16px;
}
</style>
Step 2:配置 easycom(在 pages.json 添加)
{
"easycom": {
"autoscan": true,
"custom": {
"^todo-(.*)": "@/components/todo-$1/todo-$1.vue"
}
},
"pages": [ /* 保持之前的配置 */ ],
"tabBar": { /* 保持之前的配置 */ }
}
Step 3:首页用组件
pages/index/index.vue:
<template>
<view class="container">
<view class="header">
<text class="date">{{ currentDate }}</text>
<text class="subtitle">待办事项</text>
</view>
<todo-item v-for="item in todos" :key="item.id" :text="item.text" :done="item.done" />
</view>
</template>
<script>
export default {
data() {
return {
currentDate: '',
todos: [
{ id: 1, text: '完成 pages.json 作业', done: false },
{ id: 2, text: '复习 Vue 语法', done: true },
{ id: 3, text: '写一篇学习笔记', done: false }
]
}
},
onLoad() {
const now = new Date()
this.currentDate = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`
}
}
</script>
<style>
.container {
padding: 20px;
}
.header {
margin-bottom: 20px;
}
.date {
font-size: 14px;
color: #999;
}
.subtitle {
font-size: 22px;
font-weight: bold;
color: #333;
display: block;
margin-top: 5px;
}
</style>
预期输出:
2026-6-26
待办事项
⬜ 完成 pages.json 作业
✅ 复习 Vue 语法
⬜ 写一篇学习笔记
一句话解释:通过 easycom 自动引入组件,减少 import 代码;通过 onLoad 生命周期获取当前日期。
💪 进阶 20 分钟:常见坑 + 性能小贴士
坑 1:页面路径写了文件后缀
❌ 错误:
"path": "pages/index/index.vue"
✅ 正确:
"path": "pages/index/index"
⚠️ 路径不要写
.vue后缀!小程序平台会报错"页面不存在"。
坑 2:tabBar 页面没在 pages 数组里
❌ 错误:tabBar.list 引用了 pages/mine/mine,但 pages 数组里没有它
✅ 正确:所有 tabBar 页面必须先在 pages 数组注册
坑 3:第一个页面不是想要的启动页
❌ 意外:本来想先显示"列表页",但 pages 数组第一个是"首页"
✅ 正确:调整 pages 数组顺序,第一个就是启动页
坑 4:easycom 规则写错了正则
❌ 错误:
"^my-(.*)": "@/components/my-$1/my-$1.vue"
写成了 my* 或 $1 位置不对
✅ 正确:正则的捕获组 (.*) 要对应 $1,且路径要完整
坑 5:修改 pages.json 后没重新编译
❌ 错误:改了配置,但模拟器显示还是旧的
✅ 正确:保存文件后重新编译(HBuilderX 会自动热更新,但有时候需要手动停止再运行)
性能小贴士:tabBar 图标别太大
tabBar 图标建议 81x81 像素,格式用 png。图标太大会影响加载速度。
调试技巧:看控制台报错
打开 HBuilderX 的控制台面板,页面路由相关的报错会显示"页面不存在"或"页面注册冲突"。结合报错信息和 pages.json 配置排查,一般 2 分钟内能找到问题。
✏️ 练习题 + 作业题
练习题(10 分钟)
练习 1(2 分钟):改标题
- 输入:把 pages/index/index 的页面标题改成"欢迎来到首页"
- 预期输出:顶部导航栏显示"欢迎来到首页"
- 提示:改 pages.json 里对应页面的 navigationBarTitleText
练习 2(2 分钟):加一个 tab
- 输入:在现有 tabBar 里加一个"设置"tab,页面路径 pages/settings/settings
- 预期输出:底部 tab 多了一个"设置"项
- 提示:需要两步——创建页面文件 + 在 pages 和 tabBar.list 里注册
练习 3(2 分钟):改全局背景色
- 输入:把 globalStyle.backgroundColor 改成 #f0f8ff
- 预期输出:所有页面背景变成浅蓝色
- 提示:在 globalStyle 节点下添加或修改 backgroundColor
练习 4(3 分钟):easycom 规则推理
- 输入:写出匹配 my-button 到 @/components/my-button/my-button.vue 的 easycom 规则
- 预期输出:能正确映射
- 提示:捕获组 (.*) 捕获的是"button",然后用 $1 拼路径
练习 5(1 分钟):找错
- 输入:下面这个 pages.json 有什么问题?
{
"pages": [
{ "path": "pages/index/index.vue" }
]
}
- 预期输出:报错"页面不存在"
- 提示:路径里多了
.vue后缀
作业题(30 分钟 - 2 小时)
作业:做一个「小明的课堂表」小程序
需求描述:
用 uniapp 做一个简单的课表展示工具,锻炼 pages.json 配置 + 页面跳转能力。
功能点:
1. 底部 tabBar 有"课表"和"设置"两个 tab
2. 课表页显示周一到周五的课程(用 JSON 数据或写死在代码里)
3. 设置页有一个开关,可以切换"深色模式"(修改 globalStyle)
加分项:
1. 用 easycom 引入自定义课程卡片组件
2. 课表数据放到 static 目录的 JSON 文件里,用 uni.request 读取
验收标准:
- 能跑起来,tabBar 切换正常
- 课表页显示课程信息
- 切换深色模式后,页面主题颜色变化
📚 总结 + 资源
本文学了 3 个核心点:
1. pages.json 的 pages 数组管理所有页面,第一个是启动页
2. tabBar 配置底部导航,引用的页面必须先注册
3. easycom 自动引入组件,省去手动 import
延伸学习资源:
- uniapp 官方文档 - pages.json 配置
- uniapp 官方文档 - tabBar 配置
- Vue3 文档 - 组件基础
互动钩子:
你在配置 tabBar 的时候踩过什么坑?是图标路径写错了,还是页面没注册?评论区聊聊,老粉优先回复!
📍 下一章我们要学习「Vue3 语法 + uniapp 组件」,学完你就能写出真正有交互的页面了——不只是展示数据,还能响应用户的点击、输入。敬请期待!

评论(0)