不二博客

V1

2022/09/04阅读:26主题:山吹

provide/inject 依赖注入

依赖注入

在我们日常开发的过程中,我们需要从父组件向子组件传递数据,会使用 props

如果组件层级过多,使用 props 沿着组件链逐级传递下去,十分的麻烦。

而在 Vue 中使用 provideinject 来帮助我们解决这一问题。

父组件可以为其所有的后代组件提供依赖,无论层级有多深。

<script lang="ts" setup>
import { provide, inject } from "vue";
</script>

provide

provide 可以提供一个值,可以被后代组件注入,它是一个后代组件依赖的提供者。

// parent.vue
import { provide } from "vue";

provide("data", {
  name"Buerjia",
  age23,
});

inject

inject 用于声明 provide 提供的依赖。

// chlid.vue
import { inject } from "vue";
const data = inject("data");

console.log(data);
// log
// { name: "Buerjia", age: 23 }

当父组件没有传入提供给我们相关依赖的情况下,我们还可以为 inject 设置默认值。

// chlid.vue
import { inject } from "vue";
const data = inject("data", {
  name"jack",
  age24,
});

console.log(data);
// log
// { name: "jack", age: 24 }

思考?

在多人协作开发的项目中,我们很难确保 provide 提供的 key 保持唯一性,那么我们可以这么去解决它呢?

使用 Symbol 作为注入名

我们都知道 es6 中给我们提供了一个新的数据类型 Symbol ,代表着独一无二的值,我们可以将 Symbol 作为 key 值来比避免谈对协作中的命名冲突。

// ./injectionSymbols.ts

export const dataSymbol = Symbol();
// parent.vue
import { provide } from "vue";
import { dataSymbol } from "./injectionSymbols";

provide(dataSymbol, {
  name"Buerjia",
  age23,
});
// child.vue
import { inject } from "vue";
import { dataSymbol } from "../injectionSymbols";

const data = inject(dataSymbol, {
  name"jack",
  age24,
});

响应式的数据注入

provide 提供的数据本身就是响应式的,我们只需要修改 provide 提供的数即可。

// parent.vue
import { ref, provide } from "vue";
import { dataSymbol } from "../injectionSymbols";
import { dataRaw } from "../types/inject";

const data = ref<dataRaw>({
  name: "",
  age: 18,
});

provide(dataSymbol, data.value);

setTimeout(() => {
  data.value.name = "Mary";
}, 3000);

当我在我们日常的业务场景中我们不仅仅通过父组件去更新状态,我们还需要通过子组件去更新状态。

Vue 的官方推荐我们在创建 provide 的时候暴露给 inject 修改的状态的的方法。

// parent.vue
import { ref, provide } from "vue";
import { dataSymbol } from "../injectionSymbols";
import { dataRaw } from "../types/inject";

const data = ref<dataRaw>({
  name: "Buerjia",
  age: 18,
});

const updateData = (name: string) => {
  data.value.name = name;
};

provide(dataSymbol, {
  data: data.value,
  updateData,
});
// child.vue
import { inject } from "vue";
import { dataSymbol } from "../injectionSymbols";

const { data, updateData } = inject(dataSymbol, {
  data: {
    name: "Jack",
    age: 24,
  },
});

setTimeout(() => {
  updateData && updateData("Mary");
}, 3000);

当然有些时候我们并不希望我们的数据被改变,我们使用使用 readonly() 来包装,当我们尝试修改时将会失败。

// parent.vue
import { ref, provide, readonly } from "vue";
import { dataSymbol } from "../injectionSymbols";
import { dataRaw } from "../types/inject";

const data = ref<dataRaw>({
  name: "Buerjia",
  age: 18,
});

provide(dataSymbol, readonly(data.value));
// child.vue
import { inject } from "vue";
import { dataSymbol } from "../injectionSymbols";

const data = inject(dataSymbol, {
  name: "Jack",
  age: 24,
});

data.name = "Mary";
// [Vue warn] Set operation on key "name" failed: target is readonly.

为 provide/inject 添加类型

provideinject 通常在不同组件中运行,所以我们需要为其添加类型支持。

Vue 为我们提供了 InjectionKey 的类型接口,可以 provideinject 中同步注入值的类型。

import { InjectionKey } from "vue";
import { dataRaw } from "./types/inject";

export const dataSymbol = Symbol() as InjectionKey<dataRaw>;

总结

provide/inject 依赖注入相比 props,极大的减少了依赖嵌套,使得代码更加的简洁并且利于维护,日常开发中要思考多加思考是否能够依赖注入解决问题。

最后

感谢你的阅读~

如果你有任何的疑问欢迎您在后台私信,我们一同探讨学习!

如果觉得这篇文章对你有所帮助,点赞、在看是最大的支持!

分类:

前端

标签:

Vue.js

作者介绍

不二博客
V1