Vue 3组合式API自定义Hook设计模式谈

2021-03-12  本文已影响0人  microkof

前言

之前我写了一篇《为什么说组合式API解决了mixins的痛点?》,介绍了一下如何用函数封装代码,这种封装没什么深奥的道理,它既不属于构造函数,也不属于面向对象,顶多就是个闭包。它类似于React里的自定义Hook。

为什么Vue 2没人提到这种简单的设计模式?

原因也很简单,Vue 2的数据响应式基于Vue实例,任何数据都是实例上的数据,所以只能引入另一个.vue文件,做一次选项合并,而不能把数据和方法独立成.js文件。现在Vue 3的数据响应式基于Proxy,不依赖Vue实例,当然就可以写独立.js文件然后引入了。

那么,函数封装的模式,是不是可以用在组合式API里呢?当然能,而且也是推荐做法。

题目

比如现在一个组件,引入了3个Dialog子组件,我要为它们提供title、opened状态、submitDisabled(用来控制按钮是否可点击)……等等,还要提供几个方法。这三个Dialog组件的样子和代码各异,不适合合并。

代码怎么写?

按照基础教程,你要写let aDialogTitle = ...let bDialogTitle = ...let cDialogTitle = ...,是不是想死?

于是你打算试试设计模式,写成let aDialog = {title: ..., opened: ...}let bDialog = {title: ..., opened: ...}let cDialog = {title: ..., opened: ...},这特么跟选项式API也没什么区别,干嘛还要用组合式API呢?

于是你甚至打算用构造函数……打住,没那么复杂,用闭包就可以了。

方案

基础代码是这样:

let aDialogTitle = ...;
let aDialogOpened = ...;

function openADialog() {
  //...
}

闭包写法是这样:

function useADialog() {
  let title = ...;
  let opened = ...;

  function openDialog() {
    //...
  }

  return reactive(ADialog: {title, opened, openDialog});
}

在<template>里的用法举例:

<template>
  <ADialog v-if="ADialog.opened" />
</template>

在ref语法糖中的写法差别

常规写法举例

<template>
  <div class="hello" @click="x.c = x.c + 1">
    {{ x.c }}
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  setup() {
    function x() {
      let c = ref(3);
      return {
        c,
      };
    }
    return reactive({x: x()});
  },
};
</script>

ref语法糖写法举例

<template>
  <div class="hello" @click="x.c = x.c + 1">
    {{ x.c }}
  </div>
</template>

<script setup>
import { reactive, ref } from "vue";

const x = (function () {
  let c = ref(3);
  return reactive({
    c,
  });
})();
</script>

差别

由于语法糖写法没有return,所以必须声明变量(x),而且必须使用自执行函数,才能将数据返给模板。常规写法没什么可说的。

返给模板的都得是Proxy对象。

如果模板不用x.c格式,也就是不用模块前缀,只写c,怎么做?

常规写法

太简单了,把return reactive({x: x()});改成return x();就行了,清爽!

语法糖写法

太复杂了,呵呵。你必须给返回的对象解构,也就是const {c} =,但是解构一定会丢失响应,所以还要用上toRefs包住自执行函数。

总之

常规写法 语法糖写法
模板加模块前缀 return reactive({x: x()})即可 return reactive({c})即可
模板不加模块前缀 无任何心智负担,无需reactive 需用:解构、toRefs、reactive

所以,采用Hook模式的话,是不是采用语法糖写法,你自己慎重考虑吧。

跨Hook调用数据和方法怎么做?

这时候,就不可在末行return执行Hook了,必须提前赋值给变量。以常规写法为例,比如:

<template>
  <div class="hello" @click="y.changeC">{{ x.c }}</div>
</template>

<script>
import { reactive, ref, shallowRef } from "vue";
export default {
  setup() {
    function useX() {
      let c = shallowRef(3);
      return {
        c,
      };
    }
    function useY() {
      function changeC() {
        x.c.value = 33;
      }
      return {
        changeC,
      };
    }
    const x = useX();
    const y = useY();
    return reactive({ x, y });
  },
};
</script>

总结

这个模式真的没什么深奥,setup(){}本身就是这种设计模式!

上一篇 下一篇

猜你喜欢

热点阅读