> 文章列表 > vue 组件封装单向数据流

vue 组件封装单向数据流

vue 组件封装单向数据流

vue 组件封装单向数据流

vue 组件封装想要杜绝双休数据流,不然离代码shi山越来越近

在 Vue 中,双向绑定是通过将数据流向子组件并在子组件中使用 v-model 实现的。如果在子组件中直接修改了父组件中的数据,就会打破单向数据流的规则,从而导致一些问题。

具体来说,如果在子组件中直接修改了父组件中的数据,那么这个修改会立即反映在父组件中,但是父组件并不会触发更新,也就是说父组件的视图不会更新。这会导致父组件和子组件之间的数据不一致,进而导致一些难以排查的 bug。

本文将介绍 vue 组件中封装单向数据流的方法。

在 vue 组件中,为了保证数据的单向流动,我们通常会将数据定义在父组件中,通过 props 传递给子组件。子组件通过事件向父组件发送数据,保证数据的单向流动。

下面是封装的一个简单的不考虑单项数据流的组件:

在这里插入图片描述

  • 具体实现方法:
    1. 在父组件通过 v-model 将数据传递给子组件
    2. 子组件通过props接受参数
    3. 子组件直接使用v-model 使用传递过来的值

父组件:

<script setup lang="ts">
import { ref } from "vue";
import SearchBar from "./SearchBar.vue";const searchData = ref({keyword: "",placeholder: "请输入你想要查询的关键字",options: [{ label: "视频", value: "video" },{ label: "文章", value: "article" },{ label: "用户", value: "user" },],selectedValue: "video",
});
</script><template><SearchBar v-model="searchData"></SearchBar></template>
<style scoped>
</style>

子组件:

<template><el-input v-model="modelValue.keyword" :placeholder="modelValue.placeholder"><template #prepend><el-selectv-model="modelValue.selectedValue"placeholder="Select"style="width: 85px"><el-optionv-for="item in modelValue.options":key="item.value":label="item.label":value="item.value"></el-option></el-select></template><template #append><el-button :icon="Search" /></template></el-input>
</template><script setup>
import { Search } from "@element-plus/icons-vue";
const props = defineProps({modelValue: {type: Object,required: true,},
});
</script><style scoped></style>

数据变化视图:

在这里插入图片描述

这样会产生的问题是:

如果在子组件中直接修改了父组件中的数据,那么这个修改会立即反映在父组件中,但是父组件并不会触发更新,也就是说父组件的视图不会更新。这会导致父组件和子组件之间的数据不一致,进而导致一些难以排查的 bug。

所以需要杜绝类似的问题,就不能打破单项数据流。

初步解决办法:

通过model-value**@update:modelValue 解决**
在这里插入图片描述

具体实现方法如下:

  1. 在父组件中定义数据并将数据通过 props 传递给子组件。
  2. 在子组件中定义一个名为 emit 的方法,用于向父组件发送数据。
  3. 在子组件中使用 $emit 方法调用 emit 方法,并传递需要发送的数据。

示例代码如下:

<template><el-input:model-value="modelValue.keyword"@update:modelValue="handleKeywordChange":placeholder="modelValue.placeholder"><template #prepend><el-selectv-model="modelValue.selectedValue"placeholder="Select"style="width: 85px"><el-optionv-for="item in modelValue.options":key="item.value":label="item.label":value="item.value"></el-option></el-select></template><template #append><el-button :icon="Search" /></template></el-input>
</template><script setup>
import { Search } from "@element-plus/icons-vue";
const props = defineProps({modelValue: {type: Object,required: true,},
});
const emit = defineEmits(["update:modelValue"]);function handleKeywordChange(value) {emit("update:modelValue", { ...props.modelValue, keyword: value });
}
</script><style scoped></style>

但是数据多的话这样和麻烦。真的很麻烦

进阶解决办法:
在这里插入图片描述

通过computed 和**get set 解决**

具体实现方法如下:(官方建议的)

  1. 子组件将父组件传递的值通过***computed*** 处理。
  2. 在子组件的***computed*** 属性中设置 get() {}set(val) {} 方法。
  3. 父子组件直接使用 v-model

示例代码如下:

<template><el-input v-model="keyword" :placeholder="modelValue.placeholder"><template #prepend><el-selectv-model="modelValue.selectedValue"placeholder="Select"style="width: 85px"><el-optionv-for="item in modelValue.options":key="item.value":label="item.label":value="item.value"></el-option></el-select></template><template #append><el-button :icon="Search" /></template></el-input>
</template><script setup>
import { Search } from "@element-plus/icons-vue";
import { computed } from "vue";
const props = defineProps({modelValue: {type: Object,required: true,},
});
const emit = defineEmits(["update:modelValue"]);
const keyword = computed({get() {return props.modelValue.keyword;},set(val) {emit("update:modelValue", { ...props.modelValue, keyword: val });},
});
</script><style scoped></style>

但是数据多和数据结构是复杂数据类型的时候,要么一个一个设置getset。不然会导致改复杂数据类型的时候,set 不会触发。

更好的解决办法:
在这里插入图片描述

通过***computed***get set 和***Proxy* 解决

具体实现方法如下:

  1. 子组件将父组件传递的值通过***computed*** 处理。
  2. 在子组件的***computed*** 属性中设置 get() {} get方法获取的值是通过*Proxy 代理过的*。
  3. 父子组件直接使用 v-model

示例代码如下:

<template><el-input v-model="model1.keyword" :placeholder="model1.placeholder"><template #prepend><el-selectv-model="model1.selectedValue"placeholder="Select"style="width: 85px"><el-optionv-for="item in model1.options":key="item.value":label="item.label":value="item.value"></el-option></el-select></template><template #append><el-button :icon="Search" /></template></el-input>
</template><script setup>
import { Search } from "@element-plus/icons-vue";
import { computed } from "vue";
const props = defineProps({modelValue: {type: Object,required: true,},
});
const emit = defineEmits(["update:modelValue"]);
const model1 = computed({get() {return new Proxy(props.modelValue, {set(obj, name, val) {console.log("emit", val);emit("update:modelValue", { ...obj, [name]: val });return true;},});},
});
</script><style scoped></style>

通过以上方法,我们可以很容易地实现 vue 组件中的单向数据流封装,保证了数据的单向流动,降低了组件之间数据流动的耦合度,提高了组件的可维护性。