首页 > 解决方案 > $emit 和 Watchers 的 VueJS 性能成本

问题描述

我已经构建了一个音乐应用程序(使用 Vue 和 ToneJS),用户可以在其中创建循环曲目,这些曲目会根据用户的选择以各种方式发生变化。这利用了一组相当复杂的缩放计数器机制。在构建了音乐功能之后,我正在制作一个“进度条”,它会显示下一次转换即将发生的时间。

目前,我这样做的方式是计算所需的总步数(每个音符是一个“步”),并将其与每个计数器的进度(在 Vuex 状态下)进行比较。就代码而言,这是很多精神开销。

执行此操作的更好方法可能是使用 $emit 每次前进一个步骤时发送一个“滴答”,这将由具有进度条的组件拾取并与所需的步骤进行比较。或者,在组件上使用观察者可以检测到更改并发送滴答声。

但是,我已经遇到了应用程序的一些计时性能问题,而计时对此至关重要。我是一个相对较新的开发人员,还不太了解性能。所以我想知道的是它的使用$emit或观察者有多“昂贵”?由于它将连接到应用程序的“电机”,因此会不断地调用它。有没有可能使齿轮卡住的危险?

标签: performancevue.js

解决方案


First, you need to understand what their differences are!

Emitting an event with $emit in vue.js:

Vuejs is using the publish-subscribe pattern for emitting the events. In software architecture, publish–subscribe is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers..

Let's visualize this pattern:

Object1 will emit event fooEvent. Other objects might register a subscriber to this event, so whenever any fooEvent event emits the subscribers will be called.

This is how vuejs register subscribers (source code on Github):

  Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
    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[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
      }
    }

In a nutshell it just store them in an array vm._events:

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

And this how it calls the subscribers (source-code on Github):

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

It iterates over all subscribers and calls them one by one in registration order.

So? It doesn't cost anything big!

How watch works?:

Well, it's a long story if we are going to talk about in the source-code but here is the short one:

Whenever you mark a property to be watched inside Vuejs, it runs a big code to watch its changes and schedule it inside a scheduler! SO vue understand the changes by pooling and checking if there is a change or not.

  1. This is how it creates a watcher (every-time you mark an object to be watched, it creates a new Watcher object for it).
  2. The run method inside a watcher will be called by the scheduler.
  3. And how it runs them.

So? It's quite heavy, isn't it?


So the summary of the results and my opinion according to the codes and my personal experience:

Emitting an event isn't such a heavy thing, it's very very lighter than a watcher.


推荐阅读