TOC
provide / inject
provide / inject
Vue.js 2.2.0 版本后新增的 API.
还发出了这样的警告provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
我们这次的目标就是让该 API 实现响应式。为了了解这个 API 为什么无法响应式呢,我们先来看看源码
走进初始化
export function initProvide(vm: Component) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function' ? provide.call(vm) : provide
}
}
export function initInjections(vm: Component) {
const result = resolveInject(vm.$options.inject, vm)
if (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])
}
})
toggleObserving(true)
}
}
接着我们来看看初始化的顺序
vm._self = vm
initLifecycle(vm) // 初始化一些实例上的值
initEvents(vm) // 将父组件的事件监听回到绑定子组件的_events上
initRender(vm) // 给实例增加上关于render的属性
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm) // 对Props进行迁移,并对方法 计算属性 data 进行解剖和响应绑定
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
从以上的信息我们可以了解到,Provide无法响应式的真正原因其实就是没有加依赖收集,所以无论你如何调用$watch,都是无效的,你都无法以任何的形式进行监听。接着我们发现Provide的初始化发生在state之后,并且Provide支持函数的方式进行导入,所以我们可以在这里做一些文章
利用Data
我来演示一下我的方法
首先是父组件,利用data来为foo对象增加依赖收集机制,紧接着在provide完成初始化之后,对_provide.foo进行监听,这时候data中的foo中的dep就会增加依赖到这个新生成的watcher中。接下来,我们需要在子组件中使用他
export default {
data() {
return {
foo: {
a: 1
}
};
},
provide() {
return {
foo: this.foo
};
},
created() {
this.$watch(
"_provide.foo",
function() {
console.log("changne");
},
{
deep: true
}
);
},
}
<template>
<div class="hello">
{{foo.a}}
</div>
</template>
export default {
name: "HelloWorld",
inject: ["foo"],
components: {
anode
},
created() {
setInterval(() => {
this.foo.a = Date.now();
}, 1000);
}
};
其实在完成这一步之后,我们的foo正在被两个watcher所监听,分别是父组件中触发的$watch,已经子组件中的render watcher . 这样一但子组件修改foo.a 这两个watcher都会接收到更新。
如此,我们的目的就达到了,但是在这里提醒大家,使用这个东西前,你最好清楚的明白你需要什么,以及你到底做了什么。
我想到用这个东西的需求是,我需要写一个需要保存用户进度,角色等级的一个H5小游戏,如此,在父组件中声明一个比较大的笔记本对象,然后子组件修改这个笔记本对象中的内容,在父组件中进行观察者模式监听,并且利用防抖函数不停的存到后端。在局势可以掌控的情况下,我认为这种模式会带来一定的便利。
tip: 这里提醒一下各位 这种方法实际用起来其实并不好用 我更加推荐直接provide父对象的实例 在子组件中直接修改父对象实例上的属性,主要原因还是上面这种方法一但遇到异步,就需要在data里建立一个引用嵌套层。