91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue2實現composition API的原理是什么

發布時間:2023-01-14 11:34:00 來源:億速云 閱讀:134 作者:iii 欄目:編程語言

本篇內容主要講解“Vue2實現composition API的原理是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Vue2實現composition API的原理是什么”吧!

如何使用

// 入口文件引入并注冊
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'

Vue.use(VueCompositionAPI)
// vue文件使用
import { defineComponent, ref, reactive } from '@vue/composition-api'

export default defineComponent({
  setup () {
     const foo = ref('foo');
     const obj = reactive({ bar: 'bar' });
     
     return {
        foo,
        obj
     }
  }
})

怎么樣,看完是不是感覺和 vue3 一模一樣,你可能會想:

  • 這是 vue2 啊,我之前的 datamethods 里面也有變量和方法,怎么做到跟 setup 返回值打通合并在一起的。

  • vue2 不是只有定義在 data 里面的數據才會被處理成響應式的嗎? refreactive 是怎么做到的呢?

  • vue2 響應式數據定義的約束(添加賦值原對象沒有的屬性,數組下標修改等),改用 refreactive 就沒問題嗎?

當然還有很多的疑惑,因為插件提供的 API 相當多,覆蓋了絕大部分 Vue3 所擁有的,這里主要從這幾個問題來分析一下是如何做到的。

原理解析

得益于 Vue 的插件系統,@vue/composition-apivue-routervuex 一樣也是通過官方提供的插件式來注入。

// 這里只貼跟本章要講的相關代碼

funciton mixin (Vue) {
    Vue.mixin({
      beforeCreate: functionApiInit
   }
}

function install (Vue) {
    mixin(Vue);
}

export const Plugin = {
  install: (Vue: VueConstructor) => install(Vue),
}

Vue 插件就是向外面暴露一個 install 的方法,當調用 use 的時候會調用該方法,并把 Vue 構造函數作為參數傳入,然后調用 Vue.mixin 混入對應鉤子時要處理的函數。

接下來主要看下 functionApiInit 做了什么

function functionApiInit(this: ComponentInstance) {
  const vm = this
  const $options = vm.$options
  const { setup, render } = $options
  
  // render 相關
  
  
  const { data } = $options
  
  $options.data = function wrappedData() {
    initSetup(vm, vm.$props)
    return isFunction(data)
     ? (
        data as (this: ComponentInstance, x: ComponentInstance) => object
      ).call(vm, vm)
     : data || {}
  }

因為 VuebeforeCreatedcreated 生命周期之間,會 initState 對數據進行處理,其中對 data的處理時就會調用 $options.data拿到定義的數據,所以這里重新對該函數其包裹一層,這也是為什么要選擇 beforeCreate 鉤子注入的一個原因,必須在該函數調用前進行包裹。 接下來看 initSetup都做了什么

function initSetup(vm: ComponentInstance, props: Record<any, any> = {}) {
  const setup = vm.$options.setup!
  const ctx = createSetupContext(vm)
  const instance = toVue3ComponentInstance(vm)
  instance.setupContext = ctx
  
  def(props, '__ob__', createObserver())
  resolveScopedSlots(vm, ctx.slots)

  let binding: ReturnType<SetupFunction<Data, Data>> | undefined | null
  activateCurrentInstance(instance, () => {
    binding = setup(props, ctx)
  })

   // setup返回是函數的情況 需要重寫render函數

  const bindingObj = binding

  Object.keys(bindingObj).forEach((name) => {
    let bindingValue: any = bindingObj[name]

    // 數據處理
    
    asVmProperty(vm, name, bindingValue)
  })
  return
  }
}

這個函數比較長,不在本次要講解的主線上代碼邏輯都刪除了,這個函數主要是創建了 ctx 和把 vm 實例轉換成 Vue3 數據類型定義的 instance ,然后執行 setup 函數得到返回值,然后遍歷每個屬性,調用 asVmProperty 掛載到 vm 上面,當然這里的掛載不是直接通過把屬性和值添加到 vm 上面,這么做會有一個問題,就是后續對該屬性的修改不能同步到 vm 中,這里采用的還是 Vue 最常見的數據代理。

export function asVmProperty(
  vm: ComponentInstance,
  propName: string,
  propValue: Ref<unknown>
) {
  const props = vm.$options.props
  if (!(propName in vm) && !(props && hasOwn(props, propName))) {
    if (isRef(propValue)) {
      proxy(vm, propName, {
        get: () => propValue.value,
        set: (val: unknown) => {
          propValue.value = val
        },
      })
    } else {
      proxy(vm, propName, {
        get: () => {
          if (isReactive(propValue)) {
            ;(propValue as any).__ob__.dep.depend()
          }
          return propValue
        },
        set: (val: any) => {
          propValue = val
        },
      })
    }
}

看到這里,相信你已經明白了在 setup 中定義返回的為什么能夠在 templatedatamethods 等之中去使用了,因為返回的東西都已經被代理到 vm 之上了。

響應式( ref  reactive 的實現)

接下來我們來說說響應式相關的,為什么 refreactive 也可以讓數據成為響應式的。

ref 的實現其實是對 reactive 再次封裝,主要用來給基本類型使用。

function ref(raw?: unknown) {
  if (isRef(raw)) {
    return raw
  }

  const value = reactive({ [RefKey]: raw })
  return createRef({
    get: () => value[RefKey] as any,
    set: (v) => ((value[RefKey] as any) = v),
  })
}

因為 reactive 接受的必須是一個對象,所有這里使用了一個常量作為 refkey, 也就是

const value = reactive({
  "composition-api.refKey": row
})
export function createRef<T>(
  options: RefOption<T>,
  isReadonly = false,
  isComputed = false
): RefImpl<T> {
  const r = new RefImpl<T>(options)

  const sealed = Object.seal(r)
  if (isReadonly) readonlySet.set(sealed, true)
  return sealed
}

export class RefImpl<T> implements Ref<T> {
  readonly [_refBrand]!: true
  public value!: T
  constructor({ get, set }: RefOption<T>) {
    proxy(this, 'value', {
      get,
      set,
    })
  }
}

通過 new RefImpl 實例,該實例上有一個 value 的屬性,對 value 做代理,當取值的時候返回 value[RefKey],賦值的時候賦值給 value[RefKey], 這就是為什么 ref 可以用在基本類型,然后對返回值的 .value 進行操作。調用 object.seal 是把對象密封起來(會讓這個對象變的不能添加新屬性,且所有已有屬性會變的不可配置。屬性不可配置的效果就是屬性變的不可刪除,以及一個數據屬性不能被重新定義成為訪問器屬性,或者反之。但屬性的值仍然可以修改。)

我們主要看下 reactive 的實現

export function reactive<T extends object>(obj: T): UnwrapRef<T> {
    const observed = observe(obj)
    setupAccessControl(observed)
    return observed as UnwrapRef<T>
}


export function observe<T>(obj: T): T {
  const Vue = getRegisteredVueOrDefault()
  let observed: T
  if (Vue.observable) {
    observed = Vue.observable(obj)
  } else {
    const vm = defineComponentInstance(Vue, {
      data: {
        $$state: obj,
      },
    })
    observed = vm._data.$$state
  }

  return observed
}

我們通過 ref 或者 reactive 定義的數據,最終還是通過了變成了一個 observed 實例對象,也就是 Vue2 在對 data 進行處理時,會調用 observe 返回的一樣,這里在 Vue2.6+observe 函數向外暴露為 Vue.observable,如果是低版本的話,可以通過重新 new 一個 vue 實例,借助 data 也可以返回一個 observed 實例,如上述代碼。

因為在 reactive 中定義的數據,就如你在 data 中定義的數據一樣,都是在操作返回的 observed ,當你取值的時候,會觸發 getter 進行依賴收集,賦值時會調用 setter 去派發更新, 只是定義在 setup 中,結合之前講到的 setup 部分,比如當我們在 template 中訪問一個變量的值時,vm.foo -> proxysetup 里面的 foo -> observedfoo ,完成取值的流程,這會比直接在 data 上多代理了一層,因此整個過程也會有額外的性能開銷。

因此使用該 API 也不會讓你可以直接規避掉 vue2 響應式數據定義的約束,因為最終還是用 Object.defineProperty 去做對象攔截,插件同樣也提供了 set API 讓你去操作對象新增屬性等操作。

到此,相信大家對“Vue2實現composition API的原理是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新竹县| 招远市| 百色市| 永丰县| 陆良县| 沙雅县| 兴仁县| 沾化县| 鄂伦春自治旗| 喀喇沁旗| 太白县| 民权县| 两当县| 新乡市| 屯留县| 云浮市| 武清区| 遂宁市| 仪陇县| 湾仔区| 容城县| 平和县| 石台县| 射洪县| 赤壁市| 铜梁县| 黄石市| 开封市| 抚顺县| 清苑县| 松潘县| 苍溪县| 潍坊市| 方正县| 宁南县| 明水县| 唐山市| 渑池县| 安泽县| 牟定县| 衡水市|