什么是单向数据流

单向数据流是指数据只能在一个方向上流动,即从父组件到子组件。在 Vue.js 中,单向数据流是指数据只能从父组件传递给子组件,而不能从子组件传递给父组件。

为什么需要单向数据流

单向数据流可以确保数据的流动清晰,易于维护和调试。同时,单向数据流也可以避免子组件意外修改父组件的数据,从而导致数据的不一致。

<!-- 这样直接绑定数据是不应该的,会直接修改父组件传递的数据,而造成打破单向数据流,为以后的维护调试埋雷 -->
<input type="text" v-model="modelValue.keyWord" />
<select>
<option v-for="item in modelValue.select" :key="item" :value="item">
{{ item }}
</option>
</select>

<script setup>
const _ = defineProps({ modelValue: { type: Object, default: () => {} } });
</script>

保护单向数据流的方案

  • 为每个数据绑定一个事件,改变触发通知父组件 (每个数据都绑定一个,不是最优解)
  • 为每个数据绑定一个计算属性,get 返回父组件传递的数据,set 就触发自定义事件通知父组件修改了
    (也不是最优解,每个属性都要绑定一个,和上一个方案差不多,
    因为计算属性监听的修改是对应的属性直接赋值才会触发 set)
    比如:data.xxx=xxx? 才会触发但是我们修改属性是 data.xxx.value=xxx
  • 计算属性结合代理
    父组件
<template>
<childComp v-model="data" @updateProps="handleUpdateProps" />
</template>

<script setup lang="ts">
import { reactive } from "vue";
import childComp from "./child.vue";
const data = reactive({
keyWord: "hello",
select: ["a", "b", "c"],
});
const handleUpdateProps = (obj) => {
data.keyWord = obj.keyWord;
data.select = obj.select;
};
</script>

<style scoped lang="scss"></style>

子组件

<script setup lang="ts">
import { computed } from "vue";
const _ = defineProps({
modelValue: {
type: Object,
default: () => {},
},
});

//不使用update:modelValue 只是想把变化显示出来
const emit = defineEmits(["updateProps"]);
const model = computed({
get() {
const proxy = new Proxy(_.modelValue, {
get(tar, val) {
return Reflect.get(tar, val);
},
set(tar, key, val) {
emit("updateProps", {
...tar,
[key]: val,
});
return true;
},
});
return proxy;
},
set(val) {
emit("updateProps", val);
},
});
</script>

函数抽离

export function useVModel(props, propName,emit) {
const model = computed({
get() {
const proxy = new Proxy(props[propName], {
get(tar, val) {
return Reflect.get(tar, val);
},
set(tar, key, val) {
emit(`update:${propName}`, {
...tar,
[key]: val,
});
return true;
},
});
return proxy;
},
set(val) {
emit("updateProps", val);
},
});
}