Vue 事件的高級使用方法


Vue 事件的高級使用方法

事件方法

在Vue中提供了4中事件監聽方法,分別是:

  • $on(event: string | Array, fn)
  • $emit(event: string)
  • $once(event: string, fn)
  • $off(event?: string|Array, fn?)

1. $on

監聽當前實例上的自定義事件。事件可以由 vm.$emit 觸發。回調函數會接收所有傳入事件觸發函數的額外參數。

從上述傳參可以看出。第一個參數可以傳遞一個字符串,或者一個數組,如果傳遞的是數組,則會對數組中的每一個事件進行監聽,只要有一個觸發,就會執行后面的回調函數

mounted () {
  this.$on('event1', () => {
    console.log('ok');
  });

  this.$on(['event1', 'event2', 'event3'], () => {
    console.log('ok');
  });

  setTimeout(() => {
    this.$emit('event1');
  }, 1000);
}

當調用 $emit 之后,兩個 $on 都會被觸發

2. $emit

觸發當前實例上的事件。附加參數都會傳給監聽器回調。

此處的用法非常簡單,可以和 $on 配合使用,也可以和組件中的自定義事件配合使用

Vue.component('welcome-button', {
  template: `
    <button v-on:click="$emit('welcome')">
      Click me to be welcomed
    </button>
  `
});

<div id="emit-example-simple">
  <welcome-button v-on:welcome="sayHi"></welcome-button>
</div>

methods: {
  sayHi: function () {
    alert('Hi!')
  }
}

 

3. $once

監聽一個自定義事件,但是只觸發一次。一旦觸發之后,監聽器就會被移除。

4. $off

移除自定義事件監聽器

  • 如果沒有提供參數,則移除所有的事件監聽器;
  • 如果只提供了事件,則移除該事件所有的監聽器;
  • 如果同時提供了事件與回調,則只移除這個回調的監聽器。

高級用法

1. 使用$on $emit 實現跨組件通信

有時兩個組件隔的比較遠的情況下,可以通過這兩個組件的共同父元素,或者跟節點來派發和監聽事件,達到通信的效果。

// components A
<div>
  <button @click="clickHandler">click me</button>
</div>

export default {
  name: 'componentsA',
  method: {
    clickHandler() {
      this.$root.$emit('my-events');
    }
  }
}

// components B
export default {
  name: 'componentsB',
  mounted () {
    this.$root.$on('my-events', () => {
      console.log('ok');
    });
  }
}
// 這里的$root可以換成共同的父元素

 

2. hookEvents

父組件可以直接通過自定義事件的形式監聽子組件中聲明周期鈎子的變化,固定寫法 <componentsA @hook:update="func" />, 目的是當使用了第三方組件,還想要知道里面的生命周期觸發了。可以使用此方法監聽

// child Events
export default {
  name: 'Events2',
  data () {
    return {
      counter: 0
    };
  },
  mounted () {
    setTimeout(() => {
      this.counter += 1;
    }, 2000);
  },
  updated () {
    console.log('update');
  }
};

// parent components
<Event2 @hook:updated="updateCounter"/>

export default {
  components: {
    Events2
  },
  methods: {
    updateCounter () {
      console.log('hooks ok');
    }
  }
}

 

源碼分析

  1. 定義事件源碼位置:vue/src/core/instance/events.js
export function eventsMixin (Vue: Class<Component>) {
  const hookRE = /^hook:/ // hookEvent
  Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component { // 除了可以使用字符串,還可以監聽一個數組,['event1', 'event2']
    const vm: Component = this
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$on(event[i], fn)
      }
    } else {
      // 把事件名稱和回調函數存入vm._events中
      (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 // 只要有人監聽這個事件。在callHooks中就是會派發一個事件
      }
    }
    return vm
  }

  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
  }

  Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
    const vm: Component = this
    // all
    if (!arguments.length) { // 無參數,清除所有的事件監聽
      vm._events = Object.create(null)
      return vm
    }
    // array of events
    if (Array.isArray(event)) { // 傳入的數組,把相關的事件移除
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$off(event[i], fn)
      }
      return vm
    }
    // specific event 解除特定事件
    const cbs = vm._events[event]
    if (!cbs) {
      return vm
    }
    if (!fn) { // 如果用戶沒指定fn參數,相關的所有回調都清除
      vm._events[event] = null
      return vm
    }
    // specific handler // 如果用戶指定fn參數,只移除對應的回調
    let cb
    let i = cbs.length
    while (i--) {
      cb = cbs[i]
      if (cb === fn || cb.fn === fn) {
        cbs.splice(i, 1)
        break
      }
    }
    return vm
  }

  Vue.prototype.$emit = function (event: string): Component { // 事件派發
    const vm: Component = this
    let cbs = vm._events[event]
    if (cbs) {
      cbs = cbs.length > 1 ? toArray(cbs) : cbs
      const args = toArray(arguments, 1)
      const info = `event handler for "${event}"`
      for (let i = 0, l = cbs.length; i < l; i++) {
        invokeWithErrorHandling(cbs[i], vm, args, vm, info)
      }
    }
    return vm
  }
}
  1. 派發hookEvents位置:vue/src/core/instance/lifecycle.js
export function callHook (vm: Component, hook: string) {
  if (vm._hasHookEvent) { // 如果標記了鈎子事件,則額外的派發一個自定義的事件出去
    vm.$emit('hook:' + hook) // 比如:@hook:update="xxx"
  }
}

 

測試代碼地址:https://github.com/Shenjieping/vue-events

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM