You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
//初始化inject和provide// expose real selfvm._self=vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm,'beforeCreate')initInjections(vm)// resolve injections before data/propsinitState(vm)initProvide(vm)// resolve provide after data/propscallHook(vm,'created')
exportfunctioninitInjections(vm: Component){constresult=resolveInject(vm.$options.inject,vm)// 1、根据注册的inject,通过$parent向上查找对应的provideif(result){toggleObserving(false)Object.keys(result).forEach(key=>{/* istanbul ignore else */if(process.env.NODE_ENV!=='production'){defineReactive(vm,key,result[key],()=>{warn(`Avoid mutating an injected value directly since the changes will be `+`overwritten whenever the provided component re-renders. `+`injection being mutated: "${key}"`,vm)})}else{defineReactive(vm,key,result[key])// 2、进行响应式监听}})toggleObserving(true)}}
exportfunctionresolveInject(inject: any,vm: Component): ?Object{if(inject){// inject is :any because flow is not smart enough to figure out cachedconstresult=Object.create(null)constkeys=hasSymbol
? Reflect.ownKeys(inject)
: Object.keys(inject)for(leti=0;i<keys.length;i++){// 遍历所有inject为其赋值constkey=keys[i]// #6574 in case the inject object is observed...if(key==='__ob__')continueconstprovideKey=inject[key].fromletsource=vmwhile(source){// 核心原理:通过$parent一层一层向上查找祖先节点的provide,找到则对inject进行赋值if(source._provided&&hasOwn(source._provided,provideKey)){result[key]=source._provided[provideKey]break}source=source.$parent}if(!source){if('default'ininject[key]){constprovideDefault=inject[key].defaultresult[key]=typeofprovideDefault==='function'
? provideDefault.call(vm)
: provideDefault}elseif(process.env.NODE_ENV!=='production'){warn(`Injection "${key}" not found`,vm)}}}returnresult}}
vue源码provide/inject原理解析
依赖注入
当深层嵌套的子孙组件想要拿到父组件的数据时,我们可以使用provide和inject。官网原理图:
使用案例:
我们假设组件嵌套结构如下:
我们通过provide和inject将组件TodoList的属性直接传给组件TodoListStatistics:
上例中的provide定义为一个对象。如果需要在provide里使用data中的属性,需要把provide定义成一个方法,否则会报错。
依赖注入的优缺点如下:
优点:
* 祖先组件不需要知道哪些后代组件使用它提供的数据;
* 后代组件不需要知道被注入的数据来自哪里;
缺点:
* 组件间的耦合较为紧密,不易重构;
* 提供的属性是非响应式的;解决方案见官方文档
源码解读
组件实例初始化的时候会调用Vue.prototype._init,通过下面源码,我们可以知道:
inject、provide的初始化时间在生命周期钩子函数beforeCreate之后,created之前。
**initInjections(vm)**解析inject是在初始化data/props之前,
**initProvide(vm)**解析provide是在初始化data/props之后。
这也符合数据初始化的一个处理逻辑。
Vue.prototype._init源码:
initInjections函数,功能是**获取组件注册的inject属性合集,然后遍历合集进行响应式监听。**源码如下:
resolveInject函数,功能是通过$parent一层层向上查找祖先节点的数据,直到找到对应于�inject的provide数据。
initProvide函数,该方法单纯把组件注册的provide值,赋值给vm._provided,resolveInject中有使用到。
总结
依赖注入,其核心原理就是通过$parent向上查找祖先组件中的provide,找到则赋值给对应的inject即可。
仔细一思量,老铁们会发想,依赖注入原理和JavaScript中的instanceof操作符原理有异曲同工之处。在instanceof中,通过__proto__向原型链中查找,如果__proto__与构造函数的prototype相等则返回true。哈哈哈哈哈,这就是研究原理的有趣之处。
The text was updated successfully, but these errors were encountered: