前言
Solid.js,一个比 React 更 react 的框架。每一个使用 React 的同学,你可以不使用,但不应该不了解。
目前 Solid.js 发布了最新的官方文档,但却缺少对应的中文文档。为了帮助大家学习 Solid.js,故有此系列。
我同时搭建了 Solid.js 最新的中文文档站点:solid.yayujs.com ,欢迎勘误。
虽说是翻译,但个人并不喜欢严格遵守原文,为了保证中文阅读流畅,会删减部分语句,对难懂的部分也会另做补充解释,希望能给大家带来一个好的中文学习体验。
欢迎围观朋友圈、加入低调务实优秀中国好青年前端社群,一个人走得快,一群人走得远。
事件处理程序
事件处理程序是指为响应浏览器中发生的特定事件而调用的函数,例如当用户单击某个元素时。
Solid 提供了两种向浏览器添加事件监听器的方法:
[on:__](https://docs.solidjs.com/reference/jsx-attributes/on)
: 向 element 添加事件监听器。这也称为本机事件(native event)。[on__](https://docs.solidjs.com/reference/jsx-attributes/on_)
: 将事件监听器添加到document
并将其分派到element
。这也被称为委托事件(delegated event)。
委托事件流经组件树,通过复用常用事件节省一些资源。本机事件流经 DOM 树,可以提供对事件行为的更多控制。
使用事件
要添加事件处理程序,在事件名称前加上 on
或 on:
前缀:
tsx
// delegated event
<button onClick={handleClick}>Click me</button>
// native event
<div on:scroll={handleScroll}>... very long text ...</div>
委托事件不区分大小写 ,因此在 Solid 中使用委托事件处理程序可以使用驼峰式或全部小写形式编写。请注意,虽然委托事件可以用两种方式编写,但本机事件区分大小写。
tsx
<button onclick={handleClick}>Click me</button>
对于其他事件,例如自定义事件或您不希望被代理的事件,使用 on:
属性将原样添加事件处理器,这就是事件处理器区分大小写的原因:
tsx
<button on:Custom-Event={handleClick}>Click me</button>
对于使用 on:
的标准或自定义事件的类型,TypeScript 页面有一个关于事件处理器的部分。
绑定事件
要优化事件处理程序,您可以传递一个数组替代原本的函数作为事件处理程序,此时传递到数组的第二个条目将作为处理程序的第一个参数:
tsx
const handler = (data, event) => {
console.log("Data:", data, "Event:", event);
};
<button onClick={[handler, "Hello!"]}>Click Me</button>;
在这个例子中 ,点击按钮时,Hello!
字符串将作为 handler
函数中的 data
参数传递。
通过这种方式绑定事件,Solid 避免了使用 JavaScript bind 方法造成的额外闭包开销。
动态处理程序
事件处理程序不是响应式系统的一部分,如果您将处理程序作为信号传递,它将不会响应信号的更改。换句话说,事件不会动态更新,并且绑定也不是响应式。这是因为添加和删除监听器是一项资源密集型人物。
由于事件处理程序的调用方法类似于标准函数,因为你可以将它们设计为在需要时调用响应式资源。
在下面的例子中,handleClick
表示 props 具有调用任何函数的灵活性。因为不需要这些函数是响应式的:
tsx
<div onClick={() => props.handleClick?.()} />
事件委托
Solid 没有将事件监听器添加到每个单独的元素,而是通过 on__ 的形式使用合成事件委托 。在这种方法中,事件监听器将添加到 document
元素,并在相关元素冒泡时将事件分派给它们。
通过将事件监听器的数量保持在最低限度,可以更有效地捕获事件。当处理大量元素(例如表格或列表)时特别有用。
click
、input
和 keydown
等受支持的事件只是以这种方式优化的几个示例。要查看完整列表,请参阅下面的委托事件列表。
如果您需要将事件监听器添加到 Solid 的事件委托不支持的元素(例如自定义元素,%20you%20can%20use%20the%20on:__
中的自定义事件),则可以使用 on:__ 形式。
tsx
<div on:customEvent={handleCustomEvent} />
事件委托注意事项
虽然事件委派提供了一些性能增强,但也需要权衡。
事件委托是为通过 JSX 树而不是 DOM 树传播事件而设计的。这可能与之前对事件如何运行和流动的预期不同。
需要记住的一些事情包括:
- 委托事件监听器针对每个事件类型都添加一次,并处理该类型的所有未来事件。这意味着即使添加事件监听器的元素及其处理程序被删除,委托事件监听器仍保持活动状态。例如,如果
div
监听mousemove
并随后被删除,mousemove
事件仍将分派到document
,以防还有另一个元素也在监听鼠标移动。
tsx
<div onMouseMove={handleCustomEvent} />
注意:
对于不经常发生的事件,本机事件是更好的解决方案。由于这些事件发生在特定情况下,因此它们不会从通过事件委托获得的性能改进中受益。
tsx
<div on:mousemove={handleCustomEvent} />
event.stopPropagation()
无法按预期工作,因为事件添加到document
而不是element
对于此类情况,建议使用本机事件。例如在接下来的例子中,使用本机事件将阻止事件到达 div native
处理程序,而委托事件则不能。
tsx
onMount(() => {
ref.addEventListener("click", () => {
console.log("div native");
});
});
<div ref={ref}>
<button
onClick={(event) => {
event.stopPropagation();
console.log("button");
}}
>
button
</button>
</div>;
输出结果为:
tsx
// Button clicked
div native
button
您可以在 Solid Playground 中查看此示例。
但如果将 button
改为使用本机事件:
tsx
// ...
<button
on:click={(event) => {
event.stopPropagation();
console.log("button");
}}
>
button
</button>
// ...
输出结果为:
tsx
// Button clicked
button
- Portals 按照组件树而不是 DOM 树传播事件,这使得它们更易于使用。这意味着当
Portal
添加到body
时,任何事件都会传播到container
。
tsx
<div class="container" onInput={() => console.log("portal key press")}>
<Portal mount={document.body}>
<input onInput={() => console.log("input key press")} />
</Portal>
</div>
注意:
onChange
和onInput
事件根据其原生行为工作:
onInput
将在值更改后立即触发在
<input>
字段中,onChange
仅在字段失去焦点后触发。
委托事件列表
您可以在我们的源代码中查看此列表(请参阅 DelegatedEvents
)。
- beforeinput
- click
- dblclick
- contextmenu
- focusin
- focusout
- input
- keydown
- keyup
- mousedown
- mousemove
- mouseout
- mouseover
- mouseup
- pointerdown
- pointermove
- pointerout
- pointerover
- pointerup
- touchend
- touchmove
- touchstart