说明
v-sync
是用来同步组件的prop
和它上下文中的变量的,这里面有一个非常有趣的小技巧:$watch
会返回一个unwatch
的函数来取消监听。
源码解读
const SYNC_HOOK_PROP = '$v-sync';
/**
* v-sync directive
*
* Usage:
* v-sync:component-prop="context prop name"
*
* If your want to sync component's prop "visible" to context prop "myVisible", use like this:
* v-sync:visible="myVisible"
*/
export default {
bind(el, binding, vnode) {
const context = vnode.context; // 获取上下文,即组件渲染的作用域中
const component = vnode.child; // 返回组件实例
const expression = binding.expression; // 返回表达式
const prop = binding.arg; // 返回传参
if (!expression || !prop) { // 如果没有表达式或者传参
console.warn('v-sync should specify arg & expression, for example: v-sync:visible="myVisible"');
return;
}
if (!component || !component.$watch) { // 判断是否包含 watch 来确保是 Vue 的组件
console.warn('v-sync is only available on Vue Component');
return;
}
// 将组件的 prop 与上下文中的信息同步,返回取消 watch 的函数
const unwatchContext = context.$watch(expression, (val) => {
component[prop] = val;
});
// 将上下文中的信息和组件的 prop,返回取消 watch 的函数
const unwatchComponent = component.$watch(prop, (val) => {
context[expression] = val;
});
// 保存到组件实例中
Object.defineProperty(component, SYNC_HOOK_PROP, {
value: {
unwatchContext,
unwatchComponent
},
enumerable: false
});
},
unbind(el, binding, vnode) {
const component = vnode.child;
if (component && component[SYNC_HOOK_PROP]) { // 取消监听
const { unwatchContext, unwatchComponent } = component[SYNC_HOOK_PROP];
unwatchContext && unwatchContext();
unwatchComponent && unwatchComponent();
}
}
};