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