从vue2到vue3,一文带你看完vue3的新特性

前言

这篇文章的主要意义是:针对想要从vue2快速迁移到vue3的朋友,快速理解vue3的新特性。

为了让大家更好的理解,我会从设计者的角度来讲为什么vue3要这样设计api。

目录

前言

第一部分、vue2的痛点——代码糅杂

第二部分、Vue3的组合式API(Composition API)

一、setup

1.setup的定义

2.setup的语法

1)写法1:vue3.0,setup函数

2)写法2:vue3.2以上,setup语法糖

3.setup注意点:

1)setup中没有this

二、响应式对象的绑定——reactive、ref

1.ref

2.reactive

3.reactive与ref的使用细节

4.toRef和toRefs:将对象的一个或多个属性转换为ref

1)toRef

2)toRefs

5.vue2.x 与 vue3.x 响应式(重要)

三、计算属性与监听

1.computed

2.watch 与 watchEffect 的用法

1)侦听 reactive 定义的数据

2)侦听 ref 定义的数据

3)侦听多个数据

4)侦听复杂的嵌套对象

5)stop 停止监听

四、v-model

1.v-model 变更

2.v-model 的update事件

3.v-model 参数

五、组件通信

1.父传子:props

2.子传父:emit

3.祖孙通信:provide 与 inject

4.子组件ref变量和defineExpose

五、自定义可复用的功能函数:Hooks

六、生命周期

第三部分、其他旧语法变更

 一、插槽slot

1.具名插槽

2.作用域插槽

二、nextTick

三、路由

1.路由useRoute和useRouter

2.路由导航守卫

四、Vuex

store

第四部分、vue3新组件

一、Fragment(片断)

二、Suspense(异步组件)

三、Teleport(传送门)

1.Teleport 是什么呢?

2.Teleport 的使用

第五部分、其他新增特性

一、css变量注入


vue3新特性思维导图:从vue2到vue3,一文带你看完vue3的新特性_第1张图片

第一部分、vue2的痛点——代码糅杂

从vue2到vue3,最大的改动就是组合式API——setup。

为什么要这样改?

在vue2项目中,当一个组件的代码量超过300行时,业务逻辑分散的问题很容易出现。比如下面这张图,大家可以直观的看到代码交叉在一起,错综复杂。

新增或者修改一个需求,就需要分别在data,methods,computed里修改 ,滚动条反复上下移动

尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。

同时,如果想把一段业务代码从中抽离出来,要花不少的时间——你要不停找和业务相关的变量、方法,有一些地方复制漏了还容易出bug(不说了,都是血与泪o(╥﹏╥)o)

特别是大项目的时候,随随便便就是几百个组件,我们需要共享和重用代码,但是却vue2的语法导致很难抽离业务代码复用。

那我们就想啊, 如果可以按照逻辑进行分割,将上面这张图变成下边这张图,是不是就清晰很多了呢, 这样的代码可读性和可维护性都更高:

针对这个问题,虽然vue2.x 版本给出的解决方案—— Mixin, 但是使用 Mixin 也会遇到让人苦恼的问题:

  1. 命名冲突问题
  2. 不清楚暴露出来的变量的作用
  3. 逻辑重用到其他 component 经常遇到问题

好,问题来了,如果你是设计者,你如何解决代码的共享和复用的问题??

我打个分割线,你可以思考几分钟。



好了,公布答案:在语法上,允许把业务逻辑写在单独的js文件里,就可以解决了。

而vue3也是这样做的,Vue3直接抄了一手React1.68版本推出的「hook」「hook」的含义就是上面提到的——需要大量复用的业务函数「hook」的作用类似于vue2中的mixin技术,但是功能更强。

说简单点——「hook」就是具有高度复用性的业务js文件。

那么问题来了,如果要允许把业务逻辑写在单独的js文件里,你就不能像原来一样,变量必须写在vue文件的data里,方法必须写在vue文件的methods里,否则就没办法把代码抽离出去。

因此,我们就需要一套新的语法,「hook」的实现就是这套新的语法——Vue3的组合式API

第二部分、Vue3的组合式API(Composition API)

一、setup

1.setup的定义

setup 是 Vue3.x 新增的一个选项, 他是组件内使用组合式API的入口, 所有的组合API函数都在此使用, 只在初始化时执行一次

setup的意义:允许你把vue2中的data的变量、method的方法、compute、watch等等API,直接写在JS里。

2.setup的语法

setup有两种语法,分别是vue3.0的setup函数版,和vue3.2以上的setup语法糖版,我们一个个来看。

1)写法1:vue3.0,setup函数

为方便大家理解,我把vue2.0的写法也贴上去了,setup写法看下方代码:



setup函数的注意点:

①声明变量需要ref、reactive

作为vue2.0data的语法的代替,vue3提供了新的响应式变量声明方法——ref、reactive,这个后面会详细讲。

暴露变量必须 return 

setup方法一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法。

所以,最后要把注册的对象和方法 return 给模板,这个不要忘了。如果你发现模板的变量没有正确更新,可以检查一下这个属性有没有return。

③method绑定函数的语法变化

原来在method绑定函数的语法,到vue3中变为了,在setup内声明函数,然后return即可。

④setup方法的参数 

setup中不能访问 Vue2 中最常用的this对象(后面会讲),这就带来了一个问题:

在vue2语法中,组件的this.props、子组件的this.$refs.xxx,原来这些依赖this对象的api就要调整。

vue3把这些依赖this对象的api,放在了setup 参数里,使用setup时,它接受两个参数:

参数1:props:

组件传入的属性,setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

注意:因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。

如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作:

import { toRefs } from 'vue'

setup(props) {
  const { title } = toRefs(props)

  console.log(title.value)
}

如果 title 是可选的 prop,则传入的 props 中可能没有 title 。在这种情况下,toRefs 将不会为 title 创建一个 ref 。你需要使用 toRef (比toRefs少了个s)替代它:

import { toRef } from 'vue'
setup(props) {
  const title = toRef(props, 'title')
  console.log(title.value)
}

参数2:context

接下来说一下第二个参数context,我们前面说了setup中不能访问 Vue2 中最常用的this对象,所以context中就提供了this中最常用的三个属性:attrs、slot 和emit,分别对应 Vue2.x 中的 $attr属性、slot插槽 和$emit发射事件。并且这几个属性都是自动同步最新的值,所以我们每次使用拿到的都是最新值。

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)写法2:vue3.2以上,setup语法糖

Vue3.2 中 只需要在 script 标签上加上 setup 属性,组件在编译的过程中代码运行的上下文是在 setup() 函数中,无需return,template可直接使用。

要使用这个语法,需要将 setup attribute 添加到 

里面的代码会被编译成组件 setup() 函数的内容。这意味着与普通的 

②引入的组件自动注册

import 导入的内容也会以同样的方式暴露。意味着可以在模板表达式中直接使用导入的 helper 函数,并不需要通过 methods 选项来暴露它:



③获取props、emit从函数参数改成了引入API

之前setup内获取props、emit是通过setup函数参数传递的,由于移除了setup方法,这些参数的获取换成了APIdefineProps 和 defineEmits,具体使用会在后面props、emit修改中讲到。

3.setup注意点:

1)setup中没有this

在 setup() 内部,this 不是该活跃实例的引用,因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同。这使得 setup() 在和其它选项式 API 一起使用时可能会导致混淆。

2)setup的执行时机

我在学习过程中看到很多文章都说 setup 是在 beforeCreate和created之间, 这个结论是错误的。实践是检验真理的唯一标准, 于是自己去检验了一下:

export default defineComponent({
  beforeCreate() {
    console.log("----beforeCreate----");
  },
  created() {
    console.log("----created----");
  },
  setup() {
    console.log("----setup----");
  },
});

从vue2到vue3,一文带你看完vue3的新特性_第2张图片

setup 执行时机是在 beforeCreate 之前执行,详细的可以看后面生命周期讲解。

二、响应式对象的绑定——reactive、ref

接下来要讲vue3组合式API中2个最重要的响应式API——reactive和ref,这两兄弟的作用是什么呢?

实际上就是vue2中的data,定义出响应式的对象。要知道,在setup函数里,如果用var随便定义一个变量,然后return给模板,是无法实现模板驱动视图的,即你修改了变量的值,视图不会跟着变,所以就需要一个生产响应式对象的工厂函数——也就是ref,vue称为这个对象为ref对象。

下面一个个的来看:

1.ref

  • 作用: 定义一个数据的响应式
  • 语法: const xxx = ref(initValue):
    • 创建一个包含响应式数据的引用(reference)对象
    • js中操作数据: xxx.value
    • 模板中操作数据: 不需要.value
  • 一般用来定义一个基本类型的响应式数据
  • 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象。
const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

值得注意的是, ref 不仅可以实现响应式,还可以用于模板的DOM元素

语法:

//HTML:
/"inputRef"是template中,元素设置的ref值
const inputRef = ref(null)

我们用一段代码来演示一下:




复制代码

此时浏览器的显示效果如下所示:

从vue2到vue3,一文带你看完vue3的新特性_第3张图片

我们通过在模板中绑定一个 ref ,之后在生命周期中调用,最后浏览器显示出该 DOM 元素。所以说, ref 也可以用来渲染模板中的DOM元素

2.reactive

  • 作用: 定义对象的响应式
  • const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理器对象
  • 不能用于基本数据类型,例如字符串、数字、boolean 等,方法参数只能json或对象。
  • 响应式转换是“深层的”:会影响对象内部所有嵌套的属性。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象(proxy)操作源对象内部数据都是响应式的。
  • reactive 注册的对象本身是响应式的,但reactive 对象取出的所有属性值都是非响应式的,这个时候就需要用到toRefs函数,后面会讲相应的案例。


3.reactive与ref的使用细节

  • ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式),不懂基本类型数据和引用数据类型的区别戳这里看原理
  • 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象

4.toRef和toRefs:将对象的一个或多个属性转换为ref

1)toRef

作用:可以用来为源响应式对象上的一个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3

应用:当你要将 prop 的 ref 传递给复合函数时,toRef 很有用:

问题:props作为参数接受时,是非响应式的。

解决:利用 toRef方法需要把它变成响应式的。

export default {
  setup(props) {
    useSomeFeature(toRef(props, 'foo'))
  }
}

即使源 property 不存在,toRef 也会返回一个可用的 ref。这使得它在使用可选 prop 时特别有用,可选 prop 并不会被 toRefs 处理。

2)toRefs

作用:将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。

应用:当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用

问题:reactive 对象取出的所有属性值都是非响应式的(reactive 注册的对象本身是响应式的,只是他的属性不是响应式,别搞混了)

解决:利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性




注意:toRefs 只会为源对象中包含的 property 生成 ref。如果要为特定的 property 创建 ref,则应当使用 toRef

5.vue2.x 与 vue3.x 响应式(重要)

        其实在 Vue3.x 还没有发布 bate 的时候, 很火的一个话题就是Vue3.x 将使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty。没有无缘无故的爱,也没有无缘无故的恨。为何要将Object.defineProperty换掉呢,咋们可以简单聊一下。我刚上手 Vue2.x 的时候就经常遇到一个问题,数据更新了啊,为何页面不更新呢?什么时候用$set更新,什么时候用$forceUpdate强制更新,你是否也一度陷入困境。后来的学习过程中开始接触源码,才知道一切的根源都是 Object.defineProperty。对这块想要深入了解的小伙伴可以看这篇文章 为什么 Vue3.0 不再使用 defineProperty 实现数据监听?

        要详细解释又是一篇文章,这里就简单对比一下Object.defineProperty 与 Proxy

        1.Object.defineProperty只能劫持对象的属性, 而 Proxy 是直接代理对象

由于Object.defineProperty只能劫持对象属性,需要遍历对象的每一个属性,如果属性值也是对象,就需要递归进行深度遍历。但是 Proxy 直接代理对象, 不需要遍历操作

        2.Object.defineProperty对新增属性需要手动进行Observe

        因为Object.defineProperty劫持的是对象的属性,所以新增属性时,需要重新遍历对象, 对其新增属性再次使用Object.defineProperty进行劫持。也就是 Vue2.x 中给数组和对象新增属性时,需要使用$set才能保证新增的属性也是响应式的, $set内部也是通过调用Object.defineProperty去处理的。

想了解更详细的内容戳这里。

三、计算属性与监听

1.computed

与vue2.0的computed功能一致,只是使用方式变为了API引入,有两种形态:

  1. 只有getter
  2. 有getter和setter

2.watch 与 watchEffect 的用法

watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是惰性的,也就是说仅在侦听的源数据变更时才执行回调。

watch(source, callback, [options])

参数说明:

  • source: 可以支持 string,Object,Function,Array; 用于指定要侦听的响应式变量
  • callback: 执行的回调函数
  • options:支持 deep、immediate 和 flush 选项。

接下来我会分别介绍这个三个参数都是如何使用的, 如果你对 watch 的使用不明白的请往下看:

1)侦听 reactive 定义的数据

import { defineComponent, ref, reactive, toRefs, watch } from "vue";
export default defineComponent({
  setup() {
    const state = reactive({ nickname: "xiaofan", age: 20 });

    setTimeout(() => {
      state.age++;
    }, 1000);

    // 修改age值时会触发 watch的回调
    watch(
      () => state.age,
      (curAge, preAge) => {
        console.log("新值:", curAge, "老值:", preAge);
      }
    );

    return {
      ...toRefs(state),
    };
  },
});

2)侦听 ref 定义的数据

const year = ref(0);

setTimeout(() => {
  year.value++;
}, 1000);

watch(year, (newVal, oldVal) => {
  console.log("新值:", newVal, "老值:", oldVal);
});

3)侦听多个数据

上面两个例子中,我们分别使用了两个 watch, 当我们需要侦听多个数据源时, 可以进行合并, 同时侦听多个数据:

watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => {
console.log("新值:", curAge, "老值:", preAge); console.log("新值:", newVal,
"老值:", oldVal); });

4)侦听复杂的嵌套对象

我们实际开发中,复杂数据随处可见, 比如:

const state = reactive({
  room: {
    id: 100,
    attrs: {
      size: "140平方米",
      type: "三室两厅",
    },
  },
});
watch(
  () => state.room,
  (newType, oldType) => {
    console.log("新值:", newType, "老值:", oldType);
  },
  { deep: true }
);

如果不使用第三个参数deep:true, 是无法监听到数据变化的。前面我们提到,默认情况下,watch 是惰性的, 那什么情况下不是惰性的, 可以立即执行回调函数呢?其实使用也很简单, 给第三个参数中设置immediate: true即可。关于flush配置,还在学习,后期会补充

5)stop 停止监听

我们在组件中创建的watch监听,会在组件被销毁时自动停止。如果在组件销毁之前我们想要停止掉某个监听, 可以调用watch()函数的返回值,操作如下:

const stopWatchRoom = watch(() => state.room, (newType, oldType) => {
    console.log("新值:", newType, "老值:", oldType);
}, {deep:true});

setTimeout(()=>{
    // 停止监听
    stopWatchRoom()
}, 3000)

还有一个监听函数watchEffect, 在我看来watch已经能满足监听的需求,为什么还要有watchEffect呢?虽然我没有 get 到它的必要性,但是还是要介绍一下watchEffect,首先看看它的使用和watch究竟有何不同。

import { defineComponent, ref, reactive, toRefs, watchEffect } from "vue";
export default defineComponent({
  setup() {
    const state = reactive({ nickname: "xiaofan", age: 20 });
    let year = ref(0)

    setInterval(() =>{
        state.age++
        year.value++
    },1000)

    watchEffect(() => {
        console.log(state);
        console.log(year);
      }
    );

    return {
        ...toRefs(state)
    }
  },
});

执行结果首先打印一次state和year值;然后每隔一秒,打印state和year值。从上面的代码可以看出, 并没有像watch一样需要先传入依赖,watchEffect会自动收集依赖, 只要指定一个回调函数。在组件初始化时, 会先执行一次来收集依赖, 然后当收集到的依赖中数据发生变化时, 就会再次执行回调函数。所以总结对比如下:

  1. watchEffect 不需要手动传入依赖
  2. watchEffect 会先执行一次用来自动收集依赖
  3. watchEffect 无法获取到变化前的值, 只能获取变化后的值

上面介绍了 Vue3 Composition API的部分内容, 还有很多非常好用的 API, 建议直接查看官网 composition-api。其实我们也能进行自定义封装。

四、v-model

1.v-model 变更

在使用 Vue 3 之前就了解到 v-model 发生了很大的变化, 使用过了之后才真正的 get 到这些变化, 我们先纵观一下发生了哪些变化, 然后再针对的说一下如何使用:

  • 变更:在自定义组件上使用v-model时, 属性以及事件的默认名称变了
  • 变更:v-bind的.sync修饰符在 Vue 3 中又被去掉了, 合并到了v-model里
  • 新增:同一组件可以同时设置多个 v-model
  • 新增:开发者可以自定义 v-model修饰符

2.v-model 的update事件

在 Vue2 中, 在组件上使用 v-model其实就相当于传递了value属性, 并触发了input事件:





这时v-model只能绑定在组件的value属性上,那我们就不开心了, 我们就像给自己的组件用一个别的属性,并且我们不想通过触发input来更新值,在.sync出来之前,Vue 2 中这样实现:

// 子组件:searchInput.vue
export default {
    model:{
        prop: 'search',
        event:'change'
    }
}

修改后, searchInput 组件使用v-model就相当于这样:



但是在实际开发中,有些场景我们可能需要对一个 prop 进行 “双向绑定”, 这里以最常见的 modal 为例子:modal 挺合适属性双向绑定的,外部可以控制组件的visible显示或者隐藏,组件内部关闭可以控制 visible属性隐藏,同时 visible 属性同步传输到外部。组件内部, 当我们关闭modal时, 在子组件中以 update:PropName 模式触发事件:

this.$emit('update:visible', false)

然后在父组件中可以监听这个事件进行数据更新:

此时我们也可以使用v-bind.sync来简化实现:

上面回顾了 Vue2 中v-model实现以及组件属性的双向绑定,那么在 Vue 3 中应该怎样实现的呢?在 Vue3 中, 在自定义组件上使用v-model, 相当于传递一个modelValue 属性, 同时触发一个update:modelValue事件:



如果要绑定属性名, 只需要给v-model传递一个参数就行, 同时可以绑定多个v-model:




不知道你有没有发现,这个写法完全没有.sync什么事儿了, 所以啊,Vue 3 中又抛弃了.sync写法, 统一使用v-model。

3.v-model 参数

默认情况下,组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件。我们可以通过向 v-model 传递参数来修改这些名称:


在本例中,子组件将需要一个 title prop 并发出 update:title 事件来进行同步:

app.component('my-component', {
  props: {
    title: String
  },
  emits: ['update:title'],
  template: `
    
  `
})

五、组件通信

1.父传子:props

如果你用的是vue3.0,那么直接翻回看上面的setup函数获取props的方法。

如果用的是vue3.2以上,使用的是setup语法糖,那么获取props的方式是defineProps()方法,无需引入,直接看代码吧:

子组件



父组件



2.子传父:emit

emit的也是一样,vue3.0和vue3.2获取方法不一样,vue3.0直接看上文setup函数参数部分。

在vue3.2以上版本的setup语法糖下,获取emit使用的是defineEmits方法,使用方法如下:

子组件



父组件



3.祖孙通信:provide 与 inject

provideinject提供依赖注入,功能类似 2.x 的provide/inject,是为了弥补props和emit只能子父通信的缺陷,实现跨层级组件(祖孙)间通信。

父组件:




子组件: 




孙子组件: 



4.子组件ref变量和defineExpose

  • 在标准组件写法里,子组件的数据都是默认隐式暴露给父组件的,但在 script-setup 模式下,所有数据只是默认 return 给 template 使用,不会暴露到组件外,所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。
  • 如果要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,这个操作,就是由 defineExpose 来完成。

子组件



父组件



五、自定义可复用的功能函数:Hooks

hook不是api,表示的是可复用的业务代码,是一种代码思想。当我们维护大型项目时,针对组件中大量重复的业务逻辑,就需要把这些代码重构为hook,以方便复用。

hook的使用思路如下:

  • Composition API 指抽离逻辑代码到一个函数
  • 函数的命名约定为 useXxxx 格式(React hooks也是);
  • 在 setup 中引用 useXxx 函数。

为了方便大家理解,我们写了一个实现加减的例子, 这里可以将其封装成一个 hook, 我们约定这些「自定义 Hook」以 use 作为前缀,和普通的函数加以区分。useCount.ts 实现:

import { ref, Ref, computed } from "vue";

type CountResultProps = {
  count: Ref;
  multiple: Ref;
  increase: (delta?: number) => void;
  decrease: (delta?: number) => void;
};

export default function useCount(initValue = 1): CountResultProps {
  const count = ref(initValue);

  const increase = (delta?: number): void => {
    if (typeof delta !== "undefined") {
      count.value += delta;
    } else {
      count.value += 1;
    }
  };
  const multiple = computed(() => count.value * 2);

  const decrease = (delta?: number): void => {
    if (typeof delta !== "undefined") {
      count.value -= delta;
    } else {
      count.value -= 1;
    }
  };

  return {
    count,
    multiple,
    increase,
    decrease,
  };
}

接下来看一下在组件中使用useCount这个 hook:



如果是用Vue2.x 实现,业务代码就会分散在data,method,computed等, 如果刚接手项目,实在无法快速将data字段和method关联起来,而 Vue3 的方式可以很明确的看出,将 count 相关的逻辑聚合在一起, 看起来舒服多了, 而且useCount还可以扩展更多的功能。

六、生命周期

我们可以直接看生命周期图来认识都有哪些生命周期钩子 (图片是根据官网翻译后绘制的):

从vue2到vue3,一文带你看完vue3的新特性_第4张图片

从图中我们可以看到 Vue3.0 新增了setup,这个在前面我们也详细说了, 然后是将 Vue2.x 中的beforeDestroy名称变更成beforeUnmount; destroyed 表更为 unmounted,作者说这么变更纯粹是为了更加语义化,因为一个组件是一个mount和unmount的过程。其他 Vue2 中的生命周期仍然保留。

生命周期图中并没包含全部的生命周期钩子, 还有其他的几个, 全部生命周期钩子如图所示:

从vue2到vue3,一文带你看完vue3的新特性_第5张图片

我们可以看到有三点变化:

  • beforeCreate和created被setup替换了(但是 Vue3 中你仍然可以使用, 因为 Vue3 是向下兼容的, 也就是你实际使用的是 vue2 的)。
  • 钩子命名都增加了on; 通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。
  • Vue3.x 还新增用于调试的钩子函数onRenderTriggered和onRenderTricked

第三部分、其他旧语法变更

 一、插槽slot

1.具名插槽

在 Vue2.x 中, 具名插槽的写法:


在父组件中使用: