2020-07-11 Vue-composition-api 解讀(1)

@vue/composition-api 解讀1

vue-composition-api 是 vue 官方出得, 基于vue3的RFC來(lái)兼容Vue2.x的增強(qiáng)api,具體API和vue-next 是差不多的, 具體的API詳見 vue-composition-api-rfc, 本系列文章是基于你對(duì)vue2有所了解的情況

proxy

vue2的響應(yīng)式是基于Object.definePropertysetget方法來(lái)實(shí)現(xiàn)的,具體的在源碼中的實(shí)現(xiàn), 這也是組合式API響應(yīng)式的基礎(chǔ)

// src/utils/utils.ts
export function proxy(
  target: any,
  key: string,
  { get, set }: { get?: Function; set?: Function }
) {
  sharedPropertyDefinition.get = get || noopFn
  sharedPropertyDefinition.set = set || noopFn
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

其中sharedPropertyDefinition存在getset方法

reactive

reactive是組合式API提供的一個(gè)實(shí)現(xiàn)對(duì)象響應(yīng)式的API, 使用方法也很簡(jiǎn)單,就是傳入一個(gè)object

setup() {
    const state = reactive({
        foo: 'foo'
    })

    return {
        state
    }
}

源碼中的實(shí)現(xiàn)我們一一來(lái)看

export function reactive<T extends object>(obj: T): UnwrapRef<T> {
  if (__DEV__ && !obj) {
    warn('"reactive()" is called without provide an "object".')
    // @ts-ignore
    return
  }

  if (
    !isPlainObject(obj) || // 非原始對(duì)象
    isReactive(obj) || // 已是響應(yīng)式
    isRaw(obj) || // 
    !Object.isExtensible(obj) // 對(duì)象不可擴(kuò)展
  ) {
    return obj as any
  }

  const observed = observe(obj)
  // def(obj, ReactiveIdentifierKey, ReactiveIdentifier);
  markReactive(obj)
  setupAccessControl(observed)
  return observed as UnwrapRef<T>
}

其中 observe是實(shí)現(xiàn)響應(yīng)式的關(guān)鍵踱阿, 我們打開看一看


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

  return observed
}

你沒(méi)看錯(cuò),就這么簡(jiǎn)單黎比, 實(shí)際上就是用vue2現(xiàn)成的api來(lái)實(shí)現(xiàn)的 Vue.observable, else下面是用來(lái)兼容以前老版本vue沒(méi)有observable

markReactive 標(biāo)記 reactive中的對(duì)象, 實(shí)現(xiàn)方式其實(shí)也是使用Object.defineProperty來(lái)寫入標(biāo)記值,
其中標(biāo)記都是用Symbol來(lái)標(biāo)識(shí)唯一性的

ref

ref 是組合式API中另一個(gè)響應(yīng)式api,傳入一個(gè)基本類型參數(shù)

使用

setup() {
    const state = ref(1/"hello world"/false/null)

    return {
        state
    }
}

取值方式state.value颓帝, 如果使用模版方式就不需要使用.value方式,模版已經(jīng)做了這個(gè)工作瘪板,如果使用jsxrender函數(shù)厢拭,就只能手加了

具體實(shí)現(xiàn)

export 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),
  })
}

其中在內(nèi)部實(shí)現(xiàn)了組裝了一個(gè)對(duì)象,還是用的reactive實(shí)現(xiàn)邏輯
關(guān)鍵在createRef

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

export function createRef<T>(options: RefOption<T>) {
  // seal the ref, this could prevent ref from being observed
  // It's safe to seal the ref, since we really shouldn't extend it.
  // related issues: #79
  return Object.seal(new RefImpl<T>(options))
}

其中Object.seal 封閉了一個(gè)RefImpl對(duì)象,使其對(duì)象不能添加其他任何屬性颤殴,已有屬性可以更改,具體的去查Object.seal的文檔

RefImpl中 用我們上面寫的響應(yīng)式方法proxy, 傳入getset來(lái)重寫他的getset方法

官方用ref的方式來(lái)獲得dom實(shí)例

<template>
  <div ref="root"></div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // 在渲染完成后, 這個(gè) div DOM 會(huì)被賦值給 root ref 對(duì)象
        console.log(root.value) // <div/>
      })

      return {
        root,
      }
    },
  }
</script>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載劫侧,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末魔吐,一起剝皮案震驚了整個(gè)濱河市轴踱,隨后出現(xiàn)的幾起案子雳灵,更是在濱河造成了極大的恐慌躲撰,老刑警劉巖快压,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡育灸,警方通過(guò)查閱死者的電腦和手機(jī)砸喻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門瞬哼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)讨越,“玉大人,你說(shuō)我怎么就攤上這事意蛀∠卦浚” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我面哼,道長(zhǎng)代乃,這世上最難降的妖魔是什么吭历? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任旭斥,我火速辦了婚禮,結(jié)果婚禮上古涧,老公的妹妹穿的比我還像新娘垂券。我一直安慰自己,他們只是感情好蒿褂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布圆米。 她就那樣靜靜地躺著卒暂,像睡著了一般啄栓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上也祠,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天昙楚,我揣著相機(jī)與錄音,去河邊找鬼诈嘿。 笑死堪旧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奖亚。 我是一名探鬼主播淳梦,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼昔字!你這毒婦竟也來(lái)了爆袍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎究驴,沒(méi)想到半個(gè)月后剧辐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜘醋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年胁塞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片压语。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡啸罢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出无蜂,到底是詐尸還是另有隱情伺糠,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布斥季,位于F島的核電站训桶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏酣倾。R本人自食惡果不足惜舵揭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望躁锡。 院中可真熱鬧午绳,春花似錦、人聲如沸映之。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)杠输。三九已至赎败,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蠢甲,已是汗流浹背僵刮。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鹦牛,地道東北人搞糕。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像曼追,于是被迫代替她去往敵國(guó)和親窍仰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354