医院网站建设策划案模板,网站外链快速建设,深圳哪里有做网站推广的,网站制作公司网站设计公司Vue3封装知识点#xff08;三#xff09;依赖注入#xff1a;project和inject详细介绍 文章目录 Vue3封装知识点#xff08;三#xff09;依赖注入#xff1a;project和inject详细介绍一、project和inject是什么二、为了解决什么问题三、project和inject如何使用1.provid…Vue3封装知识点三依赖注入project和inject详细介绍 文章目录 Vue3封装知识点三依赖注入project和inject详细介绍一、project和inject是什么二、为了解决什么问题三、project和inject如何使用1.provide()2.inject()3.和响应式数据配合使用4.使用 Symbol 作注入名 四、实现原理vue3中的实现原理 五、优点和缺点六、总结 一、project和inject是什么
父组件中提供数据并在子组件中注入这些数据从而实现了组件之间的数据传递。简单来说就是父组件向子组件传值的一个方式。
二、为了解决什么问题
vue3官网对依赖注入解决的问题这样介绍的
通常情况下当我们需要从父组件向子组件传递数据时会使用 props。想象一下这样的结构有一些多层级嵌套的组件形成了一颗巨大的组件树而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下如果仅使用 props 则必须将其沿着组件链逐级传递下去这会非常麻烦 注意虽然这里的 Footer 组件可能根本不关心这些 props但为了使 DeepChild 能访问到它们仍然需要定义并向下传递。如果组件链路非常长可能会影响到更多这条路上的组件。这一问题被称为“prop 逐级透传”显然是我们希望尽量避免的情况。
provide 和 inject 可以帮助我们解决这一问题。 一个父组件相对于其所有的后代组件会作为依赖提供者。任何后代的组件树无论层级有多深都可以注入由父组件提供给整条链路的依赖。 这可以说就是provide和inject的来源为了解决Prop 逐级透传问题的问题
三、project和inject如何使用
这里部分也采用的官网的介绍讲的很清楚
1.provide()
提供一个值可以被后代组件注入。 类型 function provideT(key: InjectionKeyT | string, value: T): void详细信息 provide() 接受两个参数第一个参数是要注入的 key可以是一个字符串或者一个 symbol第二个参数是要注入的值。 当使用 TypeScript 时key 可以是一个被类型断言为 InjectionKey 的 symbol。InjectionKey 是一个 Vue 提供的工具类型继承自 Symbol可以用来同步 provide() 和 inject() 之间值的类型。 与注册生命周期钩子的 API 类似provide() 必须在组件的 setup() 阶段同步调用。 示例 script setup
import { ref, provide } from vue
import { fooSymbol } from ./injectionSymbols// 提供静态值
provide(foo, bar)// 提供响应式的值
const count ref(0)
provide(count, count)// 提供时将 Symbol 作为 key
provide(fooSymbol, count)
/script2.inject()
注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。 类型 // 没有默认值
function injectT(key: InjectionKeyT | string): T | undefined// 带有默认值
function injectT(key: InjectionKeyT | string, defaultValue: T): T// 使用工厂函数
function injectT(key: InjectionKeyT | string,defaultValue: () T,treatDefaultAsFactory: true
): T详细信息 第一个参数是注入的 key这个key就是用来和provide设定的第一个参数进行匹配。Vue 会遍历父组件链通过匹配 key 来确定所提供的值。如果父组件链上多个组件对同一个 key 提供了值那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过 key 匹配到值inject() 将返回 undefined除非提供了一个默认值。 第二个参数是可选的即在没有匹配到 key 时使用的默认值。 第二个参数也可以是一个工厂函数用来返回某些创建起来比较复杂的值。在这种情况下你必须将 true 作为第三个参数传入表明这个函数将作为工厂函数使用而非值本身。 与注册生命周期钩子的 API 类似inject() 必须在组件的 setup() 阶段同步调用。 当使用 TypeScript 时key 可以是一个类型为 InjectionKey 的 symbol。InjectionKey 是一个 Vue 提供的工具类型继承自 Symbol可以用来同步 provide() 和 inject() 之间值的类型。 示例 假设有一个父组件已经提供了一些值如前面 provide() 的例子中所示 script setup
import { inject } from vue
import { fooSymbol } from ./injectionSymbols// 注入不含默认值的静态值
const foo inject(foo)// 注入响应式的值
const count inject(count)// 通过 Symbol 类型的 key 注入
const foo2 inject(fooSymbol)// 注入一个值若为空则使用提供的默认值
const bar inject(foo, default value)// 注入一个值若为空则使用提供的函数类型的默认值
const fn inject(function, () {})// 注入一个值若为空则使用提供的工厂函数
const baz inject(factory, () new ExpensiveObject(), true)
/script3.和响应式数据配合使用
project和inject也可以传递响应式数据和方法但是传递响应式数据的时候官网做了一个推荐使用
当提供 / 注入响应式的数据时建议尽可能将任何对响应式状态的变更都保持在供给方组件中。这样可以确保所提供状态的声明和变更操作都内聚在同一个组件内使其更容易维护。
如下面这个例子
!-- 在供给方组件内 --
script setup
import { provide, ref } from vueconst location ref(North Pole)function updateLocation() {location.value South Pole
}provide(location, {location,updateLocation
})
/script!-- 在注入方组件 --
script setup
import { inject } from vueconst { location, updateLocation } inject(location)
/scripttemplatebutton clickupdateLocation{{ location }}/button
/template4.使用 Symbol 作注入名
官网同样对注入名做了推荐如果你正在构建大型的应用包含非常多的依赖提供或者你正在编写提供给其他开发者使用的组件库建议最好使用 Symbol 来作为注入名以避免潜在的冲突。可能平常很少用这里对symbol做一下简单介绍。
symbol
在计算机编程中Symbol是一种基本数据类型是在ECMAScript 6 (ES6) 中引入的新特性。它是一种原始数据类型与数字、字符串、布尔值等类似。
Symbol是一种唯一且不可变的数据类型每个Symbol值都是唯一的不会与其他任何值相等包括其他Symbol值。这使得Symbol非常适合用作对象属性的标识符以确保不会发生属性名冲突。
创建Symbol可以使用全局Symbol函数例如
const mySymbol Symbol();也可以传递一个可选的描述字符串作为Symbol的标识这个描述字符串对于调试和输出Symbol时是可选的但并不影响Symbol的唯一性例如
const mySymbol Symbol(my unique symbol);应用到project和inject中
通常推荐在一个单独的文件中导出这些注入名 Symbol
// keys.js
export const myInjectionKey Symbol()// 在供给方组件中
import { provide } from vue
import { myInjectionKey } from ./keys.jsprovide(myInjectionKey, { /*要提供的数据
*/ });// 注入方组件
import { inject } from vue
import { myInjectionKey } from ./keys.jsconst injected inject(myInjectionKey)四、实现原理
翻了不少大佬的文章发现有些理解不同有说到利用到原型链的有说没有用到的仔细理了一下发现是因为vue2和vue3的组件之间的数据绑定和响应式系统不同导致的结果。
在vue2中组件实例方法和属性的继承是通过原型链来实现的而provide 和 inject 就是基于原型链的属性访问来实现跨组件通信。当一个组件通过 provide 提供数据它会将这些数据添加到其原型链上然后子组件通过 inject 可以在原型链上查找并访问这些数据。
在 Vue 3 中 组件的实例方法和属性的继承不再依赖于原型链而是 引入了 Composition API它采用了一种不同的方式来组织组件的代码和状态。组件的选项被重构为一个配置对象其中 setup 函数用于定义组件的响应式数据、计算属性、方法等。这些选项不再依赖于原型链而是直接导出给组件实例。
这个改进带来了以下好处
更稳定的数据提供在 Vue 3 中每个组件实例都有自己的私有作用域不会受到原型链的影响因此不存在 Vue 2 中的潜在问题。更好的类型检查在 Vue 3 中TypeScript 或 Flow 等类型检查工具可以更准确地检测到 inject 注入的数据类型。
因为平常大多数是在Vue3中使用接下来详细介绍一下在vue3中的原理。
vue3中的实现原理
provide 的原理
provide 是在父组件中使用的选项用于提供数据给子组件。它实际上是一个函数它会在父组件实例上创建一个名为 _provided 的对象。_provided 对象存储了提供给子组件的数据而且这些数据会在整个组件树中可用子组件可以通过inject选项来访问这些数据。。当父组件提供的数据发生变化时Vue 3 的响应式系统会追踪这些变化并通知所有依赖这些数据的子组件进行更新。
inject 的原理
子组件通过inject选项声明需要注入的数据可以是一个数组、一个对象或一个函数。这些声明告诉Vue 3要从父组件的提供数据中获取哪些属性。当子组件访问通过inject注入的数据时Vue 3会在组件树中向上搜索父组件直到找到包含提供数据的组件或到达根组件。一旦找到包含提供数据的组件Vue 3会从该组件的_provided属性中获取相应的数据。如果提供的数据是响应式的子组件将自动成为这些数据的依赖当提供的数据发生变化时子组件将被通知并进行更新。
五、优点和缺点
该方法可以说很方便解决了父组件给多级子组件传值的问题但是同时也有一定的局限性下面分析一下优点和缺点
优点
解耦合Decoupling provide 和 inject 有助于降低组件之间的耦合度。组件不需要直接了解其依赖项的实现细节而是通过注入来访问这些依赖这使得组件更加独立和可复用。可测试性Testability 依赖注入使得单元测试更加容易。你可以轻松地注入模拟对象或测试替身以测试组件的行为而不需要实际的外部依赖。可维护性Maintainability 通过将依赖项提取到外部并通过 provide 注入代码变得更清晰和易于维护。这对于大型应用程序来说特别有价值。可扩展性Scalability provide 和 inject 可以用于共享全局配置、服务或状态管理等全局性的依赖项使得应用程序更容易扩展和维护。
缺点
复杂性Complexity 对于小型应用来说使用 provide 和 inject 可能会增加一些不必要的复杂性。这些特性最有价值的地方通常在大型和复杂的应用程序中。容易滥用Overuse 有时开发人员可能会滥用 provide 和 inject将所有东西都注入到组件中导致不必要的复杂性和混乱。需要谨慎权衡。不适用于所有场景Not Suitable for All Scenarios provide 和 inject 更适合用于共享全局配置和服务等情况对于局部的、仅在某个组件内部使用的依赖项使用 props 更合适。
六、总结
总的来说provide 和 inject 是一种强大的依赖注入机制特别适用于大型和复杂的应用程序以提高代码的可维护性、可测试性和可扩展性。但在小型应用中可能会增加一些复杂性需要谨慎使用。在使用时需要根据具体的场景和需求来判断是否使用这些特性在组件封装过程中也可以根据情况进行使用。
往期更新
vue3封装知识点一组件之前传值
vue3封装知识点二v-model的应用
参考资料
vue官网