从vue2迁移到vue3,细节处有啥变化?
从vue2迁移到vue3,细节处有啥变化?
一、说明
其实vue3大部分的知识点和vue2都是一样的,这篇文章是介绍vue2中部分属性在vue3中的变化,以及vue3新增加的一些比较重要的 东西。 |
---|
二、子传父时使用的emit
- 在 Vue 2 中,你可以定义一个组件可接收的 prop,但是你无法声明它可以触发哪些事件
<template>
<div>
<p>{{ text }}</p>
<button v-on:click="$emit('accepted')">OK</button>
</div>
</template>
<script>
export default {
props: ['text']
}
</script>
- 在vue3中,和 prop 类似,现在可以通过 emits 选项来定义组件可触发的事件
<template>
<div>
<p>{{ text }}</p>
<button v-on:click="$emit('accepted')">OK</button>
</div>
</template>
<script>
export default {
props: ['text'],
emits: ['accepted']
}
</script>
该选项也可以接收一个对象,该对象允许开发者定义传入事件参数的验证器,和 props 定义里的验证器类似
举个例子
我们封装一个CounterOperation.vue的组件
内部其实是监听两个按钮的点击,点击之后通过 this.$emit的方式发出去事件
- 自定义事件的时候,我们也可以传递一些参数给父组件
- 在vue3当中,我们可以对传递的参数进行验证
三、$children
在 2.x 中,开发者可以使用 this.$children 访问当前实例的直接子组件:
<template>
<div>
<img alt="Vue logo" src="./assets/logo.png">
<my-button>Change logo</my-button>
</div>
</template>
<script>
import MyButton from './MyButton'
export default {
components: {
MyButton
},
mounted() {
console.log(this.$children) // [VueComponent]
}
}
</script>
在Vue3中已经移除了$children的属性,所以不可以使用了。如果你需要访问子组件实例,我们建议使用 $refs
|
---|
四、异步组件
-
新的
defineAsyncComponent
助手方法,用于显式地定义异步组件 -
component 选项被重命名为 loader
-
Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise
在vue2.x中 -
异步组件是通过将组件定义为返回 Promise 的函数来创建的,例如:
const asyncModal = () => import('./Modal.vue')
- 或者,对于带有选项的更高阶的组件语法
const asyncModal = {
component: () => import('./Modal.vue'),
delay: 200,
timeout: 3000,
error: ErrorComponent,
loading: LoadingComponent
}
在 Vue 3 中
- 由于函数式组件被定义为纯函数,因此异步组件需要通过将其包裹在新的
defineAsyncComponent
助手方法中来显式地定义
import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'
// 不带选项的异步组件
const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))
// 带选项的异步组件
const asyncModalWithOptions = defineAsyncComponent({
loader: () => import('./Modal.vue'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
与 2.x 不同,loader 函数不再接收 resolve 和 reject 参数,且必须始终返回 Promise。
// 2.x 版本
const oldAsyncComponent = (resolve, reject) => {
/* ... */
}
// 3.x 版本
const asyncComponent = defineAsyncComponent(
() =>
new Promise((resolve, reject) => {
/* ... */
})
)
五、$attrs包含class&style
$attrs 现在包含了所有传递给组件的 attribute,包括 class 和 style |
---|
2.x 行为
- Vue 2 的虚拟 DOM 实现对 class 和 style attribute 有一些特殊处理。因此,与其它所有 attribute 不一样,它们没有被包含在 $attrs 中。
- 上述行为在使用 inheritAttrs: false 时会产生副作用
- $attrs 中的 attribute 将不再被自动添加到根元素中,而是由开发者决定在哪添加
- 但是 class 和 style 不属于 $attrs,它们仍然会被应用到组件的根元素中
<template>
<label>
<input type="text" v-bind="$attrs" />
</label>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
像这样使用时:
<my-component id="my-id" class="my-class"></my-component>
将生成以下 HTML:
<label class="my-class">
<input type="text" id="my-id" />
</label>
3.x 行为
$attrs 包含了所有的 attribute,这使得把它们全部应用到另一个元素上变得更加容易了。现在上面的示例将生成以下 HTML
<label>
<input type="text" id="my-id" class="my-class" />
</label>
六、自定义指令
2.x 语法
- 在 Vue 2 中,自定义指令通过使用下列钩子来创建,以对齐元素的生命周期,它们都是可选的
- bind - 指令绑定到元素后调用。只调用一次
- inserted - 元素插入父 DOM 后调用。
- update - 当元素更新,但子元素尚未更新时,将调用此钩子
- componentUpdated - 一旦组件和子级被更新,就会调用这个钩子
- unbind - 一旦指令被移除,就会调用这个钩子。也只调用一次
下面是一个例子:
<p v-highlight="'yellow'">以亮黄色高亮显示此文本</p>
Vue.directive('highlight', {
bind(el, binding, vnode) {
el.style.background = binding.value
}
})
此处,在这个元素的初始设置中,通过给指令传递一个值来绑定样式,该值可以在应用中任意更改
3.x 语法
然而,在 Vue 3 中,我们为自定义指令创建了一个更具凝聚力的 API。正如你所看到的,它们与我们的组件生命周期方法有很大的不同,即使钩子的目标事件十分相似。我们现在把它们统一起来了 |
---|
- created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
- bind → beforeMount
- inserted → mounted
- beforeUpdate:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
- update → 移除!该钩子与 updated 有太多相似之处,因此它是多余的。请改用 updated。
- componentUpdated → updated
- beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。
- unbind -> unmounted
- 最终的 API 如下:
const MyDirective = {
created(el, binding, vnode, prevVnode) {}, // 新增
beforeMount() {},
mounted() {},
beforeUpdate() {}, // 新增
updated() {},
beforeUnmount() {}, // 新增
unmounted() {}
}
因此,API 可以这样使用,与前面的示例相同
<p v-highlight="'yellow'">以亮黄色高亮显示此文本</p>
const app = Vue.createApp({})
app.directive('highlight', {
beforeMount(el, binding, vnode) {
el.style.background = binding.value
}
})
边界情况:访问组件实例
- 在 Vue 2 中,必须通过 vnode 参数访问组件实例:
bind(el, binding, vnode) {
const vm = vnode.context
}
- 在 Vue 3 中,实例现在是 binding 参数的一部分:
mounted(el, binding, vnode) {
const vm = binding.instance
}
七、Data 选项
组件选项 data 的声明不再接收纯 JavaScript object,而是接收一个 function |
---|
当合并来自 mixin 或 extend 的多个 data 返回值时,合并操作现在是浅层次的而非深层次的 (只合并根级属性) |
---|
2.x 语法
- 在 2.x 中,开发者可以通过 object 或者是 function 定义 data 选项
<!-- Object 声明 -->
<script>
const app = new Vue({
data: {
apiKey: 'a1b2c3'
}
})
</script>
<!-- Function 声明 -->
<script>
const app = new Vue({
data() {
return {
apiKey: 'a1b2c3'
}
}
})
</script>
虽然这种做法对于具有共享状态的根实例提供了一些便利,但是由于其只可能存在于根实例上,因此变得混乱
3.x 更新
在 3.x 中,data 选项已标准化为只接受返回 object 的 function
- 使用上面的示例,代码只可能有一种实现
<script>
import { createApp } from 'vue'
createApp({
data() {
return {
apiKey: 'a1b2c3'
}
}
}).mount('#app')
</script>
Mixin 合并行为变更
此外,当来自组件的 data() 及其 mixin 或 extends 基类被合并时,合并操作现在将被浅层次地执行 |
---|
const Mixin = {
data() {
return {
user: {
name: 'Jack',
id: 1
}
}
}
}
const CompA = {
mixins: [Mixin],
data() {
return {
user: {
id: 2
}
}
}
}
在 Vue 2.x 中,生成的 $data 是:
{
"user": {
"id": 2,
"name": "Jack"
}
}
在 3.0 中,其结果将会是:
{
"user": {
"id": 2
}
}
八、事件 API
$on ,$off 和 $once 实例方法已被移除,组件实例不再实现事件触发接口 |
---|
2.x 语法
- 在 2.x 中,Vue 实例可用于触发由事件触发器 API 通过指令式方式添加的处理函数 (
$on
,$off
和$once
)。这可以用于创建一个事件总线,以创建在整个应用中可用的全局事件监听器:
// eventBus.js
const eventBus = new Vue()
export default eventBus
// ChildComponent.vue
import eventBus from './eventBus'
export default {
mounted() {
// 添加 eventBus 监听器
eventBus.$on('custom-event', () => {
console.log('Custom event triggered!')
})
},
beforeDestroy() {
// 移除 eventBus 监听器
eventBus.$off('custom-event')
}
}
// ParentComponent.vue
import eventBus from './eventBus'
export default {
methods: {
callGlobalCustomEvent() {
eventBus.$emit('custom-event') // 当 ChildComponent 已被挂载时,控制台中将显示一条消息
}
}
}
3.x 更新
我们从实例中完全移除了$on、$off 和 $once 方法。$emit 仍然包含于现有的 API 中 ,因为它用于触发由父组件声明式添加的事件处理函数 |
---|
事件总线
- 事件总线模式可以被替换为使用外部的、实现了事件触发器接口的库,例如
mitt
或tiny-emitter
// eventBus.js
import emitter from 'tiny-emitter/instance'
export default {
$on: (...args) => emitter.on(...args),
$once: (...args) => emitter.once(...args),
$off: (...args) => emitter.off(...args),
$emit: (...args) => emitter.emit(...args),
}
它提供了与 Vue 2 相同的事件触发器 API
-
在绝大多数情况下,
不鼓励使用全局的事件总线在组件之间进行通信
。虽然在短期内往往是最简单的解决方案,但从长期来看,它维护起来总是令人头疼。根据具体情况来看,有多种事件总线的替代方案: -
Prop
和事件
应该是父子组件之间沟通的首选。兄弟节点可以通过它们的父节点通信。 -
Provide
和inject
允许一个组件与它的插槽内容进行通信。这对于总是一起使用的紧密耦合的组件非常有用。 -
provide/inject 也能够用于组件之间的远距离通信
。它可以帮助避免“prop 逐级透传”,即 prop 需要通过许多层级的组件传递下去,但这些组件本身可能并不需要那些 prop。 -
Prop 逐级透传也可以通过重构以使用插槽来避免。如果一个中间组件不需要某些 prop,那么表明它可能存在关注点分离的问题。在该类组件中使用 slot 可以允许父节点直接为它创建内容,因此 prop 可以被直接传递而不需要中间组件的参与。
-
全局状态管理
,比如Vuex
。
九、过滤器
- 从 Vue 3.0 开始,过滤器已
移除
,且不再支持
2.x 语法
在 2.x 中,开发者可以使用过滤器来处理通用文本格式
<template>
<h1>Bank Account Balance</h1>
<p>{{ accountBalance | currencyUSD }}</p>
</template>
<script>
export default {
props: {
accountBalance: {
type: Number,
required: true
}
},
filters: {
currencyUSD(value) {
return '$' + value
}
}
}
</script>
3.x 更新
在 3.x 中,过滤器已移除,且不再支持。取而代之的是,我们建议用方法调用或计算属性来替换它们。
以上面的案例为例,以下是一种实现方式
template>
<h1>Bank Account Balance</h1>
<p>{{ accountInUSD }}</p>
</template>
<script>
export default {
props: {
accountBalance: {
type: Number,
required: true
}
},
computed: {
accountInUSD() {
return '$' + this.accountBalance
}
}
}
全局过滤器
- 如果在应用中全局注册了过滤器,那么在每个组件中用计算属性或方法调用来替换它可能就没那么方便了。
- 取而代之的是,你可以通过全局属性以让它能够被所有组件使用到
// main.js
const app = createApp(App)
app.config.globalProperties.$filters = {
currencyUSD(value) {
return '$' + value
}
}
- 然后,可以通过这个
$filters
对象修正所有的模板,就像这样
<template>
<h1>Bank Account Balance</h1>
<p>{{ $filters.currencyUSD(accountBalance) }}</p>
</template>
注意,这种方式只适用于方法,而不适用于计算属性,因为后者只有在单个组件的上下文中定义时才有意义。
十、片段
Vue 3 现在正式支持了多根节点的组件,也就是片段!
2.x 语法
- 在 2.x 中,由于不支持多根节点组件,当其被开发者意外地创建时会发出警告。结果是,为了修复这个问题,许多组件被包裹在了一个
<div>
中
<!-- Layout.vue -->
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
3.x 语法
- 在 3.x 中,组件可以包含多个根节点!但是,这要求开发者显式定义 attribute 应该分布在哪里
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
十一、key Attribute
-
新增
:对于 v-if/v-else/v-else-if 的各分支项 key 将不再是必须的,因为现在 Vue 会自动生成唯一的 key - 如果你手动提供 key,那么每个分支必须使用唯一的 key。你将不再能通过故意使用相同的 key 来强制重用分支
<template v-for> 的 key 应该设置在 <template> 标签上 (而不是设置在它的子节点上)
特殊的 key attribute 被作为 Vue 的虚拟 DOM 算法的提示,以保持对节点身份的持续跟踪。这样 Vue 就可以知道何时能够重用和修补现有节点,以及何时需要对它们重新排序或重新创建 |
---|
在条件分支中
- 在 Vue 2.x 中,建议在 v-if/v-else/v-else-if 的分支中使用 key。
<!-- Vue 2.x -->
<div v-if="condition" key="yes">Yes</div>
<div v-else key="no">No</div>
- 这个示例在 Vue 3.x 中仍能正常工作。但是我们不再建议在 v-if/v-else/v-else-if 的分支中继续使用 key attribute,因为没有为条件分支提供 key 时,也会自动生成唯一的 key
<!-- Vue 3.x -->
<div v-if="condition">Yes</div>
<div v-else>No</div>
结合 <template v-for>
在 Vue 2.x 中,<template>
标签不能拥有 key。不过,你可以为其每个子节点分别设置 key
<!-- 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>
标签上
<!-- Vue 3.x -->
<template v-for="item in list" :key="item.id">
<div>...</div>
<span>...</span>
</template>
类似地,当使用 <template v-for>
时如果存在使用 v-if 的子节点,则 key 应改为设置在<template>
标签上
<!-- Vue 2.x -->
<template v-for="item in list">
<div v-if="item.isVisible" :key="item.id">...</div>
<span v-else :key="item.id">...</span>
</template>
<!-- Vue 3.x -->
<template v-for="item in list" :key="item.id">
<div v-if="item.isVisible">...</div>
<span v-else>...</span>
</template>
十二、按键修饰符
- 不再支持使用数字 (即键码) 作为 v-on 修饰符
- 不再支持 config.keyCodes
2.x 语法
在 Vue 2 中,keyCodes 可以作为修改 v-on 方法的一种方式
<!-- 键码版本 -->
<input v-on:keyup.13="submit" />
<!-- 别名版本 -->
<input v-on:keyup.enter="submit" />
此外,也可以通过全局的 config.keyCodes 选项定义自己的别名
Vue.config.keyCodes = {
f1: 112
}
<!-- 键码版本 -->
<input v-on:keyup.112="showHelpText" />
<!-- 自定义别名版本 -->
<input v-on:keyup.f1="showHelpText" />
3.x 语法
- 从 KeyboardEvent.keyCode 已被废弃开始,Vue 3 继续支持这一点就不再有意义了。因此,现在建议对任何要用作修饰符的键使用 kebab-cased (短横线) 名称。
<!-- Vue 3 在 v-on 上使用按键修饰符 -->
<input v-on:keyup.page-down="nextPage">
<!-- 同时匹配 q 和 Q -->
<input v-on:keypress.q="quit">
十三、移除$listeners
- $listeners 对象在 Vue 3 中已被移除。事件监听器现在是 $attrs 的一部分:
2.x 语法 在 Vue 2 中,你可以通过 this.$attrs 访问传递给组件的 attribute,以及通过 this.$listeners 访问传递给组件的事件监听器。结合 inheritAttrs: false,开发者可以将这些 attribute 和监听器应用到根元素之外的其它元素:
<template>
<label>
<input type="text" v-bind="$attrs" v-on="$listeners" />
</label>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
3.x 语法
- 在 Vue 3 的虚拟 DOM 中,事件监听器现在只是以 on 为前缀的 attribute,这样它就成为了 $attrs 对象的一部分,因此 $listeners 被移除了
<template>
<label>
<input type="text" v-bind="$attrs" />
</label>
</template>
<script>
export default {
inheritAttrs: false
}
</script>
十四、过渡的 class 名更改
-
过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from
2.x 语法 -
在 v2.1.8 版本之前,每个过渡方向都有两个过渡类:初始状态与激活状态。
-
在 v2.1.8 版本中,引入了 v-enter-to 来定义 enter 或 leave 变换之间的过渡动画插帧。然而,为了向下兼容,并没有变动 v-enter 类名:
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
3.x 语法
- 为了更加明确易读,我们现在将这些初始状态重命名为:
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
十五、Transition 作为根节点
-
当使用 <transition> 作为根结点的组件从外部被切换时将不再触发过渡效果。
2.x 行为
在 Vue 2 中,通过使用<transition>
作为一个组件的根节点,过渡效果存在从组件外部触发的可能性
<!-- 模态组件 -->
<template>
<transition>
<div class="modal"><slot/></div>
</transition>
</template>
<!-- 用法 -->
<modal v-if="showModal">hello</modal>
- 切换 showModal 的值将会在模态组件内部触发一个过渡效果
这是无意为之的,并不是设计效果。一个 <transition> 原本是希望被其子元素触发的,而不是 <transition> 自身
- 这个怪异的现象现在被移除了。
- 换做向组件传递一个 prop 就可以达到类似的效果:
<template>
<transition>
<div v-if="show" class="modal"><slot/></div>
</transition>
</template>
<script>
export default {
props: ['show']
}
</script>
<!-- 用法 -->
<modal :show="showModal">hello</modal>
十六、移除v-on.native修饰符
- v-on 的 .native 修饰符已被移除
2.x 语法
- 默认情况下,传递给带有 v-on 的组件的事件监听器只能通过 this.$emit 触发。要将原生 DOM 监听器添加到子组件的根元素中,可以使用 .native 修饰符
<my-component
v-on:close="handleComponentEvent"
v-on:click.native="handleNativeClickEvent"
/>
3.x 语法
- v-on 的 .native 修饰符已被移除。同时,新增的 emits 选项允许子组件定义真正会被触发的事件。
- 因此,对于子组件中未被定义为组件触发的所有事件监听器,Vue 现在将把它们作为原生事件监听器添加到子组件的根元素中 (除非在子组件的选项中设置了 inheritAttrs: false)。
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>
MyComponent.vue
<script>
export default {
emits: ['close']
}
</script>
十七、v-model
- 以下是对变化的总体概述:
- 用于
自定义组件
时,v-model prop 和事件默认名称已更改
- prop:value -> modelValue
- 事件:input -> update:modelValue
- v-bind 的 .sync 修饰符和组件的 model 选项已移除,可在 v-model 上加一个参数代替;
-
新增
:现在可以在同一个组件上使用多个 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'
}
}
}
所以,在这个例子中 v-model 是以下的简写:
<ChildComponent :title="pageTitle" @change="pageTitle = $event" />
使用 v-bind.sync
- 在某些情况下,我们可能需要对某一个 prop 进行“双向绑定”(除了前面用 v-model 绑定 prop 的情况)。为此,我们建议使用 update:myPropName 抛出事件。例如,对于在上一个示例中带有 title prop 的 ChildComponent,我们可以通过下面的方式将分配新 value 的意图传达给父级:
this.$emit('update:title', newValue)
然后父组件可以在需要时监听该事件,并更新本地的 data property。例如
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
为了方便起见,我们可以使用 .sync 修饰符来缩写,如下所示
<ChildComponent :title.sync="pageTitle" />
3.x 语法
- 在 3.x 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>
v-model 参数
- 若需要更改 model 的名称,现在我们可以为 v-model 传递一个参数,以作为组件内 model 选项的替代:
<ChildComponent v-model:title="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
- 这也可以作为 .sync 修饰符的替代,而且允许我们在自定义组件上使用多个 v-model。
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
<!-- 是以下的简写: -->
<ChildComponent
:title="pageTitle"
@update:title="pageTitle = $event"
:content="pageContent"
@update:content="pageContent = $event"
/>
v-model 修饰符
- 除了像 .trim 这样的 2.x 硬编码的 v-model 修饰符外,现在 3.x 还支持自定义修饰符:
<ChildComponent v-model.capitalize="pageTitle" />
十八、v-if 与 v-for 的优先级对比
2.x 语法
- 2.x 版本中在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用
3.x 语法
- 3.x 版本中 v-if 总是优先于 v-for 生效。
十九、v-bind 合并行为
2.x 语法
- 在 2.x 中,如果一个元素同时定义了 v-bind=“object” 和一个相同的独立 attribute,那么这个
独立 attribute 总是会覆盖 object 中的绑定
。
<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="red"></div>
3.x 语法
- 在 3.x 中,如果一个元素同时定义了 v-bind=“object” 和一个相同的独立 attribute,那么
绑定的声明顺序将决定它们如何被合并
。换句话说,相对于假设开发者总是希望独立 attribute 覆盖 object 中定义的内容,现在开发者能够对自己所希望的合并行为做更好的控制。
<!-- 模板 -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- 结果 -->
<div id="blue"></div>
<!-- 模板 -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- 结果 -->
<div id="red"></div>
二十、侦听数组
-
当侦听一个数组时,只有当数组被替换时才会触发回调。如果你需要在数组被改变时触发回调,必须指定 deep 选项
3.x 语法 - 当使用 watch 选项侦听数组时,只有在
数组被替换时
才会触发回调
。换句话说,在数组被改变时侦听回调将不再被触发
。要想在数组被改变时触发侦听回调,必须指定 deep
选项。
watch: {
bookList: {
handler(val, oldVal) {
console.log('book list changed')
},
deep: true
},
}
二十一、生命周期
生命周期钩子 beforeDestroy 和 destroyed 已经分别被重命名为 beforeUnmount 和 unmounted |
---|