Vue(八)Composition API

161 阅读4分钟

从 Vue2 到 Vue3 最明显的差异就是组合式API,使用组合式组合式API能够让代码更加优雅、有逻辑。

为什么要用组合式 API

Vue2 中我们写代码 Option API

  • data
    • 逻辑1数据状态
    • 逻辑2数据状态
  • 生命周期钩子
    • 在 created 里执行逻辑1 逻辑2 相关代码
    • 在 mounted 里执行逻辑1 逻辑2 相关代码
  • computed
    • 逻辑1相关的计算属性
    • 逻辑2相关的计算属性
  • watch
    • 逻辑1相关的侦听
    • 逻辑2相关的侦听
export default { 
  props: ['message'], 
  data() { 
    return { 
      count: 0, // 逻辑1的数据
      name: 'lucy' // 逻辑2的数据 
    } 
  }, 
  methods: { 
    onAdd() { // 逻辑1的⽅法 
      this.count++ 
    },
    getCount() { // 逻辑2的⽅法 
      this.count = Math.floor(Math.random() * 100) 
    } 
  }, 
  computed: { 
    helloText() { // 逻辑2的计算属性
      return this.message + ' ' + this.name 
    } 
  }, 
  watch: { 
    count() { // 逻辑1的Watch
      console.log('count 变了') 
    } 
  }, 
  created() { 
    this.getCount() // 逻辑1的初始化操作
  },
}

对应的同一业务的逻辑分散在 data、computed、watch 里,滚动条反复上下移动

  • 问题:业务逻辑太分散,代码越多越看不懂
  • 期望:同一业务逻辑的代码放到一起 使用组合式 API 可以让我们的代码组织的更加优雅,让相关功能的代码更加有序的组织到一起。

使用组合式 API 写代码

  • 按需引入 ref、watch、生命周期钩子等
  • 所有逻辑放⼊ setup 函数,传入的第⼀个参数是 props 对象
  • 通过 ref 、reactive 、toRef 来创建响应式数据(原来是data里的)取值都是 变量.value
  • watch、computed 是函数
  • 生命周期钩子写法稍有区分,xxx 变成 OnXxx,如 mouted 变成 onMounted。不再需要created和 beforeCreate。
  • 视图要用的变量为 setup 函数返回到对象属性,按需导出
<template>
  <div>
    <p>{{ count }}</P>
    <button @click="onAdd">点我</button>
    <p>{{ name }}</P>
    <p>{{ helloText }}</P>
  </div>
</template>

import { ref, watch, computed, onMounted } from 'vue'
export default { 
  props: ['message'],
  setup(props) { 
    // 对 count 操作
    let count = ref(0) // 响应式数据的初始值
    const onAdd = () => { 
      count.value++
    }
    const getCount = () => {
      count.value = Math.floor(Math.random() * 100)
    }
    watch(count, (val, oldVal) => { 
      console.log(`count 从${oldVal} 变成了 ${val}`) 
    }) 
    onMounted(getCount)
    // 对 name 操作
    let name = ref('lucy')
    let helloText = computed(() => {
      props.message + ' ' + name.value
    })
    // 按需导出
    return { 
      count,
      name,
      helloText
    } 
  }
}

关于 setup

执行时机:在beforeCreate之前调用,发生在data、computed、methods被解析之前,所以无法在setup中获取this,组件实例没有被创建出来,this并没有指向当前组件实例,因此setup中不可以使用this

关于生命周期

因为 setup 是围绕 beforeCreate 和 created ⽣命周期钩子运行的,所以不需要显式地定义它们。 换句话说, 在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

选项式 API组合式 API
beforeCreate / created放在 setup 函数中
beforeMount / mountedonBeforeMount / onMounted
beforeUpdate / updatedonBeforeUpdate / onUpdated
beforeDestroy / destroyedonBeforeUnmount / onUnmounted
activated / deactivatedonActivated / onDeactivated
errorCapturedonErrorCaptured

ref、reactive、toRefs、toRef

ref

接收一个基本类型数据,返回一个响应式 ref 对象,且只包含一个.value属性。 语法:const xxx = ref(initValue)

  • 创建一个响应式 ref 类型的对象
  • js 中操作数据(setup内部):xxx.value
  • 模板中操作数据(setup外部):xxx 不需要 .value

该 ref 对象 count 内部指向 .value,我们可以在模板 template 里面直接使用 count,但是在 setup 里面通过count.value取值。

const count = ref(0) 
console.log(count.value) // 0 
count.value++ 
console.log(count.value) // 1

reactive

接收引用类型数据(普通对象),返回一个响应式代理对象。 语法:const state = reactive(obj),state 是 Proxy 类型的对象

对象 state,使用参数state.foo、state.bar即可

const state = reactive({ 
  foo: 1,
  bar: 2
}) 
state.foo++
console.log(state.foo) // 2
console.log(fooRef.value) // 3

总结

  1. 用 reactive() 创建的响应式对象,整个对象是响应式的,而对象里的每一项都是普通的值,解构赋值后,整个对象的普通值都不是响应式的。
  2. 而用 ref() 创建的响应式的值,本身就是响应式的,并不依赖于其他对象。

toRefs

toRefs() 可以将 reactive() 创建出来的响应式对象转换成内容为 ref 响应式的值的普通对象

reactive 对象取出的每一项属性都是非响应式的,利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性

state 对象是响应式的,toRefs将 state属性都转换成 ref 形式的响应式数据stateAsRefs.foo 和 stateAsRefs.bar。

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

const stateAsRefs = toRefs(state) 
state.foo++
console.log(stateAsRefs.foo.value) // 2

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

props是响应式的,直接解构 const { title } = props 会让 title 丧失响应特性,使用 toRefs 可以让解构后的 title 成为是响应式的。

import { toRefs } from 'vue';
export default {
  props: {
    title: String
  }
  setup(props) {
    const { title } = toRefs(props);
    console.log(title.value)
  }
}

toRef

直接拿到响应式对象的属性保持响应特性。

toRef 是从响应式对象 state 里拿到属性 foo,使之变成一个响应式的数据 fooRef。

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