其他分享
首页 > 其他分享> > Vue3.0 新特性以及使用经验总结

Vue3.0 新特性以及使用经验总结

作者:互联网

前言
Vue3.0 在去年 9 月正式发布了,看大家都有在热情的拥抱 Vue3.0。今年初新项目也开始使用 Vue3.0 来开发,这篇文章就是在使用后的一个总结, 包含 Vue3 新特性的使用以及一些使用经验分享。

为什么要升级 Vue3
使用 Vue2.x 的小伙伴都熟悉,Vue2.x 中所有数据都是定义在data中,方法定义在methods中的,并且使用this来调用对应的数据和方法。那 Vue3.x 中就可以不这么玩了, 具体怎么玩我们后续再说, 先说一下 Vue2.x 版本这么写有什么缺陷,所有才会进行升级变更的。
回顾 Vue2.x 实现加减

<template>
  <div class="homePage">
    <p>count: {{ count }}</p>   
    <p>倍数: {{ multiple }}</p>        
    <div>
      <button style="margin-right: 10px" @click="increase">加1</button>
      <button @click="decrease">减一</button>    
    </div>      
  </div>
</template>
<script>
export default {
  data() {
    return { count: 0 };
  },
  computed: {
    multiple() {
      return 2 * this.count;
    },
  },
  methods: {
    increase() {
      this.count++;
    },
    decrease() {
      this.count--;
    },
  },
};
</script>

上面代码只是实现了对count的加减以及显示倍数, 就需要分别在 data、methods、computed 中进行操作,当我们增加一个需求,就会出现下图的情况:

当我们业务复杂了就会大量出现上面的情况, 随着复杂度上升,就会出现这样一张图, 每个颜色的方块表示一个功能:

甚至一个功能还有会依赖其他功能,全搅合在一起。
当这个组件的代码超过几百行时,这时增加或者修改某个需求, 就要在 data、methods、computed 以及 mounted 中反复的跳转,这其中的的痛苦写过的都知道。
那我们就想啊, 如果可以按照逻辑进行分割,将上面这张图变成下边这张图,是不是就清晰很多了呢, 这样的代码可读性和可维护性都更高:


那么 vue2.x 版本给出的解决方案就是 Mixin, 但是使用 Mixin 也会遇到让人苦恼的问题:

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

关于上面经常出现的问题我就不一一举例了,使用过的小伙伴多多少少都会遇到。文章的重点不是 Mixin, 如果确实想知道的就留言啦~
所以,我们 Vue3.x 就推出了Composition API主要就是为了解决上面的问题,将零散分布的逻辑组合在一起来维护,并且还可以将单独的功能逻辑拆分成单独的文件。接下来我们就重点认识Composition API。

Composition API

setup
setup 是 Vue3.x 新增的一个选项, 他是组件内使用 Composition API的入口。
setup 执行时机
我在学习过程中看到很多文章都说 setup 是在 beforeCreate和created之间, 这个结论是错误的。实践是检验真理的唯一标准, 于是自己去检验了一下:

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


setup 执行时机是在 beforeCreate 之前执行,详细的可以看后面生命周期讲解。
setup 参数
使用setup时,它接受两个参数:

props: 组件传入的属性
context

setup 中接受的props是响应式的, 当传入新的 props 时,会及时被更新。由于是响应式的, 所以不可以使用 ES6 解构,解构会消除它的响应式。
错误代码示例, 这段代码会让 props 不再支持响应式:

// demo.vue
export default defineComponent ({
    setup(props, context) {
        const { name } = props
        console.log(name)
    },
})

那在开发中我们想要使用解构,还能保持props的响应式,有没有办法解决呢?大家可以思考一下,在后面toRefs学习的地方为大家解答。
接下来我们来说一下setup接受的第二个参数context,我们前面说了setup中不能访问 Vue2 中最常用的this对象,所以context中就提供了this中最常用的三个属性:attrs、slot 和emit,分别对应 Vue2.x 中的 $attr属性、slot插槽 和$emit发射事件。并且这几个属性都是自动同步最新的值,所以我们每次使用拿到的都是最新值。
reactive、ref 与 toRefs
在 vue2.x 中, 定义数据都是在data中, 但是 Vue3.x 可以使用reactive和ref来进行数据定义。
那么ref和reactive他们有什么区别呢?分别什么时候使用呢?说到这里,我又不得不提一下,看到很多网上不少文章说 (reactive用于处理对象的双向绑定,ref则处理 js 基础类型的双向绑定)。我其实不太赞同这样的说法,这样很容易初学者认为ref就能处理 js 基本类型, 比如ref也是可以定义对象的双向绑定的啊, 上段代码:

 setup() {
    const obj = ref({count:1, name:"张三"})
    setTimeout(() =>{
        obj.value.count = obj.value.count + 1
        obj.value.name = "李四"
    }, 1000)
    return{
        obj
    }
  }

我们将obj.count和obj.name绑定到页面上也是可以的;但是reactive函数确实可以代理一个对象, 但是不能代理基本类型,例如字符串、数字、boolean 等。 接下来使用代码展示一下ref、reactive的使用:


,我们绑定到页面是通过user.name,user.age;这样写感觉很繁琐,我们能不能直接将user中的属性解构出来使用呢? 答案是不能直接对user进行结构, 这样会消除它的响应式, 这里就和上面我们说props不能使用 ES6 直接解构就呼应上了。那我们就想使用解构后的数据怎么办,解决办法就是使用toRefs。
toRefs 用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象。具体使用方式如下

<template>
  <div class="homePage">
    <p>第 {{ year }} 年</p>
    <p>姓名: {{ nickname }}</p>
    <p>年龄: {{ age }}</p>
  </div>
</template>

<script>
import { defineComponent, reactive, ref, toRefs } from "vue";
export default defineComponent({
  setup() {
    const year = ref(0);
    const user = reactive({ nickname: "xiaofan", age: 26, gender: "女" });
    setInterval(() => {
      year.value++;
      user.age++;
    }, 1000);
    return {
      year,
      // 使用reRefs
      ...toRefs(user),
    };
  },
});
</script>

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

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


我们可以看到beforeCreate和created被setup替换了(但是 Vue3 中你仍然可以使用, 因为 Vue3 是向下兼容的, 也就是你实际使用的是 vue2 的)。其次,钩子命名都增加了on; Vue3.x 还新增用于调试的钩子函数onRenderTriggered和onRenderTricked
下面我们简单使用几个钩子, 方便大家学习如何使用,Vue3.x 中的钩子是需要从 vue 中导入的:

import { defineComponent, onBeforeMount, onMounted, onBeforeUpdate,onUpdated,
onBeforeUnmount, onUnmounted, one rrorCaptured, onRenderTracked,
onRenderTriggered } from "vue"; export default defineComponent({ //
beforeCreate和created是vue2的 beforeCreate() {
console.log("------beforeCreate-----"); }, created() {
console.log("------created-----"); }, setup() { console.log("------setup-----");
// vue3.x生命周期写在setup中 onBeforeMount(() => {
console.log("------onBeforeMount-----"); }); onMounted(() => {
console.log("------onMounted-----"); }); // 调试哪些数据发生了变化
onRenderTriggered((event) =>{ console.log("------onRenderTriggered-----",event);
}) }, });

关于生命周期相关的内容就介绍到这里。

简单对比 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

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

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

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

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

标签:setup,使用,特性,Vue3.0,Vue2,Vue3,defineProperty,ref,经验总结
来源: https://www.cnblogs.com/chenlei987/p/15961350.html