🏆 掘金技术征文|双节特别篇 vue3——composition API

1,360 阅读7分钟

vue3刚出测试版的时候尝过一次,后来学了react,才尝出点味道来,现在再尝一遍,先从重要的compositon api入手!

composition api 主要是把之前vue的核心api暴露出来,让用户可以更加理解vue的内部原理,同时也有助于梳理代码逻辑和代码结构

Reactive

Reactive 其实和2.x的Vue.observable()是等价的

 import { reactive } from 'vue'

// reactive state
const state = reactive({
  count: 0
})

这个api最重要的使用场景是在渲染期间使用,因为有依赖追踪,当响应式的state的值发生变化的时候,视图就会自动更新,在DOM中渲染某些东西通常是被视做“有副作用的”,为了根据reactive state来应用并自动应用副作用,我们可以使用watchEffect来实现

import { reactive, watchEffect } from 'vue'

const state = reactive({
  count: 0
})

watchEffect(() => {
  document.body.innerHTML = `count is ${state.count}`
})

watchEffect 会立即执行该函数,并将执行过程中用到的所有的响应式状态的属性作为依赖进行追踪

! 注:这里不像react 需要我们自动添加依赖

这里面的state.count首次执行后作为依赖被追踪,当它的值未来发生变化的时候,这个函数就会重新执行

这其实就是Vue响应式的精髓,在2.x的时候,我们把属性绑定在data()的返回值上,使其变为响应式,其内部的原理其实就是如此

我们下面来看另一个和响应式有关的api

ref

这个ref其实对应着我们2.x的计算属性,也就是computed,通常我们在依赖另一个状态的值的时候会用到计算属性,也就是我们通过watchEffect来追踪依赖,当值变化的时候进行返回,那么对于基本属性,js是值传递的,一旦返回我们就无法追踪,所以我们可以利用一个对象,把这个值包裹到一个对象中然后再返回,当然我们也得拦截这个对象,监听他的读写来实现依赖追踪

以上原理其实也就是refapi的实现原理,但我们为此付出的代价就是每次都要通过.value来获取最新的值

const double = computed(() => state.count * 2)

watchEffect(() => {
  console.log(double.value)
}) // -> 0

state.count++ // -> 2

这里double就是ref,既然ref是一个api,我们可以简写成这样

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

! 2.x中,我们已经有了ref的概念了,只是为了获取dom实例,但新的ref系统可以同时用于逻辑状态和模版引用

解开ref

如果每次使用都得带上.value,那么未免给我们编程的时候加上了一层心智负担,所以在一些场景是会自动解开ref的,也就是Vue内部会自动使用.value,我们只用写变量名字就可以啦

比如在模版中,我们不必写成{{count.value}},直接{{count}}完事

还有在ref值嵌套于reactive之中时,也会自动解开

const state = reactive({
			count: 0,
			double: computed(() => state.count * 2)
		});
        
        // 无需再使用 `state.double.value`
console.log(state.double)

我们了解了这两个响应式api后,我们需要让他们在组件中生效,那么我们还需要一个工具 setup函数

<template>
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>

<script>
  import { reactive, computed } from 'vue'

  export default {
    setup() {
      const state = reactive({
        count: 0,
        double: computed(() => state.count * 2),
      })

      function increment() {
        state.count++
      }

      return {
        state,
        increment,
      }
    },
  }
</script>

我们把代码逻辑写在setup函数中,然后把模版用到的部分返回即可,无论是方法还是变量

现在我们的组件仅仅需要一个模版 一个setup函数即可构建一个单文件组件,而且这种函数式编程还可以给我们提供更好的ts支持,让编程体验进一步提升~

然后另一个需要我们关注的问题就是———— 生命周期的钩子函数如何使用?

生命周期可太重要了,我们需要知道我们在什么时候可以做哪些事情,例如控制台打印属性,发送ajax请求等(这些也都是副作用)

在vue3中,我们可以使用形如onXXX的API(对应现有的生命周期选项)

import { onMounted } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('component is mounted!')
    })
  },
}

这些生命周期注册方法只能用在 setup 钩子中。它会通过内部的全局状态自动找到调用此 setup 钩子的实例。有意如此设计是为了减少将逻辑提取到外部函数时的冲突。

更多生命周期钩子函数点击此处查看

与现有API配合

另一个我们比较关注的问题就是: 这个组合式API能不能和我们现有的项目兼容? 答案是肯定的,注意一下两点

  • 组合式 API 会在 2.x 的选项 (data、computed 和 methods) 之前解析,并且不能提前访问这些选项中定义的 property。

  • setup() 函数返回的 property 将会被暴露给 this。它们在 2.x 的选项中可以访问到。

我们结合一个例子来看一下

我们可以看到在setup中调用data、methods里的属性和方法会报错,因为组合式api解析在前,另外组合式api里返回的属性会自动暴露给this,我们按照之前的习惯正常调用即可。

Ref vs Reactive

我们看一下官方的解释

// 风格 1: 将变量分离
let x = 0
let y = 0

function updatePosition(e) {
  x = e.pageX
  y = e.pageY
}

// --- 与下面的相比较 ---

// 风格 2: 单个对象
const pos = {
  x: 0,
  y: 0,
}

function updatePosition(e) {
  pos.x = e.pageX
  pos.y = e.pageY
}
  • 如果使用 ref,我们实际上就是将风格 (1) 转换为使用 ref (为了让基础类型值具有响应性) 的更细致的写法。

  • 使用 reactive 和风格 (2) 一致。我们只需要通过 reactive 创建这个对象。

总结起来就是ref就是让基础类型的值具有响应性,reactive就是让一个对象具有响应性,

但是reactive有一个弊端就是用它创建的对象不能被解构,否则会丢失响应性, 解决办法就是通过toRefs这个API将其每个属性变为响应式

function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0,
  })

  // ...
  return toRefs(pos)
}

// x & y 现在是 ref 形式了!
const { x, y } = useMousePosition()

与React Hooks比较

还记得本人在学习hooks之时,立马就回想起了组合式API,因为hooks也就是组合式api的灵感来源,这里我们只关注不同点,其他的见仁见智,我主要摘出我觉得比较不同的几点

  • 组合式 API 的 setup() 函数只会被调用一次,不会在每次渲染时重复执行,以降低垃圾回收的压力;
  • 不存在忘记记录依赖的问题,也不需要“useEffect”和“useMemo”并传入依赖数组以捕获过时的变量。Vue 的自动依赖跟踪可以确保侦听器和计算值总是准确无误。

vue很好的结合了他的响应式模型,可以说强化了hooks时的一些编程体验

定位

组合式API被定位一个高级特性,这意味着他并不是vue3的默认解决方案,你也可以继续选择2.x,但他会给你带来不一样的编程体验,它解决的问题主要出现在一些大型应用程序中,这完全取决于你自己。

总结

我们大概梳理一下组合式API的心智模型:

  • 单文件组件的格式可以 仅有 模版和setup函数构成

  • setup函数作为文件的入口,通过执行组合函数来实现相应逻辑,最后通过return 返回模版需要的变量或方法

  • 每个组合函数都是可拆分的、可复用的、独立的,我们不仅可以在当前组件中声明,还可以把他们放在一个单独的文件中来被其他组件引用

  • 在每个组合函数中,我们可以应用vue特有的响应式模型,通过ref、reactive这两个API来将一些变量变成响应式

  • setup中可以使用一些生命周期钩子函数来执行指定的副作用

🏆 掘金技术征文|双节特别篇