简体中文
uni-app x 项目中,页面文件的后缀名.uvue
文件。
每个uvue文件,都是一个符合Vue SFC规范
的 vue 文件。
uni-app x 只有 .uvue
页面,不支持和vue页面并存(因vue是js驱动、webview渲染,uni-app x在app-Android中没有js引擎,app中渲染是原生渲染,无法使用vue页面)。
当然某些组件可以通过条件编译同时适配uni-app和uni-app x,所以在uni-app x的项目中,看到某些组件代码也有vue文件,但这些vue文件并不在uni-app x项目中生效,或者被当做uvue组件对待。
另外uts组件的uni-app兼容模式插件的入口文件命名是index.vue。因为uts插件在uni-app和uni-app x中均可使用,所以文件后缀名为vue。
但这个vue文件并不是真正的页面,它只是uts组件插件为了尽可能照顾开发者习惯而参考vue规范定义的原生组件入口文件。
uni-app x 在非小程序平台上,提供了dialogPage,这是一种在主页面上弹出全屏的、背景透明的模态子页面。
uni-app x 在app-android上,每个页面都是一个全屏activity,不支持透明。如需要透明页面请使用dialogPage
新建页面,默认保存在工程根目录下的pages
目录下。
每次新建页面,均需在pages.json
中配置pages
列表;未在pages.json -> pages
中注册的页面,在编译阶段会被忽略,不会进入编译产物。
pages.json的完整配置参考:页面配置。
通过HBuilderX开发 uni-app x
项目时,在项目上右键“新建页面”,HBuilderX会自动在pages.json
中完成页面注册,开发更方便。
新建页面时,可以选择是否创建同名目录
。创建目录的意义在于:
不管是普通页面,还是dialogPage页面,都需要在pages.json中注册。
删除页面时,需做两件工作:
.uvue
文件pages.json -> pages
列表项中的配置 (如使用HBuilderX删除页面,会在状态栏提醒删除pages.json对应内容,点击后会打开pages.json并定位到相关配置项)操作和删除页面同理,依次修改文件名和 pages.json
。
pages.json位于工程根目录,是工程的页面管理配置文件,功能包括:页面路由注册、页面style参数配置(背景色、原生标题栏、下拉刷新...)、首页tabbar等众多功能。
其篇幅较长,另见 pages.json
pages.json -> pages
配置项中的第一个页面,作为当前工程的首页(启动页)。
{
"pages": [
{
"path": "pages/index/index", //名字叫不叫index无所谓,位置在第一个,就是首页
"style": {
"navigationBarTitleText": "首页" //页面标题
}
},
{
"path": "pages/my",
"style": {
"navigationBarTitleText": "我的"
}
},
]
}
uvue页面基于 vue 单文件组件规范。一个页面内,有3个根节点标签:
<template>
<script>
<style>
<template>
<view class="content">
<button @click="buttonClick">{{title}}</button>
</view>
</template>
<script setup>
let title = ref("Hello world") //定义一个响应式变量。类似于选项式的定义data
const buttonClick = () => { //方法不再需要写在method下面
console.log("按钮被点了")
}
onReady(() => {
console.log("页面onReady触发") // 页面生命周期,编写页面加载后的逻辑
})
</script>
<style>
.content {
width: 750rpx;
background-color: white;
}
</style>
页面内容构成,另有详细文档
运行时每个打开的页面,都有一个UniPage实例。
通过全局API getCurrentPages() 可以获取到当前应用的页面栈,即所有打开的页面数组。
页面栈数组中的每个页面都是一个UniPage实例对象。
该对象上有很多方法,比如可以获取和设置页面样式(pages.json里配置的页面Style),比如可以获取页面的子弹窗页面(dialogPages)和页面上的元素(UniElement)。详见
组合式 | 选项式 | Web | Android | iOS | 描述 |
---|---|---|---|---|---|
onLoad | onLoad | 4.0 | 3.9 | 4.11 | 生命周期回调 监听页面加载 页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。 |
onPageShow | onShow | 4.0 | 3.9 | 4.11 | 生命周期回调 监听页面显示 页面显示/切入前台时触发。 |
onReady | onReady | 4.0 | 3.9 | 4.11 | 生命周期回调 监听页面初次渲染完成 页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。 |
onPageHide | onHide | 4.0 | 3.9 | 4.11 | 生命周期回调 监听页面隐藏 页面隐藏/切入后台时触发。 如 navigateTo 或底部 tab 切换到其他页面,应用切入后台等。 |
onUnload | onUnload | 4.0 | 3.9 | 4.11 | 生命周期回调 监听页面卸载 页面卸载时触发。如 redirectTo 或 navigateBack 到其他页面时。 |
onPullDownRefresh | onPullDownRefresh | 4.0 | 3.9 | 4.11 | 监听用户下拉动作 - 需要在 pages.json 的页面配置中开启 enablePullDownRefresh 。- 可以通过 uni.startPullDownRefresh 触发下拉刷新,调用后触发下拉刷新动画,效果与用户手动下拉刷新一致。- 当处理完数据刷新后, uni.stopPullDownRefresh 可以停止当前页面的下拉刷新。 |
onReachBottom | onReachBottom | 4.0 | 3.9 | 4.11 | 页面上拉触底事件的处理函数 - 可以在 pages.json 的页面配置中设置触发距离 onReachBottomDistance 。- 在触发距离内滑动期间,本事件只会被触发一次。 |
onPageScroll | onPageScroll | 4.0 | 3.9 | 4.13 | 页面滚动触发事件的处理函数 监听用户滑动页面事件。 |
onResize | onResize | 4.0 | 3.9 | 4.11 | 页面尺寸改变时触发 |
onBackPress | onBackPress | 4.0 | 3.9 | 4.11 | 监听页面返回 |
onInit | onInit | x | x | x | 生命周期回调 监听页面初始化 页面初始化时触发。一个页面只会调用一次,可以在 onInit 的参数中获取打开当前页面路径中的参数。 |
onShareAppMessage | onShareAppMessage | x | x | x | 用户点击右上角转发 监听用户点击页面内转发按钮( <button> 组件 open-type="share" )或右上角菜单“转发”按钮的行为,并自定义转发内容。 |
onShareTimeline | onShareTimeline | x | x | x | 用户点击右上角转发到朋友圈 监听右上角菜单“分享到朋友圈”按钮的行为,并自定义发享内容。 |
onAddToFavorites | onAddToFavorites | x | x | x | 用户点击右上角收藏 监听用户点击右上角菜单“收藏”按钮的行为,并自定义收藏内容。 |
onTabItemTap | onTabItemTap | 4.0 | x | x | 当前是 tab 页时,点击 tab 时触发 |
onNavigationBarButtonTap | onNavigationBarButtonTap | 4.0 | x | x | 监听原生标题栏按钮点击事件 |
onNavigationBarSearchInputChanged | onNavigationBarSearchInputChanged | 4.0 | x | x | 监听原生标题栏搜索输入框输入内容变化事件 |
onNavigationBarSearchInputConfirmed | onNavigationBarSearchInputConfirmed | 4.0 | x | x | 监听原生标题栏搜索输入框搜索事件,用户点击软键盘上的“搜索”按钮时触发。 |
onNavigationBarSearchInputClicked | onNavigationBarSearchInputClicked | 4.0 | x | x | 监听原生标题栏搜索输入框点击事件 |
在 Vue 中,页面也是一种组件,所以也同时支持组件生命周期。
页面初始化时,会触发onLoad生命周期。此时Dom还未构建渲染完毕,ref和getElementById都拿不到Dom(需要等onReady)。
所以onLoad页面常见的用途是:
手机都是多核的,uni.request或云开发联网,在子线程运行,不会干扰UI线程的入场动画,并行处理可以更快的拿到数据、渲染界面。
通过uni.navigateTo API 或 <navigator>
组件,可跳转到目标页面,比如从list页面跳转到detail页面,如下:
// 发起跳转,并传入post_id参数
uni.navigateTo({
url: '/pages/template/list-news/detail/detail?post_id=' + post_id
})
// 在detail页面的onLoad中接收URL中传递的参数
export default {
data() {
return {
post_id: ""
}
},
onLoad(event : OnLoadOptions) { // 类型非必填,可自动推导
this.post_id = event["post_id"] ?? "";
// 可根据详情页id继续联网请求数据...
},
}
注意
uni-app x android
平台,如需获取 activity 实例,此时当前页面的 activity 实例
并未创建完成,会获取到上一个页面的 activity 实例
(首页会获取应用默认的 activity 实例
)。如需获取当前页面的 activity 实例
,应在 onShow
或 onReady
生命周期中获取。onShow是在onLoad之后,它的意义在于,onLoad是页面创建时触发一次;而当页面隐藏(比如被新窗体遮挡),然后页面再恢复显示时,onLoad不会再触发,只会触发onShow。
tabbar页面切换时,老的tabbar页面会hide,新的tabbar页面会show。
onShow和onHide是成对出现的。
在组合式API中,组件可以监听应用和页面的生命周期。但由于应用和页面都有onShow和onHide,导致重名。所以在组合式的组件中监听页面的显示隐藏,改为了onPageShow和onPageHide。
可在pages.json里定义具体页面底部的触发距离onReachBottomDistance, 比如设为50,那么滚动页面到距离底部50px时,就会触发onReachBottom事件。
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
options | OnPageScrollOptions | 是 | - | - | 页面滚动参数 | ||||||||||||
|
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
options | OnResizeOptions | 是 | - | - | 页面滚动参数 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
options | OnBackPressOptions | 是 | - | - | tab 点击参数 | |||||||||||||||||||||||||||
|
类型 | 描述 | 必备 |
---|---|---|
boolean | 返回 true 时阻止页面返回 | 否 |
注意
onBackPress
上不可使用async
,会导致无法阻止默认返回onBackPress
组合式 API
选项式 API
<template>
<view class="page container">
<text>测试 onBackPress 生命周期返回 true</text>
<button class="mt-10" @click="goChildPage">
跳转子页,测试返回值为 false
</button>
</view>
</template>
<script lang="uts" setup>
const backPressOptions = reactive({
from: 'backbutton'
} as OnBackPressOptions)
onBackPress((options : OnBackPressOptions) : boolean | null => {
console.log('onBackPress', options)
backPressOptions.from = options.from
return true
})
const goChildPage = () => {
uni.navigateTo({ url: '/pages/lifecycle/page/onBackPress/on-back-press-child-composition' })
}
defineExpose({
goChildPage,
backPressOptions
})
</script>
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
options | OnTabItemTapOption | 是 | - | - | tab 点击参数 | ||||||||||||||||||||||||
|
注意
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
options | OnNavigationBarButtonTapOption | 是 | - | - | tab 点击参数 | ||||||||||||
|
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
event | NavigationBarSearchInputEvent | 是 | - | - | - | ||||||||||||
|
名称 | 类型 | 必填 | 默认值 | 兼容性 | 描述 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
event | NavigationBarSearchInputEvent | 是 | - | - | - | ||||||||||||
|
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1" :bounces="false">
<!-- #endif -->
<view class="page container">
<text>page lifecycle 组合式 API</text>
<view class="flex flex-row justify-between mt-10">
<text>onLoad 触发:</text>
<text>{{ isOnloadTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onPageShow 触发:</text>
<text>{{ isOnPageShowTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onReady 触发:</text>
<text>{{ isOnReadyTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onPullDownRefresh 触发:</text>
<text>{{ isOnPullDownRefreshTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onReachBottom 触发:</text>
<text>{{ isOnReachBottomTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onBackPress 触发:</text>
<text>{{ isOnBackPressTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onPageHide 触发:</text>
<text>{{ isOnPageHideTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onResize 触发:</text>
<text>{{ isOnResizeTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<MonitorPageLifecycleComposition />
</view>
<button class="mt-10" @click="scrollToBottom">scrollToBottom</button>
<button class="mt-10" @click="pullDownRefresh">
trigger pullDownRefresh
</button>
<button class="mt-10" @click="goOnBackPress">
跳转 onBackPress 示例
</button>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup lang="uts">
import { state, setLifeCycleNum } from '@/store/index.uts'
import MonitorPageLifecycleComposition from './monitor-page-lifecycle-composition.uvue'
const isOnloadTriggered = ref(false)
const isOnPageShowTriggered = ref(false)
const isOnReadyTriggered = ref(false)
const isOnPullDownRefreshTriggered = ref(false)
const isOnPageScrollTriggered = ref(false)
const isOnReachBottomTriggered = ref(false)
const isOnBackPressTriggered = ref(false)
const isOnPageHideTriggered = ref(false)
const isOnResizeTriggered = ref(false)
type DataInfo = {
isScrolled : boolean
}
const dataInfo = reactive({
isScrolled: false,
} as DataInfo)
onLoad((options : OnLoadOptions) => {
console.log('onLoad', options)
isOnloadTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 100)
})
onPageShow(() => {
isOnPageShowTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onReady(() => {
isOnReadyTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onPullDownRefresh(() => {
isOnPullDownRefreshTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onPageScroll((e: OnPageScrollOptions) => {
console.log('onPageScroll', e)
isOnPageScrollTriggered.value = true
// 自动化测试
dataInfo.isScrolled = true
})
onReachBottom(() => {
isOnReachBottomTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onBackPress((options : OnBackPressOptions) : boolean | null => {
console.log('onBackPress', options)
isOnBackPressTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 10)
return null
})
onPageHide(() => {
isOnPageHideTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 10)
})
onUnload(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
})
onResize((options: OnResizeOptions) => {
console.log('onBackPress', options)
isOnResizeTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
// 自动化测试
const pageGetLifeCycleNum = () : number => {
return state.lifeCycleNum
}
// 自动化测试
const pageSetLifeCycleNum = (num : number) => {
setLifeCycleNum(num)
}
// 自动化测试
const pullDownRefresh = () => {
uni.startPullDownRefresh({
success() {
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
})
}
const scrollToBottom = () => {
uni.pageScrollTo({
scrollTop: 2000,
})
}
const goOnBackPress = () => {
uni.navigateTo({url: '/pages/lifecycle/page/onBackPress/on-back-press-composition'})
}
defineExpose({
dataInfo,
pageGetLifeCycleNum,
pageSetLifeCycleNum,
pullDownRefresh,
scrollToBottom,
})
</script>
<style>
.container {
height: 1200px;
}
</style>
下图展示了一个新页面,从创建开始,包括其中的组件,完整的时序。
所以pages.json中页面的style中各个设置是最早生效的,原生导航栏、页面背景色都会立即生效。
这里的dom创建仅包含第一批处理的静态dom。对于通过uts更新data或响应式变量,然后通过v-for再创建的列表数据,不在第一批处理。
要注意一个页面静态dom元素过多,会影响页面加载速度。
此时页面还未显示,没有开始进入的转场动画,页面dom还不存在。
所以这里不能直接操作dom(可以修改data或响应式变量,因为vue框架会等待dom准备后再更新界面)。
onLoad中获取当前的activity等原生窗体对象,拿到的是老页面的activity,只能通过页面栈获取activity。
onLoad比较适合的场景是:接受上页的参数,联网取数据,更新data或响应式变量。
onLoad之后,转场动画开始后,页面会触发onShow。
新页面开始进入的转场动画,动画默认耗时300ms。
第2步创建dom是虚拟dom,dom创建后需要经历一段时间,UI层才能完成了页面上真实元素的创建,即触发了onReady。
onReady后,页面元素就可以自由操作了,比如ref获取节点。同时首批界面也渲染了。
注意:onReady和转场动画开始、结束之间,没有必然的先后顺序,完全取决于dom的数量和复杂度。
如果元素排版和渲染够快,转场动画刚开始就渲染好了;
大多情况下,转场动画走几格就看到了首批渲染内容;
如果元素排版和渲染过慢,转场动画结束都没有内容,就会造成白屏。
联网进程从onLoad起就在异步获取数据更新data或响应式变量,如果服务器速度够快,第二批数据也可能在转场动画结束前渲染。
再次强调,5和6的先后顺序不一定,取决于首批dom渲染的速度。