这篇文章是我在去育本前一天晚上匆匆忙忙地冒出的想法,在去育本这一天早上连滚带爬写的文章。
项目已经开源在:PanDaoxi/OnceButton
思路
我这里暂且称这个按钮运行后使页面被“销毁”。
graph TD
A[用户访问页面] --> B{判断页面是否已被销毁}
B -- 是 --> C[返回 403 Forbidden]
B -- 否 --> D[显示页面 + 按钮]
D -- 用户点击按钮 --> E[标记为已被销毁<br>执行设定的逻辑]
E -- 页面刷新 --> C
这里我了解到了 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 }); };
|
接下来本地测试即可。
进阶
本地测试可以进行 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 中添加即可。