最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue3 之 响应式 ref 、computed 、reactive的区别

    正文概述 掘金(嘎子同学)   2020-11-29   828

    前言

    import { reactive } from 'vue';
    
    const arr = reactive([1,2,3,4]);
    
    console.log(arr); // 输出的是代理之后的 Proxy {0: 1, 1: 2, 2: 3, 3: 4}
    
    

    ref 原理和使用方式

    // 可以是对象的属性
    export function ref<T extends object>(
      value: T
    ): T extends Ref ? T : Ref<UnwrapRef<T>>
    // 也可以是一个任意值
    export function ref<T>(value: T): Ref<UnwrapRef<T>>
    
    export function ref<T = any>(): Ref<T | undefined>
    // ref的值是可选的,非必填项
    export function ref(value?: unknown) {
      return createRef(value)
    }
    
    import { ref } from 'vue';
    const refParam = ref();
    
    console.log(refParam) // 返回是一个RefImpl {_rawValue: undefined, _shallow: false, __v_isRef: true, _value: undefined}
    
    
    // 如果是一个对象,则使用reactive做深度代理,否则直接返回
    const convert = <T extends unknown>(val: T): T =>
      isObject(val) ? reactive(val) : val
    
    // shallow 用来表示是浅层代理还是深度代理
    function createRef(rawValue: unknown, shallow = false) {
      if (isRef(rawValue)) {
        return rawValue
      }
      let value = shallow ? rawValue : convert(rawValue) // convert 如果是一个对象,则使用reactive做深度代理,否则直接返回
      const r = {
        __v_isRef: true,
        get value() {
          // 方法追踪
          track(r, TrackOpTypes.GET, 'value')
          return value
        },
        set value(newVal) {
          // 判断新值和旧值是否是一致的,如果不是一致的就进行更新操作
          if (hasChanged(toRaw(newVal), rawValue)) {
            rawValue = newVal
            value = shallow ? newVal : convert(newVal)
            trigger(r, TriggerOpTypes.SET, 'value', newVal)
          }
        }
      }
      return r
    }
    

    computed 计算机属性3.0实现源码,与2.0版本的区别

    
    // 返回的值是只读属性,并且是响应式
    export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
    // 增加了计算机属性的读写操作
    export function computed<T>(
      options: WritableComputedOptions<T>
    ): WritableComputedRef<T>
    
    // 参数可以是一个对象形式,也可以是一个方法
    export function computed<T>(
      // 参数是一个方法或者是一个对象参数,如果是方法执行只读,如果是对象可以操作读写
      getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
    ) {
      let getter: ComputedGetter<T>
      let setter: ComputedSetter<T>
    
      // 如果是方法的话,不可以修改属性值,读取,如果是一个对象的话,则可以操作读写功能
      if (isFunction(getterOrOptions)) {
        getter = getterOrOptions
        setter = __DEV__
          ? () => {
              console.warn('Write operation failed: computed value is readonly')
            }
          : NOOP
      } else {
        getter = getterOrOptions.get
        setter = getterOrOptions.set
      }
    
      let dirty = true
      let value: T
      let computed: ComputedRef<T>
    
      // effect 传进来一个getter,这个getter = 如果当前参数是一个方法,值直接赋值当前的function,否则赋值这个对象的get方法
      const runner = effect(getter, {
        lazy: true, // 懒执行
        scheduler: () => {
          if (!dirty) {
            dirty = true
            trigger(computed, TriggerOpTypes.SET, 'value')
          }
        }
      })
      computed = {
        // 标记为这个是需要代理的响应式
        __v_isRef: true,
        // 是否是只读标记
        [ReactiveFlags.IS_READONLY]:
          isFunction(getterOrOptions) || !getterOrOptions.set,
    
        // expose effect so computed can be stopped
        effect: runner,
        get value() {
          if (dirty) {
            value = runner()
            dirty = false
          }
          track(computed, TrackOpTypes.GET, 'value')
          return value
        },
        set value(newValue: T) {
          setter(newValue)
        }
      } as any
      return computed
    }
    
    
    // 只读模式操作
    const count = ref(1)
    const plusOne = computed(() => count.value + 1)
    
    console.log(plusOne.value) // 2
    
    plusOne.value++ // 错误!
    
    // 读写模式操作
    const count = ref(1)
    const plusOne = computed({
      get: () => count.value + 1,
      set: (val) => {
        count.value = val - 1
      },
    })
    
    plusOne.value = 1
    console.log(count.value) // 0
    

    reactive

    // only unwrap nested ref
    // 解嵌套
    type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
    
    export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
    export function reactive(target: object) {
      // if trying to observe a readonly proxy, return the readonly version.
      // 如果当前的目标对象存在并且是只读则直接返回当前对象
      if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
        return target
      }
      // 创建一个响应式对象
      return createReactiveObject(
        target,
        false,
        mutableHandlers,
        mutableCollectionHandlers
      )
    }
    
    function createReactiveObject(
      target: Target,                         // 需要代理的目标对象
      isReadonly: boolean,                    // 是不是只读对象
      baseHandlers: ProxyHandler<any>,        // 基础的处理方法
      collectionHandlers: ProxyHandler<any>   // 收集依赖
    ) {
      if (!isObject(target)) {
        if (__DEV__) {
          console.warn(`value cannot be made reactive: ${String(target)}`)
        }
        return target
      }
      // target is already a Proxy, return it.
      // exception: calling readonly() on a reactive object
      // 当前对象是不是原始对象,并且是被代理过的只读对象
      if (
        target[ReactiveFlags.RAW] &&
        !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
      ) {
        return target
      }
      // target already has corresponding Proxy
      // 如果已经有了对应的代理对象就返回这个代理的对象
      const reactiveFlag = isReadonly
        ? ReactiveFlags.READONLY
        : ReactiveFlags.REACTIVE
      if (hasOwn(target, reactiveFlag)) {
        return target[reactiveFlag]
      }
      // only a whitelist of value types can be observed.
    
      if (!canObserve(target)) {
        return target
      }
      const observed = new Proxy(
        target,
        collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
      )
      def(target, reactiveFlag, observed)
      return observed
    }
    
    export const isObject = (val: unknown): val is Record<any, any> =>
      val !== null && typeof val === 'object'
    

    通过上述的分析,我们不难看出,ref和reactive,computed的区别还是蛮大的;

    今天的分享就到这了,如果有哪些地方说的不对,还望在下边的评论区发表出来,大家一起讨论;
    如果想要体验vue3.0的同学也可以参考一下我的这篇文章vue3 学习 之 vue3使用


    起源地下载网 » vue3 之 响应式 ref 、computed 、reactive的区别

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元