1. 组合式API
1)setup
组件选项
新的 setup
选项在组件创建之前执行,一旦 props
被解析,就将作为组合式 API 的入口。
使用 setup
函数时,它将接收两个参数:
props
context
因为props
是响应式的,所以我们不能使用 ES6 解构,它会消除 props 的响应性。 如果一定需要使用结构,vue提供了toRefs
方法,来完成此操作
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
如果 title
是可选的 prop,则传入的 props
中可能没有 title
。在这种情况下,toRefs
将不会为 title
创建一个 ref 。你需要使用 toRef
替代它:
import { toRef } from 'vue'
setup(props) {
const title = toRef(props, 'title')
console.log(title.value)
}
context
是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着我们可以安全地对 context
使用 ES6 解构。
export default {
setup(props, context) {
// Attribute (非响应式对象,等同于 $attrs)
console.log(context.attrs)
// 插槽 (非响应式对象,等同于 $slots)
console.log(context.slots)
// 触发事件 (方法,等同于 $emit)
console.log(context.emit)
// 暴露公共 property (函数)
console.log(context.expose)
}
}
2)不能在setup中使用this
因为setup执行在除了props之外的其他组件选项之前的
在 setup
中应该避免使用 this
,因为它不会找到组件实例。setup
的调用发生在 data
property、computed
property 或 methods
被解析之前,所以它们无法在 setup
中被获取。
setup
选项是一个接收 props
和 context
的函数
3)setup中可以使用任何生命周期钩子,需要在前面加 on
前提是需要引入这些钩子函数
4)Vue3 如何跟踪变化的
- 当一个值被读取时进行追踪:proxy 的
get
处理函数中track
函数记录了该 property 和当前副作用。 - 当某个值改变时进行检测:在 proxy 上调用
set
处理函数。 - 重新运行代码来读取原始值:
trigger
函数查找哪些副作用依赖于该 property 并执行它们。
5)带 ref
的响应式变量
import { ref } from 'vue'
const counter = ref(0)
ref解包
Ref 解包仅发生在被响应式 Object
嵌套的时候。当从 Array
或原生集合类型如 Map
访问 ref 时,不会进行解包
6)watch
响应式更改
就像我们在组件中使用 watch
选项并在 user
property 上设置侦听器一样,我们也可以使用从 Vue 导入的 watch
函数执行相同的操作。它接受 3 个参数:
- 一个想要侦听的响应式引用或 getter 函数
- 一个回调
- 可选的配置选项
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
7)独立的 computed
属性
import { ref, computed } from 'vue'
const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)
counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2
2.teleport
Teleport 提供了一种干净的方法,允许我们控制在 DOM 中哪个父节点下渲染了 HTML,而不必求助于全局状态或将其拆分为两个组件。
这仅仅是移动实际的 DOM 节点,而不是被销毁和重新创建,并且它还将保持任何组件实例的活动状态。所有有状态的 HTML 元素 (即播放的视频) 都将保持其状态。
app.component('modal-button', {
template: `
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
`,
data() {
return {
modalOpen: false
}
}
})
简而言之<teleport>
中的to="body"
告诉 Vue Teleport 这个 HTML 将会渲染为该body
标签的子级。
如果 <teleport>
包含 Vue 组件,则它仍将是 <teleport>
父组件的逻辑子组件,即使这个子组件会在不同的地方渲染,但是它仍将是 父组件的子级,并从父组件中接受传参prop
props:
+ to: 必须是有效的查询选择器或 HTMLElement (如果在浏览器环境中使用)。指定将在其中移动 <teleport>
内容的目标元素
+ disabled: 禁用<teleport>
的功能
3.触发组件选项
可以在单个组件实例上创建多个v-model
绑定
组件上的 v-model
使用 modelValue
作为 prop 和 update:modelValue
作为事件。我们可以通过向 v-model
传递参数来修改这些名称
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName"
></user-name>
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
emits: ['update:firstName', 'update:lastName'],
template: `
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)">
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)">
`
})
v-model
自定义修饰符
<my-component v-model.capitalize="myText"></my-component> // 自定义修饰符 capitalize
组件内部通过modelModifiers prop 来承接修饰符
app.component('my-component', {
props: {
modelValue: String,
modelModifiers: { // 组件内部通过modelModifiers prop 来承接修饰符
default: () => ({})
}
},
emits: ['update:modelValue'],
template: `
<input type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)">
`,
created() {
this.modelModifiers.capitalize // 默认值为 true
console.log(this.modelModifiers) // { capitalize: true }
}
})
对于带参数的 v-model
绑定,生成的 prop 名称将为 arg + "Modifiers"
:
<my-component v-model:description.capitalize="myText"></my-component>
app.component('my-component', {
props: ['description', 'descriptionModifiers'],
emits: ['update:description'],
template: `
<input type="text"
:value="description"
@input="$emit('update:description', $event.target.value)">
`,
created() {
console.log(this.descriptionModifiers) // { capitalize: true }
}
})
4.单文件组件<script setup>
<script setup>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 <script>
语法,它具有更多优势:
- 样板内容更少,代码更简洁
- 能够使用纯TS声明props和抛出事件
- 更好的运行时性能(其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)
- 更好的IDE类型推断性能
5.关于provide和inject
vue3 中在setup中使用这两个 也是要
import { provide } from 'vue'
和
import { inject } from 'vue'
来引入的。
provide
函数允许你通过两个参数定义 property:
- name (
<String>
类型) - value
provide('location', 'North Pole')
如果想让location
变为响应式的话,那么在provide这里就要使用ref和reactive使值变为响应式的
修改响应式的话需要在provide组件中修改
一定要在inject中修改的话,那么就从provide传一个修改函数过去,inject使用这个修改函数修改
inject
函数有两个参数:
- 要 inject 的 property 的 name
- 默认值 (可选)
const userLocation = inject('location', 'The Universe')
6. filter过滤器被移除了
- 建议使用计算属性和方法来处理
- 全局的过滤器 可以声明一个
$filter
挂载到全局可以使用
7.一些小改变
-
destroyed
生命周期选项被重命名为unmounted
-
beforeDestroy
生命周期选项被重命名为beforeUnmount
-
data
只接受函数的返回- mixin和组件本身的合并只会是浅拷贝
-
当侦听一个数组时,只有当数组被替换时,回调才会触发,如果需要在变更时触发,则必须指定
deep
选项 -
<template>
如果没有特殊的指令在上面(v-if/else-if/else
、v-for
或v-slot
),现在被视为普通元素,并将渲染为原生的<template>
元素,而不是渲染其内部内容。- 渲染成document-fragment
-
已挂载的应用不会取代他所挂载的元素
- 在 Vue 2.x 中,当挂载一个具有
template
的应用时,被渲染的内容会替换我们要挂载的目标元素。在 Vue 3.x 中,被渲染的应用会作为子元素插入,从而替换目标元素的innerHTML
。
- 在 Vue 2.x 中,当挂载一个具有
-
生命周期的
hook:
事件前缀改为vnode-
vue2中的写法
<template> <child-component @hook:updated="onUpdated"> </template>
vue3中可以有下面的两种写法
<template> <child-component @vnode-updated="onUpdated"> </template>
<template> <child-component @vnodeUpdated="onUpdated"> </template>
8.自定义元素检测现在在模板编译时执行
在 Vue 2.x 中,通过 Vue.config.ignoredElements
将标签配置为自定义元素:
```
// 这将使 Vue 忽略在其外部定义的自定义元素
// (例如:使用 Web Components API)
Vue.config.ignoredElements = ['plastic-button']
```
在vue3中是通过vue-loader
中的配置项来在模板编译时执行检测
```
// webpack 中的配置
rules: [
{
test: /.vue$/,
use: 'vue-loader',
options: {
compilerOptions: {
isCustomElement: tag => tag === 'plastic-button'
}
}
}
// ...
]
```
或者可以设置 app.config.compilerOptions.isCustomElement
```
const app = Vue.createApp({})
app.config.compilerOptions.isCustomElement = tag => tag === 'plastic-button'
```
9.定制内置元素
在 3.0 中,我们将 Vue 对 is
attribute 的特殊处理限制在了 <component>
标签中。
-
在保留的
<component>
标签上使用时,它的行为将与 2.x 中完全相同; -
在普通组件上使用时,它的行为将类似于普通 attribute:
<foo is="bar" />
- 2.x 的行为:渲染
bar
组件。 - 3.x 的行为:渲染
foo
组件,并将is
attribute 传递给它。
- 2.x 的行为:渲染
-
在普通元素上使用时,它将作为
is
attribute 传递给createElement
调用,并作为原生 attribute 渲染。这支持了自定义内置元素的用法。<button is="plastic-button">点击我!</button>
-
2.x 的行为:渲染
plastic-button
组件。 -
3.x 的行为:通过调用以下函数渲染原生的 button
document.createElement('button', { is: 'plastic-button' })
-
**总结: **
动态组件中
- 3.x 只关注
<component>
标签,只有<component>
标签才会把is
传入的值当做组件渲染出来,其余的 只当成is 属性来渲染 - 2.x 中,无论是
<component>
<div>
亦或者是<foo />
自定义组件,只要有is属性,都会把传入的当成组件渲染出来
10.emits选项
子组件的emits需要预先定义,子组件的emits接收一个数组或者对象
11.v-model的用法更改
-
非兼容:用于自定义组件时,
v-model
prop 和事件默认名称已更改:- prop:
value
->modelValue
; - 事件:
input
->update:modelValue
;
- prop:
- 新增:现在可以在同一个组件上使用多个
v-model
绑定; - 新增:现在可以自定义
v-model
修饰符。
2.x的语法
在 2.x 中,在组件上使用 v-model
相当于绑定 value
prop 并触发 input
事件:
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
如果想要更改 prop 或事件名称,则需要在 ChildComponent
组件中添加 model
选项:
<!-- ParentComponent.vue -->
<ChildComponent v-model="pageTitle" />
// ChildComponent.vue
export default {
model: {
prop: 'title',
event: 'change'
},
props: {
// 这将允许 `value` 属性用于其他用途
value: String,
// 使用 `title` 代替 `value` 作为 model 的 prop
title: {
type: String,
default: 'Default title'
}
}
}
3.x的语法
在 3.x 中,自定义组件上的 v-model
相当于传递了 modelValue
prop 并接收抛出的 update:modelValue
事件:
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>
若需要更改 model
的名称,现在我们可以为 v-model
传递一个参数,以作为组件内 model
选项的替代:
<ChildComponent v-model:title="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
这里子组件里面原本的modelValue
就被替换成了title
, 同理,在自定义组件中使用多个v-model
时
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
<!-- 是以下的简写: -->
<ChildComponent
:title="pageTitle"
@update:title="pageTitle = $event"
:content="pageContent"
@update:content="pageContent = $event"
/>
当我们设置自定义修饰符的时候,
<ChildComponent v-model.capitalize="pageTitle" />
在子组件中通过 props
承接一个modelModifiers
对象,而这个capitalize
自定义修饰符就是存在于modelModifiers
对象中的属性,我们需要在子组件模板中的@input
或者重新定义的事件名称的函数中,做一些这个自定义修饰符需要做的事情
12.<template v-for>
语法
<!-- Vue 2.x -->
<template v-for="item in list">
<div :key="'heading-' + item.id">...</div>
<span :key="'content-' + item.id">...</span>
</template>
<!-- Vue 3.x key应被设置在template标签上 -->
<template v-for="item in list" :key="item.id">
<div>...</div>
<span>...</span>
</template>
13.v-for 和v-if 使用在同一个元素上时,优先级修改
首先是不推荐这么使用,但是有时候我们不得不这么用
- 2.x中 v-for的优先级更高
- 3.x中 v-if的优先级更高