最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue 組件事件觸發(fā)和監(jiān)聽實(shí)現(xiàn)源碼解析

 更新時(shí)間:2022年12月22日 11:17:37   作者:田八  
這篇文章主要為大家介紹了Vue 組件事件觸發(fā)和監(jiān)聽實(shí)現(xiàn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

通常我們?cè)谑褂?code>Vue的時(shí)候,會(huì)使用$emit$on來(lái)觸發(fā)和監(jiān)聽事件,但是有沒(méi)有思考是如何實(shí)現(xiàn)的呢?

今天帶來(lái)的是一個(gè)微型的事件觸發(fā)的庫(kù),借它們的源碼來(lái)簡(jiǎn)單初步了解Vue的事件觸發(fā)和監(jiān)聽的實(shí)現(xiàn)。

mitt

tiny-emitter

mitt使用TypeScript編寫,tiny-emitter使用原生ES5編寫,兩者對(duì)比tiny-emitter功能稍微豐富一寫,所以直接看tiny-emitter就好了。

Vue 的事件觸發(fā)和監(jiān)聽

我沒(méi)有標(biāo)題黨,先來(lái)看一下Vue的組件事件怎么使用的:

// 父組件
<template>
  <div>
    <child @my-event="handleMyEvent"></child>
  </div>
</template>
<script>
import Child from './Child.vue'
export default {
  components: {
    Child
  },
  methods: {
    handleMyEvent (event) {
      console.log(event)
    }
  }
}
</script>
// 子組件
<template>
  <div>
    <button @click="handleClick">click</button>
  </div>
</template>
<script>
export default {
  methods: {
    handleClick () {
      this.$emit('my-event', { a: 1 })
    }
  }
}
</script>

這個(gè)代碼大家都熟,簡(jiǎn)化之后其實(shí)可以理解成這樣的:

// 父組件
import Child from './Child.js'
const child = new Child()
child.$on('my-event', (event) => {
  console.log(event)
});
// 子組件
export default class Child extends Emitter {
  constructor () {
      super();
  }
  handleClick () {
    this.$emit('my-event', { a: 1 })
  }
}

這里可以看到子組件是繼承自Emitter,Emitter是一個(gè)事件觸發(fā)和監(jiān)聽的類,這個(gè)類的實(shí)現(xiàn)就是我們今天要學(xué)習(xí)的。

先來(lái)學(xué)習(xí)tiny-emitter的實(shí)現(xiàn),最后把Emitter這個(gè)類完成。

源碼分析

tiny-emitter的源碼很簡(jiǎn)單,加上注釋和換行也就68行,先一睹為快:

function E () {
  // Keep this empty so it's easier to inherit from
  // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
}
E.prototype = {
  on: function (name, callback, ctx) {
    var e = this.e || (this.e = {});
    (e[name] || (e[name] = [])).push({
      fn: callback,
      ctx: ctx
    });
    return this;
  },
  once: function (name, callback, ctx) {
    var self = this;
    function listener () {
      self.off(name, listener);
      callback.apply(ctx, arguments);
    };
    listener._ = callback
    return this.on(name, listener, ctx);
  },
  emit: function (name) {
    var data = [].slice.call(arguments, 1);
    var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
    var i = 0;
    var len = evtArr.length;
    for (i; i < len; i++) {
      evtArr[i].fn.apply(evtArr[i].ctx, data);
    }
    return this;
  },
  off: function (name, callback) {
    var e = this.e || (this.e = {});
    var evts = e[name];
    var liveEvents = [];
    if (evts && callback) {
      for (var i = 0, len = evts.length; i < len; i++) {
        if (evts[i].fn !== callback && evts[i].fn._ !== callback)
          liveEvents.push(evts[i]);
      }
    }
    // Remove event from queue to prevent memory leak
    // Suggested by https://github.com/lazd
    // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
    (liveEvents.length)
      ? e[name] = liveEvents
      : delete e[name];
    return this;
  }
};
module.exports = E;
module.exports.TinyEmitter = E;

一共只有4個(gè)方法,on、onceemit、off,下面一個(gè)一個(gè)開始分析。

on

on方法是用來(lái)監(jiān)聽事件的,代碼如下:

function on(name, callback, ctx) {
    var e = this.e || (this.e = {});
    (e[name] || (e[name] = [])).push({
        fn: callback,
        ctx: ctx
    });
    return this;
}

它接收三個(gè)參數(shù):

  • name:事件名稱
  • callback:事件觸發(fā)時(shí)的回調(diào)函數(shù)
  • ctx:回調(diào)函數(shù)的上下文

它會(huì)判斷this.e是否存在,如果不存在就創(chuàng)建一個(gè)空對(duì)象;

this.e是用來(lái)存儲(chǔ)事件的,this指向就不多講了吧。

然后把name作為key,callbackctx作為value,存到this.e中,用于后面的事件觸發(fā);

最后返回this,這樣就可以鏈?zhǔn)秸{(diào)用了。

once

once方法同樣是用來(lái)監(jiān)聽事件的,但是它只會(huì)觸發(fā)一次,代碼如下:

function once(name, callback, ctx) {
    var self = this;
    function listener() {
        self.off(name, listener);
        callback.apply(ctx, arguments);
    };
    listener._ = callback
    return this.on(name, listener, ctx);
}

它同on方法一樣,接收三個(gè)參數(shù),最后也是調(diào)用的on方法,但是它會(huì)在on方法的基礎(chǔ)上做一些處理。

它內(nèi)部會(huì)定義一個(gè)listener函數(shù),然后將這個(gè)函數(shù)作為on方法的回調(diào)函數(shù),最后調(diào)用on方法。

listener函數(shù)會(huì)調(diào)用off方法,把自己從事件隊(duì)列中移除,然后再調(diào)用callback函數(shù)。

emit

emit方法是用來(lái)觸發(fā)事件的,代碼如下:

function emit(name) {
    var data = [].slice.call(arguments, 1);
    var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
    var i = 0;
    var len = evtArr.length;
    for (i; i < len; i++) {
        evtArr[i].fn.apply(evtArr[i].ctx, data);
    }
    return this;
}

它接收一個(gè)參數(shù),就是事件名稱,然后把除了第一個(gè)參數(shù)以外的參數(shù),轉(zhuǎn)成數(shù)組,存到data中。

然后從this.e中取出name對(duì)應(yīng)的事件隊(duì)列,如果沒(méi)有就創(chuàng)建一個(gè)空數(shù)組,然后把這個(gè)數(shù)組復(fù)制一份,存到evtArr中。

這里老是對(duì)this.e做判斷好麻煩,其實(shí)可以在構(gòu)造函數(shù)中初始化this.e,這樣就不用每次都判斷了。

然后遍歷evtArr,依次調(diào)用每個(gè)事件的回調(diào)函數(shù),把data作為參數(shù)傳進(jìn)去,最后返回this。

off

off方法是用來(lái)移除事件的,代碼如下:

function off(name, callback) {
    var e = this.e || (this.e = {});
    var evts = e[name];
    var liveEvents = [];
    if (evts && callback) {
      for (var i = 0, len = evts.length; i < len; i++) {
        if (evts[i].fn !== callback && evts[i].fn._ !== callback)
          liveEvents.push(evts[i]);
      }
    }
    (liveEvents.length)
      ? e[name] = liveEvents
      : delete e[name];
    return this;
  }

它接收兩個(gè)參數(shù),namecallbackname是事件名稱,callback是回調(diào)函數(shù)。

它會(huì)從this.e中取出name對(duì)應(yīng)的事件隊(duì)列,然后通過(guò)遍歷,把不等于callback的事件,存到liveEvents中。

最后判斷liveEvents是否為空,如果不為空,就把liveEvents賦值給e[name],否則就刪除e[name]。

動(dòng)手實(shí)現(xiàn)

上面已經(jīng)分析完tiny-emitter的源碼了,我們現(xiàn)在就來(lái)實(shí)現(xiàn)最開始提到的Emitter類。

當(dāng)然這里還是簡(jiǎn)化版,只有onemit方法。

class Emitter {
    constructor() {
        this.events = {};
    }
    $on(name, callback) {
        if (!this.events[name]) {
            this.events[name] = [];
        }
        this.events[name].push(callback);
    }
    $emit(name, ...args) {
        if (this.events[name]) {
            this.events[name].forEach(callback => callback(...args));
        }
    }
}

這里使用ES6class語(yǔ)法來(lái)實(shí)現(xiàn)這個(gè)功能;

constructor方法用來(lái)初始化events對(duì)象,這樣對(duì)比tiny-emitter的源碼,就不用每次都判斷this.e是否存在了;

$emit通過(guò)...args來(lái)接收除了第一個(gè)參數(shù)以外的參數(shù),就可以不用使用arguments了;

整體來(lái)說(shuō)使用ES6class語(yǔ)法來(lái)實(shí)現(xiàn),代碼量更少,更加簡(jiǎn)潔,來(lái)看了結(jié)果:

總結(jié)

通過(guò)學(xué)習(xí)tiny-emitter的源碼,我們學(xué)習(xí)到一個(gè)事件總線的設(shè)計(jì)思路以及實(shí)現(xiàn)方式。

了解事件總線的設(shè)計(jì)思路,可以幫助我們更好地理解Vue的事件總線,這可能對(duì)我們使用或者閱讀源碼有幫助。

最后通過(guò)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的事件總線,讓我們加深對(duì)這個(gè)事件總線的理解,也通過(guò)將這個(gè)實(shí)現(xiàn)轉(zhuǎn)換為ES6class語(yǔ)法,讓我們對(duì)ES6class語(yǔ)法有更深的理解。

以上就是Vue 組件事件觸發(fā)和監(jiān)聽實(shí)現(xiàn)源碼解析的詳細(xì)內(nèi)容,更多關(guān)于Vue 組件事件觸發(fā)監(jiān)聽的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解ESLint在Vue中的使用小結(jié)

    詳解ESLint在Vue中的使用小結(jié)

    ESLint是一個(gè)QA工具,這篇文章主要介紹了詳解ESLint在Vue中的使用小結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-10-10
  • vue2.x雙向數(shù)據(jù)綁定原理解析

    vue2.x雙向數(shù)據(jù)綁定原理解析

    雙向數(shù)據(jù)綁定原理主要運(yùn)用了發(fā)布訂閱模式來(lái)實(shí)現(xiàn)的,通過(guò)Object.defineProperty對(duì)數(shù)據(jù)劫持,觸發(fā)getter,setter方法,這篇文章主要介紹了vue2.x雙向數(shù)據(jù)綁定原理,需要的朋友可以參考下
    2023-02-02
  • vue下跨域設(shè)置的相關(guān)介紹

    vue下跨域設(shè)置的相關(guān)介紹

    本篇文章主要介紹了vue下跨域設(shè)置的相關(guān)介紹,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • Vue echarts封裝組件需求分析與實(shí)現(xiàn)

    Vue echarts封裝組件需求分析與實(shí)現(xiàn)

    在平常的項(xiàng)目中,echarts圖表我們也是使用的非常多的,通常我們從后端拿到數(shù)據(jù),需要在圖表上動(dòng)態(tài)的進(jìn)行呈現(xiàn),接下來(lái)我們就模擬從后端獲取數(shù)據(jù)然后進(jìn)行呈現(xiàn)的方法,這篇文章主要介紹了Vue echarts封裝組件需求分析與實(shí)現(xiàn)
    2022-10-10
  • Vue實(shí)現(xiàn)一種簡(jiǎn)單的無(wú)限循環(huán)滾動(dòng)動(dòng)畫的示例

    Vue實(shí)現(xiàn)一種簡(jiǎn)單的無(wú)限循環(huán)滾動(dòng)動(dòng)畫的示例

    這篇文章主要介紹了Vue實(shí)現(xiàn)一種簡(jiǎn)單的無(wú)限循環(huán)滾動(dòng)動(dòng)畫的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • vue實(shí)現(xiàn)高德地圖添加多個(gè)點(diǎn)標(biāo)記

    vue實(shí)現(xiàn)高德地圖添加多個(gè)點(diǎn)標(biāo)記

    地圖多點(diǎn)標(biāo)注其實(shí)是個(gè)非常簡(jiǎn)單的問(wèn)題,這篇文章主要給大家介紹了關(guān)于vue實(shí)現(xiàn)高德地圖添加多個(gè)點(diǎn)標(biāo)記的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • Vue入門之?dāng)?shù)量加減運(yùn)算操作示例

    Vue入門之?dāng)?shù)量加減運(yùn)算操作示例

    這篇文章主要介紹了Vue入門之?dāng)?shù)量加減運(yùn)算操作,結(jié)合實(shí)例形式分析了vue.js基本數(shù)值運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下
    2018-12-12
  • elementUI Pagination 分頁(yè)指定最大頁(yè)的問(wèn)題及解決方法(page-count)

    elementUI Pagination 分頁(yè)指定最大頁(yè)的問(wèn)題及解決方法(page-count)

    項(xiàng)目中遇到數(shù)據(jù)量大,查詢的字段多,但用戶主要使用的是最近的一些數(shù)據(jù),1萬(wàn)條以后的數(shù)據(jù)一般不使用,這篇文章主要介紹了elementUI Pagination 分頁(yè)指定最大頁(yè)的問(wèn)題及解決方法(page-count),需要的朋友可以參考下
    2024-08-08
  • Vuex 使用 v-model 配合 state的方法

    Vuex 使用 v-model 配合 state的方法

    這篇文章主要介紹了Vuex 使用 v-model 配合 state的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-11-11
  • vue+highcharts實(shí)現(xiàn)3D餅圖效果

    vue+highcharts實(shí)現(xiàn)3D餅圖效果

    這篇文章主要為大家詳細(xì)介紹了vue+highcharts實(shí)現(xiàn)3D餅圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論

奉化市| 舟山市| 道孚县| 海宁市| 河北省| 翁源县| 江北区| 启东市| 平邑县| 腾冲县| 宁夏| 西华县| 昌平区| 环江| 桐城市| 天柱县| 湟中县| 苗栗市| 育儿| 广昌县| 松阳县| 南丰县| 万山特区| 赣榆县| 阿克陶县| 利辛县| 扶余县| 湖州市| 平利县| 库尔勒市| 湘阴县| 临桂县| 中超| 临夏县| 封丘县| 阳信县| 禄丰县| 灌阳县| 错那县| 蓬莱市| 平塘县|