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-select
        v-model="modelValue.selectedValue"
        placeholder="Select"
        style="width: 85px"
      >
        <el-option
          v-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-select
        v-model="modelValue.selectedValue"
        placeholder="Select"
        style="width: 85px"
      >
        <el-option
          v-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-select
        v-model="modelValue.selectedValue"
        placeholder="Select"
        style="width: 85px"
      >
        <el-option
          v-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-select
        v-model="model1.selectedValue"
        placeholder="Select"
        style="width: 85px"
      >
        <el-option
          v-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 组件中的单向数据流封装,保证了数据的单向流动,降低了组件之间数据流动的耦合度,提高了组件的可维护性。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值