vue3和vue2区别

288 阅读4分钟

打包工具的不同

Vue2中通常使用Webpack作为默认的打包工具;Vue3中官方推荐使用Vite作为默认的打包工具

搭配的生态系统不同

Vue2中状态管理库使用的是VuexVue3中官方推荐使用Pinia,更小巧灵活

响应性系统的底层实现不同

Vue2中使用Object.definePropert来劫持 data 数据的 getter 和 setter 操作,使得 data中属性在被访问或赋值时,动态更新绑定的 template 模板。而 Object.defineProperty 必须遍历所有的预值才能劫持每一个属性

Object.defineProperty的缺点:

对于对象Object.defineProperty只能监听对象的已有属性,无法监听对象新增属性或删除属性,因为它在对象上的属性需要被显式定义。这在实现响应式系统时可能需要额外的处理。


//解决方法1 
this.$set(this.someObject,'b',2) 

//解决方法2 添加的属性比较多 
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
this.someObject = {name:"bwf", age:12}

//解决方法3 在初始化实例前声明所有根级响应式 property,哪怕只是一个空值:

<template>
  <div class="">
    <p>user:{{ user }}</p>
    <button @click="onClick">add user info</button>
  </div>,
</template>

<script>
export default {
  name: "App",
  components: {},
  props: {},
  data() {
    return {
      user: {},
    };
  },
  methods: {
    onClick() {
      // 不会响应式
      // this.user.name = "bwf";

       // 可以响应式
      this.$set(this.user, "name", "bwf")

      // 可以响应式
      // this.user = { name: "bwf" };
    },
  },
};
</script>

对于数组:通过下标直接改变数组或直接改变数组长度,视图也是无法更新的,也是因为监听不到(vue2 将被侦听的数组的变更方法进行了包裹, push/pop/shift/unshift/splice/sort/reverse)

当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue 

当你修改数组的长度时,例如:
vm.items.length = newLength

// 方法1 
Vue.set(vm.items, indexOfItem, newValue) 

// 方法2 
vm.items.splice(indexOfItem, 1, newValue)

深度监听的问题:Object.defineProperty对于对象的深度监听较为繁琐,需要递归遍历对象的所有属性,并对每个属性进行设置,性能相对较低。

相比之下Vue3 Proxy,则没有这些问题

/**
 * 模拟vue3 Proxy代理 实现对于对象的拦截
 * target: 被代理的对象 data
 * key: 操作的属性名
 * value:  值
 */
const p = new Proxy(data, {
  //读取属性会执行的回调
  get(target, key, receiver) {
    console.log('get', target, key);
    return Reflect.get(target, key, receiver);
  },
  //修改或添加属性会执行的回调
  set(target, key, value, receiver) {
    console.log('set', target, key, value);
    return Reflect.set(target, key, value, receiver);
  },
  //删除属性会执行的回调
  deleteProperty(target, key) {
    console.log('deleteProperty', target, key);
    return delete target[key];
  },
});

Vue3更好的 ts 支持

Vue3Fragments(片段)功能,Vue3允许组件返回多个根元素,而不再需要一个外层的包裹元素

选项式 api VS 组合式 api

vue2 中采用选项式 api ,代码过于分散。
vue3 新增了组合式 api ,一个功能模块代码会集中到一起,实现高内聚,低耦合

生命周期变化

beforeCreate  -> setup()	开始创建组件之前,创建的是data和method
created       -> setup()
beforeMount   -> onBeforeMount	组件挂载到节点上之前执行的函数。
mounted       -> onMounted	组件挂载完成后执行的函数
beforeUpdate  -> onBeforeUpdate	组件更新之前执行的函数。
updated       -> onUpdated	组件更新完成之后执行的函数。
beforeDestroy -> onBeforeUnmount	组件挂载到节点上之前执行的函数。
destroyed     -> onUnmounted	组件卸载之前执行的函数。
activated     -> onActivated	组件卸载完成后执行的函数
deactivated   -> onDeactivated  在组件切换中老组件消失的时候执行

v-if 和 v-for的优先级

vue2同1个元素上v-for 优先于 v-if,最好不要把v-if和v-for同时用在一个元素上,这样会带来性能浪费,解决方案是使用computed,将处理后的数据进行遍历 vue3同1个元素上v-if 优先于 v-for

vue3增加了watchEffect,更加的灵活

<script setup>
import { ref, watchEffect } from 'vue'

const id = ref('1')
const pid = ref('0000')
const data = ref(null)


// pid id任何一个发生改变时都会触发
watchEffect(async () => {
  console.log('watchEffect---start');
  const response = await fetch(`api/${pid.value}/${id.value}`)
  data.value = await response.json()
})

const changePid = () => {
   pid.value = "99"
}
</script>

vue3不推荐mixins,更推荐组合式函数。

  1. 不清晰的数据来源:当使用了多个 mixin 时,实例上的数据属性来自哪个 mixin 变得不清晰,这使追溯实现和理解组件行为变得困难。这也是我们推荐在组合式函数中使用 ref + 解构模式的理由:让属性的来源在消费组件时一目了然。
  2. 命名空间冲突:多个来自不同作者的 mixin 可能会注册相同的属性名,造成命名冲突。若使用组合式函数,你可以通过在解构变量时对变量进行重命名来避免相同的键名。
// 创建组合式函数 composables/useCounter

import { ref, computed } from 'vue'
// 按照惯例,组合式函数名以“use”开头
export default function useCounter(initialValue) {
 // 用于初始化计数器的值
  const count = ref(initialValue)
  function increment() {
    count.value++
  }
  function decrement() {
    count.value--
  }
  const doubleCount = computed(() => count.value * 2)
  // 返回值 count doubleCount 是两个ref, ref 则可以维持这一响应性连接。
  return {
    count,
    increment,
    decrement,
    doubleCount
  }
}


//使用组合式函数
<template>
  <div>
    <p>Count: {{ counter.count }}</p>
    <p>Double Count: {{ counter.doubleCount }}</p>
    <button @click="counter.increment">Increment</button>
    <button @click="counter.decrement">Decrement</button>
  </div>
</template>

<script setup>
import { isRef } from 'vue';
import useCounter from './composables/useCounter'
const counter = useCounter(0)
// true
console.log('counter', isRef(counter.count));
</script>