api文档
源码地址
分析
import { instance } from './components' export function provide(key, value) { // provide 必须要用在setup中 因为要拿实例 if (!instance) return // 如果没有实例 则直接返回 说明这个方法没有在setup中使用 let parentProvides = instance.parent && instance.parent.provide // 获取父亲的provide + 自己的provide // 第一次应该创建一个全新的provides,后续就用自己的 let currentProvides = instance.provides // 自己的 if (currentProvides === parentProvides) { // provides: parent ? parent.provides : Object.create(null), 默认是相同的 // 说明是第一次 currentProvides = instance.provides = Object.create(parentProvides) // 这样就可以让子不能修改父provide } // 下一个儿子会获取所有的provide currentProvides[key] = value } // 前置条件 组件的实例的provides 指向的是parent的provides // 1) 我们先取出自己父亲身上的provides ,默认是父亲的provides,创建一个新的provides 重新给自己的provides赋值 // 2) 在上面添加属性 // 3) 再次provide的时候, 拿父亲的provides,再取出自己的provides,此时不想等了,直接用自己身上的添加属性 // provide('a', 1) // provide('b', 1) // provide('c', 1) // 不能覆盖 export function inject(key, defaultValue?) { // debugger if (!instance) return const provides = instance.parent && instance.parent.provides // 父组件的provides if (provides && key in provides) { return provides[key] } else { return defaultValue } }
首先vue创建组件实例的时候会在自己身上默认初始化一个provides对象,这样就可以用===去比较是否进行provide属性,没有则用父亲的provides属性当原型新建一个对象(这样能避免相同的值被覆盖)
总结
实际上vue用到了原型式继承(Object.create)的方法,让每一层能够去获取到上层提供的属性,并且让每一层都能隔离开,无法影响到上层的数据,这样就形成了一个单项数据流