返回

随意使用 $attr与 $listeners 可能引发的潜在 bug

前端

在经历了各路文件类型检测的折磨后,前端的事件处理也给予我们与 JavaScript 一般的自由。以致于我们恨不能在监听的回调函数中修改原生事件。这里要重点提到的是,无论在渲染阶段还末在挂载阶段,“事件”所注重的始终是 DOM 的原生事件处理。而我们借助 .addEventListener 和 .attachEvent 等方式来定制自己的监听处理。于是乎,在前端的抽象化之下,无论我们自己的代码,还是第三方混入的库代码,都是基于原生 DOM 的事件机制运作的。

浏览器端的三大“框架”:Angular,React,还有我们最爱的 vue,采纳了一份类似的事件处理思想,它在某一事件发生了后,依托自己的实现的方式将其进行抽象的处理。
通过这种方式可以将用户指定的事件处理函数包裹在自己的体系内。事件函数可以传入自己的自定义的参数列表,以待事件处理时以其为准绳。以此同时简化了复杂的事件传递机制,也让框架本身的兼容性得到了进一步的加强。
实际上,这就是 vue.js 框架提供了 $listeners$attr 的原因。这些“变量”绑定了所有事件处理函数以及 html 属性。

见仁见智

在众所周示的事件处理当中,$listeners 的出现解决了多个组件之间无缝的事件传递问题。甚至,多个框架内部的事件监听者在被触发之后,为了最大效率常常并不会立刻执行回调函数,并对其函数参数进行处理,保证函数能够调用成功后才会执行。

不过在 $listeners 当中隐藏着一个细小的坑,就以我这种喜欢不停折叠多次更新以至达成代码最优化的狂人,容易掉进这个坑。那就是 $listeners 在相同的事件处理的回调函数中会多次执行相同的操作。我们常常会使用的场景就是:将某个事件无脑的传递给其子孙元素,如果多次事件触发后发现其中存在一个事件被触发了数次,那么这个元素的行为便不可避免的出现预期外的事件。

为了更好的解释这个问题,我们可以看看以下代码:

// 在组件内
<ul>
  <li v-for="item in items"><button v-on:click="count++"></button></li>
</ul>

因为每当计数加一后,事件处理函数便会被其自身的 DOM 元素“提交”。同一个事件触发的无数次相同事件传递。如何能解决这种多次事件触发的回调函数本身?

实际上,在以前的文章中就已经给出了答案,使用 $on$once 来完成。只要有一个人知道它,我愿意将其写在这里,问题已得到解决。

// 在组件内
<ul>
  <li v-for="item in items"><button v-on:click="count++"></button></li>
</ul>

这个修改的工作原理显然与先前所述的方式大相径庭。
当事件处理函数被立刻执行时,我们能够减少回调函数中自身的执行次数。

一旦我们能够意识到其带来的好处。事情往往会变得更坏。

$listeners 并不是这个幕布屏幕的最糟糕的部分。
以下内容时我在 vue.js 仓库里分享的内容。

// 在组件内
<ul>
  <li v-for="item in items"><button v-on:click="count++"></button></li>
</ul>

通过该方式,若按照我们常做的处理方式,我们会被假设 $attr 在挂载后我们便不再需要它。但事实上,再进行更新时,它依然会进行执行。

解决方式同样是通过 $on$once
最后代码得到

// 在组件内
<ul>
  <li v-for="item in items"><button v-on:click="count++"></button></li>
</ul>