# vue

uni-app x的vue规范,按照vue3规范实现,从4.0起支持组合式写法。

本文暂时只包括兼容性表格,vue功能详情另见 vue3概述Vue3 API

uni-app x中vue的用法,有单独的示例应用:hello uvue。这里都是可以跑通的使用样例代码。

# 全局 API兼容性

# 应用实例

Android iOS web
createApp() 4.11 4.0
createSSRApp() 4.11 4.0
app.mount() 4.11 4.0
app.unmount() 4.11 4.0
app.component() 4.11 4.0
app.directive() - - -
app.use() 3.99 4.11 4.0
app.mixin() 3.99 4.11 4.0
app.provide() 3.99 4.11 4.0
app.runWithContext() - - -
app.version 4.11 4.0
app.config - - -
app.config.errorHandler x 4.11 4.0
app.config.warnHandler - - -
app.config.performance - - -
app.config.compilerOptions - - -
app.config.globalProperties 3.99 4.11 4.0
app.config.optionMergeStrategies - - -

注意:

  • app.use: app.use 支持通过对象字面量、函数及 definePlugin 方式定义插件。
    支持传递插件参数,当传递插件参数时,app 的类型需要指定为 VueApp
// main.uts
export function createApp() {
  const app = createSSRApp(App)

  // 通过对象字面量方式注册插件
  app.use({
    install(app) {
      app.config.globalProperties.plugin1 = "plugin1"
    }
  })

  // 通过函数方式注册插件
  app.use(function (app) {
    app.config.globalProperties.plugin2 = "plugin2"
  })

  // 通过 definePlugin + 对象字面量方式注册插件
  const plugin3= definePlugin({
    install(app) {
      app.config.globalProperties.plugin3 = "plugin3"
    }
  })
  app.use(plugin3)

  // 通过 definePlugin + 函数方式注册插件
  const plugin4= definePlugin(function (app) {
    app.config.globalProperties.plugin4 = "plugin4"
  })
  app.use(plugin4)

  // 注册插件时传递参数
  // 注意:当传递插件参数时,app 的类型需要指定为 VueApp
  app.use(function (app: VueApp, arg1:string, arg2:string) {
	  app.config.globalProperties.plugin5 = `${arg1}-${arg2}`
  }, "arg1", "arg2");
}
  • app.config.globalProperties: 请注意,globalProperties 是一个保留关键字,因此在项目中请勿声明名为 globalProperties 的变量。
    在向 globalProperties 注册方法时,请使用直接函数表达式方式进行赋值。不支持先声明函数,再将其注册到 globalProperties 上的方式。同时,注册的函数一旦被赋值,不允许进行修改。
    globalProperties 在编译时处理,因此确保你的操作在编译时是可知的。例如,将变量赋值给 globalProperties 时,这个变量在编译时必须是已知的,而不能是在运行时才能确定的变量。

# 通用

Android iOS web
version 4.11 4.0
nextTick() 4.11 4.0
defineComponent() x x 4.0
defineAsyncComponent() - - -
defineCustomElement() - - -

# nextTick 使用注意事项

目前 nextTick 可以保证当前数据已经同步到 DOM,但是由于排版和渲染是异步的的,所以 nextTick 不能保证 DOM 排版以及渲染完毕。如果需要获取排版后的节点信息推荐使用 uni.createSelectorQuery 不推荐直接使用 Element 对象。在修改 DOM 后,立刻使用 Element 对象的同步接口获取 DOM 状态可能获取到的是排版之前的,而 uni.createSelectorQuery 可以保障获取到的节点信息是排版之后的。

# 组合式 API

注意:

  • 暂不支持 <script setup><script> 同时使用,如果需要配置 options 内容,比如 name,可以使用 defineOptions
  • 暂不支持顶层 await
  • 暂不支持 <script setup> 配置 generic 泛型类型参数。
  • App.uvue 暂不支持组合式 API。

# 响应式: 核心

Android iOS web
ref() 4.11 4.0
computed() - - -
reactive() 4.11 4.0
readonly() 4.0 4.11 4.0
watchEffect() 4.0 4.11 4.0
watchPostEffect() 4.0 4.11 4.0
watchSyncEffect() 4.0 4.11 4.0
watch() 4.0 4.11 4.0

注意:

  • computed 需通过泛型指定返回值类型。
const count = ref(0)
const doubleCount = computed<number>(() : number => {
  return count.value * 2
})

# 响应式: 工具

Android iOS web
isRef() 4.0 4.11 4.0
unref() 4.0 4.11 4.0
toRef() 4.0 4.11 4.11
toValue() 4.0 4.11 4.11
toRefs() 4.0 4.11 4.11
isProxy() 4.0 4.11 4.0
isReactive() 4.0 4.11 4.0
isReadonly() 4.0 4.11 4.0

注意:

  • toRefs 仅支持 ArrayUTSJSONObject, 不支持自定义类型。

# 响应式: 进阶

Android iOS web
shallowRef() 4.0 4.11 4.0
triggerRef() 4.0 4.11 x
customRef() 4.0 4.11 4.0
shallowReactive() 4.0 4.11 4.0
shallowReadonly() 4.0 4.11 4.0
toRaw() 4.0 4.11 4.0
markRaw() - - -
effectScope() 4.0 4.11 4.0
getCurrentScope() 4.0 4.11 4.0
onScopeDispose() 4.0 4.11 4.0

# 生命周期钩子

Android iOS web
onMounted() 4.0 4.11 4.0
onUpdated() 4.0 4.11 4.0
onUnmounted() 4.0 4.11 4.0
onBeforeMount() 4.0 4.11 4.0
onBeforeUpdate() 4.0 4.11 4.0
onBeforeUnmount() 4.0 4.11 4.0
onErrorCaptured() - - -
onRenderTracked() - - -
onRenderTriggered() - - -
onActivated() x x 4.0
onDeactivated() x x 4.0
onServerPrefetch() - - -

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

# 页面生命周期 示例

hello uvue

<template>
 <!-- #ifdef APP -->
 <scroll-view style="flex:1" :bounces="false">
 <!-- #endif -->
   <view class="page container">
     <text>page lifecycle</text>
     <button class="uni-common-mt" @click="scrollToBottom">scrollToBottom</button>
   </view>
 <!-- #ifdef APP -->
 </scroll-view>
 <!-- #endif -->
</template>

<script setup>
 import { state, setLifeCycleNum } from '@/store/index.uts'
 
 const isScrolled = ref(false)
 
 onLoad((_: OnLoadOptions) => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 100)
 })
 onPageShow(() => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 })
 onReady(() => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 })
 onPullDownRefresh(() => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 })
 onPageScroll((_) => {
   // 自动化测试
   isScrolled.value = true
 })
 onReachBottom(() => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 })
 onBackPress((_: OnBackPressOptions): boolean | null => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum - 10)
   return null
 })
 onPageHide(() => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum - 10)
 })
 onUnload(() => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum - 100)
 })
 onResize((_) => {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 })
 
 // 自动化测试
 const getLifeCycleNum = () : 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 getIsScrolled = (): boolean => {
   return isScrolled.value
 }
 
 defineExpose({
   getLifeCycleNum,
   pageSetLifeCycleNum,
   pullDownRefresh,
   scrollToBottom,
   getIsScrolled
 })
</script>

<style>
 .container {
   height: 1200px;
 }
</style>

# 组件生命周期 示例

hello uvue

<template>
 <!-- #ifdef APP -->
 <scroll-view style="flex:1">
 <!-- #endif -->
   <view class="pag container">
     <text class="uni-common-mb">component lifecycle</text>
     <component-lifecycle class="component-lifecycle" @updateIsScroll="updateIsScroll" />
     <button class="uni-common-mt" @click="scrollToBottom">scrollToBottom</button>
   </view>
 <!-- #ifdef APP -->
 </scroll-view>
 <!-- #endif -->
</template>

<script setup>
 import ComponentLifecycle from '@/components/CompositionAPILifecycle.uvue'
 import { state, setLifeCycleNum } from '@/store/index.uts'

 const isScrolled = ref(false)

 // 自动化测试
 const getLifeCycleNum = () : number => {
   return state.lifeCycleNum
 }

 // 自动化测试
 const pageSetlifeCycleNum = (num : number) => {
   setLifeCycleNum(num)
 }

 // 自动化测试
 const pullDownRefresh = () => {
   uni.startPullDownRefresh({
     success() {
       setTimeout(() => {
         uni.stopPullDownRefresh()
       }, 1500)
     },
   })
 }

 const scrollToBottom = () => {
   uni.pageScrollTo({
     scrollTop: 3000,
   })
 }

 const updateIsScroll = (val : boolean) => {
   isScrolled.value = val
 }

 // 自动化测试
 const getIsScrolled = () : boolean => {
   return isScrolled.value
 }

 defineExpose({
   getLifeCycleNum,
   pageSetlifeCycleNum,
   pullDownRefresh,
   scrollToBottom,
   getIsScrolled
 })
</script>

<style>
 .container {
   height: 1200px;
 }
</style>

# 函数 event 参数的类型

# 指令

Android iOS web
v-text x x 4.0
v-html 3.99 x 4.0
v-show 3.9 4.11 4.0
v-if 3.9 4.11 4.0
v-else 3.9 4.11 4.0
v-else-if 3.9 4.11 4.0
v-for 3.9 4.11 4.0
v-on 3.9 4.11 4.0
v-bind 3.9 4.11 4.0
v-model 3.9 4.11 4.0
v-slot 3.9 4.11 4.0
v-pre 3.99 4.11 4.0
v-once 3.99 4.11 x
v-memo 3.99 4.11 x
v-cloak x x 4.0

# 指令 示例

注意:

  • v-html:App-android 平台,v-html 指令通过编译为 rich-text 组件实现。因此,v-html 指令的内容必须是 rich-text 支持的格式, 并且要遵循标签嵌套规则,例如, swiper 标签内只允许嵌套 swiper-item 标签。
    同时,受限于 rich-text 组件不支持 class 样式,v-html 指令中同样不支持 class 样式。
    绑定 v-html 的标签内的内容会被忽略,v-html 指令的内容会编译为 rich-text 组件渲染为该标签的子节点。

# 事件处理

# script

  • 仅支持 export default {} 方式定义组件。
  • data 仅支持函数返回对象字面量方式。
<script lang="uts">
	export default {
		data() {
			return {
				// 必须写这里
			}
		}
	}
</script>

# Class 与 Style 绑定

  • uni-app x 支持绑定 UTSJSONObjectMap 类型数据。

在App-Android平台上 Map 的性能高于 UTSJSONObject 数据类型。从 uni-app x 4.01 起,Web平台也支持了 Map 类型绑定。

<template>
  <view>
    <view :style="styleMap" :class="classMap"></view>
  </view>
</template>

<script lang="uts">
  export default {
    data() {
      return {
        styleMap: new Map<string, string>([['border', '2px solid red'], ['background-color', 'green']]),
        classMap: new Map<string, boolean>([['w-100', true], ['h-100', true], ['rounded', false]])
      }
    }
  }
</script>

<style>
  .w-100 {
    width: 100px;
  }
  .h-100 {
    height: 100px;
  }
  .rounded {
    border-radius: 8px;
  }
</style>

# 深度选择器

App Web
x 4.0

处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,可以使用 :deep() 这个伪类:

<style scoped>
.a :deep(.b) {
  /* ... */
}
</style>

# CSS Modules

App Web
x 4.0

一个 <style module> 标签会被编译为 CSS Modules 并且将生成的 CSS class 作为 $style 对象暴露给组件:

<template>
  <text :class="$style.red">This should be red</text>
</template>

<style module>
.red {
  color: red;
}
</style>

得出的 class 将被哈希化以避免冲突,实现了同样的将 CSS 仅作用于当前组件的效果。

自定义注入名称

你可以通过给 module attribute 一个值来自定义注入 class 对象的属性名:

<template>
  <text :class="classes.red">red</text>
</template>

<style module="classes">
.red {
  color: red;
}
</style>

与组合式 API 一同使用

可以通过 useCssModule API 在 setup()<script setup> 中访问注入的 class。对于使用了自定义注入名称的 <style module> 块,useCssModule 接收一个匹配的 module attribute 值作为第一个参数:

import { useCssModule } from 'vue'

// 在 setup() 作用域中...
// 默认情况下, 返回 <style module> 的 class
useCssModule()

// 具名情况下, 返回 <style module="classes"> 的 class
useCssModule('classes')

# CSS 中的 v-bind()

App Web
x 4.13+

单文件组件的 <style> 标签支持使用 v-bind CSS 函数将 CSS 的值链接到动态的组件状态:

<template>
  <text class="text">hello</text>
</template>

<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>

<style>
.text {
  color: v-bind(color);
}
</style>

这个语法同样也适用于 <script setup>,且支持 UTS 表达式 (需要用引号包裹起来):

<script setup>
const theme = {
  color: 'red'
}
</script>

<template>
  <text class="view">hello</text>
</template>

<style scoped>
.text {
  color: v-bind('theme.color');
}
</style>

# 应用生命周期

uni-app x 新增了 onLastPageBackPressonExit 应用级生命周期,Android退出应用逻辑写在app.uvue里,新建项目的模板自动包含相关代码。如需修改退出逻辑,请直接修改相关代码。

# 组件

注意

  1. App 端,如需页面级滚动,根节点必须是 scroll-view 标签。

# 特殊元素

# script

# script 兼容性
Android iOS web
3.9 4.11 4.0
# 属性
名称 类型 默认值 描述
setup Any - -
lang Any -
值名称 描述
uts uts
# lang 兼容性
Android iOS web
uts 4.0 4.11 4.0
# script 属性兼容性
Android iOS web
setup 4.0 4.11 4.0
lang 4.0 4.11 4.0

# template

# template 兼容性
Android iOS web
3.9 4.11 4.0
# 属性
名称 类型 默认值 描述
lang string -
值名称 描述
html html
pug 仅 Web 端支持
# template 属性兼容性
Android iOS web
lang 3.9 x 4.0

# slot

组件类型:string

<slot> 元素作为组件模板之中的内容分发插槽。<slot> 元素自身将被替换。

# slot 兼容性
Android iOS web
3.9 4.11 4.0
# 属性
名称 类型 默认值 描述
name string - 用于命名插槽。
# 参见

# style

# style 兼容性
Android iOS web
3.9 4.11 4.0
# 属性
名称 类型 默认值 描述
lang Any -
值名称 描述
scss -
less -
stylus -
scoped Any - -
module Any - -
# style 属性兼容性
Android iOS web
lang 3.9 4.11 4.0
scoped x x 4.0
module x x 4.0

# keep-alive

组件类型:string

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。

# keep-alive 兼容性
Android iOS web
4.0 4.11 4.0
# 属性
名称 类型 默认值 描述
include string - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude string - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max string - 最多可以缓存多少组件实例。
# 参见

# component

组件类型:string

渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。

# component 兼容性
Android iOS web
3.99 4.11 4.0
# 属性
名称 类型 默认值 描述
is Any - -
inline-template Any - -
# 参见

# transition

组件类型:string

<transition> 元素作为单个元素/组件的过渡效果。<transition> 只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在检测过的组件层级中。

# transition 兼容性
Android iOS web
x x 4.0
# 属性
名称 类型 默认值 描述
name string - 用于自动生成 CSS 过渡类名。例如:name: 'fade' 将自动拓展为.fade-enter,.fade-enter-active等。默认类名为 "v"
appear string(true | false) - 是否在初始渲染时使用过渡。默认为 false。
css string(true | false) - 是否使用 CSS 过渡类。默认为 true。如果设置为 false,将只通过组件事件触发注册的 JavaScript 钩子。
type string - 指定过渡事件类型,侦听过渡何时结束。有效值为 "transition" 和 "animation"。默认 Vue.js 将自动检测出持续时间长的为过渡事件类型。
值名称 描述
transition -
animation -
mode string - 控制离开/进入的过渡时间序列。有效的模式有 "out-in" 和 "in-out";默认同时生效。
值名称 描述
out-in -
in-out -
duration string - 指定过渡的持续时间。默认情况下,Vue 会等待过渡所在根元素的第一个 transitionend 或 animationend 事件。
enter-class Any - -
leave-class Any - -
appear-class Any - -
enter-to-class Any - -
leave-to-class Any - -
appear-to-class Any - -
enter-active-class Any - -
leave-active-class Any - -
appear-active-class Any - -
@before-enter Any - -
@before-leave Any - -
@before-appear Any - -
@enter Any - -
@leave Any - -
@appear Any - -
@after-enter Any - -
@after-leave Any - -
@after-appear Any - -
@enter-cancelled Any - -
@leave-cancelled string - v-show only
@appear-cancelled Any - -
# 参见

# transition-group

组件类型:string

<transition-group> 元素作为多个元素/组件的过渡效果。<transition-group> 渲染一个真实的 DOM 元素。默认渲染 <span>,可以通过 tag 属性配置哪个元素应该被渲染。

# transition-group 兼容性
Android iOS web
x x 4.0
# 属性
名称 类型 默认值 描述
tag string - 默认为 span。
move-class string - 覆盖移动过渡期间应用的 CSS 类。
name string - 用于自动生成 CSS 过渡类名。例如:name: 'fade' 将自动拓展为.fade-enter,.fade-enter-active等。默认类名为 "v"
appear string(true | false) - 是否在初始渲染时使用过渡。默认为 false。
css string(true | false) - 是否使用 CSS 过渡类。默认为 true。如果设置为 false,将只通过组件事件触发注册的 JavaScript 钩子。
type string - 指定过渡事件类型,侦听过渡何时结束。有效值为 "transition" 和 "animation"。默认 Vue.js 将自动检测出持续时间长的为过渡事件类型。
值名称 描述
transition -
animation -
mode Any - -
duration string - 指定过渡的持续时间。默认情况下,Vue 会等待过渡所在根元素的第一个 transitionend 或 animationend 事件。
enter-class Any - -
leave-class Any - -
appear-class Any - -
enter-to-class Any - -
leave-to-class Any - -
appear-to-class Any - -
enter-active-class Any - -
leave-active-class Any - -
appear-active-class Any - -
@before-enter Any - -
@before-leave Any - -
@before-appear Any - -
@enter Any - -
@leave Any - -
@appear Any - -
@after-enter Any - -
@after-leave Any - -
@after-appear Any - -
@enter-cancelled Any - -
@leave-cancelled string - v-show only
@appear-cancelled Any - -
# 参见

# teleport

组件类型:string

Teleport 提供了一种干净的方法,允许我们控制在 DOM 中哪个父节点下呈现 HTML,而不必求助于全局状态或将其拆分为两个组件。

# teleport 兼容性
Android iOS web
4.0 4.11 4.0
# 属性
名称 类型 默认值 描述
to string - 必须是有效的查询选择器或 HTMLElement (如果在浏览器环境中使用)。指定将在其中移动 <teleport> 内容的目标元素
disabled boolean - 此可选属性可用于禁用 <teleport> 的功能,这意味着其插槽内容将不会移动到任何位置,而是在您在周围父组件中指定了 <teleport> 的位置渲染。

注意:

  • App-Android 平台暂不支持动态修改 to 属性。
# 参见

# 特殊 Attributes

Android iOS web
key 3.9 4.11 4.0
ref 3.9 4.11 4.0
is 3.99 4.11 4.0

# 生命周期选项

Android iOS web
beforeCreate 3.9 4.11 4.0
created 3.9 4.11 4.0
beforeMount 3.9 4.11 4.0
mounted 3.9 4.11 4.0
beforeUpdate 3.9 4.11 4.0
updated 3.9 4.11 4.0
beforeUnmount 3.9 4.11 4.0
unmounted 3.9 4.11 4.0
errorCaptured x x 4.0
renderTracked x x 4.0
renderTriggered x x 4.0
activated 4.0 4.11 4.0
deactivated 4.0 4.11 4.0
serverPrefetch - - -

# 页面生命周期示例

hello uvue

<template>
 <!-- #ifdef APP -->
 <scroll-view style="flex: 1" :bounces="false">
   <!-- #endif -->
   <view class="page container">
     <text>page lifecycle</text>
     <button class="uni-common-mt" @click="scrollToBottom">scrollToBottom</button>
   </view>
   <!-- #ifdef APP -->
 </scroll-view>
 <!-- #endif -->
</template>

<script lang="uts">
import { state, setLifeCycleNum } from '@/store/index.uts'

export default {
 data() {
   return {
     isScrolled: false,
   }
 },
 onLoad(_ : OnLoadOptions) {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 100)
 },
 onShow() {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 },
 onReady() {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 },
 onPullDownRefresh() {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 },
 onPageScroll(_) {
   // 自动化测试
   this.isScrolled = true
 },
 onReachBottom() {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 },
 onBackPress(_ : OnBackPressOptions) : boolean | null {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum - 10)
   return null
 },
 onHide() {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum - 10)
 },
 onUnload() {
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum - 100)
 },
 onResize(_){
   // 自动化测试
   setLifeCycleNum(state.lifeCycleNum + 10)
 },
 methods: {
   // 自动化测试
   getLifeCycleNum() : number {
     return state.lifeCycleNum
   },
   // 自动化测试
   setLifeCycleNum(num : number) {
     setLifeCycleNum(num)
   },
   // 自动化测试
   pullDownRefresh() {
     uni.startPullDownRefresh({
       success() {
         setTimeout(() => {
           uni.stopPullDownRefresh()
           // 一秒后立即停止下拉刷新不会触发 onPullDownRefresh,因为下拉动画时间大概需要1.1~1.2秒
         }, 1500)
       },
     })
   },
   scrollToBottom() {
     uni.pageScrollTo({
       scrollTop: 2000,
     })
   },
 },
}
</script>

<style>
.container {
 height: 1200px;
}
</style>

# 组件生命周期示例

hello uvue

<template>
 <view class="page">
   <text class="uni-common-mb">component lifecycle</text>
   <component-lifecycle class="component-lifecycle" />
 </view>
</template>

<script>
import ComponentLifecycle from '@/components/OptionsAPILifecycle.uvue'
import { state } from '@/store/index.uts'
export default {
 components: { ComponentLifecycle },
 methods: {
   // 自动化测试
   getLifeCycleNum(): number {
     return state.lifeCycleNum
   },
 },
}
</script>

# mounted、unmounted 使用注意事项

目前 mounted、unmounted 可以保证当前数据已经同步到 DOM,但是由于排版和渲染是异步的的,所以 mounted、unmounted 不能保证 DOM 排版以及渲染完毕。如果需要获取排版后的节点信息推荐使用 uni.createSelectorQuery 不推荐直接使用 Element 对象。在修改 DOM 后,立刻使用 Element 对象的同步接口获取 DOM 状态可能获取到的是排版之前的,而 uni.createSelectorQuery 可以保障获取到的节点信息是排版之后的。

# 插件

暂不支持vue插件,比如pinia、vuex、i18n、router。简单的状态管理可以参考文档全局变量和状态管理

# 选项式 API兼容性

# 状态选项

Android iOS web
data 3.9 4.11 4.0
props 3.9 4.11 4.0
computed 3.9 4.11 4.0
methods 3.9 4.11 4.0
watch 3.9 4.11 4.0
emits 3.9 4.11 4.0
expose x x 4.0
注意:
  • watch immediate 第一次调用时,App-Android 平台旧值为初始值,web 平台为 null。

# 状态选项 示例

# 渲染选项

Android iOS web
template - - -
render 3.9 4.11 4.0
compilerOptions - - -
slots 3.9 4.11 4.0

# 渲染选项 示例

# 组合选项

Android iOS web
provide 3.99 4.11 4.0
inject 3.99 4.11 4.0
mixins 3.99 4.11 4.0
extends - - -

注意:

  • inject: 当使用 inject 声明从上层提供方注入的属性时,支持两种写法:字符串数组和对象。推荐使用对象写法,因为当使用数组方法时,类型会被推导为 any | null 类型。
    使用对象写法时,额外增加 type 属性用于标记类型。如果注入的属性类型不是基础数据类型,需要通过 PropType 来标记类型。
export default {
  inject: {
    provideString: {
      type: String,
      default: 'default provide string value'
    },
    provideObject: {
      type: Object as PropType<UTSJSONObject>
    },
    provideMap: {
      type: Object as PropType<Map<string, string>>,
      default: (): Map<string, string> => {
        return new Map<string, string>([['key', 'default provide map value']])
      }
    }
  }
}
  • mixins: mixins 仅支持通过字面量对象方式和 defineMixin 函数方式定义。
const mixin1 = defineMixin({
  onLoad() {
    console.log('mixin1 onLoad')
  }
})
export default {
  mixins: [
    mixin1,
    {
      data() {
        return {
          mixin2: 'mixin2'
        }
      }
    }
  ]
}

同名属性会被覆盖,同名生命周期会依次执行。

同名属性的优先级如下:

  • app.mixin 内嵌入的 mixin < 在 app.mixin 中声明的 mixin < 在 page.mixin 内嵌入的 mixin < 在 page.mixin 中声明的 mixin < 在 component.mixin 内嵌入的 mixin < 在 component.mixin 中声明的 mixin

同名生命周期的执行顺序如下:

  1. app.mixin 内嵌入的 mixin
  2. app.mixin 中声明的 mixin
  3. page.mixin 内嵌入的 mixin
  4. page.mixin 中声明的 mixin
  5. component.mixin 内嵌入的 mixin
  6. component.mixin 中声明的 mixin

# 其他杂项

Android iOS web
name 3.9 4.11 4.0
inheritAttrs 3.9 4.11 4.0
components 3.9 4.11 4.0
directives - - -

# 组件实例

Android iOS web
$data 4.11 4.0
$props 4.11 4.0
$el 4.11 4.0
$options 4.11 4.0
$parent 4.11 4.0
$root 4.11 4.0
$slots 4.11 4.0
$refs 4.11 4.0
$attrs 4.11 4.0
$watch() 4.11 4.0
$emit 4.11 4.0
$forceUpdate 4.11 4.0
$nextTick 4.11 4.0
$callMethod 4.11 4.0

# 组件实例 示例

# $nextTick 使用注意事项

目前 $nextTick 可以保证当前数据已经同步到 DOM,但是由于排版和渲染是异步的的,所以 $nextTick 不能保证 DOM 排版以及渲染完毕。如果需要获取排版后的节点信息推荐使用 uni.createSelectorQuery 不推荐直接使用 Element 对象。在修改 DOM 后,立刻使用 Element 对象的同步接口获取 DOM 状态可能获取到的是排版之前的,而 uni.createSelectorQuery 可以保障获取到的节点信息是排版之后的。

# $data 使用注意事项

data内$开头的属性不可直接使用this.$xxx访问,需要使用this.$data['$xxx'],这是vue的规范。目前安卓端可以使用this.$xxx访问是Bug而非特性,请勿使用此特性。

示例

<template>
  <view></view>
</template>
<script>
export default {
  data() {
    return {
      $a: 1
    }
  },
  onReady() {
    console.log(this.$data['$a'] as number) // 1
  }
}
</script>

# 进阶 API兼容性

# 渲染函数

Android iOS web
h() 3.99 4.11 4.0
mergeProps() 4.0 4.11 4.0
cloneVNode() 4.0 4.11 4.0
isVNode() 4.11 4.0
resolveComponent() 4.11 4.0
resolveDirective() - - -
withDirectives() 4.11 4.0
withModifiers() 4.11 4.0

# 单文件组件兼容性

# <script setup>

Android iOS web
defineProps() 4.0 4.11 4.0
defineEmits() 4.0 4.11 4.0
defineModel() 4.0 4.11 4.11
defineExpose() 4.0 4.11 4.0
defineOptions() 4.0 4.11 4.11
defineSlots() 4.0 4.11 4.0
useSlots() 4.0 4.11 4.0
useAttrs() 4.0 4.11 4.0

# <script setup> 示例

hello uvue

<template>
 <!-- #ifdef APP -->
 <scroll-view style="flex: 1">
 <!-- #endif -->
   <view class="page">
     <text class='uni-common-mt' id="str">str: {{ str }}</text>
     <text class='uni-common-mt' id="num">num: {{ num }}</text>
     <text class='uni-common-mt' id="bool">bool: {{ bool }}</text>
     <text class='uni-common-mt' id="count">count: {{count}}</text>
     <button class='uni-common-mt' id="increment-btn" @click="increment">increment</button>
     <text class='uni-common-mt' id="obj-str">obj.str: {{ obj['str'] }}</text>
     <text class='uni-common-mt' id="obj-num">obj.num: {{ obj['num'] }}</text>
     <text class='uni-common-mt' id="obj-bool">obj.bool: {{ obj['bool'] }}</text>
     <button class='uni-common-mt' id="update-obj-btn" @click="updateObj">update obj</button>
     <!-- #ifdef APP -->
     <RenderFunction :str='str' :count='count' :obj='obj' @compUpdateObj='compUpdateObj' :isShow='true' />
     <!-- #endif -->
     <Foo>
       <text class="uni-common-mt" id="default-slot-in-foo">default slot in Foo</text>
     </Foo>
   </view>
 <!-- #ifdef APP -->
 </scroll-view>
 <!-- #endif -->
</template>

<script>
 // #ifdef APP
 import RenderFunction from './RenderFunction.uvue'
 // #endif
 import Foo from './Foo.uvue'
 export default {
   components: {
     // #ifdef APP
     RenderFunction,
     // #endif
     Foo
   },
   setup() {
     const count = ref(0)
     // 函数只能通过声明变量,赋值函数的方式,不支持 function xxx(){}
     const increment = () => { count.value++ }
     const obj = reactive({
       str: 'obj default str',
       num: 0,
       bool: false,
     })
     const updateObj = () => {
       obj['str'] = 'obj new str'
       obj['num'] = 100
       obj['bool'] = true
     }
     const compUpdateObj = () => {
       obj['str'] = 'obj new str by comp update'
       obj['num'] = 200
       obj['bool'] = true
     }
     return {
       str: 'default str',
       num: 0,
       bool: false,
       count,
       increment,
       obj,
       updateObj,
       compUpdateObj
     }
   }
 }
</script>

注意:

  • defineProps 仅支持数组字面量、对象字面量定义(等同于 options 中的 props规则)及使用纯类型参数的方式来声明。
// 数组字面量
defineProps(['str', 'num', 'bool', 'arr', 'obj', 'fn'])

// 对象字面量
defineProps({
  str: String,
  num: Number,
  bool: {
    type: Boolean,
    default: true
  },
  arr: {
    type: Array as PropType<string[]>,
    default: () : string[] => [] as string[]
  },
  obj: {
    type: Object as PropType<UTSJSONObject>,
    default: () : UTSJSONObject => ({ a: 1 })
  },
  fn: {
    type: Function as PropType<() => string>,
    default: () : string => ''
  }
})

// 纯类型参数
defineProps<{
  str : String,
  num : Number,
  bool : Boolean,
  arr : PropType<string[]>,
  obj : PropType<UTSJSONObject>,
  fn : PropType<() => string>
}>()
  • defineEmits 仅支持数组字面量和纯类型参数的方式来声明。
// 数组字面量
const emit = defineEmits(['change'])

// 纯类型参数
const emit = defineEmits<{
  (e : 'change', id : number) : void
}>()
const emit = defineEmits<{
  // 具名元组语法
  change : [id: number]
}>()

  • defineOptions 仅支持对象字面量方式定义。
defineOptions({
  data() {
    return {
      count: 0,
      price: 10,
      total: 0
    }
  },
  computed: {
    doubleCount() : number {
      return this.count * 2
    },
  },
  watch: {
    count() {
      this.total = this.price * this.count
    },
  },
  methods: {
    increment() {
      this.count++
    }
  }
})
  • defineExpose 仅支持对象字面量方式定义,导出的变量或方法,必须是 setup 中定义的,暂不支持外部定义。
<script setup>
  const str = 'str'
  const num = ref(0)
  const increment = () => {
    num.value++
  }

  defineExpose({
    str,
    num,
    increment
  })
</script>

# 其他vue特性

# 递归组件

实现递归组件时不要使用组件import自身的写法,直接在模板内使用组件名即可。

// component-a.uvue
<template>
  <view>
    <text>component-a::{{text}}</text>
    <component-a v-if="!end" :text="text" :limit="limit-1"></component-a>
  </view>
</template>

<script>
  // import componentA from './component-a' // 错误用法
  export default {
    name: "component-a",
    props: {
      text: {
        type: String,
        default: ''
      },
      limit: {
        type: Number,
        default: 2
      }
    },
    computed: {
      end() : boolean {
        return this.limit <= 0
      }
    }
  }
</script>

# 其他示例