国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

人人都能懂的Vue源碼系列—09—initEvents

lovXin / 3494人閱讀

摘要:回調(diào)函數(shù)會(huì)接收所有傳入事件觸發(fā)函數(shù)的額外參數(shù)。這種方式類(lèi)似于中的監(jiān)聽(tīng)事件和觸發(fā)事件如果不是上面這種方法指定的鉤子函數(shù),就需要執(zhí)行源碼上半部分的代碼邏輯。

上篇文章中,我們主要講了initLiftcycle方法,它的作用是初始化vm實(shí)例中和生命周期相關(guān)的屬性。今天為大家介紹另一個(gè)方法——initEvents。
從這個(gè)方法的名稱(chēng)來(lái)看,我們知道它是和事件相關(guān)的方法,具體怎么相關(guān),我們先來(lái)看源碼。

export function initEvents (vm: Component) {
  vm._events = Object.create(null)
  vm._hasHookEvent = false
  // init parent attached events
  const listeners = vm.$options._parentListeners
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}

先看第一行

vm._events = Object.create(null)

該行代碼創(chuàng)建了一個(gè)原型為null的空對(duì)象,并把它賦值給vm實(shí)例的_events屬性。關(guān)于Object.create,不明白的同學(xué)可以點(diǎn)這里查看。關(guān)于vm._events,很多博文中提到該屬性就是籠統(tǒng)的說(shuō)是用來(lái)存放事件的對(duì)象,那么到底存放什么事件呢?vm的所有事件都存放在里面?顯然是不對(duì)的,那具體是什么,我們來(lái)看下面的例子。
html部分:

  

js部分

  const childComponent = Vue.component("child", {
      template: "

{{msgFromFather}} and {{hitFromFather}}

", data: function () { return { childMsg: "Hello, I am Child" } }, props: ["msgFromFather", "hitFromFather"], methods: { showMsgFromSon () { console.log("Hello Vue from son") } }, mounted () { console.log("child mounted") } }) const app = new Vue({ el: "#app", data: function () { return { msg: "Hello Chris, I am your father", hit: "I will hit you if you do not study", } }, components: { childComponent }, methods: { hoverFromParent () { console.log("attch event") }, hookFromParent () { console.log("attch hook") } } })

下圖表示的是上述demo中vm._events屬性的值

上面例子中,child組件上除了父組件綁定的方法之外,其組件內(nèi)部還有showMsgFromSon和mounted鉤子方法,但是這兩個(gè)方法都沒(méi)有出現(xiàn)在_events屬性中。綜上可知,vm._events表示的是父組件綁定在當(dāng)前組件上的事件。
接下來(lái)看代碼

vm._hasHookEvent = false

這行代碼把我們vm實(shí)例上的_hasHookEvent屬性設(shè)置為false。該屬性表示父組件是否通過(guò)"@hook:"把鉤子函數(shù)綁定在當(dāng)前組件上。該用法可以在上個(gè)demo中找到,通過(guò)下列方式完成綁定。

@hook:鉤子名稱(chēng)="綁定的函數(shù)"

繼續(xù)回到源碼中

// init parent attached events
const listeners = vm.$options._parentListeners

從英文注釋中,我們知道這行代碼的作用是初始化父組件添加的事件。那具體是什么意思呢?通過(guò)追蹤vm.$options._parentListeners的賦值過(guò)程(這個(gè)過(guò)程有點(diǎn)復(fù)雜,在之后講雙向綁定和虛擬dom的時(shí)候會(huì)說(shuō)到),我們知道vm.$options._parentListeners其實(shí)和上面的_events一樣,都是用來(lái)表示父組件綁定在當(dāng)前組件上的事件。(當(dāng)然還是略有點(diǎn)不同,這個(gè)之后會(huì)講解)如果存在這些綁定的事件,那么就執(zhí)行下面代碼

 if (listeners) {
   updateComponentListeners(vm, listeners)
 }

如果事件存在,則調(diào)用updateComponentListeners更新這些方法。

export function updateComponentListeners (
  vm: Component,
  listeners: Object,
  oldListeners: ?Object
) {
  target = vm
  updateListeners(listeners, oldListeners || {}, add, remove, vm)
  target = undefined
}

來(lái)看updateComponentListeners方法的源碼

target = vm

這行代碼的主要作用是保留對(duì)vm實(shí)例的引用,在執(zhí)行updateListeners方法時(shí)能訪(fǎng)問(wèn)到實(shí)例對(duì)象,并執(zhí)行add和remove方法。

updateListeners(listeners, oldListeners || {}, add, remove, vm)

在研究updateListeners源碼之前,我們先來(lái)了解一下傳入的這幾個(gè)參數(shù)。listeners我們前面說(shuō)過(guò),是父組件綁定在當(dāng)前組件上的事件對(duì)象,oldListeners表示當(dāng)前組件上舊的事件對(duì)象,vm是vue實(shí)例對(duì)象。這三個(gè)沒(méi)什么好說(shuō)的,我們具體來(lái)講講另外兩個(gè)參數(shù)add和remove。

add方法

add方法源碼如下:

function add (event, fn, once) {
  if (once) {
    target.$once(event, fn)
  } else {
    target.$on(event, fn)
  }
}

如果第三個(gè)參數(shù)once為true,則執(zhí)行vue.$once方法,否則執(zhí)行vue.$on方法。我們先來(lái)看vue.$on

vue.$on方法

為什么要先講$on方法,因?yàn)?once方法中也需要用到$on,在看$on源碼之前,我們先來(lái)看看官方文檔里對(duì)它的定義。

監(jiān)聽(tīng)當(dāng)前實(shí)例上的自定義事件。事件可以由vm.$emit觸發(fā)。回調(diào)函數(shù)會(huì)接收所有傳入事件觸發(fā)函數(shù)的額外參數(shù)。

知道了vue.$on的定義之后,我們?cè)賮?lái)看源碼。

  Vue.prototype.$on = function (event: string | Array, fn: Function): Component {
    const vm: Component = this
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        this.$on(event[i], fn)
      }
    } else {
      (vm._events[event] || (vm._events[event] = [])).push(fn)
      // optimize hook:event cost by using a boolean flag marked at registration
      // instead of a hash lookup
      if (hookRE.test(event)) {
        vm._hasHookEvent = true
      }
    }
    return vm
  }

else之前的代碼都很簡(jiǎn)單,先緩存this,如果傳入的事件是事件數(shù)組的話(huà),則分別對(duì)數(shù)組內(nèi)的每一項(xiàng)調(diào)用$on綁定事件。接下來(lái)重點(diǎn)看看else塊內(nèi)的代碼。

(vm._events[event] || (vm._events[event] = [])).push(fn)

我們知道_events是表示直接綁定在組件上的事件,如果是通過(guò)$on新添加的事件(也相當(dāng)于直接綁定在組件上的事件),我們也要把事件和回調(diào)方法傳入到_events對(duì)象中。
回到源碼中

// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash look
if (hookRE.test(event)) {
  vm._hasHookEvent = true
}

關(guān)于這句代碼的解釋?zhuān)W(wǎng)上很多文章說(shuō)的都是類(lèi)似下面的話(huà)

這個(gè)bool標(biāo)志位來(lái)表明是否存在鉤子,而不需要通過(guò)哈希表的方法來(lái)查找是否有鉤子,這樣做可以減少不必要的開(kāi)銷(xiāo),優(yōu)化性能。

這句話(huà)除了是翻譯原文注釋之外,還存在明顯的錯(cuò)誤,這個(gè)tag不是表明是否存在鉤子,而是表示是否使用下面的方式綁定鉤子。
如果是下列形式綁定的鉤子,則_hasHookEvent屬性為true。

而像下面這種形式,它也存在鉤子函數(shù),但是它的_hasHookEvent就是false。

    const childComponent = Vue.component("child", {
      ...
      created () {
        console.log("child created")
      }
    })

所以_hasHookEvent不是表示是否存在鉤子,它表示的是父組件有沒(méi)有直接綁定鉤子函數(shù)在當(dāng)前組件上。說(shuō)這么多,只是希望大家盡可能的少被誤導(dǎo)。那么,那句注釋到底是什么意思呢?我們可以從callHook的源碼中來(lái)尋找答案。

export function callHook (vm: Component, hook: string) {
  const handlers = vm.$options[hook]
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      try {
        handlers[i].call(vm)
      } catch (e) {
        handleError(e, vm, `${hook} hook`)
      }
    }
  }
  if (vm._hasHookEvent) {
    vm.$emit("hook:" + hook)
  }
}

當(dāng)前實(shí)例的鉤子函數(shù)如果是通過(guò)父組件的:hook方式來(lái)指定的,那么它在執(zhí)行鉤子函數(shù)的回調(diào)方法時(shí)就是直接觸發(fā)vm.$emit來(lái)執(zhí)行。(這種方式類(lèi)似于dom中的addEventListener監(jiān)聽(tīng)事件和dispatchEvent觸發(fā)事件)
如果不是上面這種方法指定的鉤子函數(shù),就需要執(zhí)行callhook源碼上半部分的代碼邏輯。找到vm實(shí)例上的鉤子函數(shù),然后執(zhí)行綁定在它上面的回調(diào)。至于執(zhí)行效率的問(wèn)題,沒(méi)有去研究過(guò),但是原文注釋里都說(shuō)了是優(yōu)化鉤子,那么證明第一種方法執(zhí)行效率應(yīng)該是優(yōu)于第二種方法。
我們回到$on的源碼中,最后是返回vm實(shí)例對(duì)象。

return vm

現(xiàn)在我們知道了vm.$on方法主要就是把傳入的方法給push到_events屬性里,方便之后被$emit調(diào)用。

vm.$once

講過(guò)了vm.$on的主要作用之后,我們接著來(lái)分析vm.$once的源碼,先看文檔中關(guān)于vm.$once的定義。

監(jiān)聽(tīng)一個(gè)自定義事件,但是只觸發(fā)一次,在第一次觸發(fā)之后移除監(jiān)聽(tīng)器。

了解了vm.$once的定義之后,我們?cè)賮?lái)看源碼

  Vue.prototype.$once = function (event: string, fn: Function): Component {
    const vm: Component = this
    function on () {
      vm.$off(event, on)
      fn.apply(vm, arguments)
    }
    on.fn = fn
    vm.$on(event, on)
    return vm
  }

結(jié)合上面的定義和之前講的vm.$on方法,我們應(yīng)該比較容易理解解$once了,它和$on方法的核心區(qū)別主要在on方法

   function on () {
      vm.$off(event, on)
      fn.apply(vm, arguments)
    }

on方法包裝了event的回調(diào)事件,這是on和once最本質(zhì)的區(qū)別,當(dāng)觸發(fā)once綁定的回調(diào)時(shí)候,執(zhí)行on方法,先調(diào)用$off方法(這個(gè)方法是移除監(jiān)聽(tīng)的方法,我們待會(huì)兒就會(huì)講)移除監(jiān)聽(tīng),然后再執(zhí)行回調(diào)函數(shù)。這樣就實(shí)現(xiàn)了只觸發(fā)一次的功能,講到這里,add方法中所有的內(nèi)容就已經(jīng)講完了。
由于文章篇幅的原因,其他關(guān)于initEvents的內(nèi)容我們下篇文章繼續(xù)講,主要有$off,$emit和updateListeners相關(guān)的實(shí)現(xiàn)。敬請(qǐng)期待。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/95169.html

相關(guān)文章

  • 人人都能懂的Vue源碼系列—08—initLifecycle

    摘要:主要是通過(guò)為我們屬性添加一些自定義的行為。方法用來(lái)初始化一些生命周期相關(guān)的屬性,以及為等屬性賦值,來(lái)看源碼。名稱(chēng)說(shuō)明指定已創(chuàng)建的實(shí)例之父實(shí)例,在兩者之間建立父子關(guān)系。一個(gè)對(duì)象,持有已注冊(cè)過(guò)的所有子組件。 上篇文章,我們講了vm._renderProxy相關(guān)的內(nèi)容。主要是通過(guò)Proxy為我們vm屬性添加一些自定義的行為。今天我們回到init方法中,為大家講解initLifecycle。i...

    Cristalven 評(píng)論0 收藏0
  • 人人都能懂的Vue源碼系列—02—Vue構(gòu)造函數(shù)

    摘要:果然我們找到了的構(gòu)造函數(shù)定義。告訴你是一個(gè)構(gòu)造函數(shù),需要用操作符去調(diào)用。在深入方法之前,我們先把目光移到文件里在的構(gòu)造函數(shù)定義之后,有一系列方法會(huì)被立即調(diào)用。下篇博文主要介紹相關(guān)的內(nèi)容,涉及到原型鏈和構(gòu)造函數(shù)以及部分的實(shí)現(xiàn),敬請(qǐng)期待 上篇博文中說(shuō)到了Vue源碼的目錄結(jié)構(gòu)是什么樣的,每個(gè)目錄的作用我們應(yīng)該也有所了解。我們知道core/instance目錄主要是用來(lái)實(shí)例化Vue對(duì)象,所以我...

    X_AirDu 評(píng)論0 收藏0
  • 人人都能懂的Vue源碼系列—01—Vue源碼目錄結(jié)構(gòu)

    摘要:閱讀的源碼,或者說(shuō)閱讀一個(gè)框架的源碼,了解它的目錄結(jié)構(gòu)都是很有幫助的。人人都能懂的源碼系列文章將會(huì)詳細(xì)的介紹源碼的方方面面。 閱讀Vue的源碼,或者說(shuō)閱讀一個(gè)框架的源碼,了解它的目錄結(jié)構(gòu)都是很有幫助的。下面我們來(lái)看看Vue源碼的目錄結(jié)構(gòu)。showImg(https://segmentfault.com/img/bV8fLS?w=598&h=654); Vue各目錄簡(jiǎn)介 下圖是Vue各個(gè)...

    MAX_zuo 評(píng)論0 收藏0
  • 人人都能懂的Vue源碼系列—04—resolveConstructorOptions函數(shù)-下

    摘要:上一篇文章中說(shuō)道,函數(shù)要分兩種情況進(jìn)行說(shuō)明,第一種是為基礎(chǔ)構(gòu)造器的情況,這個(gè)已經(jīng)向大家介紹過(guò)了,今天這篇文章主要介紹第二種情況,是創(chuàng)建的子類(lèi)。表示的是當(dāng)前構(gòu)造器上新增的,表示的是當(dāng)前構(gòu)造器上新增的封裝。 上一篇文章中說(shuō)道,resolveConstructorOptions函數(shù)要分兩種情況進(jìn)行說(shuō)明,第一種是Ctor為基礎(chǔ)構(gòu)造器的情況,這個(gè)已經(jīng)向大家介紹過(guò)了,今天這篇文章主要介紹第二種情況...

    My_Oh_My 評(píng)論0 收藏0
  • 人人都能懂的Vue源碼系列—04—resolveConstructorOptions函數(shù)-下

    摘要:上一篇文章中說(shuō)道,函數(shù)要分兩種情況進(jìn)行說(shuō)明,第一種是為基礎(chǔ)構(gòu)造器的情況,這個(gè)已經(jīng)向大家介紹過(guò)了,今天這篇文章主要介紹第二種情況,是創(chuàng)建的子類(lèi)。表示的是當(dāng)前構(gòu)造器上新增的,表示的是當(dāng)前構(gòu)造器上新增的封裝。 上一篇文章中說(shuō)道,resolveConstructorOptions函數(shù)要分兩種情況進(jìn)行說(shuō)明,第一種是Ctor為基礎(chǔ)構(gòu)造器的情況,這個(gè)已經(jīng)向大家介紹過(guò)了,今天這篇文章主要介紹第二種情況...

    BlackHole1 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<