Vue3 的新特性 —— Composition-Api

Vue3 的新特性 —— Composition-Api

作者:旋目

前言

随着Vue3.0正式的发布,正如你所期望的那样,Vue3.0带来了很多令人兴奋的新功能。今天我们就来探究一下新特性(Composition-Api)。

什么是Composition API

Vue团队主要是在Vue2.x的API的基础上引入了一些补充和升级,对一些API进行了替换升级和重命名,就这样组成了Vue3.0+Composition API这种全新的逻辑重用和代码组织方法。我们可以叫他合成函数或者集成API。

Composition API的出现解决了什么

我们来看一下Vue2.x一个大型组件的示例:

image.png
其中逻辑关注点是按颜色分组,由methods,computed,watch,data中等等定义属性和方法,共同处理页面逻辑。
项目小还好,清晰明了,但是项目大了后,一个methods中可能包含20多个方法,你往往分不清哪个方法对应着哪个功能。这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。

我们再看一下Vue3.0的结构,一个功能所定义的所有api会放在一起:

image.png
这里我们可以看到不同颜色的代码片段由composition-api 把复杂组件的逻辑编写的更紧凑,而且可以将公共逻辑进行抽取。

下面举例看下如何抽取分离代码:
count.js

import { ref } from 'vue'
function useCount() {
  const count = ref(0)
  function addCount () {
      count.value++
      console.error(count.value)
  }
  return {
      count,
      addCount
  }
}
export default useCount;
复制代码

上面的函数我们定义了变量count,然后声明一个addCount函数,以控制count数量的累加,我们继续看调用:

<template>
  <view @click="addCount">
    count:{{state.count}}
  </view>
</template>
import useCount from './count.js'
<script lang='ts'>
import {defineComponent} from "vue";
import useCount from './count.js'
export default defineComponent ({
  setup() {
    const { count, addCount } = useCount()
    return {
      count,
      addCount
    };
  }
});
</script>
复制代码

实现点击效果:

image.png

上面将useCount方法注入后便可以实现调用,useCount暴露出来的count变量、addcount方法可以供所有有关联性业务需要的组件调用。

Composition API的语法糖

Composition API提供了以下几个函数式的 API

  • ref
  • reactive
  • toRefs
  • computed
  • watchEffect
  • watch
  • readonly()

ref()

ref() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。

<template>
  <view @click="addCount">
    count:{{state}}
  </view>
</template>

<script lang='ts'>
import { defineComponent, ref, toRefs } from "vue";
export default defineComponent ({
  setup() {
    let state = ref(0); // 建立一个响应式对象
    function addCount() {
      state.value++; //使用ref创建的变量需要通过.value访问
    }
    return {
      state,
      addCount
    };
  }
});
</script>
复制代码

我们可以看到在DOM中直接渲染就可以展示state的值,这里需要注意的是在dom中可以直接使用state但是在函数体中需要使用state.value来进行修改(因为ref返回的是一个对象)

实现点击效果:
image.png

reactive()

reactive() 函数接收一个普通的对象,返回出一个响应式对象。
在Vue2.x的版本中,我们只需要在 data() 中定义一个数据就能将它变为响应式数据,在 Vue3.0 中,需要用 reactive 函数或者 ref 来创建响应式数据

<template>
  <view @click="addCount">
    count1:{{state2}}
    <br />
    count2:{{state.count}}
  </view>
</template>

<script lang='ts'>
import {defineComponent, reactive, ref } from "vue";
export default defineComponent({
  setup() {
    const state = reactive({ // 建立一个响应式对象
      count: 0
    });
    let state2 = ref(0);
    function addCount() {
      state.count++;  //使用reactive创建的对象需要通过state.count来访问
      state2.value++; 
    }
    return {
      state,
      state2,
      addCount
    };
  }
});
</script>
复制代码

实现点击效果:
image.png
这里我们可以看到reactive跟ref创建的count都变化了。可能这里会有人问,既然实现结果都一样,那这两个API我随便用?下面来看一下reactive() + toRefs()

toRefs()

看一下官方介绍toRefs():将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的ref。

image.png

13531618394095_.pic.jpg

image.png

这里我们使用了…toRefs(state),这个API可以把reactive的值处理为ref(),分解后返回的是一个普通对象,但是它还保存着数据的响应性

这里可以看出来

  • ref 函数传入一个值作为参数,一般传入基本数据类型(类似于react-hooks中useState),返回一个基于该值的响应式Ref对象,该对象中的值一旦被改变和访问,都会被跟踪到,就像我们改写后的示例代码一样,通过修改 count.value 的值,可以触发模板的重新渲染,显示最新的值。
  • reactive是用来定义更加复杂的数据类型,但是定义后里面的变量取出来就不在是响应式Ref对象数据了。

computed()

computed()计算属性,返回值返回一个ref对象,有2种写法。

1.创建只读计算属性

const count = ref(1)

// 创建一个计算属性,使其值比 count 大 1
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 输出 2
plusOne.value++ // error 不可写
复制代码

修改count,plusOne会跟着自动+1,但如果修改nextAge,会有警告:计算属性不能修改

image.png

2.创建可读可写计算属性

const count = ref(1)
const plusOne = computed({
  //取值
  get: () => count.value + 1,
  set: val => {//赋值
    count.value = val - 1
  }
})
// 给计算属性赋值,会触发 set 函数
plusOne.value = 1
// set后 count.value的值会更新
console.log(count.value) // 0
复制代码

Vue3.0中计算属性的函数中如果只传入一个回调函数,表示的是get,如果传入一个对象,表示的是get和set。

watchEffect()

watchEffect()是在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> 打印结果 0

setTimeout(() => {
  count.value++
  //打印结果 1
}, 100)
复制代码

执行结果
image.png

第一次打印的是0,当通过定时器触发值的修改时,触发函数打印1。它比较与watch使用起来更为便捷,每次初始化时会执行一次回调函数来自动获取依赖,没有惰性。

watch()

watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调。

监听一个ref值

<template>
  <view @click="addCount">
    count:{{state}}
  </view>
</template>
<script>
import { reactive, watch } from 'vue'
export default {
  setup () {
    const count = ref(0)
    const addCount = () => state.count++;
    watch(count, (count, prevCount) => {
      console.log(count,prevCount)  //改变触发回调返回新值旧值
    })
    return { state, addCount }
  }
}
</script>
复制代码

监听一个对象

<template>
  <view @click="addCount">
    count:{{state}}
  </view>
</template>
<script>
import { reactive, watch } from 'vue'
export default {
  setup () {
    const state = reactive({ 
        count: 0 
    })
    const addCount = () => state.count++;
    watch(
      () => state.count, //可以监听一个对象也可以是内部某个值
      (count, prevCount) => {
        console.log(count,prevCount)  //改变触发回调返回新值旧值
      }
    )
    return { state, addCount }
  }
}
</script>
复制代码

侦听多个源

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  console.log(foo,bar,prevFoo,prevBar)  //改变触发回调
})
复制代码

与 watchEffect相比:

  • 访问侦听状态的先前值和当前值。
  • 可以侦听多个数据的变化。
  • watch存在惰性第一次进来不会执行,值变了才会执行,watchEffect没有惰性,进页面便会加载,只有涉及到相关 的依赖变化了就会执行

readonly()

readonly()传入一个响应式对象、普通对象或 ref ,返回一个只读的对象代理。这个代理是深层次的,对象内部的数据也是只读的。

const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
  // 适用于响应性追踪
  console.log(copy.count)
})

/// original 上的修改会触发 copy 上的侦听
original.count++

// 只读属性是不能修改的
copy.count++ // 警告!
复制代码

小结

说到这里我们对Vue3.0的Composition-Api有了一个大致的了解,它标志性的展示了集成函数的灵活,支持的复杂性更高,希望大家看了上文能更好地了解Composition-Api的带来的变化。


技术分享宣传图@3x.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享