uni-app 页面开发基础
章节概述
本章节将详细介绍 uni-app 页面开发的基础知识,包括如何创建新页面、页面间的导航跳转方式、参数传递的方法等。通过本章节的学习,您将掌握 uni-app 页面开发的核心技能,能够创建和管理多个页面,并实现页面间的导航和数据传递。
核心知识点
1. 页面创建
创建新页面的步骤
在 pages 目录下创建页面目录
- 在项目的
pages目录下创建一个新的目录,目录名称建议与页面功能相关 - 例如:创建
detail目录用于存放详情页
- 在项目的
创建页面文件
- 在新创建的目录中创建
.vue文件,文件名建议与目录名称一致 - 例如:在
detail目录中创建detail.vue文件
- 在新创建的目录中创建
配置页面路由
- 打开
pages.json文件 - 在
pages数组中添加新页面的配置 - 配置页面路径和导航栏样式等
- 打开
页面文件结构
一个标准的 uni-app 页面文件包含以下三个部分:
<template>
<!-- 页面结构 -->
</template>
<script>
export default {
// 页面逻辑
}
</script>
<style>
/* 页面样式 */
</style>示例:创建详情页
创建目录结构
pages/ ├── index/ # 首页 └── detail/ # 详情页 └── detail.vue # 详情页文件配置 pages.json
{ "pages": [ { "path": "pages/index/index", "style": { "navigationBarTitleText": "首页" } }, { "path": "pages/detail/detail", "style": { "navigationBarTitleText": "详情页" } } ] }创建 detail.vue 文件
<template> <view class="detail-page"> <view class="detail-content"> <text>详情页内容</text> </view> </view> </template> <script>
export default {
onLoad(options) {
console.log('详情页加载', options)
}
}
### 2. 导航跳转
#### uni-app 提供的导航 API
uni-app 提供了以下导航 API 用于页面跳转:
| API 名称 | 功能描述 | 适用场景 |
|---------|---------|----------|
| uni.navigateTo | 保留当前页面,跳转到应用内的某个页面 | 从列表页跳转到详情页 |
| uni.redirectTo | 关闭当前页面,跳转到应用内的某个页面 | 登录成功后跳转到首页 |
| uni.reLaunch | 关闭所有页面,打开到应用内的某个页面 | 退出登录后重新打开应用 |
| uni.switchTab | 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 | 从非 tabBar 页面跳转到 tabBar 页面 |
| uni.navigateBack | 关闭当前页面,返回上一页面或多级页面 | 从详情页返回列表页 |
#### 导航 API 的使用方法
##### 1. uni.navigateTo
```javascript
// 在起始页面跳转到详情页并传递参数
uni.navigateTo({
url: '/pages/detail/detail?id=1&name=uni-app'
});
// 在详情页接收参数
export default {
onLoad(options) {
console.log(options.id); // 输出:1
console.log(options.name); // 输出:uni-app
}
}2. uni.redirectTo
// 关闭当前页面,跳转到登录页
uni.redirectTo({
url: '/pages/login/login'
});3. uni.reLaunch
// 关闭所有页面,跳转到首页
uni.reLaunch({
url: '/pages/index/index'
});4. uni.switchTab
// 跳转到 tabBar 页面
uni.switchTab({
url: '/pages/home/home'
});5. uni.navigateBack
// 返回上一页
uni.navigateBack({
delta: 1
});
// 返回上两页
uni.navigateBack({
delta: 2
});导航动画
uni-app 提供了导航动画效果,可以通过配置 animationType 和 animationDuration 参数来控制:
uni.navigateTo({
url: '/pages/detail/detail',
animationType: 'slide-in-right', // 动画类型
animationDuration: 300 // 动画持续时间(毫秒)
});3. 参数传递
1. 通过 URL 传递参数
这是最常用的参数传递方式,适用于从列表页跳转到详情页等场景。
传递参数:
// 传递单个参数
uni.navigateTo({
url: '/pages/detail/detail?id=1'
});
// 传递多个参数
uni.navigateTo({
url: '/pages/detail/detail?id=1&name=uni-app&price=99.9'
});
// 传递对象参数(需要编码)
const userInfo = { id: 1, name: '张三', age: 25 };
uni.navigateTo({
url: '/pages/detail/detail?userInfo=' + encodeURIComponent(JSON.stringify(userInfo))
});接收参数:
export default {
onLoad(options) {
// 接收单个参数
const id = options.id;
console.log('id:', id);
// 接收多个参数
const { id, name, price } = options;
console.log('id:', id);
console.log('name:', name);
console.log('price:', price);
// 接收对象参数(需要解码)
if (options.userInfo) {
const userInfo = JSON.parse(decodeURIComponent(options.userInfo));
console.log('userInfo:', userInfo);
}
}
}2. 通过全局变量传递参数
适用于需要在多个页面间共享数据的场景。
在 App.vue 中定义全局变量:
// App.vue
export default {
globalData: {
userInfo: null,
token: ''
}
}设置全局变量:
// 在登录页设置用户信息
getApp().globalData.userInfo = { id: 1, name: '张三' };
getApp().globalData.token = 'token123456';获取全局变量:
// 在其他页面获取用户信息
const userInfo = getApp().globalData.userInfo;
const token = getApp().globalData.token;
console.log('userInfo:', userInfo);
console.log('token:', token);3. 通过 Vuex 传递参数
适用于复杂应用中多个组件和页面间的数据共享。
创建 store:
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userInfo: null,
token: ''
},
mutations: {
setUserInfo(state, userInfo) {
state.userInfo = userInfo;
},
setToken(state, token) {
state.token = token;
}
},
actions: {
login({ commit }, userInfo) {
// 模拟登录请求
return new Promise(resolve => {
setTimeout(() => {
commit('setUserInfo', userInfo);
commit('setToken', 'token123456');
resolve();
}, 1000);
});
}
}
});在 main.js 中引入:
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
store,
...App
})
app.$mount()使用 Vuex:
// 在登录页
this.$store.dispatch('login', { id: 1, name: '张三' });
// 在其他页面
const userInfo = this.$store.state.userInfo;
const token = this.$store.state.token;
console.log('userInfo:', userInfo);
console.log('token:', token);4. 通过事件总线传递参数
适用于兄弟组件或跨页面的数据传递。
创建事件总线:
// utils/eventBus.js
import Vue from 'vue'
export default new Vue();发送事件:
// 在页面 A 中发送事件
import eventBus from '@/utils/eventBus';
eventBus.$emit('updateUser', { id: 1, name: '张三' });接收事件:
// 在页面 B 中接收事件
import eventBus from '@/utils/eventBus';
export default {
onLoad() {
// 监听事件
eventBus.$on('updateUser', (userInfo) => {
console.log('userInfo:', userInfo);
});
},
onUnload() {
// 移除事件监听
eventBus.$off('updateUser');
}
};实用案例分析
实现页面间导航和数据传递
案例目标
创建一个包含首页和详情页的应用,实现从首页跳转到详情页并传递商品信息的功能。
实现步骤
创建项目结构
- 创建首页
pages/index/index.vue - 创建详情页
pages/detail/detail.vue - 配置
pages.json文件
- 创建首页
实现首页
- 展示商品列表
- 实现跳转详情页功能
- 传递商品参数
实现详情页
- 接收首页传递的参数
- 展示商品详情
- 实现返回首页功能
具体实现
1. 配置 pages.json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "商品列表"
}
},
{
"path": "pages/detail/detail",
"style": {
"navigationBarTitleText": "商品详情"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}2. 实现首页(index.vue)
<template>
<view class="index-page">
<view class="goods-list">
<view
v-for="goods in goodsList"
:key="goods.id"
class="goods-item"
@tap="navigateToDetail(goods)"
>
<view class="goods-image">
<image :src="goods.image" mode="aspectFill"></image>
</view>
<view class="goods-info">
<text class="goods-name">{{ goods.name }}</text>
<text class="goods-price">¥{{ goods.price }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
goodsList: [
{
id: 1,
name: 'uni-app 开发实战',
price: 99.9,
image: 'https://img-cdn-tc.dcloud.net.cn/uploads/cover/007/762/762089b7-6c24-4010-a470-445d5b477f37.png',
description: 'uni-app 跨平台开发实战教程,从零开始学习 uni-app 开发'
},
{
id: 2,
name: 'Vue.js 核心原理',
price: 89.9,
image: 'https://img-cdn-tc.dcloud.net.cn/uploads/cover/007/762/762089b7-6c24-4010-a470-445d5b477f37.png',
description: '深入理解 Vue.js 核心原理,掌握前端框架精髓'
},
{
id: 3,
name: '微信小程序开发',
price: 79.9,
image: 'https://img-cdn-tc.dcloud.net.cn/uploads/cover/007/762/762089b7-6c24-4010-a470-445d5b477f37.png',
description: '微信小程序开发实战,从入门到精通'
}
]
};
},
methods: {
navigateToDetail(goods) {
// 传递商品信息
uni.navigateTo({
url: `/pages/detail/detail?id=${goods.id}&name=${encodeURIComponent(goods.name)}&price=${goods.price}&image=${encodeURIComponent(goods.image)}&description=${encodeURIComponent(goods.description)}`
});
}
}
};
</script>
<style>
.index-page {
padding: 20rpx;
}
.goods-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.goods-item {
display: flex;
background-color: #fff;
border-radius: 10rpx;
padding: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.goods-image {
width: 160rpx;
height: 160rpx;
margin-right: 20rpx;
}
.goods-image image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
.goods-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.goods-name {
font-size: 32rpx;
font-weight: 500;
color: #333;
margin-bottom: 10rpx;
line-height: 1.4;
}
.goods-price {
font-size: 36rpx;
font-weight: bold;
color: #ff4757;
}
</style>3. 实现详情页(detail.vue)
<template>
<view class="detail-page">
<view class="goods-image">
<image :src="goodsInfo.image" mode="aspectFill"></image>
</view>
<view class="goods-info">
<text class="goods-name">{{ goodsInfo.name }}</text>
<text class="goods-price">¥{{ goodsInfo.price }}</text>
<text class="goods-description">{{ goodsInfo.description }}</text>
</view>
<view class="action-area">
<button @tap="navigateBack" class="back-btn">返回首页</button>
<button class="buy-btn">立即购买</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
goodsInfo: {
id: '',
name: '',
price: '',
image: '',
description: ''
}
};
},
onLoad(options) {
// 接收并解析参数
this.goodsInfo = {
id: options.id,
name: decodeURIComponent(options.name),
price: options.price,
image: decodeURIComponent(options.image),
description: decodeURIComponent(options.description)
};
console.log('商品信息:', this.goodsInfo);
},
methods: {
navigateBack() {
// 返回上一页
uni.navigateBack({
delta: 1
});
}
}
};
</script>
<style>
.detail-page {
padding: 20rpx;
background-color: #f5f5f5;
}
.goods-image {
width: 100%;
height: 400rpx;
background-color: #fff;
border-radius: 10rpx;
overflow: hidden;
margin-bottom: 20rpx;
}
.goods-image image {
width: 100%;
height: 100%;
}
.goods-info {
background-color: #fff;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.goods-name {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.goods-price {
font-size: 40rpx;
font-weight: bold;
color: #ff4757;
margin-bottom: 20rpx;
}
.goods-description {
font-size: 28rpx;
color: #666;
line-height: 1.6;
}
.action-area {
display: flex;
gap: 20rpx;
}
.back-btn {
flex: 1;
padding: 20rpx;
background-color: #f0f0f0;
color: #333;
border-radius: 10rpx;
font-size: 32rpx;
}
.buy-btn {
flex: 1;
padding: 20rpx;
background-color: #ff4757;
color: #fff;
border-radius: 10rpx;
font-size: 32rpx;
}
</style>运行效果
- 首页:显示商品列表,包含商品图片、名称和价格
- 点击商品:跳转到详情页,显示完整的商品信息
- 详情页:显示商品图片、名称、价格和描述,包含返回首页按钮
- 点击返回:回到首页
代码示例
1. 页面生命周期与参数传递结合示例
<template>
<view class="life-cycle-params">
<view class="title">页面生命周期与参数传递示例</view>
<view class="params-info">
<text>接收到的参数:</text>
<text v-for="(value, key) in receivedParams" :key="key" class="param-item">
{{ key }}: {{ value }}
</text>
</view>
<view class="lifecycle-log">
<text>生命周期执行顺序:</text>
<text v-for="(log, index) in lifecycleLogs" :key="index" class="log-item">
{{ index + 1 }}. {{ log }}
</text>
</view>
<button @tap="navigateToNext" class="next-btn">跳转到下一页</button>
</view>
</template>
<script>
export default {
data() {
return {
receivedParams: {},
lifecycleLogs: []
};
},
onLoad(options) {
this.lifecycleLogs.push('onLoad: 页面加载');
this.receivedParams = options;
console.log('页面加载,参数:', options);
},
onShow() {
this.lifecycleLogs.push('onShow: 页面显示');
console.log('页面显示');
},
onReady() {
this.lifecycleLogs.push('onReady: 页面初次渲染完成');
console.log('页面初次渲染完成');
},
onHide() {
this.lifecycleLogs.push('onHide: 页面隐藏');
console.log('页面隐藏');
},
onUnload() {
this.lifecycleLogs.push('onUnload: 页面卸载');
console.log('页面卸载');
},
methods: {
navigateToNext() {
uni.navigateTo({
url: '/pages/next/next?from=page1×tamp=' + Date.now()
});
}
}
};
</script>
<style>
.life-cycle-params {
padding: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
text-align: center;
margin-bottom: 30rpx;
color: #333;
}
.params-info {
background-color: #f5f5f5;
padding: 20rpx;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
.param-item {
display: block;
margin-top: 10rpx;
font-size: 28rpx;
color: #666;
}
.lifecycle-log {
background-color: #f5f5f5;
padding: 20rpx;
border-radius: 10rpx;
margin-bottom: 30rpx;
}
.log-item {
display: block;
margin-top: 10rpx;
font-size: 28rpx;
color: #666;
}
.next-btn {
padding: 20rpx;
background-color: #007AFF;
color: #fff;
border-radius: 10rpx;
font-size: 32rpx;
}
</style>2. 多种导航方式示例
<template>
<view class="navigation-demo">
<view class="title">导航方式示例</view>
<view class="nav-buttons">
<button @tap="navigateTo" class="nav-btn">navigateTo</button>
<button @tap="redirectTo" class="nav-btn">redirectTo</button>
<button @tap="reLaunch" class="nav-btn">reLaunch</button>
<button @tap="switchTab" class="nav-btn">switchTab</button>
<button @tap="navigateBack" class="nav-btn">navigateBack</button>
</view>
</view>
</template>
<script>
export default {
methods: {
// 保留当前页面,跳转到应用内的某个页面
navigateTo() {
uni.navigateTo({
url: '/pages/detail/detail?id=1&name=测试商品',
animationType: 'slide-in-right',
animationDuration: 300
});
},
// 关闭当前页面,跳转到应用内的某个页面
redirectTo() {
uni.redirectTo({
url: '/pages/detail/detail?id=2&name=重定向测试',
animationType: 'fade-in',
animationDuration: 300
});
},
// 关闭所有页面,打开到应用内的某个页面
reLaunch() {
uni.reLaunch({
url: '/pages/index/index'
});
},
// 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
switchTab() {
uni.switchTab({
url: '/pages/index/index'
});
},
// 关闭当前页面,返回上一页面或多级页面
navigateBack() {
uni.navigateBack({
delta: 1
});
}
}
};
</script>
<style>
.navigation-demo {
padding: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
text-align: center;
margin-bottom: 30rpx;
color: #333;
}
.nav-buttons {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.nav-btn {
padding: 20rpx;
background-color: #f0f0f0;
color: #333;
border-radius: 10rpx;
font-size: 32rpx;
}
</style>常见问题与解决方案
1. 页面跳转失败
- 问题:调用导航 API 后页面没有跳转
- 解决方案:检查目标页面路径是否正确,确保在 pages.json 中已配置
2. 参数传递失败
- 问题:页面跳转后无法接收到参数
- 解决方案:检查参数传递格式是否正确,确保参数名称与接收时一致
3. 中文参数乱码
- 问题:传递中文参数时出现乱码
- 解决方案:使用 encodeURIComponent() 编码参数,使用 decodeURIComponent() 解码参数
4. 对象参数传递失败
- 问题:传递对象参数时接收不到完整数据
- 解决方案:将对象转换为 JSON 字符串并编码后传递,接收时解码并解析
5. 页面栈溢出
- 问题:多次使用 navigateTo 后页面栈溢出
- 解决方案:合理使用不同的导航方式,对于不需要返回的页面使用 redirectTo
6. 导航动画不生效
- 问题:配置了导航动画但不生效
- 解决方案:检查动画类型是否正确,确保动画参数配置格式正确
学习总结
通过本章节的学习,您已经了解了:
- 页面创建:如何创建新页面、配置页面路由
- 导航跳转:
- uni.navigateTo:保留当前页面,跳转到新页面
- uni.redirectTo:关闭当前页面,跳转到新页面
- uni.reLaunch:关闭所有页面,跳转到新页面
- uni.switchTab:跳转到 tabBar 页面
- uni.navigateBack:返回上一页面
- 参数传递:
- 通过 URL 传递参数
- 通过全局变量传递参数
- 通过 Vuex 传递参数
- 通过事件总线传递参数
- 页面生命周期:页面生命周期函数与参数传递的结合使用
现在您已经掌握了 uni-app 页面开发的基础知识,可以开始学习组件开发了。下一章节将详细介绍 uni-app 的组件开发方法,包括组件创建、组件通信和插槽使用等内容。