vue2与vue3 对比解析

233 阅读5分钟

Vue2.x与Vue3详细对比

  • 生命周期对比

截屏2022-03-22 10.54.00.png

使用setup代替之前的beforeCreate和created。

为啥使用setup代替之前的beforeCreate和created?

  • Diff算法的提升

vue2中当数据发生变化,会新生成一个DOM树,并和之前的DOM树比较,找到不同的节点然后才更新。对每个节点都会比较,消耗时间。

vue3改进情况:

在创建虚拟DOM树时,会根据DOM中的内容会不会发生变化,添加一个静态标记。在与上次虚拟节点对比时,只会对比带有静态标记的节点。

  • 打包体积的变化

vue3中实现了按需编译

  • mixins更改 vue2中使用mixins实现相同逻辑的抽离,每个组件只需要引入mixins,就能实现复用 vue3中使用自定义的hook,封装可复用的功能函数

createApp

  • vue2使用new的方式创建vue实例,组件中使用选项组合代码逻辑
  • vue3使用createApp创建应用实例,组件中使用组合式API和选项组合代码。

props、emits

  • vue2中用props、$emit进行父子组件间的通信
  • vue3中用props、emits进行父子组件间的通信

setup函数

setup函数是一个新的组件选项。作为在组件内使用composition Api的入口点。

创建组件实例,然后初始化props,紧接着就调用setup函数。从生命周期看,会在beforeCreate之间执行。data、methods、watch等全部都用对应的新增的api写在setup()函数中。

<script setup>
    setup(props,context) {
        context.attrs  //非响应式对象,等同于$attrs
        context.slots  // 非响应式对象,等同于$slots
        context.emit // 触发事件,方法等同于$emit
        context.expose  // 暴露公共property
        return {}
    }
</script>
  • props:用来接受props数据,props是响应式的,当传入新的props时,将被更新
  • context:用来定义上下文,上下文对象中包含了一些有用的属性。
  • this:在setup()函数中无法访问,是undefined
  • return{}:返回响应式数据,值必须是一个对象,模板中需要使用的函数
组件自动注册,无需再通过components注册
<template>
   <Child/>
</template>
<script setup>
  import Child from './Child.vue'
</script>

CompositionAPI 创建响应式对象的方式

组合式API的好处: 逻辑可以整块使用,代码结构清晰

  • vue2中实现响应式主要是使用Object.defineProperty()函数,通过getter方法进行依赖搜集,数据变化时,触发setter进行更新。
  • vue3 使用以下四个方法创建响应式对象
ref
  • 函数参数可以是基本数据类型,也可以接受对象类型
  • 如果参数是对象类型时,底层的本质还是reactive,系统会自动根据给ref传入的值转换成:
ref(1)->reactive({value:1})
  • 在template中访问,系统会自动添加.value,在js中需要手动添加.value
  • ref接受的参数是基本类型,其响应式原理是依赖于Object.defineProperty()中的get()和set()方法
  • 如果参数是引用类型,底层ref会借助reactive的proxy定义响应式reactive({value:'xxxx'})
<template>
  <div>
    <span>{{count}}</span>
  </div>
</template>
<script setup>
import {ref} from 'vue'
export default {
  setup(){
    const count = ref(1)
    setTimeout(()=>{
      count.value = 2 // 通过value改变值
    },3000)
    return {count}
  }
}
</script>
reactive
  • reactive适合定义复杂的数据类型(json/arr)
  • 参数必须是对象或者数组,如果是让对象的某个元素实现响应式时,需要使用toRefs
<template>
  <div>
    <p>{{state.name}}</p>
    <p>{{state.address.city}}</p>
  </div>
 </template>
 <script setup>
 import {reactive} from 'vue'
 export default {
   setup(){
     const state = reactive ({
       name:'nihao',
       address:{
       city:'shenzhen'
       }
     })
     setTimeout(()=>{
       state.name = 'beijing',// 模板中的值会改变
       state.address.city = 'henan'//模板中的值会改变
     },2000)
     return {state} // 不能使用...state方式结构,会丢失响应式
   }
 }
 </script>
 
ref与reactive的区别
  • ref定义基本数据类型。reactive定义复杂的数据类型,不能直接结构,需要使用toRefs()转成普通对象后解构。
toRef
  • 针对一个响应式对象(reactive封装)的prop(属性)创建一个ref,且保持响应式
  • 两者保持引用关系
<template>
  <div>
    <span>姓名:{{state.name}}</span>
    <span>姓名2:{{nameRef}}</span>
  </div>
</template>
<script setup>
 import {reactive,toRef} form 'vue'
 setup(){
   const state = reactive({
     name:'nihao'
   })
   //通过toRef创建一个ref响应式
   const nameRef = toRef(state,'name')
   setTimeout(()=>{
     nameRef.value = 'beijing'
   },2000)
   return {state,nameRef}
 }
</script>
toRefs

用于破坏响应式对象并将其所有属性转换为ref的实用方法

  • 将响应式对象(reactive)转成普通对象
  • 对象的每个属性(prop)都是对应的ref
  • 两者保持引用关系
<template>
  <div>
    <span>姓名:{{name}}</span>
    <span>姓名2:{{nameRef}}</span>
  </div>
</template>
<script setup>
 import {reactive,toRefs} form 'vue'
 setup(){
   const state = reactive({
     name:'nihao'
   })
   //通过toRefs创建一个响应式对象属性的Ref
   const toRefsValue = toRefs(state)
   setTimeout(()=>{
     toRefsValue.name.value = 'beijing'
   },2000)
   return {state,...toRefsValue}
 }
</script>

ref、toRef、toRefs三者的区别
  • 视图层面:ref数据进行修改时,视图会发生变化,toRef数据和toRefs数据修改时,视图不会发生变化
  • 数据层面:ref数据修改时,原数据不会发生变化,toRef数据和toRefs数据修改时,原数据会发生改变
  • 参数层面:toRef需要传递2个参数,第一个参数为需要转换的对象,第二个参数为该对象中的属性名,toRefs只需要传递一个参数,为需要转换的对象即可。

teleport 传送门

需求:通过点击一个按钮,实现模态框的效果。

Suspense

发送异步请求时,有时需要判断异步请求的状态,展示不同的界面。

store使用

vue2中 ,可以直接通过this.$store进行获取,vue3中,没有this概念 vue3的使用方式

import {useStore} from 'vuex' 
import {defineComponent,ref,computed} from 'vue' 
export default defineComponent ({ 
setup(){ 
const counter = ref(0) 
const store = useStore() 
const storeData = computed(()=>store) // 配合computed,获取store的值 return{counter,storeData} 
} 
})

Tree-shaking

  • vue2版本中,很多函数是挂载在全局vue对象上,有的可能用不上,但打包时会打包进bundle中。
  • vue3引入Tree-shaking,带来的bundle体积更小
    • 以前在全局配置的组件(Vue.component)、指令(Vue.directive)、混入(Vue.mixin)和插件(Vue.use)等变为直接挂载在实例上的方法,通过创建的实例来调用,好处是一个应用可以有多个Vue实例,不同实例之间的配置也不会相互影响。

v-if和v-for优先级

  • vue2中,v-for的优先级比v-if的高
  • vue2中,v-if比v-for的优先级高

Provide/Inject

Provide/Inject可以在祖(父)组件和子(孙)组件间实现传值。

祖(父)组件中使用一个provide函数提供数据,而子(孙)组件使用inject函数来获取数据

  • vue2中provide/inject不拥有响应性的 跨级组件传递数据,父组件用provide选项提供数据,子组件用inject选项接收数据 provide/inject API可以实现状态共享和依赖注入功能,可以代替vuex
  • provide有两个参数:第一个参数表示字符串类型的key,第二个参数是value,可以是对象,也可以是普通数据类型
import {provide} from 'vue'
setup(){
  // 第一种写法
  provide('name','shenzhen')
  // 第二种写法
  provide('state',{
    name:'nihao',
    age:12
  })
}
  • inject有两个参数:第一个参数表示需要获取的key,第二个参数为key未取到值时填充的默认值
// setup()函数中调用inject函数获取数据
import {inject} from 'vue'
setup(){
   // 第一种写法
   const username = inject('name','henan')
   // 第二种
   const userState = inject('state')
}
  • 为provide添加响应性 provide提供的数据发生变化时,inject使用数据的组件需要进行刷新界面。
// 修改后的provide
setup(){
   provide('name',ref('shenzhen'))
   provide ('state',reactive({
      name:'test',
      age:12
   }))
}