# 页面简介

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中完成页面注册,开发更方便。

新建页面时,可以选择是否创建同名目录。创建目录的意义在于:

  • 如果你的页面较复杂,需要拆分多个附属的uts、css、组件等文件,则使用目录归纳比较合适。
  • 如果只有一个页面文件,大可不必多放一层目录。

不管是普通页面,还是dialogPage页面,都需要在pages.json中注册。

# 删除页面

删除页面时,需做两件工作:

  • 删除.uvue文件
  • 删除pages.json -> pages列表项中的配置 (如使用HBuilderX删除页面,会在状态栏提醒删除pages.json对应内容,点击后会打开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>

页面内容构成,另有详细文档

# 页面的可滚动性

web及使用webview的小程序,页面默认是可滚动的。 而标准原生应用中的页面默认是不可滚动的,需要滚动时需显式的在template中写scroll-view等滚动容器的代码。

uni-app中,vue页面默认可滚动,nvue为了多端一致性默认给页面套了一个scroll-view。

uni-app x中,App平台的页面是一个原生页面,

  • VDOM模式中,App平台的页面默认不可滚动,需要开发者在页面中显示使用scroll-view。
  • 蒸汽模式中,App平台的页面默认可滚动,与web和小程序拉齐。需HBuilder 5.12+

在uni-app x的vdom模式中,出于性能考虑,app平台默认没有隐式给页面套scroll-view,所以新建页面时,选择可滚动页面会出现下面的代码

<template>
  <!-- #ifdef APP -->
  <scroll-view style="flex:1">
  <!-- #endif -->

 <!-- #ifdef APP -->
  </scroll-view>
  <!-- #endif -->
</template>

uni-app x的蒸汽模式,因为已经做到比原生渲染更快,套一层scroll-view对性能影响很小,所以拉平了多端一致性。

如果开发者希望页面不可滚动,那么可以在pages.json的页面Style中配置disableScroll:true禁用页面滚动。此配置项不影响页面中自行写scroll-view。

如下几种情况,推荐禁用页面滚动:

  1. 使用了自定义导航栏、自定义tabbar。如果不禁用页面滚动,bounce回弹效果会在导航栏上面和tabbar下面。
  2. 页面根组件是 scroll-view、list-view、waterflow 等滚动容器。如果不禁用页面滚动,会发生嵌套滚动。

uni-app x在编译期间,会对上述情况进行静态检查和告警。

如果是vdom模式页面升级为蒸汽模式时,控制台收到编译告警,建议去掉这段条件编译套scroll-view的代码:

	<!-- #ifdef APP -->
  <scroll-view style="flex:1">
  <!-- #endif -->

 <!-- #ifdef APP -->
  </scroll-view>
  <!-- #endif -->

注意:

  • 只有滚动区的子内容高度大于滚动容器,才能发生滚动。

写如下代码,页面实际上无法滚动,因为这个view占据所有剩余空间,导致滚动区内容的高度永远无法超过滚动容器的高度。

<template>
  <view style="flex:1">
  </view>
</template>
  • 当页面Style中开启了页面下拉刷新,即enablePullDownRefresh为true时,且disableScroll:true,此时页面仍无法滚动,但是否同时还支持下拉刷新,各平台有差异。

此时微信小程序在Android、iOS、鸿蒙上的表现也不同。 App平台的策略是disableScroll:true时不能下拉刷新。

# 页面滚动相关的生命周期、api

页面滚动有一批相关的生命周期、api,比如:onPageScrollonReachBottomuni.pageScrollTo()

如果页面不可滚动,这些生命周期不会触发,相关API不可用。

在app平台vdom模式,由于页面不可滚动,会判断页面根节点是否为scroll-view(不认list-view等其他滚动容器)。

  • 如果是,页面滚动相关的生命周期和API继续生效,效果如前。
  • 如果不是scroll-view,全部失效。

# 页面无法滚动引起的position差异

app平台vdom模式无页面滚动,且其根节点高度为从导航栏底部到tabBar顶部。如果在页面根节点的子元素使用position: absolute;,页面内部scroll-view滚动时不会改变此元素位置。

有页面滚动时,如果在页面根节点的子元素使用position: absolute;页面滚动会改变此元素的位置。

在app平台vdom模式下,如果有不随页面滚动变化位置的需求,建议使用position: fixed

注意:web端需要使用css安全区变量使元素不覆盖在navigationBar和tabBar上。

# 页面对象实例

运行时每个打开的页面,都有一个UniPage实例。

通过全局API getCurrentPages() 可以获取到当前应用的页面栈,即所有打开的页面数组

页面栈数组中的每个页面都是一个UniPage实例对象。

也可以通过this.$pagegetCurrentInstance()等vue方式直接获取当前页面。详见

UniPage对象上有很多方法,比如可以获取和设置页面样式(pages.json里配置的页面Style),比如可以获取页面的子弹窗页面(dialogPages)和页面上的元素(UniElement)。详见

# 页面生命周期

组合式 选项式 兼容性 描述
onLoad onLoad
生命周期回调 监听页面加载

页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
onPageShow onShow
生命周期回调 监听页面显示

页面显示/切入前台时触发。
onReady onReady
生命周期回调 监听页面初次渲染完成

页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
onPageHide onHide
生命周期回调 监听页面隐藏

页面隐藏/切入后台时触发。 如 navigateTo 或底部 tab 切换到其他页面,应用切入后台等。
onUnload onUnload
生命周期回调 监听页面卸载

页面卸载时触发。如 redirectTonavigateBack 到其他页面时。
onPullDownRefresh onPullDownRefresh
监听用户下拉动作
- 需要在 pages.json 的页面配置中开启 enablePullDownRefresh
- 可以通过 uni.startPullDownRefresh 触发下拉刷新,调用后触发下拉刷新动画,效果与用户手动下拉刷新一致。
- 当处理完数据刷新后,uni.stopPullDownRefresh 可以停止当前页面的下拉刷新。
onReachBottom onReachBottom
页面上拉触底事件的处理函数
- 可以在 pages.json 的页面配置中设置触发距离 onReachBottomDistance
- 在触发距离内滑动期间,本事件只会被触发一次。
onPageScroll onPageScroll
页面滚动触发事件的处理函数

监听用户滑动页面事件。
onResize onResize
页面尺寸改变时触发
onBackPress onBackPress
监听页面返回
onInit onInit
生命周期回调 监听页面初始化

页面初始化时触发。一个页面只会调用一次,可以在 onInit 的参数中获取打开当前页面路径中的参数。
onShareAppMessage onShareAppMessage
用户点击右上角转发

监听用户点击页面内转发按钮(<button> 组件 open-type="share")或右上角菜单“转发”按钮的行为,并自定义转发内容。
onShareTimeline onShareTimeline
用户点击右上角转发到朋友圈

监听右上角菜单“分享到朋友圈”按钮的行为,并自定义发享内容。
onAddToFavorites onAddToFavorites
用户点击右上角收藏

监听用户点击右上角菜单“收藏”按钮的行为,并自定义收藏内容。
onTabItemTap onTabItemTap
当前是 tab 页时,点击 tab 时触发
onNavigationBarButtonTap onNavigationBarButtonTap
监听原生标题栏按钮点击事件
onNavigationBarSearchInputChanged onNavigationBarSearchInputChanged
监听原生标题栏搜索输入框输入内容变化事件
onNavigationBarSearchInputConfirmed onNavigationBarSearchInputConfirmed
监听原生标题栏搜索输入框搜索事件,用户点击软键盘上的“搜索”按钮时触发。
onNavigationBarSearchInputClicked onNavigationBarSearchInputClicked
监听原生标题栏搜索输入框点击事件

在 Vue 中,页面也是一种组件,所以也同时支持组件生命周期

# 页面 onLoad 生命周期

页面初始化时,会触发onLoad生命周期。此时Dom还未构建渲染完毕,ref和getElementById使用同步方式的话拿不到Dom(需要等onReady或使用异步获取)。

所以onLoad页面常见的用途是:

  1. 开始联网取数,由于联网是异步的,在onLoad发起联网,等到获取到服务器数据后,也就可以更新到data或响应式变量上了
  2. 页面的URL支持参数传递,这个参数也是在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继续联网请求数据...
  },
}

注意

  • OnLoadOptions类型,可不填。不填时可自动推导。
  • App-iOS平台的窗体动画是异步的,onLoad时可能窗体动画已经开始,此时再设置页面的pageStyle(比如设置背景色),会出现闪烁现象。
  • onLoad里不适合进行大量同步耗时运算,因为此时转场动画还没开始。尤其app-Android平台,onLoad里的代码(除了联网和加载图片)默认是在UI线程运行的,大量同步耗时计算很容易卡住页面动画不启动。除非开发者显式指定在其他线程运行。
  • uni-app x android 平台,如需获取 activity 实例,此时当前页面的 activity 实例并未创建完成,会获取到上一个页面的 activity 实例(首页会获取应用默认的 activity 实例)。如需获取当前页面的 activity 实例,应在 onShowonReady 生命周期中获取。

# 页面 onShow 生命周期

onShow是在onLoad之后,它的意义在于,onLoad是页面创建时触发一次;而当页面隐藏(比如被新窗体遮挡),然后页面再恢复显示时,onLoad不会再触发,只会触发onShow。

tabbar页面切换时,老的tabbar页面会hide,新的tabbar页面会show。

onShow和onHide是成对出现的。

在组合式API中,组件可以监听应用和页面的生命周期。但由于应用和页面都有onShow和onHide,导致重名。所以在组合式的组件中监听页面的显示隐藏,改为了onPageShow和onPageHide。

在微信小程序下,关闭弹出的原生窗体也会触发页面的onShow。比如关闭chooseImage、chooseVideo、chooseMedia、previewImage、chooseLocation、openLocation、scanCode等弹出的窗体。

# 页面 onHide 生命周期

页面被隐藏/遮挡时会触发页面隐藏生命周期。

比如跳转到下一个页面,会触发之前页面的隐藏。

在微信小程序下,打开全屏原生窗体也会触发页面的onHide。比如chooseImage、chooseVideo、chooseMedia、previewImage、chooseLocation、openLocation、scanCode。可以简单理解为弹出的这些原生窗体盖住了js写的小程序。

# 页面 onResize 生命周期

# onResize 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS HarmonyOS(Vapor)
4.0 4.41 3.9 x 4.11 x 4.61 5.03

# 参数

名称 类型 必填 默认值 兼容性 描述
options OnResizeOptions
页面尺寸改变时回调参数
名称 类型 必备 默认值 兼容性 描述
deviceOrientation string
屏幕方向
size OnResizeSize
新的显示区域尺寸
名称 类型 必备 默认值 兼容性 描述
screenHeight number
新的屏幕高度
screenWidth number
新的屏幕宽度
windowHeight number
新的显示区域高度
windowWidth number
新的显示区域宽度

# onReachBottom

可在pages.json里定义具体页面底部的触发距离onReachBottomDistance, 比如设为50,那么滚动页面到距离底部50px时,就会触发onReachBottom事件。

# 页面 onPageScroll 生命周期

# onPageScroll 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS HarmonyOS(Vapor)
4.0 4.41 3.9 x 4.13 x 4.61 5.08

# 参数

名称 类型 必填 默认值 兼容性 描述
options OnPageScrollOptions
页面滚动参数
名称 类型 必备 默认值 兼容性 描述
scrollTop number
页面在垂直方向已滚动的距离(单位 px)

注意

  • iOS 平台蒸汽模式开启下拉刷新会触发页面 onPageScroll 生命周期,此时 scrollTop 为负值。

# 页面 onBackPress 生命周期

# onBackPress 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS
4.0 x 3.9 x 4.11 5.11 4.61

# 参数

名称 类型 必填 默认值 兼容性 描述
options OnBackPressOptions
监听页面返回回调参数
名称 类型 必备 默认值 兼容性 描述
from string
- backbutton 顶部导航栏左边的返回按钮或 Android 实体返回键
- navigateBack 返回 API,即 uni.navigateBack()
合法值 兼容性 描述
backbutton
navigateBack

# 返回值

类型 描述 必备
boolean 返回 true 时阻止页面返回

注意

  • onBackPress上不可使用async,会导致无法阻止默认返回
  • iOS 端侧滑返回不会触发 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>

# 页面 onTabItemTap 生命周期

# onTabItemTap 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS HarmonyOS(Vapor)
4.0 4.41 x x x x 4.61 x

# 参数

名称 类型 必填 默认值 兼容性 描述
options OnTabItemTapOption
监听 tab 点击回调参数
名称 类型 必备 默认值 兼容性 描述
index number
被点击 tabItem 的序号,从0开始
pagePath string
被点击 tabItem 的页面路径
text string
被点击 tabItem 的按钮文字

注意

  • onTabItemTap常用于点击当前 tabItem,滚动或刷新当前页面。如果是点击不同的 tabItem,一定会触发页面切换。

# 页面 onNavigationBarButtonTap 生命周期

# onNavigationBarButtonTap 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS HarmonyOS(Vapor)
4.0 x x x x x 4.61 x

# 参数

名称 类型 必填 默认值 兼容性 描述
options OnNavigationBarButtonTapOption
原生标题栏按钮点击回调参数
名称 类型 必备 默认值 兼容性 描述
index number
原生标题栏按钮数组的下标

# 页面 onNavigationBarSearchInputChanged 生命周期

# onNavigationBarSearchInputChanged 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS HarmonyOS(Vapor)
4.0 x x x x x 4.61 x

# 参数

名称 类型 必填 默认值 兼容性 描述
event NavigationBarSearchInputEvent
监听原生标题栏搜索输入框搜索回调参数
名称 类型 必备 默认值 兼容性 描述
text string
搜索输入框输入内容

# 页面 onNavigationBarSearchInputConfirmed 生命周期

# onNavigationBarSearchInputConfirmed 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS HarmonyOS(Vapor)
4.0 x x x x x 4.61 x

# 参数

名称 类型 必填 默认值 兼容性 描述
event NavigationBarSearchInputEvent
监听原生标题栏搜索输入框搜索回调参数
名称 类型 必备 默认值 兼容性 描述
text string
搜索输入框输入内容

# 页面生命周期示例

详情

<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: 2200px;
}
</style>

# 页面及组件生命周期流程图

下图展示了一个新页面,从创建开始,包括其中的组件,完整的时序。

  1. uni-app x框架,首先根据pages.json的配置,创建页面

所以pages.json中页面的style中各个设置是最早生效的,原生导航栏、页面背景色都会立即生效。

  1. 根据页面template里的组件,创建dom。

这里的dom创建仅包含第一批处理的静态dom。对于通过uts更新data或响应式变量,然后通过v-for再创建的列表数据,不在第一批处理。

要注意一个页面静态dom元素过多,会影响页面加载速度。

  • 在app-Android平台,可能会阻碍页面进入的转场动画。因为此时,页面转场动画还没有启动
  • 在app-iOS平台,窗体动画是异步的,不会阻塞
  1. 触发onLoad

此时页面还未显示,没有开始进入的转场动画,页面dom还不存在。

所以这里不能直接操作dom(可以修改data或响应式变量,因为vue框架会等待dom准备后再更新界面)。

onLoad中获取当前的activity等原生窗体对象,拿到的是老页面的activity,只能通过页面栈获取activity。

onLoad比较适合的场景是:接受上页的参数,联网取数据,更新data或响应式变量。

  1. 页面onShow

onLoad之后,转场动画开始后,页面会触发onShow。

新页面开始进入的转场动画,动画默认耗时300ms。

  1. 页面onReady

第2步创建dom是虚拟dom,dom创建后需要经历一段时间,UI层才能完成了页面上真实元素的创建,即触发了onReady。

onReady后,页面元素就可以自由操作了,比如ref获取节点。同时首批界面也渲染了。

注意:onReady和转场动画开始、结束之间,没有必然的先后顺序,完全取决于dom的数量和复杂度。

如果元素排版和渲染够快,转场动画刚开始就渲染好了;

大多情况下,转场动画走几格就看到了首批渲染内容;

如果元素排版和渲染过慢,转场动画结束都没有内容,就会造成白屏。

联网进程从onLoad起就在异步获取数据更新data或响应式变量,如果服务器速度够快,第二批数据也可能在转场动画结束前渲染。

  1. 转场动画结束

再次强调,5和6的先后顺序不一定,取决于首批dom渲染的速度。

# 通过props接收页面参数

HBuilderX 4.71+ 全平台支持通过 props 接收页面参数

组合式

选项式

<script setup>
const props = defineProps(["title"])
onLoad((options)=>{
  console.log(options['title'] == props.title) // true
})
</script>

# 页面作为组件

HBuilderX 4.71+ 全平台支持页面作为组件来渲染,通常用于宽屏适配等场景,比如一个新闻网站,在新闻列表页面,宽屏模式下,左侧显示列表,右侧用组件来显示详情页面。

  • 需要手动引入页面做为组件使用
<template>
    <TestPage></TestPage>
</template>

<script setup>
    import TestPage from '@/pages/test/test.uvue'
</script>
  • 页面作为组件渲染时,所有页面特有的生命周期不再生效,仅支持组件的生命周期(注意:页面组合式生命周期API还可以使用,但监听到的是作为组件时所在页面的,而非它自身的)
  • 支持定义props,作为页面渲染时,props会接收url中的参数,作为组件使用时,可以正常传递props(注意:如果某个页面要作为组件渲染,且需要接收参数,请使用props传递,而不是使用onLoad生命周期)
  • 如果想判断当前是作为页面渲染,还是组件渲染,可以通过 this.$page.vm === this 来判断,如果相等,说明是作为页面渲染的,不相等,说明是作为组件渲染的
  • 宽屏示例详见:https://ext.dcloud.net.cn/plugin?id=23613