Svelte 官方入门教程(10) - 动作

svelte cover

上一讲是动画相关的知识,内容还是很多的,今天来看看动作(Actions)

一、use 指令

Actions 本质上是元素级的生命周期函数。 它们对以下事情很有用:

  • 与第三方库的接口
  • 延迟加载的图像
  • 工具提示
  • 添加自定义事件处理程序

例子

App

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script>
let showModal = true;
</script>

<button on:click={() => (showModal = true)}>Show Modal</button>
{#if showModal}
<div class="box" on:outclick={() => (showModal = false)}>
Click outside me!
</div>
{/if}

<style>
.box {
--width: 100px;
--height: 100px;
position: absolute;
width: var(--width);
height: var(--height);
left: calc(50% - var(--width) / 2);
top: calc(50% - var(--height) / 2);
display: flex;
align-items: center;
padding: 8px;
border-radius: 4px;
background-color: #ff3e00;
color: #fff;
text-align: center;
font-weight: bold;
}
</style>

click_outside.js

1
2
3
4
5
6
7
8
9
export function clickOutside(node) {
// setup work goes here...
return {
destroy() {
// ...cleanup goes here
}
};
}

在上面的这个例子中,我们希望用户点击橙色模态框外部时关闭它。 它有一个用于 outclick 事件的事件处理程序,但它不是原生 DOM 事件。 我们必须自己触发它。 首先,在App中导入 clickOutside 函数…

1
import { clickOutside } from "./click_outside.js";

然后将其与元素一起使用:

1
2
3
<div class="box" use:clickOutside on:outclick="{() => (showModal = false)}">
Click outside me!
</div>

打开 click_outside.js 文件。 与转换函数一样,动作函数接收一个节点(这是应用动作的元素)和一些可选参数,并返回一个动作对象。 该对象可以有一个销毁函数,该函数在元素卸载时调用。

当用户点击橙色框外时,我们希望触发 outclick 事件。 一种可能的实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export function clickOutside(node) {
const handleClick = (event) => {
if (!node.contains(event.target)) {
node.dispatchEvent(new CustomEvent("outclick"));
}
};

document.addEventListener("click", handleClick, true);

return {
destroy() {
document.removeEventListener("click", handleClick, true);
},
};
}

更新 clickOutside 函数,单击按钮以显示模态框,然后单击外部将其关闭。

REPL

二、附加参数

正如 transition 过渡效果和 animate 动画一样,动作(Action)也支持附带参数,动作函数会与它所在的元素一起被调用。

例子
App

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
import { longpress } from './longpress.js';

let pressed = false;
let duration = 2000;
</script>

<label>
<input type=range bind:value={duration} max={2000} step={100}>
{duration}ms
</label>

<button use:longpress
on:longpress="{() => pressed = true}"
on:mouseenter="{() => pressed = false}"
>press and hold</button>

{#if pressed}
<p>congratulations, you pressed and held for {duration}ms</p>
{/if}

longpress.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
export function longpress(node, duration) {
let timer;

const handleMousedown = () => {
timer = setTimeout(() => {
node.dispatchEvent(
new CustomEvent('longpress')
);
}, 500);
};

const handleMouseup = () => {
clearTimeout(timer)
};

node.addEventListener('mousedown', handleMousedown);
node.addEventListener('mouseup', handleMouseup);

return {
destroy() {
node.removeEventListener('mousedown', handleMousedown);
node.removeEventListener('mouseup', handleMouseup);
}
};
}

在这例子里,我们使用了一个长按动作,只要用户按下并按住按钮给定的持续时间,就会触发一个具有相同名称的事件。 现在,如果您切换到 longpress.js 文件,您会看到它被硬编码为 500 毫秒。

我们可以更改动作函数以接受持续时间作为第二个参数,并将该 duration 传递给 setTimeout 调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
export function longpress(node, duration) {
// ...

const handleMousedown = () => {
timer = setTimeout(() => {
node.dispatchEvent(
new CustomEvent('longpress')
);
}, duration);
};

// ...
}

回到 App.svelte,我们可以将 duration 值传递给操作:

1
<button use:longpress={duration}

这几乎可以运行的——该事件现在仅在 2 秒后触发。 但是,如果您向下滑动持续时间,仍然需要2秒钟。说白了就是改变滑块并没有更新duration的值。

为了改变这一点,我们可以在 longpress.js 中添加一个更新方法。 每当参数更改时都会调用它:

1
2
3
4
5
6
return {
update(newDuration) {
duration = newDuration;
},
// ...
};

到现在才是action的比较完整的api形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
export function longpress(node, duration) {
let timer;

const handleMousedown = () => {
timer = setTimeout(() => {
node.dispatchEvent(
new CustomEvent('longpress')
);
}, duration);
};

const handleMouseup = () => {
clearTimeout(timer)
};

node.addEventListener('mousedown', handleMousedown);
node.addEventListener('mouseup', handleMouseup);

return {
update(newDuration) {
duration = newDuration;
},
destroy() {
node.removeEventListener('mousedown', handleMousedown);
node.removeEventListener('mouseup', handleMouseup);
}
};
}

如果您需要将多个参数传递给一个动作,请将它们组合成一个对象,如使用:longpress={{duration, spiciness}}

REPL

总结

  • 从action的学习我们知道,use传递的第一个参数就是DOM节点,之前学习绑定这一章节的时候我们知道可以使用bind:this={el}来获取DOM节点,action 所不同的是,它有更强的封装性和复用性,将逻辑代码拆分到 JS 文件中供反复应用。

  • Action 作为一个方法用于标签被创建时调用。调用destroy函数返回表示标签被销毁。

  • Action 可以含有参数。如果返回的值含有update 方法, 在对 Svelte 标记的内容更新之后,只要update指定的参数发生变更,它都会立即应用变更。

  • Action 的use指令很好使用,可以用来抽离复用的代码。


Svelte 官方入门教程(10) - 动作
http://yoursite.com/2022/07/18/svelte-tutorial-10/
作者
昂藏君子
发布于
2022年7月18日
许可协议