美文网首页
Vue 3组合式API自定义Hook设计模式谈

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

作者: microkof | 来源:发表于2021-03-12 09:58 被阅读0次

    前言

    之前我写了一篇《为什么说组合式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(){}本身就是这种设计模式!

    相关文章

      网友评论

          本文标题:Vue 3组合式API自定义Hook设计模式谈

          本文链接:https://www.haomeiwen.com/subject/wnwaqltx.html