关于vue.js:vue3源码toRefsref干了什么

明天在我的项目里对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

萌新上路,期待大佬们的斧正呀~

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据