cbvirtua 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cevent.txt +190 -0
- package/element-image-preview-main.zip +0 -0
- package/package.json +1 -1
- package/vv.txt +0 -10442
package/cevent.txt
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
JS 自定义事件:从 CustomEvent 到 dispatchEvent
|
|
2
|
+
2025-10-27
|
|
3
|
+
427
|
|
4
|
+
阅读7分钟
|
|
5
|
+
前言
|
|
6
|
+
在前端开发中,事件是交互逻辑的核心载体。除了 click、scroll 等浏览器内置事件,我们还可以通过 CustomEvent 和 dispatchEvent 构建自定义事件系统,实现组件通信、逻辑解耦等复杂需求,下面将解析这两个 API 的工作机制与实践。
|
|
7
|
+
|
|
8
|
+
一、什么是自定义事件?
|
|
9
|
+
自定义事件是开发者根据业务需求手动创建的事件类型,它与浏览器内置事件的核心区别在于:触发时机、传递数据和作用范围完全由开发者控制。
|
|
10
|
+
|
|
11
|
+
在 JavaScript 中,自定义事件的实现依赖两个核心 API:
|
|
12
|
+
|
|
13
|
+
CustomEvent:用于创建携带自定义数据的事件对象;
|
|
14
|
+
dispatchEvent:用于在指定 DOM 元素上触发事件,执行所有监听函数。
|
|
15
|
+
这两个 API 并非独立存在,而是协同工作:CustomEvent 负责 “定义事件”,dispatchEvent 负责 “触发事件”,二者结合构成了完整的自定义事件生命周期。
|
|
16
|
+
|
|
17
|
+
二、CustomEvent:构建自定义事件的 “数据载体”
|
|
18
|
+
CustomEvent 是一个原生构造函数,用于创建自定义事件实例。它的核心作用是将业务数据封装到事件中,并配置事件的行为特性(如是否冒泡、是否可取消)。
|
|
19
|
+
|
|
20
|
+
基本语法
|
|
21
|
+
const event = new CustomEvent(type, options);
|
|
22
|
+
参数详解
|
|
23
|
+
type(必填) 事件名称(字符串),需遵循命名规范(建议使用小写字母 + 连字符,如 user-login,避免与内置事件冲突)。
|
|
24
|
+
|
|
25
|
+
options(可选) 配置对象,支持三个核心属性:
|
|
26
|
+
|
|
27
|
+
detail:任意类型,自定义事件的核心数据载体(唯一推荐用于传递数据的属性);
|
|
28
|
+
bubbles:布尔值,默认 false,表示事件是否支持冒泡(从触发元素向上传播至父级);
|
|
29
|
+
cancelable:布尔值,默认 false,表示事件是否可被 preventDefault() 取消。
|
|
30
|
+
关键特性:detail 属性的特殊性
|
|
31
|
+
detail 是 CustomEvent 区别于普通 Event 的核心标志。它允许开发者传递任意类型的数据(对象、数组、函数等),且在事件传播过程中始终保持可访问。例如:
|
|
32
|
+
|
|
33
|
+
// 创建携带用户信息的自定义事件
|
|
34
|
+
const userEvent = new CustomEvent('user-update', {
|
|
35
|
+
detail: {
|
|
36
|
+
id: 1001,
|
|
37
|
+
name: '张三',
|
|
38
|
+
action: 'profile-edit'
|
|
39
|
+
},
|
|
40
|
+
bubbles: true,
|
|
41
|
+
cancelable: true
|
|
42
|
+
});
|
|
43
|
+
这里的 detail 数据会被绑定到事件对象中,在监听函数中可通过 event.detail 获取。
|
|
44
|
+
|
|
45
|
+
三、dispatchEvent:触发事件的 “执行引擎”
|
|
46
|
+
dispatchEvent 是 DOM 元素的方法,用于在指定元素上触发一个事件(包括内置事件和自定义事件)。当事件被触发后,浏览器会按照事件流(捕获 -> 目标 -> 冒泡)的顺序执行所有相关的监听函数。
|
|
47
|
+
|
|
48
|
+
基本语法
|
|
49
|
+
const isDispatched = targetElement.dispatchEvent(event);
|
|
50
|
+
参数与返回值
|
|
51
|
+
event(必填) :事件对象(可以是 CustomEvent 实例,也可以是内置事件如 new Event('click'));
|
|
52
|
+
返回值:布尔值。若事件可取消(cancelable: true)且被 preventDefault() 阻止,则返回 false;否则返回 true。
|
|
53
|
+
触发逻辑:事件流的完整生命周期
|
|
54
|
+
当调用 dispatchEvent 时,事件会经历三个阶段(与内置事件一致):
|
|
55
|
+
|
|
56
|
+
捕获阶段:从 window 向下传播至目标元素的父级;
|
|
57
|
+
目标阶段:到达触发事件的目标元素;
|
|
58
|
+
冒泡阶段:从目标元素向上传播至 window(仅当 bubbles: true 时生效)。
|
|
59
|
+
例如,在子元素上触发一个支持冒泡的事件,父元素和 document 都能监听到:
|
|
60
|
+
|
|
61
|
+
<div id="parent">
|
|
62
|
+
<button id="child">点击我</button>
|
|
63
|
+
</div>
|
|
64
|
+
// 监听父元素的自定义事件
|
|
65
|
+
document.getElementById('parent').addEventListener('custom-click', (e) => {
|
|
66
|
+
console.log('父元素监听到事件');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// 在子元素上触发事件
|
|
70
|
+
const child = document.getElementById('child');
|
|
71
|
+
child.dispatchEvent(new CustomEvent('custom-click', { bubbles: true }));
|
|
72
|
+
// 输出:"父元素监听到事件"(因冒泡机制)
|
|
73
|
+
四、完整工作流程:从定义到触发的全链路
|
|
74
|
+
自定义事件的使用需经过 “定义事件 -> 监听事件 -> 触发事件” 三个步骤,缺一不可。以下是一个完整示例:
|
|
75
|
+
|
|
76
|
+
步骤 1:定义自定义事件(含数据)
|
|
77
|
+
// 定义一个携带表单数据的事件
|
|
78
|
+
const formSubmitEvent = new CustomEvent('form-submit', {
|
|
79
|
+
detail: {
|
|
80
|
+
username: 'test',
|
|
81
|
+
password: '123456'
|
|
82
|
+
},
|
|
83
|
+
bubbles: true, // 允许冒泡,方便父级监听
|
|
84
|
+
cancelable: true // 允许取消提交
|
|
85
|
+
});
|
|
86
|
+
步骤 2:在目标元素上监听事件
|
|
87
|
+
// 在表单容器上监听事件
|
|
88
|
+
const formContainer = document.getElementById('form-container');
|
|
89
|
+
formContainer.addEventListener('form-submit', (e) => {
|
|
90
|
+
console.log('表单数据:', e.detail);
|
|
91
|
+
|
|
92
|
+
// 验证数据,若无效则阻止默认行为
|
|
93
|
+
if (e.detail.password.length < 6) {
|
|
94
|
+
e.preventDefault(); // 仅当 cancelable: true 时有效
|
|
95
|
+
alert('密码长度不足');
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
步骤 3:在触发点调用 dispatchEvent
|
|
99
|
+
// 表单提交按钮点击时触发自定义事件
|
|
100
|
+
document.getElementById('submit-btn').addEventListener('click', () => {
|
|
101
|
+
const isSuccess = formContainer.dispatchEvent(formSubmitEvent);
|
|
102
|
+
if (isSuccess) {
|
|
103
|
+
console.log('表单提交已触发');
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
当点击按钮时,事件会从 formContainer 触发并冒泡,监听函数会接收数据并执行验证逻辑。若验证失败,preventDefault() 会阻止事件的默认行为。
|
|
107
|
+
|
|
108
|
+
五、典型使用场景:解决实际开发痛点
|
|
109
|
+
自定义事件的核心价值在于解耦代码逻辑和灵活传递信息,以下是几个高频场景:
|
|
110
|
+
|
|
111
|
+
1. 组件间通信(尤其适用于非父子组件)
|
|
112
|
+
在前端框架(如 React、Vue)或原生组件开发中,非父子组件(如兄弟组件、跨层级组件)的通信往往难以通过 props 直接实现。此时,自定义事件可作为 “桥梁”:
|
|
113
|
+
|
|
114
|
+
// 组件 A(子组件)触发事件
|
|
115
|
+
class ComponentA extends HTMLElement {
|
|
116
|
+
handleClick() {
|
|
117
|
+
this.dispatchEvent(new CustomEvent('data-change', {
|
|
118
|
+
detail: { value: '来自组件A的数据' },
|
|
119
|
+
bubbles: true // 冒泡到父级
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 组件 B(兄弟组件)监听事件
|
|
125
|
+
class ComponentB extends HTMLElement {
|
|
126
|
+
connectedCallback() {
|
|
127
|
+
document.addEventListener('data-change', (e) => {
|
|
128
|
+
console.log('组件B收到数据:', e.detail.value);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
通过事件冒泡,组件 B 无需依赖组件 A 的实例,即可接收数据,实现完全解耦。
|
|
133
|
+
|
|
134
|
+
2. 模拟用户行为(测试与自动化)
|
|
135
|
+
dispatchEvent 可触发内置事件,常用于测试场景中模拟用户操作(如点击、输入):
|
|
136
|
+
|
|
137
|
+
// 模拟按钮点击
|
|
138
|
+
const button = document.querySelector('button');
|
|
139
|
+
button.dispatchEvent(new Event('click'));
|
|
140
|
+
|
|
141
|
+
// 模拟输入框输入
|
|
142
|
+
const input = document.querySelector('input');
|
|
143
|
+
input.value = '测试文本';
|
|
144
|
+
input.dispatchEvent(new Event('input')); // 触发输入事件,更新相关逻辑
|
|
145
|
+
这在单元测试中尤为重要,可自动验证用户交互后的逻辑是否正常。
|
|
146
|
+
|
|
147
|
+
3. 跨模块状态同步
|
|
148
|
+
当多个独立模块需要响应同一状态变化时(如用户登录状态更新),自定义事件可避免模块间的直接依赖:
|
|
149
|
+
|
|
150
|
+
// 登录模块:登录成功后触发事件
|
|
151
|
+
loginModule.onSuccess = (user) => {
|
|
152
|
+
window.dispatchEvent(new CustomEvent('user-login', { detail: user }));
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// 导航模块:监听登录事件更新UI
|
|
156
|
+
navModule.init = () => {
|
|
157
|
+
window.addEventListener('user-login', (e) => {
|
|
158
|
+
this.renderUserAvatar(e.detail.avatar);
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// 消息模块:监听登录事件拉取消息
|
|
163
|
+
messageModule.init = () => {
|
|
164
|
+
window.addEventListener('user-login', (e) => {
|
|
165
|
+
this.fetchMessages(e.detail.id);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
登录模块无需关心哪些模块依赖登录状态,只需触发事件;其他模块按需监听,实现 “发布 - 订阅” 模式。
|
|
169
|
+
|
|
170
|
+
六、实践与注意事项
|
|
171
|
+
事件命名规范采用 “领域 - 行为” 格式(如 form-submit、user-logout),避免使用单个单词(可能与内置事件冲突),且统一小写字母 + 连字符,增强可读性。
|
|
172
|
+
|
|
173
|
+
控制事件冒泡范围虽然 bubbles: true 方便跨层级监听,但过度冒泡可能导致性能问题或意外触发其他监听函数。建议:
|
|
174
|
+
|
|
175
|
+
非必要不冒泡;
|
|
176
|
+
如需冒泡,可在特定层级使用 event.stopPropagation() 终止传播。
|
|
177
|
+
**谨慎使用 cancelable**仅当事件需要被 “阻止默认行为” 时才设置 cancelable: true(如表单提交验证),否则保持默认 false,减少不必要的性能消耗。
|
|
178
|
+
|
|
179
|
+
清理事件监听在组件销毁或页面卸载时,需通过 removeEventListener 移除自定义事件监听,避免内存泄漏:
|
|
180
|
+
|
|
181
|
+
// 组件卸载时清理
|
|
182
|
+
disconnectedCallback() {
|
|
183
|
+
window.removeEventListener('user-login', this.handleLogin);
|
|
184
|
+
}
|
|
185
|
+
避免过度使用自定义事件对于简单的父子组件通信,props 或回调函数可能更直观;自定义事件适用于复杂场景(跨层级、多模块),避免滥用导致逻辑混乱。
|
|
186
|
+
|
|
187
|
+
七、总结
|
|
188
|
+
CustomEvent 与 dispatchEvent 共同构成了 JavaScript 自定义事件系统的核心,能够像操作内置事件一样,灵活定义业务相关的交互逻辑。通过自定义事件,我们可以实现组件解耦、跨模块通信、行为模拟等功能。
|
|
189
|
+
|
|
190
|
+
掌握这两个 API 的关键在于理解 “事件流” 与 “数据传递” 的本质:事件是载体,数据是核心,传播是手段。合理使用它们,能让代码逻辑更清晰、扩展性更强。
|
|
Binary file
|