用Vue3写了一个简单的Composition API

3,561 阅读4分钟

就在2020年9月的时候,尤大大发布了Vue3的正式版。在这个新版与旧版之间,我觉得变化最大的是多了一个Composition API

当你在开发一个大型项目的时候,遇到创建Vue组件时,一个组件跟项目中另外一个组件中存在着大量的重复代码,一旦维护起来所花费成本就大了,最后在维护代码这条路上越走越远。而这里,你或许会想到用 mixins 这个API,虽然这API本身就是为了减少代码的重复性,提交开发效率,但是维护上并没有多大发挥到他的作用,一旦我在共用代码部分修改某项的时候,我这一改的话,会影响到另外一个组件的交互,而这样就不得不跑去另外一个组件修改代码了,我不喜欢这样的操作。

Composition API 就是为了提高共用代码的维护效率而诞生的,此API里面就是用在setup这个位置上。setup 选项应该是接受propscontext的函数,我们可以将其返回的所有内容都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。这里有点要注意的是,由于在执行 setup 时尚未创建组件实例,因此在 setup 选项中没有 this。这意味着,除了 props 之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。

下面为了起到更好对比效果,我分别用 vue2vue3 来实现同样的效果,已达到对比性。

演示效果

Vue3

主页面

<template>
  <submit @submit="changeTarget"></submit>
  <say v-model:say="change"></say>
  <display :say="change" :target="start"></display>
</template>

一开始我设置了三个组件,他们分别是:

  • submit 用于提交自己的开始值
  • say 用于输入你的内容
  • display 用于展示你的效果

say组件

在组件say我用了vue3中的 v-model 进行双向绑定,在3.x中,自定义组件上的我可以直接更改v-model的名称,而不是更改组件内的model选项,这样也就意味我们可以在自定义组件上使用多个 v-model

// app.vue
<template>
  <say v-model:say="change"></say>
</template>

say组件内部

// say.vue
<template>
  <div class="say">请输入你的内容</div>
  <input @input="changeValue" :value="say"/>
</template>

<script type="text/ecmascript-6">
export default {
  name: "hello",
  props: ["say"],
  methods: {
    changeValue(e) {
      let says = e.target.value;
      this.$emit("update:say", says);
    },
  }
};
</script>

display组件

现在开始主要说下display部分,这一部分我用了Vue3中的Composition API,而我把setup部分写在 display组件那里

// display.vue

<template>
  <p>内容展示</p>
  <div>乘法:{{muliplyTotal}}</div>
  <div>加法:{{addTotal}}</div>
</template>

<script>
import { toRef } from 'vue'
import {muliplyNum, addNum} from "../common/util/tool";
export default {
  props: ['target', 'say'],
  setup(props) {
    let muliplyTotal= muliplyNum(toRef(props, 'target'), toRef(props, 'say'))
    let addTotal = addNum(toRef(props, 'target'), toRef(props, 'say'))
    return {
      muliplyTotal,
      addTotal
    }
  }
}
</script>

props 传递的值,通过 toRef 为源响应对象的property性创建一个 ref,保持对其源 property的响应式链接,并传递给 muliplyNumaddNum 函数,最后将函数返回的结果返回组件上。

// tool.js

import {watch, ref} from 'vue'

export function addNum(start, value) {
    let display = ref(0)
    watch(value, (newVal) => {
        let total = start.value
        let arr = newVal.split('')
        arr.forEach((i) => {
            total = total + parseInt(i)
        })
        display.value = total
    })
    return display
}

export function muliplyNum(start, value) {
    let display = ref(0)
    watch(value, (newVal) => {
        let eachValue = start.value
        let value = parseInt(newVal)
        display.value = parseInt(eachValue) * value
    })
    return display
}

通过 ref 来为对象 display 创建一个响应式, watch 来观察 props 传递过来的值变化并作出相应的逻辑,逻辑的最后将响应式的数据返回出去。

Vue2

我用了vue2写了一个相同业务逻辑

主页面

在主页面部门大部分相同,只是在组件 say 部分,由于 v-model 双向绑定的要求,要在属性 say 处增加一个 .sync 修饰符来达到双向绑定。

<template>
  <div>
    <submit @submit="changeTarget"></submit>
    <say :say.sync="change"></say>
    <display :say="change" :target="start"></display>
  </div>
</template>

display组件

<template>
  <div>
    <p>内容展示</p>
    <div>乘法:{{muliplyTotal}}</div>
    <div>加法:{{addTotal}}</div>
  </div>
</template>

<script type="text/ecmascript-6">
import { addNum, muliplyNum } from '../common/js/tool'

export default {
  props: ['say', 'target'],
  data () {
    return {
      muliplyTotal: 0,
      addTotal: 0
    }
  },
  watch: {
    say (newVal) {
      this.addTotal = addNum(this.target, newVal)
      this.muliplyTotal = muliplyNum(this.target, newVal)
    }
  }
}
</script>

与之前的vue3中的 display 组件不同的地方,主要多了一个watchapi来监控从外面传入进来的 say 数,因变化来做出相应的代码逻辑,

// tool.js
export function addNum (start, value) {
  let total = start
  let arr = value.split('')
  arr.forEach((i) => {
    total = total + parseInt(i)
  })
  return total
}

export function muliplyNum (start, value) {
  return parseInt(value) * start
}

总结

看到这里或许你会说,干嘛不直接用computed 来进行实现了呢。确实是可以这样操作,代码量确实比现在还要少,更为简单处理。这里我只是为了演示操作,才这样写的。

经过vue3与vue2这两者的代码比较,发现优缺点如下:

  • vue2代码虽少,维护起来不友好,某些响应式特性不能单独跟函数封装在一起,造成需要另外在主页面单独设置;
  • vue3虽然代码多了,但是组件间通用性高了,因为我把 watchref 等各类响应式api封装在函数里面,如果下次想在别的组件,直接调用该函数即可;

由起看来,Vue3的Composition API确实能在维护组件方面增强了不少,更多的使用场景需要我们不断去探索,也希望Vue3越来越好。