[JavaScript] 事件委托,以及 Vue 列表循环渲染事件绑定的性能问题

前言

事件委托(Event Delegation) 是一种通过将事件监听器绑定到父元素,而不是直接绑定到每个子元素上的技术。这样可以减少事件监听器的数量,提升性能,并使得对动态添加或移除的元素更容易进行事件处理。

事件冒泡和事件捕获

事件冒泡:从里往外

<div id="parent" style="padding: 50px; background-color: lightblue">
  Parent Div
  <button id="child" style="padding: 20px">Child Button</button>
</div>

<script>
  const parentDiv = document.getElementById("parent");
  const childButton = document.getElementById("child");

  parentDiv.addEventListener(
    "click",
    event => {
      console.log("Parent Div Clicked (Capturing)");
    }
  );

  childButton.addEventListener("click", () => {
    console.log("Button Clicked");
  });
</script>

事件冒泡:从里往外

事件捕获:从外往里

const parentDiv = document.getElementById("parent");
const childButton = document.getElementById("child");

parentDiv.addEventListener(
  "click",
  event => {
    console.log("Parent Div Clicked (Capturing)");
  },
  true
); // 捕获阶段处理

childButton.addEventListener("click", () => {
  console.log("Button Clicked");
});

事件捕获:从外往里

事件委托

事件委托主要是依赖于事件冒泡(事件从最深层的子元素冒泡到父元素)。通过将事件监听器绑定到父元素,当子元素触发事件时,事件会冒泡到父元素并在父元素上捕捉到事件。我们可以通过 event.target 来判断事件最初是由哪个子元素触发的。

事件监听器绑定到 li 的父元素 ul 身上,点击其子元素 li,就可以触发点击事件。点击子元素会依次往上冒泡,直到 ul 的事件监听器捕获到点击事件。

<button id="btn">点击新增标签项</button>
<ul id="ul">
  <li>标签项</li>
  <li>标签项</li>
  <li>标签项</li>
</ul>

<script>
  const btn = document.querySelector("btn");
  const ul = document.querySelector("#ul");

  ul.addEventListener("click", function (e) {
    console.log(e.target);
  });
</script>

Vue 列表循环性能优化

当初通过 B站 网上视频学习 Vue,对于这类需求视频中直接把事件绑定给 li,直到了解到事件委托和事件冒泡,才知道这样做对性能有很大影响。

v-for 循环中将索引 index 绑定到 <li> 元素的 data- 属性中,然后在事件委托的处理函数中通过 event.target 获取它。

<template>
  <ul @click="handleClick">
    <li v-for="(item, index) in items" :key="index" :data-index="index">
      {{ item }}
    </li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      items: ['Item 1', 'Item 2', 'Item 3'],
    };
  },
  methods: {
    handleClick(event) {
      // 判断点击的目标是否为 <li> 元素
      if (event.target.tagName === 'LI') {
        // 通过自定义属性 data-index 获取对应的索引
        const index = event.target.dataset.index;
        console.log('Clicked item index:', index);
        console.log('Clicked item value:', this.items[index]);
      }
    }
  }
}
</script>
posted @ 2024-09-11 10:27  Himmelbleu  阅读(16)  评论(0编辑  收藏  举报