明天在我的项目里对 vant 的弹窗组件 popup 又包了一层作为子组件 Child,以实现弹窗的定制化。
父组件 Father 传递数据到 Child。
props: {showDialog: Boolean}
props 是一个 proxy 对象。子组件能够用 computed 计算属性对 props.showDialog 进行转换。
const visible = computed({get: ()=>props.showDialog,
set: newVal=>{context.emit('toggleStatus', newVal)
}
})
但明天次要是为了看看 ref 干了什么。
setup(props, context){const {showDialog} = toRefs(props)
const visible = ref(showDialog)
watch(()=>visible.value, newVal=>{context.emit('toggleStatus', newVal)
})
return {visible,}
}
对象构造会使之失去响应性,而 toRefs
包裹的对象能够防止这一点。
子组件创立 visible
变量,用来管制 popup
组件是否显示。
用 ref
接管 showDailog
属性,返回一个响应式且可变的 ref
对象。再通过监测 visible.value
值的变动,触发父组件 showDialog
更新值。嗯,看起来如同放弃了对源头的响应式连贯了!我的子组件必定能够接管到 Father 的更新了!
但这样做有个问题。控制台很快报出⚠️
reactivity.esm-bundler.js:336 Set operation on key “showDialog” failed: target is readonly. Proxy {showDialog: true}
toRefs
文档原文:
将响应式对象转换为一般对象,其中后果对象的每个 property 都是指向原始对象相应 property 的
ref
。
那么,showDailog
是怎么变成 ref 对象的呢?
toRefs
办法遍历其参数 object
,给object
的每个值加上 __v_isRef = true
属性:
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
constructor(private readonly _object: T, private readonly _key: K) {}
get value() {return this._object[this._key]
}
set value(newVal) {this._object[this._key] = newVal
}
}
__v_isRef
标记该对象为 ref 对象。
接下来看看 ref 办法的性能。
export function ref(value?: unknown) {return createRef(value)
}
function createRef(rawValue: unknown, shallow = false) {if (isRef(rawValue)) {return rawValue}
return new RefImpl(rawValue, shallow)
}
ref 接管的 showDailog
,当判断是一个 ref 对象时,返回它本人。当弹窗组件被敞开,visible
被间接赋值为 false,实际上就是在挑战 props 的 readonly 属性。控制台正告,并且拒绝执行你的赋值操作。
既然如此,那就给 visible
初始化为 false
,再监测showDialog
触发 visible
即可。
const visible = ref(false)
const toggle = (newVal: boolean)=>(visible.value=newVal)
watch(()=>showDialog.value, newVal=>{toggle(newVal)
})
watch(()=>visible.value, newVal=>{console.log('visible=------', visible)
context.emit('toggleStatus', newVal)
})
问题还没完结。如果是 ref 函数接管的参数不是 ref 对象呢?换句话说,new RefImpl(rawValue, shallow)
又干了啥?
class RefImpl<T> {
private _value: T
public readonly __v_isRef = true
constructor(private _rawValue: T, public readonly _shallow = false) {this._value = _shallow ? _rawValue : convert(_rawValue)
}
get value() {track(toRaw(this), TrackOpTypes.GET, 'value')
return this._value
}
set value(newVal) {if (hasChanged(toRaw(newVal), this._rawValue)) {
this._rawValue = newVal
this._value = this._shallow ? newVal : convert(newVal)
trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
}
}
}
次要看 set value(newVal)
这个办法。ref 办法并没有传递 shallow
参数,convert
办法将这个值转换为一个响应式的 proxy 对象。
const convert = <T extends unknown>(val: T): T =>
isObject(val) ? reactive(val) : val
萌新上路,期待大佬们的斧正呀~