模版语法
插值
- 数据绑定最常见的形式就是文本插值,{
- v-once,一次性插值
- v-html,输出真的html
- v-bind指令
- 动态地绑定一个或多个属性,或一个组件
prop
到表达式。 - 可以缩写为:
- 在绑定
prop
时,prop
必须在子组件中声明。 - 可以用修饰符指定不同的绑定类型。
- 动态地绑定一个或多个属性,或一个组件
- JS表达式
- 三元
- 算术
- 复杂的函数运算
指令
- 带有
v-
前缀的特殊属性 - v-bind
- v-on
- 它用于监听 DOM 事件
- 可以缩写为
@
,v-on:click
->@click
- v-once
- 只渲染元素和组件一次。
- 添加了
v-once
能保证节点只渲染一次,但是并不一定能优化渲染性能,反而可能会拖慢客户端复用节点时的比对效率。
- v-html
- 内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。
- App端和H5端支持
v-html
,微信小程序会被转为rich-text
- data属性
- 必须声明为返回一个初始数据对象的函数
JavaScript//正确用法,使用函数返回对象 data() { return { title: 'Hello' } }
- 两种绑定方式:,属性区域通过:属性名称
Class 与 Style 绑定
- 对象语法
- 可以传给 v-bind:class 一个对象,实现动态地切换 class。
- 数组语法
- 可以把一个数组传给 v-bind:class,以应用一个 class 列表。
:class="[activeClass,errorClass]"
- 以:style=""这样的方式设置px像素值,其值为实际像素,不会被编译器转换。
- 小程序端不支持
classObject
和styleObject
语法。
条件渲染
- v-if和v-else
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回truthy
值的时候被渲染。 使用v-else
指令来表示 v-if 的“else 块”。
- 条件渲染分组
- 因为
v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 标签元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含该 元素。
- 因为
- v-show
- 带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的
CSS
属性的display
。
- 带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的
- v-if 和 v-show 区别
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-show
不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换,来控制元素的显示和隐藏。- 如果需要非常频繁地切换,则使用 v-show 较好。
- 如果在运行时条件很少改变,则使用 v-if 较好。
- 不推荐同时使用
v-if
和v-for
。
- 不推荐同时使用
- 当
v-if
与v-for
一起使用时,v-for
具有比v-if
更高的优先级。
- 当
列表渲染
- 在v-for里使用数组
v-for
指令需要使用item in items
形式的特殊语法,其中items
是源数据数组,而item
则是被迭代的数组元素的别名。v-for="(item, index) in items"
- 在v-for里使用对象
- 可以用 v-for 来遍历一个对象的
property
。 v-for="(value, name, index) in object"
- 第一个参数
value
是被迭代的对象元素的属性值。- 第二个参数为property
名称 (也就是键名)。- 第三个参数作为索引。
- 可以用 v-for 来遍历一个对象的
- 列表渲染分组
- 可以利用带有
v-for
的template
来循环渲染一段包含多个元素的内容。
- 可以利用带有
- key
- 在H5平台 item 从 1 开始
- 在组件上使用 v-for
- 当在组件上使用 v-for 时,key是必须的。
事件处理
- 监听事件
- 可以用@事件监听 DOM 事件,并在触发时运行一些
JavaScript
代码。
- 可以用@事件监听 DOM 事件,并在触发时运行一些
- 事件处理方法
- 在
methods
对象中定义方法 event
是原生 DOM 事件
- 在
- 内联处理器中的方法
- 事件修饰符
.stop
: 各平台均支持, 使用时会阻止事件冒泡,在非 H5 端同时也会阻止事件的默认行为.native
: 监听原生事件,各平台均支持.prevent
: 仅在 H5 平台支持.capture
: 仅在 H5 平台支持.self
: 仅在 H5 平台支持.once
: 仅在 H5 平台支持.passive
: 仅在 H5 平台支持
- 事件映射表
// 事件映射表,左侧为 WEB 事件,右侧为 ``uni-app`` 对应事件
{
click: 'tap',
touchstart: 'touchstart',
touchmove: 'touchmove',
touchcancel: 'touchcancel',
touchend: 'touchend',
tap: 'tap',
longtap: 'longtap', //推荐使用longpress代替
input: 'input',
change: 'change',
submit: 'submit',
blur: 'blur',
focus: 'focus',
reset: 'reset',
confirm: 'confirm',
columnchange: 'columnchange',
linechange: 'linechange',
error: 'error',
scrolltoupper: 'scrolltoupper',
scrolltolower: 'scrolltolower',
scroll: 'scroll'
}
表单控件绑定
- v-model
- 可以用 v-model 指令在表单
input
、textarea
及select
元素上创建双向数据绑定。
- 可以用 v-model 指令在表单
- uni-app表单组件
组件基础
自定义组件
<button-counter title="title:"@clicknow="clicknow">
</button-counter>
<script type="text/javascript">
Vue.component('button-counter',{
props:['title'],
data:function(){
return{
count:0
}
},
template:'<div><h1>1111</h1><button v-on:click="clickfun">{{title}}:{{count}}times</button></div>',
methods:{
clickfun:function(){
this.count++;
this.$emit('clicknow',this.count)
}
}
})
</script>
计算属性和侦听器
- 计算属性computed
- 每一个计算属性都包含一个
getter
和一个setter
,默认是利用getter
来读取。
- 每一个计算属性都包含一个
- 计算属性的getter
- 在模板中绑定表达式是非常便利的,但是它们实际上只用于简单的操作。在模板中放入太多的逻辑会让模板过重且难以维护。
html<view> {{ message.split('').reverse().join('') }} </view>
- 对于任何复杂逻辑,你都应当使用计算属性。
- 计算属性的 setter
- 需要时也可以提供一个
setter
函数, 当手动修改计算属性的值时,就会触发setter
函数,执行一些自定义的操作。
html<template> <view> <view>{{ fullName }}</view> </view> </template> <script> export default { data() { return { firstName: 'Foo', lastName: 'Bar' } }, computed: { fullName: { // getter get(){ return this.firstName + ' ' + this.lastName }, // setter set(newValue){ var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } } </script> //再运行 `fullName = 'John Doe'` 时,`setter` 会被调用,`firstName` 和 `lastName` 也会相应地被更新。
- 需要时也可以提供一个
- getter与setter区别
- get:通过设置get方法可以得到fullName的新值。
- set:通过set的方法,设置一个值(newValue)来改变fullName相关联的值,引起fullName重新的计算,相应的页面上fullName也会发生改变成新的内容。
计算属性缓存vs方法
<template>
<view>
<view>Reversed message: "{{ reversedMessage() }}"</view>
</view>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
methods: {
reversedMessage(){
return this.message.split('').reverse().join('')
}
}
}
</script>
可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。 只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。 每当触发重新渲染时,调用方法将总会再次执行函数。 我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
计算属性vs侦听属性
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
。然而,通常更好的做法是使用计算属性而不是命令式的 watch
回调。
- 侦听器watch
- 类型:
interface Watch { [key: string]: string | Function | Object | Array }
- 一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项对象。Vue实例将会在实例化时调用
$watch()
,遍历watch
对象的每一个property
。
- 类型:
<script>
export default {
data() {
return {
a: 1,
b: 2,
c: 3,
d: 4,
e: {
f: {
g: 5
}
}
}
},
watch: {
a: function(val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// 方法名
b: 'someMethod',
// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
c: {
handler: function(val, oldVal) { /* ... */ },
deep: true
},
// 该回调将会在侦听开始之后被立即调用
d: {
handler: 'someMethod',
immediate: true
},
// 你可以传入回调数组,它们会被逐一调用
e: [
'handle1',
function handle2(val, oldVal) { /* ... */ },
{
handler: function handle3(val, oldVal) { /* ... */ },
/* ... */
}
],
// watch vm.e.f's value: {g: 5}
'e.f': function(val, oldVal) { /* ... */ }
}
}
</script>
组件
组件基础理论
- 组件是视图层的基本组成单元
- 组件是一个单独且可复用的功能模块的封装
- 一个组件包括开始标签和结束标签,标签上可以写属性,并对属性赋值。内容写在两个标签内。
- 根节点为
<template>
,这个<template>
下只能且必须有一个根<view>
组件。这是vue单文件组件规范。 - 一个组件的 data 选项必须是一个函数。
- 根节点为
- 基础组件是内置在uni-app框架中的,包括view、text、input、button、video等几十个基础组件,列表详见:uni-app基础组件
- 实际开发中会有很多封装的组件。比如我们需要一个五角星点击评分的组件,在DCloud的插件市场里可以获取到:https://ext.dcloud.net.cn/plugin?id=33
- 优势:
- 可以将组件进行任意次数的复用。
- 合理的划分组件,有助于提高应用性能。
- 代码更加方便组织和管理,并且扩展性也更强,便于多人协同开发。
- 组件化开发能大幅度提高应用开发效率、测试性、复用性等。
- 注册
- 在注册一个组件的时候,始终要起一个名字
- 使用kebab-case:当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
<my-component-name>
。 - 使用PascalCase:当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。 也就是说
<my-component-name>
和<MyComponentName>
都是可接受的。
- 使用kebab-case:当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
- 在注册一个组件的时候,始终要起一个名字
- 全局注册
uni-app
支持配置全局组件,需在main.js
里进行全局注册,注册后就可在所有页面里使用该组件。- 注意:
- Vue.component 的第一个参数必须是静态的字符串。
- nvue 页面暂不支持全局组件。
main.js
里进行全局导入和注册JavaScriptimport Vue from 'vue' import pageHead from './components/page-head.vue' Vue.component('page-head',pageHead)
index.vue
里可直接使用组件html<template> <view> <page-head></page-head> </view> </template>
- 局部注册
- 局部注册之前,在需要引用该组件的页面,导入你想使用的组件。
- 页面引入组件方式
- 传统vue规范:在 index.vue 页面中,通过
import
方式引入组件 ,在components
选项中定义你想要使用的组件。
html对于<!-- 在index.vue引入 uni-badge 组件--> <template> <view> <uni-badge text="1"></uni-badge><!-- 3.使用组件 --> </view> </template> <script> import uniBadge from '@/components/uni-badge/uni-badge.vue';//1.导入组件(这步属于传统vue规范,但在uni-app的easycom下可以省略这步) export default { components:{uniBadge }//2.注册组件(这步属于传统vue规范,但在uni-app的easycom下可以省略这步) } </script>
components
对象中的每个 property 来说,其 property 名就是自定义元素的名字,其 property 值就是这个组件的选项对象。 2. 通过uni-app的easycom:将组件引入精简为一步。只要组件安装在项目的components
目录下,并符合components/组件名称/组件名称.vue
目录结构。就可以不用引用、注册,直接在页面中使用。htmleasycom是自动开启的,不需要手动开启,有需求时可以在<!-- 在index.vue引入 uni-badge 组件--> <template> <view> <uni-badge text="1"></uni-badge><!-- 3.使用组件 --> </view> </template> <script> // 这里不用import引入,也不需要在components内注册uni-badge组件。template里就可以直接用 export default { data() { return { } } } </script>
pages.json
的easycom
节点进行个性化设置,详见。 不管components目录下安装了多少组件,easycom打包后会自动剔除没有使用的组件,对组件库的使用尤为友好。 组件是vue
技术中非常重要的部分,组件使得与ui相关的轮子可以方便的制造和共享,进而使得vue
使用者的开发效率大幅提升。 - 传统vue规范:在 index.vue 页面中,通过
props
props
可以是数组或对象,用于接收来自父组件的数据。props
可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
选项 | 类型 | 说明 |
---|---|---|
type | String 、 Number 、Boolean 、Array 、 Object 、Date 、Function 、Symbol ,任何自定义构造函数、或上述内容组成的数组 | 会检查一个 prop 是否是给定的类型,否则抛出警告 |
default | any | 为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。 |
required | boolean | 定义该 prop 是否是必填项 |
validator | Function | 自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 false 的值 (也就是验证失败),一个控制台警告将会被抛出 |
//子组件
<template>
<view>
<!-- 我是子组件componentA -->
<view>{{age}}</view>
</view>
</template>
<script>
export default {
props: {
// 检测类型 + 其他验证
age: {
type: Number,
default: 0,
required: true,
validator: function(value) {
return value >= 0
}
}
}
}
</script>
//父组件
<template>
<view>
<!-- 我是父组件 -->
<componentA :age="10"></componentA>
</view>
</template>
- 传递静态或动态的Prop
- 可以给
prop
传入一个静态的值:
html<blog-post title="My journey with Vue"></blog-post>
- 可以通过
v-bind
动态赋值
html<!-- 动态赋予一个变量的值 --> <blog-post v-bind:title="post.title"></blog-post> <!-- 包含该 prop 没有值的情况在内,都意味着 `true`。--> <blog-post is-published></blog-post> <blog-post v-bind:is-published="post.isPublished"></blog-post>
- 传入一个对象的所有
property
(微信小程序暂不支持该用法,即:<blog-post v-bind="post"(错误)>)
- 想要将一个对象的所有
property
都作为prop
传入,你可以使用不带参数的v-bind
(取代 v-bind:prop-name)。例如,对于一个给定的对象post
- 可以给
- 单向数据流 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。 这个
prop
用来传递一个初始值;这个子组件接下来希望将其作为一个本地的prop
数据来使用。在这种情况下,最好定义一个本地的data property
并将这个prop
用作其初始值 这个prop
以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个prop
的值来定义一个计算属性
ref
被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs
对象上。 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
<!-- 非H5端不支持通过this.$refs.content来获取view实例 -->
<view ref="content">hello</view>
<!-- 支持通过this.$refs.child来获取child-component实例 -->
<child-component ref="child"></child-component>
当 ref
和 v-for
一起用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。 关于ref注册时间的重要说明: 因为 ref
本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们,它们还不存在!$refs
也不是响应式的,因此你不应该用它在模板中做数据绑定。 子组件ref: 尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。 访问子组件实例或子元素,通过 ref 为子组件赋予一个 ID 引用,在vue的js中可通过this.$refs.XXX
来获取到组件对象。
<!-- base-input子组件页面 -->
<template>
<view>
<input :focus="isFocus" type="text" placeholder="请输入内容" />
</view>
</template>
<script>
export default {
name:"base-input",
data() {
return {
"isFocus":false
};
},
methods:{
focus(){
this.isFocus = true
}
}
}
</script>
<!-- index 父组件页面 -->
<template>
<view>
<base-input ref="usernameInput"></base-input>
<button type="default" @click="getFocus">获取焦点</button>
</view>
</template>
<script>
export default {
methods:{
getFocus(){
//通过组件定义的ref调用focus方法
this.$refs.usernameInput.focus()
}
}
}
</script>
非H5端只能用于获取自定义组件,不能用于获取内置组件实例(如:view、text)
自定义事件
将原生事件绑定到组件 你可能有很多次想要在一个组件的根元素上直接监听一个原生事件。 这时,你可以使用 @事件的 .native
修饰符。 在app、小程序端和h5端表现不一致,h5端获取到的是浏览器原生事件。
<template>
<view>
<!-- 我是父组件 -->
<componentA @click.native="clickComponentA" style="height: 200px;"></componentA>
</view>
</template>
<script>
export default {
methods: {
clickComponentA(){
console.log("clickComponentA");
}
}
}
</script>
<template>
<view>
<!-- 我是子组件 -->
<view type="default" @click.stop="open" style="height: 30px;">点击</view>
</view>
</template>
<script>
export default {
methods:{
open(){
console.log("open");
}
}
}
</script>
.sync修饰符 个子组件改变了一个 prop
的值时,这个变化也会同步到父组件中所绑定。 .sync
它会被扩展为一个自动更新父组件属性的 v-on
监听器。
<!-- 父组件 -->
<template>
<view>
<syncA :title.sync="title"></syncA>
</view>
</template>
<script>
export default {
data() {
return {
title:"hello vue.js"
}
}
}
</script>
<!-- 子组件 -->
<template>
<view>
<view @click="changeTitle">{{title}}</view>
</view>
</template>
<script>
export default {
props: {
title: {
default: "hello"
},
},
methods:{
changeTitle(){
//触发一个更新事件
this.$emit('update:title',"uni-app")
}
}
}
</script>
插槽 Vue 实现了一套内容分发的 API,将 slot
元素作为承载分发内容的出口。
<template>
<view>
<componentA>
Your Profile
</componentA>
</view>
</template>
<template>
<view>
<!-- 我是子组件componentA -->
<view >{{title}}</view>
<slot></slot>
</view>
</template>
<template>
<view>
<!-- 我是父组件 -->
<componentA>
<view>Your Profile</view>
<!-- 添加一个 uni-icons 图标 -->
<uni-icons type="contact" size="30"></uni-icons>
</componentA>
</view>
</template>
如果 `<componentA>` 的 `template` 中没有包含一个 `<slot>` 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
编译作用域 当你想在一个插槽中使用数据时,该插槽跟模板的其它地方一样可以访问相同的实例 property
(也就是相同的“作用域”),而不能访问 <navigation-link>
的作用域。例如 url
是访问不到的。规则:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
这里的 `url` 会是 undefined,
因为其 (指该插槽的) 内容是_传递给_ <navigation-link> 的
而不是在 <navigation-link> 组件*内部*定义的。
-->
</navigation-link>
默认内容 有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 <submit-button>
组件中。
<button type="submit">
<slot></slot>
</button>
<button type="submit">
<slot>Submit</slot>
</button>
<!-- 父级组件:不提供任何插槽内容-->
<submit-button></submit-button>
<!-- 子组件:后备内容 “Submit” 将会被渲染: -->
<button type="submit">
Submit
</button>
<!-- 父级组件:提供插槽内容-->
<submit-button>
Save
</submit-button>
<!-- 子组件:则这个提供的内容将会被渲染从而取代后备内容: -->
<button type="submit">
Save
</button>
具名插槽 需要多个插槽时,可以利用 <slot>
元素的一个特殊的特性:name
来定义具名插槽
<template>
<view class="container">
<header>
<!-- 我们希望把页头放这里 -->
<slot name="header"></slot>
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
<slot></slot>
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
<slot name="footer"></slot>
</footer>
</view>
</template>
不要使用 `v-for` 对 slot 进行循环处理,组件中多次出现相同 name 属性的 slot 会产生意料之外的问题。比如 `<slot name='footer'>` 出现一次以上在各家小程序中表现不同,合理的做法是传递不同 name 的 slot,或者在外层父组件进行循环,保证多端一致。
在向具名插槽提供内容的时候,我们可以在一个 `<template>` 元素上使用 `v-slot` 指令,并以 v-slot 的参数的形式提供其名称
<template>
<view>
<!-- 父组件使用子组件`<base-layout>`,节点上使用slot特性: -->
<base-layout>
<template v-slot:header>
<view>Here might be a page title</view>
</template>
<template v-slot:default>
<view>A paragraph for the main content.</view>
<view>And another one.</view>
</template>
<template v-slot:footer>
<view>Here's some contact info</view>
</template>
</base-layout>
</view>
</template>
v-slot 只能添加在 `<template>` 上
一个不带 name 的 `<slot>` 出口会带有隐含的名字 “default”
具名插槽的缩写 跟 v-on 和 v-bind 一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header
可以被重写为 #header
<template>
<view>
<!-- 父组件使用子组件`<base-layout>`,节点上使用slot特性: -->
<base-layout>
<template #header>
<view>Here might be a page title</view>
</template>
<view>A paragraph for the main content.</view>
<view>And another one.</view>
<template #footer>
<view>Here's some contact info</view>
</template>
</base-layout>
</view>
</template>
作用域插槽 在作用域插槽内,父组件可以拿到子组件的数据。子组件可以在 slot
标签上绑定属性值。
<!-- 子组件 <current-user>-->
<template>
<view>
<slot>{{ user.lastName }}</slot>
</view>
</template>
<!-- 用名而非姓来显示-->
<template>
<view>
<current-user>
{{ user.firstName }}
</current-user>
</view>
</template>
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 `<slot>` 元素的一个 `attribute` 绑定上去
<!-- 子组件 <current-user>-->
<template>
<view>
<slot :user="user">{{user.lastName}}</slot>
</view>
</template>
<script>
export default {
data() {
return {
user:{
"lastName":"bar",
"firstName":"foo"
}
}
}
}
</script>
绑定在 `<slot>` 元素上的 `attribute` 被称为**插槽 prop**。现在在父级作用域中,我们可以使用带值的 `v-slot` 来定义我们提供的插槽 `prop` 的名字
<template>
<view>
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
</view>
</template>
独占默认插槽的缩写语法 就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot
被假定对应默认插槽。
<template>
<view>
<current-user>
<template v-slot="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
</view>
</template>
默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。
<template>
<view>
<!-- 无效,会导致警告 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
</view>
</template>
<template>
<view>
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:other="otherSlotProps">
...
</template>
</current-user>
</view>
</template>
解构插槽Prop 作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里。这意味着 v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript
表达式。 所以在支持的环境下 ( 单文件组件 或 现代浏览器),你也可以使用 ES2015 解构 来传入具体的插槽 prop
。
<script>
function (slotProps) {
// 插槽内容
}
</script>
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
小程序不支持组件列表
- 作用域插槽(HBuilderX 3.1.19 以下仅支持解构插槽且不可使用作用域外数据以及使用复杂的表达式)
- 动态组件
- 异步组件
inline-template
X-Templates
keep-alive
(App端也未支持)transition
(可使用animation
或 CSS 动画替代)
组件命名限制
在 uni-app 中以下这些作为保留关键字,不可作为组件名。
a
canvas
cell
content
countdown
datepicker
div
element
embed
header
image
img
indicator
input
link
list
loading-indicator
loading
marquee
meta
refresh
richtext
script
scrollable
scroller
select
slider-neighbor
slider
slot
span
spinner
style
svg
switch
tabbar
tabheader
template
text
textarea
timepicker
transition-group
transition
video
view
web
Tips
- 除以上列表中的名称外,标准的 HTML 及 SVG 标签名也不能作为组件名。
- 在百度小程序中使用时,不要在 data 内使用 hidden ,可能会导致渲染错误。
- methods中不可使用与生命周期同名的方法名。
API
全局配置
Vue 全局配置 | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
Vue.config.silent | 取消 Vue 所有的日志与警告 详情 | √ | √ | √ | |
Vue.config.optionMergeStrategies | 自定义合并策略的选项 详情 | √ | √ | √ | |
Vue.config.devtools | 配置是否允许 vue-devtools 检查代码 详情 | √ | x | x | 只在Web环境下支持 |
Vue.config.errorHandler | 指定组件的渲染和观察期间未捕获错误的处理函数 详情 | √ | √ | √ | |
Vue.config.warnHandler | 为 Vue 的运行时警告赋予一个自定义处理函数 详情 | √ | √ | √ | |
Vue.config.ignoredElements | 须使 Vue 忽略在 Vue 之外的自定义元素 详情 | √ | √ | √ | 强烈不推荐,会覆盖uni-app框架配置的内置组件 |
Vue.config.keyCodes | 给 v-on 自定义键位别名 详情 | √ | x | x | |
Vue.config.performance | 设置为 true 以在浏览器开发工具的性能/时间线面板中启用对组件初始化、编译、渲染和打补丁的性能追踪 详情 | √ | x | x | 只在Web环境下支持 |
Vue.config.productionTip | 设置为 false 以阻止 vue 在启动时生成生产提示 详情 | √ | √ | √ | - |
全局API
Vue 全局 API | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
Vue.extend | 使用基础 Vue 构造器,创建一个“子类” 详情 | √ | √ | x | 不可作为组件使用 |
Vue.nextTick | 在下次 DOM 更新循环结束之后执行延迟回调 详情 | √ | x | x | |
Vue.set | 向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新 详情 | √ | √ | √ | |
Vue.delete | 删除对象的 property。如果对象是响应式的,确保删除能触发更新视图 详情 | √ | √ | √ | |
Vue.directive | 注册或获取全局指令 详情 | √ | √ | x | |
Vue.filter | 注册或获取全局过滤器 详情 | √ | √ | x | |
Vue.component | 注册或获取全局组件。注册还会自动使用给定的 id 设置组件的名称 详情 | √ | √ | √ | |
Vue.use | 安装 Vue.js 插件 详情 | √ | √ | √ | |
Vue.mixin | 全局注册一个混入,影响注册之后所有创建的每个 Vue 实例 详情 | √ | √ | √ | nvue 页面暂不支持 |
Vue.version | 提供字符串形式的 Vue 安装版本号 详情 | √ | √ | √ | |
Vue.compile | 将一个模板字符串编译成 render 函数。只在完整版时可用。详情 | √ | x | x | uni-app使用的vue是只包含运行时的版本 |
选项
Vue 选项 | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
data | Vue 实例的数据对象 详情 | √ | √ | √ | |
props | props 可以是数组或对象,用于接收来自父组件的数据 详情 | √ | √ | √ | |
propsData | 创建实例时传递 props。主要作用是方便测试 详情 | √ | √ | √ | |
computed | 计算属性将被混入到 Vue 实例中 详情 | √ | √ | √ | |
methods | methods 将被混入到 Vue 实例中 详情 | √ | √ | √ | |
watch | 一个对象,键是需要观察的表达式,值是对应回调函数 详情 | √ | √ | √ | |
el | 提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标 详情 | √ | x | x | |
template | 一个字符串模板作为 Vue 实例的标识使用 详情 | √ | x | x | uni-app使用的vue是只包含运行时的版本 |
render | 字符串模板的代替方案,该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode。详情 | √ | x | x | |
renderError | 当 render 函数遭遇错误时,提供另外一种渲染输出,只在开发者环境下工作 详情 | √ | x | x | |
directives | 包含 Vue 实例可用指令的哈希表 详情 | √ | √ | x | |
filters | 包含 Vue 实例可用过滤器的哈希表 详情 | √ | √ | √ | |
components | 包含 Vue 实例可用组件的哈希表 详情 | √ | √ | √ | |
parent | 指定已创建的实例之父实例,在两者之间建立父子关系 详情 | √ | √ | √ | 不推荐 |
mixins | 选项接收一个混入对象的数组 详情 | √ | √ | √ | |
extends | 允许声明扩展另一个组件 详情 | √ | √ | √ | |
provide/inject | 允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效 详情 | √ | √ | √ | |
name | 允许组件模板递归地调用自身 详情 | √ | √ | √ | |
delimiters | 改变纯文本插入分隔符 详情 | √ | x | x | |
functional | 使组件无状态 (没有 data) 和无实例 (没有 this 上下文) 详情 | √ | x | x | |
model | 允许一个自定义组件在使用 v-model 时定制 prop 和 event 详情 | √ | √ | x | |
inheritAttrs | inheritAttrs属性默认值为true,表示允许组件的根节点继承$attrs包含的属性 详情 | √ | √ | x | |
comments | 当设为 true 时,将会保留且渲染模板中的 HTML 注释 详情 | √ | x | x | - |
生命周期
生命周期钩子 | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
beforeCreate | 在实例初始化之后被调用 详情 | √ | √ | √ | |
created | 在实例创建完成后被立即调用 详情 | √ | √ | √ | |
beforeMount | 在挂载开始之前被调用 详情 | √ | √ | √ | |
mounted | 挂载到实例上去之后调用 详情 注意:此处并不能确定子组件被全部挂载,如果需要子组件完全挂载之后在执行操作可以使用$nextTick 详情 | √ | √ | √ | |
beforeUpdate | 数据更新时调用,发生在虚拟 DOM 打补丁之前 详情 | √ | √ | √ | |
updated | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子 详情 | √ | √ | √ | |
activated | 被 keep-alive 缓存的组件激活时调用 详情 | √ | √ | x | |
deactivated | 被 keep-alive 缓存的组件停用时调用 详情 | √ | √ | x | |
beforeDestroy | 实例销毁之前调用。在这一步,实例仍然完全可用 详情 | √ | √ | √ | |
destroyed | Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁 详情 | √ | √ | √ | |
errorCaptured | 当捕获一个来自子孙组件的错误时被调用 详情 | √ | √ | √ | - |
页面及组件生命周期流程图 |
实例属性
Vue 实例属性 | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
vm.$data | Vue 实例观察的数据对象 详情 | √ | √ | √ | |
vm.$props | 当前组件接收到的 props 对象 详情 | √ | √ | √ | |
vm.$el | Vue 实例使用的根 DOM 元素 详情 | √ | x | x | |
vm.$options | 用于当前 Vue 实例的初始化选项 详情 | √ | √ | √ | |
vm.$parent | 父实例,如果当前实例有的话 详情 | √ | √ | √ | H5端 view 、text 等内置标签是以 Vue 组件方式实现,$parent 会获取这些到内置组件,导致的问题是 this.$parent 与其他平台不一致,解决方式是使用 this.$parent.$parent 获取或自定义组件根节点由 view 改为 div |
vm.$root | 当前组件树的根 Vue 实例 详情 | √ | √ | √ | |
vm.$children | 当前实例的直接子组件 详情 | √ | √ | √ | H5端 view 、text 等内置标签是以 Vue 组件方式实现,$children 会获取到这些内置组件,导致的问题是 this.$children 与其他平台不一致,解决方式是使用 this.$children.$children 获取或自定义组件根节点由 view 改为 div |
vm.$slots | 用来访问被插槽分发的内容 详情 | √ | √ | √ | |
vm.$scopedSlots | 用来访问作用域插槽 详情 | √ | √ | √ | |
vm.$refs | 一个对象,持有注册过 ref attribute 的所有 DOM 元素和组件实例详情 | √ | √ | √ | 非H5端只能用于获取自定义组件,不能用于获取内置组件实例(如:view、text) |
vm.$isServer | 当前 Vue 实例是否运行于服务器 详情 | √ | √ | x | App端总是返回false |
vm.$attrs | 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 详情 | √ | √ | x | |
vm.$listeners | 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器 详情 | √ | √ | x | - |
实例方法
实例方法 | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
vm.$watch() | 观察 Vue 实例上的一个表达式或者一个函数计算结果的变化 详情 | √ | √ | √ | |
vm.$set() | 这是全局 Vue.set 的别名 详情 | √ | √ | √ | |
vm.$delete() | 这是全局 Vue.delete 的别名 详情 | √ | √ | √ | |
vm.$on() | 监听当前实例上的自定义事件 详情 | √ | √ | √ | |
vm.$once() | 监听一个自定义事件,但是只触发一次 详情 | √ | √ | √ | |
vm.$off() | 移除自定义事件监听器 详情 | √ | √ | √ | |
vm.$emit() | 触发当前实例上的事件 详情 | √ | √ | √ | |
vm.$mount() | 手动地挂载一个未挂载的实例 详情 | √ | x | x | |
vm.$forceUpdate() | 迫使 Vue 实例重新渲染 详情 | √ | √ | √ | |
vm.$nextTick() | 将回调延迟到下次 DOM 更新循环之后执行 详情 | √ | √ | √ | |
vm.$destroy() | 完全销毁一个实例 详情 | √ | √ | √ | - |
模版指令
Vue 指令 | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
v-text | 更新元素的 textContent 详情 | √ | √ | √ | |
v-html | 更新元素的 innerHTML 详情 | √ | √ | x | 微信小程序会被转成 rich-text |
v-show | 根据表达式之真假值,切换元素的 display CSS属性 详情 | √ | √ | √ | |
v-if | 根据表达式的值的 truthiness 来有条件地渲染元素 详情 | √ | √ | √ | |
v-else | 为 v-if 或者 v-else-if 添加“else 块” 详情 | √ | √ | √ | |
v-else-if | 表示 v-if 的“else if 块”。可以链式调用 详情 | √ | √ | √ | |
v-for | 基于源数据多次渲染元素或模板块 详情 | √ | √ | √ | |
v-on | 绑定事件监听器 详情 | √ | √ | √ | |
v-bind | 动态地绑定一个或多个 attribute,或一个组件 prop 到表达式 详情 | √ | √ | √ | |
v-model | 在表单控件或者组件上创建双向绑定 详情 | √ | √ | √ | |
v-pre | 跳过这个元素和它的子元素的编译过程 详情 | √ | √ | x | |
v-cloak | 这个指令保持在元素上直到关联实例结束编译 详情 | √ | x | x | |
v-once | 只渲染元素和组件一次 详情 | √ | √ | x | - |
特殊属性
特殊属性 | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
key | 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes 详情 | √ | √ | √ | |
ref | ref 被用来给元素或子组件注册引用信息 详情 | √ | √ | √ | 非 H5 平台只能获取 vue 组件实例不能获取到内置组件实例 |
is | 用于动态组件且基于 DOM 内模板的限制来工作 详情 | √ | √ (需传入 String 类型) | x | - |
内置组件
内置组件 | 描述 | H5 | App端 | 小程序 | 说明 |
---|---|---|---|---|---|
component | 渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染 详情 | √ | √ | x | |
transition | 作为单个元素/组件的过渡效果 详情 | √ | x | x | |
transition-group | 作为多个元素/组件的过渡效果 详情 | √ | x | x | |
keep-alive | 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们 详情 | √ | x | x | |
slot | 作为组件模板之中的内容分发插槽 详情 | √ | √ | √ | - |
template | 并不是一个组件,它仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性 详情 | √ | √ | √ | - |
全局变量
实现全局变量的方式需要遵循 Vue 单文件模式的开发规范。详细参考:uni-app全局变量的几种实现方式
其他配置
Vue 组件编译到小程序平台的时候会编译为对应平台的组件,部分小程序平台支持 options 选项(具体选项参考对应小程序平台文档的自定义组件部分),一般情况默认即可,如有特殊需求可在 Vue 组件中增加 options 属性。
属性 | 类型 | 默认值 | 描述 | 平台兼容性 |
---|---|---|---|---|
multipleSlots | Boolean | true | 在组件定义时的选项中启动多slot支持 | |
styleIsolation | String | apply-shared | 组件样式隔离方式,具体配置选项参见:组件样式隔离 | 微信小程序 |
addGlobalClass | Boolean | true | 微信小程序 | |
virtualHost | Boolean | false | 将自定义节点设置成虚拟的,更加接近Vue组件的表现。我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等,而是希望自定义组件内部的第一层节点能够响应 flex 布局或者样式由自定义组件本身完全决定,启用后可以通过 mergeVirtualHostAttributes 合并合并组件虚拟节点外层属性 | 支付宝小程序(默认值为 true)、微信小程序、抖音小程序(4.02+) |
常见问题
- 如何获取上个页面传递的数据 在 onLoad 里得到,onLoad 的参数是其他页面打开当前页面所传递的数据。
- 如何设置全局的数据和全局的方法 uni-app 内置了 Vuex ,在app里的使用,可参考
hello-uniapp
store/index.js
。JavaScript//store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state: {...}, mutations: {...}, actions: {...} }) export default store //main.js ... import store from './store' Vue.prototype.$store = store const app = new Vue({ store,... }) ... //test.vue 使用时: import {mapState,mapMutations} from 'vuex'
- 如何捕获 app 的 onError 由于 onError 并不是完整意义的生命周期,所以只提供一个捕获错误的方法,在 app 的根组件上添加名为 onError 的回调函数即可。
- 组件属性设置不生效解决办法 当重复设置某些属性为相同的值时,不会同步到view层。 例如:每次将scroll-view组件的scroll-top属性值设置为0,只有第一次能顺利返回顶部。 这和props的单向数据流特性有关,组件内部scroll-top的实际值改动后,其绑定的属性并不会一同变化。 解决办法有两种(以scroll-view组件为例):
- 监听scroll事件,记录组件内部变化的值,在设置新值之前先设置为记录的当前值。
- 监听scroll事件,获取组件内部变化的值,实时更新其绑定值
状态管理Vuex
介绍
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 uni-app 内置了 Vuex
什么是“状态管理模式”
<!-- view 视图-->
<template>
<view>
{{count}}
</view>
</template>
<script>
export default {
// state 数据源
data() {
return {
count: 0
}
},
// actions 控制状态变化
methods: {
increment() {
this.count++
}
}
}
</script>
这个状态自管理应用包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。 当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。