浅谈Vue3 Composition API ref

什么是Composition Api

Vue3.0带来了一个全新的特性(Composition API),字面意思就是“组合 API”,它是为了实现基于函数的逻辑复用机制而产生的。

Vue2中的Option API

在了解Composition Api之前,首先回顾下我们在Vue2中使用Option Api遇到的问题,我们在Vue2中常常会需要在特定的区域(data,methods,watch,computed…)编写负责相同功能的代码。

我们通过下面这张图看下option api
浅谈Vue3 Composition API ref_第1张图片

option api的缺陷

随着业务复杂度越来越高,代码量会不断的加大;由于相关业务的代码需要遵循option的配置写到特定的区域,导致后续维护非常的复杂,代码可复用性也不高。

即相同的功能函数写了多个,复用性不高,当代码量以及项目越来越大时,不好维护。
浅谈Vue3 Composition API ref_第2张图片

Vue3中的Composition Api

Composition API在Vue 3中开箱即用,如果想在Vue 2中使用Composition API,可以在项目中添加@vue/composition-api插件.
浅谈Vue3 Composition API ref_第3张图片
浅谈Vue3 Composition API ref_第4张图片

从图中可以看出来,composition api会把相同或类似功能的函数代码放在一起。

Composition API优点

  • 代码可维护性高
  • 复用性高
  • 实现逻辑复用

Composition API规则

  • Composition API指抽离逻辑代码到一个函数
  • 函数的命名约定为useXxxx 格式
  • 在setup 中引用useXxx 函数

Composition API都有哪些?

通过下面这张图来看下:
浅谈Vue3 Composition API ref_第5张图片

ref

ref

ref是什么呢?

  • 生成值类型(即基本数据类型) 的响应式数据
  • 用于模板和reactive
  • 通过.value来修改值,注意一定要加上.value
  • 不仅可以用于响应式,还可以用于模板的DOM元素

为什么要用ref呢?

  1. 值类型(即基本数据类型)无处不在,如果不用 ref 而直接返回值类型,会丢失响应式
  2. 比如在 setup 、 computed 、合成函数等各种场景中,都有可能返回值类型
  3. Vue 如果不定义 ref ,用户将自己制造 ref ,这样反而会更加混乱

为何ref需要.value属性?

ref需要通过.value来修改值

  1. ref 是一个对象,这个对象不丢失响应式,且这个对象用 value 来存储值
  2. 通过 .value 属性的 get 和 set 来实现响应式
  3. 当用于 模板 和 reactive 时,不需要 .value 来实现响应式,而其他情况则都需要

看个例子

ref实现值的响应式



// 输出结果:
ref demo 18 monday // 响应式前的数据
ref demo 20 mondaylab // 响应式后的数据
总结

ref可以实现值类型的数据响应式

ref用于模版的DOM元素



// 输出结果
ref template 今天是周一

今天是周一

总结

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

toRef

toRef是什么呢?

对一个普通对象来说,如果这个普通对象要实现响应式,就用reactive。用了reactive后,它就是一个响应式对象。那么在 一个响应式对象里面,如果其中有一个属性要拿出来单独做响应式的话,就可以用toRef。

  • toRef可以响应对象Object ,其针对的是某一个响应式对象( reactive 封装)的属性prop
  • toRef和对象Object两者保持引用关系,即一个改完另外一个也跟着改
  • toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式
语法

toRef(Object, prop) 的形式来传对象名和具体的属性名,达到某个属性数据响应式的效果。

看个例子




// 输出结果:
toRef demo - 18 - monday 18
toRef demo - 20 - monday 20
toRef demo - 25 - monday 25

toRefs

toRefs是什么呢?

  • 与toRef不一样的是, toRefs是针对整个对象的所有属性,目标在于将响应式对象( reactive 封装)转换为普通对象
  • 普通对象里的每一个属性prop都对应一个ref
  • toRefs和对象Object两者保持引用关系,即一个改完另外一个也跟着改

看个例子




// 输出结果
age 18 name monday
age 20 name 周一
总结
  • 对于解构后的对象来说,如果直接解构reactive ,那么解构出来的对象会直接失去响应式
  • toRefs 在把响应式对象转变为普通对象后,不会丢失掉响应式的功能
  • 用了toRefs ,普通对象的值成功被取出来了,并且还不会丢失响应式的功能.

为什么需要toRef和toRefs?

toRef和toRefs这两个兄弟,它们不创造响应式,而是延续响应式。创造响应式一般由ref或者reactive来解决,而toRef和toRefs则是把对象的数据进行分解和扩散,其这个对象针对的是响应式对象而非普通对象。

  1. 在不丢失响应式的情况下,把对象数据进行分解或扩散
  2. 针对的是响应式对象( reactrive 封装的)而非普通对象
  3. 不创造响应式,而是延续响应式

合成函数如何返回响应式对象

function fn(){	
	const state = reactive({		
		x: 1,		
		y: 2	
	})		
	// TODO 代码逻辑
	return toRefs(state) // 用toRefs将state对象进行返回
}
export default{	
setup(){
	const {x, y} = fn() // 组件里面直接调用响应式对象		
	return{            
		x,            
		y		
	}
}}

通过demo了解Composition API

// 模版代码

setup写法一

// 通过vue3中setup实现上述功能
// 引入ref和computed
import { ref, computed } from "vue"

setup() {
    const capacity = ref(4)
    const attending = ref(["Tim", "Bob", "Joe"])
    // computed计算属性的声明方式
    const spacesLeft = computed(() => {
      // 注意,通过ref声明的响应式数据需要通过.value拿到该值
      return capacity.value - attending.value.length
    })
    // 代替methods声明方法
    function increaseCapacity () {
      capacity.value++
    }
    function sort () {
      attending.value = lodash.shuffle(attending.value)
    }
    function addAttending () {
      attending.value.push(lodash.random(3, 7))
    }
    // 通过一个对象暴露以上变量和方法
    return {
      capacity,
      attending,
      spacesLeft,
      increaseCapacity,
      sort,
      addAttending
    }
  }

总结

  • computed计算属性,是通过computed()方法声明的(全部替换成了函数式的声明)
  • 通过一个对象暴露以上变量和方法
  • 通过函数声明代替methods声明方法

setup写法二

// 将需要使用的变量添加到event的属性中
  setup(props, context) {
    console.log(props)
    // Attributes (Non-reactive object)
    console.log(context.attrs)
    
    // Slots (Non-reactive object)
    console.log(context.slots)
    
    // Emit Events (Method)
    console.log(context.emit)

    const event = reactive({
      capacity: 4,
      attending: ['Tim', 'Bob', 'Joe'],
      spacesLeft: computed(() => { return event.capacity - event.attending.length })
    })
    // 方法的声明方法不变
    function increaseCapacity () {
      event.capacity++
    }
    function sort () {
      event.attending = lodash.shuffle(event.attending)
    }
    function addAttending () {
      event.attending.push(lodash.random(3, 7))
    }
    // toRefs()
    return {...toRefs(event), increaseCapacity, addAttending, sort}
  },

总结

  • 改变的地方是toRefs()。toRefs()方法接受一个Reactive 实例作为入参,返回一个响应式的object。
  • 在setup()函数体内直接通过event属性拿到相关变量值,不需要额外加上.value

setup说明

参数

接受2个参数,第一个参数props,就是Vue的props。setup()执行时还没有vue实例,所以在setup()函数体内拿不到data、computed和methods,所以需要借助第二个参数context拿到vue实例相关的数据,其中包含attrs、slots和emit。

生命周期hooks

setup()内部有相应的vue生命周期hooks供开发者使用,具体可以参考官方文档。

我们来对比打印一下新旧两版生命周期hooks:

import {
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onUnmounted
} from "vue"

function printLifeCycle () {
  onBeforeMount(() => {console.log('onBeforeMount')})
  onBeforeUpdate(() => {console.log('onBeforeUpdate')})
  onMounted(() => {console.log('onMounted')})
  onUpdated(() => {console.log('onUpdated')})
  onUnmounted(() => {console.log('onUnmounted')})
}

export default {
  // setup内部生命周期hooks
  setup(props, context) {
    printLifeCycle()
  },
  // 老生命周期hooks
  beforeCreate() {
    console.log('beforeCreate')
  },
  created() {
    console.log('created')
  },
  beforeMount() {
    console.log('beforeMount')
  },
  mounted() {
    console.log('mounted')
  },
  beforeUpdate() {
    console.log('beforeUpdate')
  },
  updated() {
    console.log('updated')
  }
}
总结:

setup()内部的hooks执行早于vue实例上的老hooks

浅谈Vue3 Composition API ref_第6张图片

总结

  1. reactive做对象的响应式,ref做值类型的响应式
  2. setup中返回toRefs(对象) ,或者toRef(对象, ‘属性’)
  3. ref 的变量命名尽量都用xxxRef
  4. 合成函数返回响应式对象时,使用toRefs

参考网址:
https://juejin.cn/post/6890545920883032071(简单的动画,清楚的描述,通俗易懂)
https://juejin.cn/post/6976679225239535629#heading-9

你可能感兴趣的:(前端框架vue,vue)