允许可组合项接受响应性参数
在组件中经常使用响应式数据,并且开发者也希望能够直观地将响应式数据传递到任何可组合项中:
const list = ref(['Dog', 'Cat', 'Lizard']);
const { state, prev, next } = useCycleList(list);
因此,让我们更新可组合项,以接受响应式列表(使用 ref
或 reactive
定义的列表)
import { ref, computed, watch, type Ref } from 'vue';
// 👇 now we're accepting a ref that is an array of anything
export const useCycleList = (list: Ref<any[]>) => {
//...
// And then throughout the composable, you'll need to replace all uses of
// `list` with `list.value`
// for example 👇
const state = computed(() => list.value[activeIndex.value]);
// do the same for list in next, prev, etc...
// 👇 finally, since the list can change now
// let's run a little cleanup on the activeIndex
// if the list is changed out to something shorter
// than the activeIndex
watch(list, () => {
if (activeIndex.value >= reactiveList.value.length) {
activeIndex.value = 0;
}
});
// ...
};
允许可组合项接受非响应式和响应式参数(以及 Getter!)
上面的内容非常适合接受响应式列表。但现在限制了可组合消费者只能传递一些响应式的东西。
更完备的实现是也支持只传递一个普通数组,可以使用 Vue 中的 toRef
辅助函数来支持这两者。
import { ref, computed } from "vue";
import { type MaybeRefOrGetter, toRef, watch } from "vue";
// 👇 notice we're using the `MaybeRefOrGetter` type from Vue core
export const useCycleList = (list: MaybeRefOrGetter<any[]>) => {
// calling toRef normalizes the list to a ref (no matter how it was passed)
const reactiveList = toRef(list);
// replace all uses of list.value
// with reactiveList.value
// for example 👇
const state = computed(() => reactiveList.value[activeIndex.value]);
// do the same for list in next, prev, watch, etc...
//...
}
现在可以支持此列表的两种类型的数据,此外还免费获得最后第三种类型的支持!
以下所有功能现在都可以工作:
// As plain data
const { state, prev, next } = useCycleList(
['Dog', 'Cat', 'Lizard']
);
// As reactive data
const list = ref(['Dog', 'Cat', 'Lizard']);
const { state, prev, next } = useCycleList(list);
// As a getter
const list = ref(['Dog', 'Cat', 'Lizard']);
const { state, prev, next } = useCycleList(()=> list.value);
使用可写计算属性改进可组合 API
目前,如果我们尝试从可组合项设置 state
,它将不起作用。为什么?我们将其定义为 computed
const state = computed(() => reactiveList.value[activeIndex.value]);
为了使 API 更加灵活,我认为写入 state
更新数组中的底层项目是有意义的。
没问题! Vue 也可以处理这个问题!
const state = computed({
// the get method is called when state is read
get() {
// this is the same as the return from the function before
return reactiveList.value[activeIndex.value];
},
// the set method is called when state is written to
set(value) {
// take the given value and apply it to the array item
// at the currently active index
reactiveList.value[activeIndex.value] = value;
},
});
注意!这并不是 100% 需要添加的功能,但考虑所有用户可能如何使用您的可组合项并使其尽可能直观地使用是件好事。
使可组合类型安全
到目前为止,我们已经使用 any[]
来定义可组合的 list
参数。这是有道理的,因为我们希望我们的可组合项适用于任何内容的数组(而不仅仅是示例中的字符串)。
然而,大多数 TS 用户都知道使用 any
有点代码味道。它的存在意味着还有改进的空间。
因此,让我们使用泛型来表示数组中的每个项目都是某种变量类型。
export const useCycleList = <T>(list: MaybeRefOrGetter<T[]>) => {}
这有什么用?
现在,TypeScript 可以推断我们的状态是与传递列表中的项目具有相同类型的 WritableComputedRef
。对于我们的示例,这是一个字符串。
下面是我们使用泛型(即使用 any
)之前 IDE 中的情况的屏幕截图
这是我们实现泛型后的样子
如果我们为列表中的每个项目提供另一种类型的数据: