password
Created
Feb 19, 2023 03:50 PM
type
Post
status
Published
date
Oct 26, 2021
slug
summary
Vue中的依赖注入实现机制
tags
Vue
Vue源码
category
源码
icon
依赖注入是Vue中重要的, 解决跨多层级组件传值的方式之一. 与React中的Provider具有类似的效果
Vue2中的依赖注入
// 在其上(N)层组件中提供一个provide函数 export default { provide: function() { return { foo: this.foo } } } // 在子孙组件中进行注入 export default { inject: ['foo'] }
Vue3中的依赖注入
Vue3中除了直接使用与Vue2相同的语法外, 还支持Hook化的依赖注入声明方式
// provider import { provide, ref } from 'vue' export default { setup() { const theme = ref('dark') provide('theme', theme) } } // inject import { inject } from 'vue' export default { setup() { // inject支持第二个参数, 作为依赖注入的默认值 const theme = inject('theme', 'light') return { theme } } }
provide API
provide可以被其上级任意一个组件提供, 实例创建的时候, 会使用其最邻近上级的provide的值.
也就意味着, 即使同一个provide的key被定义多次, 组件只会以其最近的父节点值为准
function provide(key, value) { let provides = currentInstance.provides const parentProvides = currentInstance.parent && currentInstance.parent.provides if (parentProvides === provides) { provides = currentInstance.provides = Object.create(parentProvides) } provides[key] = value } // 在实例创建过程中, 将provides对象指向父组件实例的provides对象 const instance = { // ... provides: parent ? parent.provides : Object.create(appContext.provides) // ... }
inject API
function inject(key, defaultValue) { const instance = currentInstance || currentRenderingInstance if (instance) { const provides = instance.provides // 能够从父级找到provide, 且有对应的key, 返回该value if (key in provides) { return provides[key] // 否则, 如果有默认值, 就返回这个默认值 } else if (arguments.length > 1) { return defaultValue // 否则, 如果是非正式环境, 抛出找不到的警告 } else if ((process.env.NODE_ENV !== 'production')) { // throw warn } } }
为何要用依赖注入?
乍一看过去, 其实依赖注入的机制完全可以通过定义一个数据变量, 然后不同组件之间引用和修改的方式来进行啊, 那依赖注入的机制的意义何在呢?
这里其实主要有这几点:
- 直接使用全局数据的话数据的可溯性差, 如果使用全局数据, 我们并不知道这个数据在哪里被修改.
- 依赖注入只作用在该组件的子孙...组件中, 并没有影响到其他的组件
- 依赖注入饿后代组件并不需要知道注入的数据来自哪里, 只需要注入并使用即可. 而对于定义变量引入的方式来说, 我们需要知道它是在哪里被定义的, 才能够引入它
但是, 这里随之衍生的问题是: 祖先组件并不知道有哪些组件使用了它提供的数据, 后代组件也不需要知道注入的数据来自哪里. 虽然这是一种很好的解耦机制, 但是相互不知, 这里就有可能会产生混乱
因此, 一般来讲, 建议在公共类库中进行使用, 但业务的组件中则并不推荐使用, 业务共享可以考虑使用
Pinia
等状态管理工具来实现