这篇文章是我在去育本前一天晚上匆匆忙忙地冒出的想法,在去育本这一天早上连滚带爬写的文章。

项目已经开源在:PanDaoxi/OnceButton


思路

我这里暂且称这个按钮运行后使页面被“销毁”。

这里我了解到了 Netlify Blobs 这个东西,可以用来存储我们是否有“销毁”这个状态。比再利用第三方的 DB 容易多了。

Netlify Blobs 提供零配置的对象存储,可从任何 Netlify 计算环境(functions、edge functions、framework server routes)访问,无需预置资源。 通过 @netlify/blobs 安装,可提供简单的站点作用域和部署作用域存储(getStore、getDeployStore),并为需要即时读取的场景提供显式的一致性选项。
——netlify-blobs | Skills Marketplace · LobeHub

实现

项目结构:

1
2
3
4
5
6
7
8
.
│ netlify.toml // 负责指引 functions 的重定向
│ README.md

└─netlify
└─functions
destroy.js // 销毁页面的 API
page.js // 根据状态不同返回不同的响应

准备环境

1
2
3
4
5
npm install -g netlify-cli // 便于本地开发测试
netlify login

// 这个是必需的
npm install @netlify/blobs

(国内可用 cnpm 代替 npm,会快很多)

两个函数

首先是重定向,使访问 / 时重定向到 /page

1
2
3
4
5
6
7
[build]
functions = "netlify/functions"

[[redirects]]
from = "/"
to = "/.netlify/functions/page"
status = 200

然后 page.js,根据 destroyed 这个开关来判断展示什么样的页面:

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
31
import { getStore } from "@netlify/blobs";

export default async (req) => {
const store = getStore({ name: "state", consistency: "strong" });
const destroyed = await store.get("destroyed");

if (destroyed === "true") {
return new Response("403 Forbidden", {
status: 403,
headers: { "Content-Type": "text/html; charset=utf-8" },
});
}

return new Response(
`<!DOCTYPE html>
<html><body>
<h1>Once Button</h1>
<button onclick="destroy()">按钮</button>
<script>
async function destroy() {
await fetch('/.netlify/functions/destroy', { method: 'POST' });
location.reload();
}
</script>
</body></html>`,
{
status: 200,
headers: { "Content-Type": "text/html; charset=utf-8" },
},
);
};

这个 destory.js 是标记 destroyed 的 API,

1
2
3
4
5
6
7
import { getStore } from "@netlify/blobs";

export default async (req) => {
const store = getStore({ name: "state", consistency: "strong" });
await store.set("destroyed", "true");
return new Response("ok", { status: 200 });
};

接下来本地测试即可。

1
netlify dev

进阶

本地测试可以进行 reset

添加 netlify/functions/reset.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { getStore } from "@netlify/blobs";

export default async (req) => {
const isLocal =
req.headers.get("host")?.includes("localhost") ||
req.headers.get("x-forwarded-host")?.includes("localhost");

if (!isLocal) {
return new Response("Forbidden", { status: 403 });
}

const store = getStore({ name: "state", consistency: "strong" });
await store.delete("destroyed");

return new Response("ok", {
status: 200,
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
};

netlify.toml 添加一条重定向:

1
2
3
4
5
6
7
8
9
10
11
[build]
functions = "netlify/functions"

[[redirects]]
from = "/"
to = "/.netlify/functions/page"
status = 200

[[redirects]]
from = "/reset"
to = "/.netlify/functions/reset"

这样访问 localhost:8888/reset 时即可快速重置状态。

添加自定义函数

page.js 中添加即可。