Files
fuwari/index.html
afoim 33d483e8f3 feat: 添加对暗色主题的Cookie Consent支持
- 更新Cookie Consent脚本以动态适应暗色/亮色主题
- 添加CSS类切换功能,当文档主题变化时自动更新Cookie Consent界面
- 标记Cookie Consent相关资源为持久化,防止页面切换时被清理
- 优化Google Adsense脚本的格式和属性
- 添加@types/hast类型依赖以支持相关功能 53e134f106
2026-01-30 14:24:19 +00:00

1054 lines
133 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="zh-CN" class="bg-[var(--page-bg)] transition text-[12px] md:text-[16px] dark" data-overlayscrollbars-initialize data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;"> <!-- 手机端适配 --> <head><title>AcoFork Blog - 技术分享与实践</title><meta charset="UTF-8"><meta name="description" content="分享网络技术、服务器部署、内网穿透、静态网站搭建、CDN优化、容器化部署等技术教程与实践经验的个人技术博客专注于云原生、无服务器架构和前后端开发作者为afoim/二叉树树"><meta name="author" content="二叉树树"><meta name="keywords" content><meta property="og:site_name" content="AcoFork Blog"><meta property="og:url" content="https://blog.acofork.com/"><meta property="og:title" content="AcoFork Blog - 技术分享与实践"><meta property="og:description" content="分享网络技术、服务器部署、内网穿透、静态网站搭建、CDN优化、容器化部署等技术教程与实践经验的个人技术博客专注于云原生、无服务器架构和前后端开发作者为afoim/二叉树树"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta property="twitter:url" content="https://blog.acofork.com/"><meta name="twitter:title" content="AcoFork Blog - 技术分享与实践"><meta name="twitter:description" content="分享网络技术、服务器部署、内网穿透、静态网站搭建、CDN优化、容器化部署等技术教程与实践经验的个人技术博客专注于云原生、无服务器架构和前后端开发作者为afoim/二叉树树"><meta name="viewport" content="width=device-width"><meta name="generator" content="Astro v5.7.9"><link rel="preconnect" href="https://pic1.acofork.com"><link rel="preconnect" href="https://umami.acofork.com"><link rel="preconnect" href="https://support.nodeget.com"><script src="/js/umami-share.js" defer></script><script data-swup-ignore-script src="https://pic1.acofork.com/random.js" defer></script><link rel="icon" href="https://q2.qlogo.cn/headimg_dl?dst_uin=2726730791&spec=0"><!-- Set the theme before the page is rendered to avoid a flash --><script>(function(){const DEFAULT_THEME = "auto";
const LIGHT_MODE = "light";
const DARK_MODE = "dark";
const AUTO_MODE = "auto";
const BANNER_HEIGHT_EXTEND = 30;
const PAGE_WIDTH = 75;
const configHue = 361;
const forceDarkMode = false;
// Force dark mode if configured
if (forceDarkMode) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', DARK_MODE);
} else {
// Load the theme from local storage
const theme = localStorage.getItem('theme') || DARK_MODE;
switch (theme) {
case LIGHT_MODE:
document.documentElement.classList.remove('dark');
break
case DARK_MODE:
document.documentElement.classList.add('dark');
break
case AUTO_MODE:
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}
}
// Load the hue from local storage
const hue = localStorage.getItem('hue') || configHue;
document.documentElement.style.setProperty('--hue', hue);
// calculate the --banner-height-extend, which needs to be a multiple of 4 to avoid blurry text
let offset = Math.floor(window.innerHeight * (BANNER_HEIGHT_EXTEND / 100));
offset = offset - offset % 4;
document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`);
})();</script><link rel="alternate" type="application/rss+xml" title="二叉树树" href="https://blog.acofork.com/rss.xml"><link rel="stylesheet" href="/_astro/Layout.BuewHHUq.css">
<link rel="stylesheet" href="/_astro/Layout.Ds13mcav.css">
<link rel="stylesheet" href="/_astro/_page_.Duy76CKh.css">
<link rel="stylesheet" href="/_astro/_page_.DupG3mc-.css">
<link rel="stylesheet" href="/_astro/_page_.BYnNTCmG.css">
<link rel="stylesheet" href="/_astro/_slug_.Bf2USRy1.css">
<style>.donate-card[data-astro-cid-3z6uernf]{display:flex;flex-direction:column;border-radius:.5rem;border-width:1px;border-color:#0000001a;background-color:var(--card-bg);padding:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.donate-card[data-astro-cid-3z6uernf]:hover{border-color:#0003}.donate-card[data-astro-cid-3z6uernf]:is(.dark *){border-color:#ffffff1a}.donate-card[data-astro-cid-3z6uernf]:hover:is(.dark *){border-color:#fff3}.donate-card[data-astro-cid-3z6uernf]{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.support-card[data-astro-cid-3z6uernf]{border-radius:.5rem;border-width:1px;border-color:#0000001a;background-color:var(--card-bg);padding:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.support-card[data-astro-cid-3z6uernf]:hover{border-color:#0003;background-color:#0000000d}.support-card[data-astro-cid-3z6uernf]:is(.dark *){border-color:#ffffff1a}.support-card[data-astro-cid-3z6uernf]:hover:is(.dark *){border-color:#fff3;background-color:#ffffff0d}.support-card[data-astro-cid-3z6uernf]{backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.qr-code-placeholder[data-astro-cid-3z6uernf]{text-align:center}.sponsors-section[data-astro-cid-3z6uernf]{margin-top:1.5rem}.sponsors-grid[data-astro-cid-3z6uernf]{display:grid;grid-template-columns:repeat(1,minmax(0,1fr));gap:1rem}@media (min-width: 768px){.sponsors-grid[data-astro-cid-3z6uernf]{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width: 1024px){.sponsors-grid[data-astro-cid-3z6uernf]{grid-template-columns:repeat(3,minmax(0,1fr))}}.sponsor-placeholder[data-astro-cid-3z6uernf]{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.sponsor-placeholder[data-astro-cid-3z6uernf]:hover{--tw-scale-x: 1.05;--tw-scale-y: 1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sponsor-card[data-astro-cid-3z6uernf]{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.sponsor-card[data-astro-cid-3z6uernf]:hover{--tw-scale-x: 1.05;--tw-scale-y: 1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}
.friend-card[data-astro-cid-spp2p3no]{display:flex;flex-direction:column;gap:.25rem;border-radius:.5rem;border-width:1px;border-color:#0000001a;background-color:var(--card-bg);padding:1rem}.friend-card[data-astro-cid-spp2p3no]:hover{border-color:#0003;background-color:#0000000d}.friend-card[data-astro-cid-spp2p3no]:is(.dark *){border-color:#ffffff1a}.friend-card[data-astro-cid-spp2p3no]:hover:is(.dark *){border-color:#fff3;background-color:#ffffff0d}.friend-card[data-astro-cid-spp2p3no]{transition-property:background-color,border-color,scale;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;transform:translateZ(0)}.friend-card[data-astro-cid-spp2p3no]:active{scale:.98;background-color:var(--btn-regular-bg-active)}.friend-card[data-astro-cid-spp2p3no]:hover{background-color:var(--btn-regular-bg-hover)}.friend-card[data-astro-cid-spp2p3no]:hover .font-bold[data-astro-cid-spp2p3no]{color:var(--btn-content)}.friend-card[data-astro-cid-spp2p3no]:hover .text-sm[data-astro-cid-spp2p3no]{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.friend-card[data-astro-cid-spp2p3no]:hover .text-sm[data-astro-cid-spp2p3no]:is(.dark *){--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.friend-card[data-astro-cid-spp2p3no] .font-bold[data-astro-cid-spp2p3no],.friend-card[data-astro-cid-spp2p3no] .text-sm[data-astro-cid-spp2p3no]{transition-property:color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}
input.svelte-wkye0l:focus{outline:0}.search-panel.svelte-wkye0l{background-color:var(--float-panel-bg-opaque);max-height:calc(100vh - 100px);overflow-y:auto;scrollbar-width:none;-ms-overflow-style:none}.search-panel.svelte-wkye0l::-webkit-scrollbar{display:none}#display-setting.svelte-1rks09j input[type=number]:where(.svelte-1rks09j){-moz-appearance:textfield}#display-setting.svelte-1rks09j input[type=number]:where(.svelte-1rks09j)::-webkit-inner-spin-button,#display-setting.svelte-1rks09j input[type=number]:where(.svelte-1rks09j)::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}#display-setting.svelte-1rks09j input[type=range]:where(.svelte-1rks09j){-webkit-appearance:none;height:1.5rem;background-color:transparent;transition:background-image .15s ease-in-out}#display-setting.svelte-1rks09j input[type=range]:where(.svelte-1rks09j):not(#colorSlider){background-image:linear-gradient(to right,var(--primary) 0%,var(--primary) var(--value-percent),transparent var(--value-percent),transparent 100%)}#display-setting.svelte-1rks09j input[type=range]:where(.svelte-1rks09j)::-webkit-slider-thumb{-webkit-appearance:none;height:0;width:0;background:transparent;box-shadow:none;border:none}#display-setting.svelte-1rks09j input[type=range]:where(.svelte-1rks09j)::-moz-range-thumb{-webkit-appearance:none;height:0;width:0;background:transparent;box-shadow:none;border:none}#display-setting.svelte-1rks09j input[type=range]:where(.svelte-1rks09j)::-ms-thumb{-webkit-appearance:none;height:0;width:0;background:transparent;box-shadow:none;border:none}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j){background-image:var(--color-selection-bar)}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-webkit-slider-thumb{-webkit-appearance:none;height:1rem;width:.5rem;border-radius:.125rem;background:#ffffffb3;box-shadow:none;margin-top:0;transform:none;-webkit-transition:background .15s;transition:background .15s}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-webkit-slider-thumb:hover{background:#fffc}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-webkit-slider-thumb:active{background:#fff9}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-moz-range-thumb{-webkit-appearance:none;height:1rem;width:.5rem;border-radius:.125rem;border-width:0;background:#ffffffb3;box-shadow:none;transform:none;-moz-transition:background .15s;transition:background .15s}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-moz-range-thumb:hover{background:#fffc}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-moz-range-thumb:active{background:#fff9}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-ms-thumb{-webkit-appearance:none;height:1rem;width:.5rem;border-radius:.125rem;background:#ffffffb3;box-shadow:none;transform:none;-ms-transition:background .15s;transition:background .15s}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-ms-thumb:hover{background:#fffc}#display-setting.svelte-1rks09j #colorSlider:where(.svelte-1rks09j)::-ms-thumb:active{background:#fff9}#display-setting.svelte-1rks09j .toggle-switch:where(.svelte-1rks09j){-webkit-appearance:none;-moz-appearance:none;appearance:none;width:3rem;height:1.5rem;background:var(--btn-regular-bg);border-radius:999px;position:relative;cursor:pointer;transition:background .3s}#display-setting.svelte-1rks09j .toggle-switch:where(.svelte-1rks09j):after{content:"";position:absolute;top:.25rem;left:.25rem;width:1rem;height:1rem;background:var(--btn-content);border-radius:50%;transition:transform .3s}#display-setting.svelte-1rks09j .toggle-switch:where(.svelte-1rks09j):checked{background:var(--primary)}#display-setting.svelte-1rks09j .toggle-switch:where(.svelte-1rks09j):checked:after{transform:translate(1.5rem);background:#fff}
</style><script type="module" src="/_astro/page.G51m6u_A.js"></script></head> <body class=" min-h-screen transition lg:is-home" data-overlayscrollbars-initialize data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;"> <!-- Cookie Consent by TermsFeed https://www.TermsFeed.com --> <script data-swup-ignore-script type="text/javascript" src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js" charset="UTF-8"></script> <script data-swup-ignore-script type="text/javascript" charset="UTF-8">
(function () {
const getStoredTheme = () => {
try {
const theme = localStorage.getItem("theme");
return theme === "light" || theme === "dark" || theme === "auto" ? theme : null;
} catch {
return null;
}
};
const getHue = () => {
try {
const stored = localStorage.getItem("hue");
if (stored) return Number.parseInt(stored);
} catch {
}
const configCarrier = document.getElementById("config-carrier");
const fallback = "250";
const raw = configCarrier?.dataset.hue || fallback;
return Number.parseInt(raw);
};
const getPalette = () => {
const theme = getStoredTheme();
if (theme === "dark") return "dark";
if (theme === "light") return "light";
if (document.documentElement.classList.contains("dark")) return "dark";
return window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light";
};
const baseConfig = {
notice_banner_type: "simple",
consent_type: "express",
language: "en",
page_load_consent_levels: ["strictly-necessary"],
notice_banner_reject_button_hide: false,
preferences_center_close_button_hide: false,
page_refresh_confirmation_buttons: false,
website_name: "AcoFork Blog",
website_privacy_policy_url: "https://blog.acofork.com/posts/privacy-policy/",
};
const markCookieConsentHeadAssetsPersistent = () => {
const attr = "data-swup-theme";
const candidates = document.head?.querySelectorAll?.("link[rel='stylesheet'][href], style, script[src]") ?? [];
candidates.forEach(el => {
if (!(el instanceof HTMLElement)) return;
if (el.hasAttribute(attr)) return;
const tag = el.tagName.toLowerCase();
if (tag === "link") {
const href = el.getAttribute("href") || "";
if (/termsfeed|cookie-consent|cookieconsent/i.test(href)) el.setAttribute(attr, "");
return;
}
if (tag === "script") {
const src = el.getAttribute("src") || "";
if (/termsfeed|cookie-consent|cookieconsent/i.test(src)) el.setAttribute(attr, "");
return;
}
if (tag === "style") {
const css = el.textContent || "";
if (
/termsfeed-com---/.test(css) ||
/\bcc-(nb|pc|cp)\b/.test(css) ||
/\bcc_(dialog|preferences|cp|nb|overlay|banner|modal)\b/.test(css) ||
/cookie\s*consent|cookieconsent/i.test(css)
) {
el.setAttribute(attr, "");
}
}
});
};
let scheduled = false;
const scheduleApplyPalette = () => {
if (scheduled) return;
scheduled = true;
requestAnimationFrame(() => {
scheduled = false;
const palette = getPalette();
document
.querySelectorAll(
".termsfeed-com---palette-dark, .termsfeed-com---palette-light",
)
.forEach(el => {
el.classList.remove("termsfeed-com---palette-dark", "termsfeed-com---palette-light");
el.classList.add(`termsfeed-com---palette-${palette}`);
});
document
.querySelectorAll(
".cc_dialog, .cc_preferences, .cc_cp, .cc_nb, .cc_overlay, .cc_banner, .cc_modal",
)
.forEach(el => {
el.classList.remove("dark", "light");
el.classList.add(palette);
el.style.setProperty("--hue", String(getHue()));
});
});
};
const runCookieConsent = () => {
const cc = window.cookieconsent;
if (!cc || typeof cc.run !== "function") return;
cc.run({ ...baseConfig, palette: getPalette() });
scheduleApplyPalette();
};
document.addEventListener("DOMContentLoaded", () => {
const ensureOverrides = () => {
if (document.getElementById("cookie-consent-overrides")) return;
const style = document.createElement("style");
style.id = "cookie-consent-overrides";
style.textContent = `
.termsfeed-com---nb a, .termsfeed-com---pc a, .termsfeed-com---cp a { color: oklch(0.75 0.14 var(--hue)); }
.termsfeed-com---palette-dark .cc-nb-okagree, .termsfeed-com---palette-light .cc-nb-okagree { background-color: oklch(0.7 0.14 var(--hue)); border-color: oklch(0.7 0.14 var(--hue)); }
.termsfeed-com---palette-dark .cc-cp-footer-save, .termsfeed-com---palette-light .cc-cp-footer-save { background-color: oklch(0.7 0.14 var(--hue)); border-color: oklch(0.7 0.14 var(--hue)); }
`.trim();
document.head.appendChild(style);
};
ensureOverrides();
markCookieConsentHeadAssetsPersistent();
new MutationObserver(markCookieConsentHeadAssetsPersistent).observe(document.head, {
childList: true,
subtree: true,
});
runCookieConsent();
new MutationObserver(scheduleApplyPalette).observe(document.documentElement, {
attributes: true,
attributeFilter: ["class", "style"],
});
new MutationObserver(scheduleApplyPalette).observe(document.body, {
childList: true,
subtree: true,
});
});
})();
</script> <!-- Basic Analytics --> <!-- Umami --> <script type="text/plain" data-cookie-consent="strictly-necessary" defer src="https://umami.acofork.com/script.js" data-website-id="5d710dbd-3a2e-43e3-a553-97b415090c63" data-swup-ignore-script></script> <!-- end of Basic Analytics--> <!-- Google Analytics --> <!-- Google tag (gtag.js) --> <script type="text/plain" data-cookie-consent="tracking" async src="https://www.googletagmanager.com/gtag/js?id=G-YG02LLPGWC" data-swup-ignore-script></script> <script type="text/plain" data-cookie-consent="tracking">
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-YG02LLPGWC');
</script> <!-- end of Google Analytics--> <!-- Microsoft Clarity --> <script type="text/plain" data-cookie-consent="tracking" data-swup-ignore-script>
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "v94yrasi99");
</script> <!-- end of Site Experience & Interaction Analytics--> <!-- Google Adsense --> <script type="text/plain" data-cookie-consent="targeting" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1683686345039700" data-swup-ignore-script crossorigin="anonymous"></script> <!-- end of Google Adsense--> <noscript>Free cookie consent management tool by <a href="https://www.termsfeed.com/" data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;">TermsFeed</a></noscript> <!-- End Cookie Consent by TermsFeed https://www.TermsFeed.com --> <noscript> <style>
#bg-box {
opacity: 1 !important;
}
</style> </noscript> <div id="bg-box" data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;"></div> <div id="config-carrier" data-hue="361"></div> <div id="new-post-notification" class="fixed bottom-4 right-4 z-50 flex flex-col items-end pointer-events-none" data-astro-cid-6yyxny4o> <!-- Minimized State (Bell Icon) --> <button id="notification-minimized" class="pointer-events-auto bg-[var(--card-bg)] border border-[var(--primary)] text-[var(--primary)] p-3 rounded-full shadow-lg transform translate-y-20 opacity-0 transition-all duration-500 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:scale-110 active:scale-95 flex items-center justify-center relative group" data-astro-cid-6yyxny4o> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-astro-cid-6yyxny4o><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" data-astro-cid-6yyxny4o></path><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" data-astro-cid-6yyxny4o></path></svg> <span id="notification-dot" class="absolute top-0 right-0 w-3 h-3 bg-red-500 rounded-full border-2 border-[var(--card-bg)] hidden animate-pulse" data-astro-cid-6yyxny4o></span> </button> <!-- Expanded State (Panel) --> <div id="notification-panel" class="pointer-events-auto bg-[var(--card-bg)] border border-[var(--primary)] rounded-xl shadow-lg p-4 max-w-[90vw] w-80 transform translate-y-4 opacity-0 scale-95 origin-bottom-right transition-all duration-300 hidden absolute bottom-16 right-0 sm:bottom-14" data-astro-cid-6yyxny4o> <div class="flex items-center justify-between mb-3" data-astro-cid-6yyxny4o> <div class="flex items-center gap-2 text-[var(--primary)]" data-astro-cid-6yyxny4o> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-astro-cid-6yyxny4o><path d="M4 11a9 9 0 0 1 9 9" data-astro-cid-6yyxny4o></path><path d="M4 4a16 16 0 0 1 16 16" data-astro-cid-6yyxny4o></path><circle cx="5" cy="19" r="1" data-astro-cid-6yyxny4o></circle></svg> <h3 class="font-bold text-black dark:text-white" data-astro-cid-6yyxny4o>发现新文章</h3> </div> <div class="flex items-center gap-1" data-astro-cid-6yyxny4o> <button id="clear-notification" class="text-black/50 dark:text-white/50 hover:text-red-500 transition-colors p-1 rounded-md hover:bg-red-500/10" title="清空通知" data-astro-cid-6yyxny4o> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-astro-cid-6yyxny4o><path d="M3 6h18" data-astro-cid-6yyxny4o></path><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" data-astro-cid-6yyxny4o></path><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" data-astro-cid-6yyxny4o></path></svg> </button> <button id="minimize-notification" class="text-black/50 dark:text-white/50 hover:text-[var(--primary)] transition-colors p-1 rounded-md hover:bg-[var(--primary)]/10" title="隐藏" data-astro-cid-6yyxny4o> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-astro-cid-6yyxny4o><line x1="18" y1="6" x2="6" y2="18" data-astro-cid-6yyxny4o></line><line x1="6" y1="6" x2="18" y2="18" data-astro-cid-6yyxny4o></line></svg> </button> </div> </div> <div id="new-post-list" class="text-sm text-black/80 dark:text-white/80 transition-colors space-y-1 max-h-[60vh] overflow-y-auto overflow-x-hidden custom-scrollbar" data-astro-cid-6yyxny4o></div> </div> </div> <script type="module" src="/_astro/NewPostNotification.astro_astro_type_script_index_0_lang.BVVyTSCq.js"></script> <div id="top-row" class="z-50 pointer-events-none relative transition-all duration-700 max-w-[var(--page-width)] px-4 md:px-4 mx-auto"> <div id="navbar-wrapper" class="pointer-events-auto sticky top-0 transition-all"> <div id="navbar" class="z-50 onload-animation"> <div class="absolute h-8 left-0 right-0 -top-8 bg-[var(--card-bg)] transition"></div> <!-- used for onload animation --> <div class="card-base border border-black/10 dark:border-white/10 !overflow-visible max-w-[var(--page-width)] h-[4.5rem] !rounded-t-none mx-auto flex items-center justify-between px-4"> <a href="/" class="btn-plain scale-animation rounded-lg h-[3.25rem] px-5 font-bold active:scale-95"> <div class="flex flex-row text-[var(--primary)] items-center text-md"> <svg width="1em" height="1em" class="text-[1.75rem] mb-1 mr-2" data-icon="material-symbols:home-outline-rounded"> <symbol id="ai:material-symbols:home-outline-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M6 19h3v-5q0-.425.288-.712T10 13h4q.425 0 .713.288T15 14v5h3v-9l-6-4.5L6 10zm-2 0v-9q0-.475.213-.9t.587-.7l6-4.5q.525-.4 1.2-.4t1.2.4l6 4.5q.375.275.588.7T20 10v9q0 .825-.588 1.413T18 21h-4q-.425 0-.712-.288T13 20v-5h-2v5q0 .425-.288.713T10 21H6q-.825 0-1.412-.587T4 19m8-6.75"/></symbol><use href="#ai:material-symbols:home-outline-rounded"></use> </svg> AcoFork Blog </div> </a> <div class="hidden md:flex"> <a aria-label="首页" href="/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"> <div class="flex items-center"> 首页 </div> </a><a aria-label="归档" href="/archive/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"> <div class="flex items-center"> 归档 </div> </a><a aria-label="友链" href="/friends/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"> <div class="flex items-center"> 友链 </div> </a><a aria-label="赞助" href="/sponsors/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"> <div class="flex items-center"> 赞助 </div> </a><a aria-label="统计" href="https://umami.acofork.com/share/CdkXbGgZr6ECKOyK" target="_blank" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"> <div class="flex items-center"> 统计 <svg width="1em" height="1em" class="text-[0.875rem] transition -translate-y-[1px] ml-1 text-black/[0.2] dark:text-white/[0.2]" data-icon="fa6-solid:arrow-up-right-from-square"> <symbol id="ai:fa6-solid:arrow-up-right-from-square" viewBox="0 0 512 512"><path fill="currentColor" d="M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32zM80 32C35.8 32 0 67.8 0 112v320c0 44.2 35.8 80 80 80h320c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32v112c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16h112c17.7 0 32-14.3 32-32s-14.3-32-32-32z"/></symbol><use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </div> </a><a aria-label="监控" href="https://status.acofork.com" target="_blank" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"> <div class="flex items-center"> 监控 <svg width="1em" height="1em" viewBox="0 0 512 512" class="text-[0.875rem] transition -translate-y-[1px] ml-1 text-black/[0.2] dark:text-white/[0.2]" data-icon="fa6-solid:arrow-up-right-from-square"> <use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </div> </a><a aria-label="论坛" href="https://i.2x.nz" target="_blank" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"> <div class="flex items-center"> 论坛 <svg width="1em" height="1em" viewBox="0 0 512 512" class="text-[0.875rem] transition -translate-y-[1px] ml-1 text-black/[0.2] dark:text-white/[0.2]" data-icon="fa6-solid:arrow-up-right-from-square"> <use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </div> </a> </div> <div class="flex"> <!--<SearchPanel client:load>--> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();;(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="2j02uq" component-url="/_astro/Search.Ciq6IALQ.js" component-export="default" renderer-url="/_astro/client.svelte.B_1DUc79.js" props="{}" ssr client="only" opts="{&quot;name&quot;:&quot;Search&quot;,&quot;value&quot;:&quot;svelte&quot;}"></astro-island> <button aria-label="Display Settings" class="btn-plain scale-animation rounded-lg h-11 w-11 active:scale-90" id="display-settings-switch"> <svg width="1em" height="1em" class="text-[1.25rem]" data-icon="material-symbols:palette-outline"> <symbol id="ai:material-symbols:palette-outline" viewBox="0 0 24 24"><path fill="currentColor" d="M12 22q-2.05 0-3.875-.788t-3.187-2.15t-2.15-3.187T2 12q0-2.075.813-3.9t2.2-3.175T8.25 2.788T12.2 2q2 0 3.775.688t3.113 1.9t2.125 2.875T22 11.05q0 2.875-1.75 4.413T16 17h-1.85q-.225 0-.312.125t-.088.275q0 .3.375.863t.375 1.287q0 1.25-.687 1.85T12 22m-5.5-9q.65 0 1.075-.425T8 11.5t-.425-1.075T6.5 10t-1.075.425T5 11.5t.425 1.075T6.5 13m3-4q.65 0 1.075-.425T11 7.5t-.425-1.075T9.5 6t-1.075.425T8 7.5t.425 1.075T9.5 9m5 0q.65 0 1.075-.425T16 7.5t-.425-1.075T14.5 6t-1.075.425T13 7.5t.425 1.075T14.5 9m3 4q.65 0 1.075-.425T19 11.5t-.425-1.075T17.5 10t-1.075.425T16 11.5t.425 1.075T17.5 13M12 20q.225 0 .363-.125t.137-.325q0-.35-.375-.825T11.75 17.3q0-1.05.725-1.675T14.25 15H16q1.65 0 2.825-.962T20 11.05q0-3.025-2.312-5.038T12.2 4Q8.8 4 6.4 6.325T4 12q0 3.325 2.338 5.663T12 20"/></symbol><use href="#ai:material-symbols:palette-outline"></use> </svg> </button> <button aria-label="Menu" name="Nav Menu" class="btn-plain scale-animation rounded-lg w-11 h-11 active:scale-90 md:!hidden" id="nav-menu-switch"> <svg width="1em" height="1em" class="text-[1.25rem]" data-icon="material-symbols:menu-rounded"> <symbol id="ai:material-symbols:menu-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M4 18q-.425 0-.712-.288T3 17t.288-.712T4 16h16q.425 0 .713.288T21 17t-.288.713T20 18zm0-5q-.425 0-.712-.288T3 12t.288-.712T4 11h16q.425 0 .713.288T21 12t-.288.713T20 13zm0-5q-.425 0-.712-.288T3 7t.288-.712T4 6h16q.425 0 .713.288T21 7t-.288.713T20 8z"/></symbol><use href="#ai:material-symbols:menu-rounded"></use> </svg> </button> </div> <div id="nav-menu-panel" class="float-panel float-panel-closed absolute transition-all fixed right-4 px-2 py-2"> <a href="/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
"> <div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]"> 首页 </div> <svg width="1em" height="1em" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded"> <symbol id="ai:material-symbols:chevron-right-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M12.6 12L8.7 8.1q-.275-.275-.275-.7t.275-.7t.7-.275t.7.275l4.6 4.6q.15.15.213.325t.062.375t-.062.375t-.213.325l-4.6 4.6q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7z"/></symbol><use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </a><a href="/archive/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
"> <div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]"> 归档 </div> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </a><a href="/friends/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
"> <div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]"> 友链 </div> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </a><a href="/sponsors/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
"> <div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]"> 赞助 </div> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </a><a href="https://umami.acofork.com/share/CdkXbGgZr6ECKOyK" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
" target="_blank"> <div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]"> 统计 </div> <svg width="1em" height="1em" viewBox="0 0 512 512" class="transition text-[0.75rem] text-black/25 dark:text-white/25 -translate-x-1" data-icon="fa6-solid:arrow-up-right-from-square"> <use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </a><a href="https://status.acofork.com" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
" target="_blank"> <div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]"> 监控 </div> <svg width="1em" height="1em" viewBox="0 0 512 512" class="transition text-[0.75rem] text-black/25 dark:text-white/25 -translate-x-1" data-icon="fa6-solid:arrow-up-right-from-square"> <use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </a><a href="https://i.2x.nz" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8
hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition
" target="_blank"> <div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]"> 论坛 </div> <svg width="1em" height="1em" viewBox="0 0 512 512" class="transition text-[0.75rem] text-black/25 dark:text-white/25 -translate-x-1" data-icon="fa6-solid:arrow-up-right-from-square"> <use href="#ai:fa6-solid:arrow-up-right-from-square"></use> </svg> </a> </div> <astro-island uid="O8dFd" component-url="/_astro/DisplaySettings.C8pYka5b.js" component-export="default" renderer-url="/_astro/client.svelte.B_1DUc79.js" props="{}" ssr client="only" opts="{&quot;name&quot;:&quot;DisplaySettings&quot;,&quot;value&quot;:&quot;svelte&quot;}"></astro-island> </div> </div> <script type="module">function l(){let e=document.getElementById("display-settings-switch");e&&(e.onclick=function(){let t=document.getElementById("display-setting");t&&t.classList.toggle("float-panel-closed")});let n=document.getElementById("nav-menu-switch");n&&(n.onclick=function(){let t=document.getElementById("nav-menu-panel");t&&t.classList.toggle("float-panel-closed")})}l();</script> </div> </div> <div class="absolute w-full z-30 pointer-events-none" style="top: 5.5rem"> <!-- The pointer-events-none here prevent blocking the click event of the TOC --> <div class="relative max-w-[var(--page-width)] mx-auto pointer-events-auto"> <div id="main-grid" class="transition duration-700 w-full left-0 right-0 grid grid-cols-[17.5rem_auto] grid-rows-[auto_auto_auto] lg:grid-rows-[auto_1fr]
mx-auto gap-4 px-4 md:px-4"> <!-- Banner image credit --> <div id="sidebar" class="mb-4 row-start-2 col-span-2 lg:row-start-1 lg:col-span-1 lg:max-w-[17.5rem] onload-animation w-full"> <div class="flex flex-col w-full gap-4 mb-4"> <div class="card-base p-3 border border-black/10 dark:border-white/10" data-astro-cid-ufwobukd> <div class="relative mx-auto mt-1 lg:mx-0 lg:mt-0 mb-3 max-w-[12rem] lg:max-w-none rounded-xl overflow-hidden" style="--theme-hue: 361" data-astro-cid-ufwobukd> <img src="https://q2.qlogo.cn/headimg_dl?dst_uin=2726730791&spec=0" alt="Profile Image of the Author" class="w-full h-full object-cover transition duration-500" data-astro-cid-ufwobukd> </div> <div class="px-2" data-astro-cid-ufwobukd> <div class="font-bold text-xl text-center mb-1 dark:text-neutral-50 transition" data-astro-cid-ufwobukd>二叉树树</div> <div class="h-1 w-5 bg-[var(--primary)] mx-auto rounded-full mb-2 transition" data-astro-cid-ufwobukd></div> <div class="text-center text-neutral-400 mb-2.5 transition" data-astro-cid-ufwobukd>Protect What You Love.</div> <div class="flex flex-wrap gap-2 justify-center mb-1" data-astro-cid-ufwobukd> <a rel="me" aria-label="QQ" href="https://qm.qq.com/q/FWqOHlwL2m" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90" data-astro-cid-ufwobukd> <svg width="0.88em" height="1em" class="text-[1.5rem]" data-astro-cid-ufwobukd="true" data-icon="qq"> <symbol id="ai:local:qq" viewBox="0 0 448 512"><path fill="currentColor" d="M433.754 420.445c-11.526 1.393-44.86-52.741-44.86-52.741 0 31.345-16.136 72.247-51.051 101.786 16.842 5.192 54.843 19.167 45.803 34.421-7.316 12.343-125.51 7.881-159.632 4.037-34.122 3.844-152.316 8.306-159.632-4.037-9.045-15.25 28.918-29.214 45.783-34.415-34.92-29.539-51.059-70.445-51.059-101.792 0 0-33.334 54.134-44.859 52.741-5.37-.65-12.424-29.644 9.347-99.704 10.261-33.024 21.995-60.478 40.144-105.779C60.683 98.063 108.982.006 224 0c113.737.006 163.156 96.133 160.264 214.963 18.118 45.223 29.912 72.85 40.144 105.778 21.768 70.06 14.716 99.053 9.346 99.704"/></symbol><use href="#ai:local:qq"></use> </svg> </a><a rel="me" aria-label="Telegram" href="https://t.me/+_07DERp7k1ljYTc1" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90" data-astro-cid-ufwobukd> <svg width="0.97em" height="1em" class="text-[1.5rem]" data-astro-cid-ufwobukd="true" data-icon="telegram"> <symbol id="ai:local:telegram" viewBox="0 0 496 512"><path fill="currentColor" d="M248 8C111.033 8 0 119.033 0 256s111.033 248 248 248 248-111.033 248-248S384.967 8 248 8m114.952 168.66c-3.732 39.215-19.881 134.378-28.1 178.3-3.476 18.584-10.322 24.816-16.948 25.425-14.4 1.326-25.338-9.517-39.287-18.661-21.827-14.308-34.158-23.215-55.346-37.177-24.485-16.135-8.612-25 5.342-39.5 3.652-3.793 67.107-61.51 68.335-66.746.153-.655.3-3.1-1.154-4.384s-3.59-.849-5.135-.5q-3.283.746-104.608 69.142-14.845 10.194-26.894 9.934c-8.855-.191-25.888-5.006-38.551-9.123-15.531-5.048-27.875-7.717-26.8-16.291q.84-6.7 18.45-13.7 108.446-47.248 144.628-62.3c68.872-28.647 83.183-33.623 92.511-33.789 2.052-.034 6.639.474 9.61 2.885a10.45 10.45 0 0 1 3.53 6.716 43.8 43.8 0 0 1 .417 9.769"/></symbol><use href="#ai:local:telegram"></use> </svg> </a><a rel="me" aria-label="Bilibli" href="https://space.bilibili.com/325903362" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90" data-astro-cid-ufwobukd> <svg width="1em" height="1em" class="text-[1.5rem]" data-astro-cid-ufwobukd="true" data-icon="bilibili"> <symbol id="ai:local:bilibili" viewBox="0 0 512 512"><path fill="currentColor" d="M488.6 104.1c16.7 18.1 24.4 39.7 23.3 65.7v202.4c-.4 26.4-9.2 48.1-26.5 65.1-17.2 17-39.1 25.9-65.5 26.7H92.02c-26.45-.8-48.21-9.8-65.28-27.2C9.682 419.4.767 396.5 0 368.2V169.8c.767-26 9.682-47.6 26.74-65.7C43.81 87.75 65.57 78.77 92.02 78h29.38L96.05 52.19c-5.75-5.73-8.63-13-8.63-21.79 0-8.8 2.88-16.06 8.63-21.797C101.8 2.868 109.1 0 117.9 0q13.2 0 21.9 8.603L213.1 78h88l74.5-69.397C381.7 2.868 389.2 0 398 0q13.2 0 21.9 8.603c5.7 5.737 8.6 12.997 8.6 21.797 0 8.79-2.9 16.06-8.6 21.79L394.6 78h29.3c26.4.77 48 9.75 64.7 26.1m-38.8 69.7c-.4-9.6-3.7-17.4-10.7-23.5-5.2-6.1-14-9.4-22.7-9.8H96.05c-9.59.4-17.45 3.7-23.58 9.8-6.14 6.1-9.4 13.9-9.78 23.5v194.4c0 9.2 3.26 17 9.78 23.5s14.38 9.8 23.58 9.8H416.4c9.2 0 17-3.3 23.3-9.8s9.7-14.3 10.1-23.5zm-264.3 42.7c6.3 6.3 9.7 14.1 10.1 23.2V273c-.4 9.2-3.7 16.9-9.8 23.2-6.2 6.3-14 9.5-23.6 9.5s-17.5-3.2-23.6-9.5-9.4-14-9.8-23.2v-33.3c.4-9.1 3.8-16.9 10.1-23.2s13.2-9.6 23.3-10c9.2.4 17 3.7 23.3 10m191.5 0c6.3 6.3 9.7 14.1 10.1 23.2V273c-.4 9.2-3.7 16.9-9.8 23.2s-14 9.5-23.6 9.5-17.4-3.2-23.6-9.5c-7-6.3-9.4-14-9.7-23.2v-33.3c.3-9.1 3.7-16.9 10-23.2s14.1-9.6 23.3-10c9.2.4 17 3.7 23.3 10"/></symbol><use href="#ai:local:bilibili"></use> </svg> </a><a rel="me" aria-label="GitHub" href="https://github.com/afoim" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90" data-astro-cid-ufwobukd> <svg width="0.97em" height="1em" class="text-[1.5rem]" data-astro-cid-ufwobukd="true" data-icon="github"> <symbol id="ai:local:github" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></symbol><use href="#ai:local:github"></use> </svg> </a><a rel="me" aria-label="Folo" href="https://app.folo.is/share/feeds/236818461447222272" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90" data-astro-cid-ufwobukd> <svg width="1em" height="1em" class="text-[1.5rem]" data-astro-cid-ufwobukd="true" data-icon="folo"> <symbol id="ai:local:folo" viewBox="0 0 24 24"><path fill="currentColor" d="M5.382 0h13.236A5.37 5.37 0 0 1 24 5.383v13.235A5.37 5.37 0 0 1 18.618 24H5.382A5.37 5.37 0 0 1 0 18.618V5.383A5.37 5.37 0 0 1 5.382.001Zm7.887 17.31a1.813 1.813 0 1 0-3.626.002 1.813 1.813 0 0 0 3.626-.002m-.535-6.527H7.213a1.813 1.813 0 1 0 0 3.624h5.521a1.813 1.813 0 1 0 0-3.624m4.417-4.712H8.87a1.813 1.813 0 1 0 0 3.625h8.283a1.813 1.813 0 1 0 0-3.624z"/></symbol><use href="#ai:local:folo"></use> </svg> </a> </div> <!-- 全站文章与字数统计 --> <div class="grid grid-cols-2 mt-3 pt-3 border-t border-neutral-300 dark:border-neutral-700" data-astro-cid-ufwobukd> <div class="text-center" data-astro-cid-ufwobukd> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1" data-astro-cid-ufwobukd> <svg width="1em" height="1em" class="text-base" data-astro-cid-ufwobukd="true" data-icon="material-symbols:article-outline"> <symbol id="ai:material-symbols:article-outline" viewBox="0 0 24 24"><path fill="currentColor" d="M7 17h7v-2H7zm0-4h10v-2H7zm0-4h10V7H7zM5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h14q.825 0 1.413.588T21 5v14q0 .825-.587 1.413T19 21zm0-2h14V5H5zM5 5v14z"/></symbol><use href="#ai:material-symbols:article-outline"></use> </svg> <span class="text-xs" data-astro-cid-ufwobukd>文章数</span> </div> <div class="font-bold text-lg text-neutral-700 dark:text-neutral-300" data-astro-cid-ufwobukd>148</div> </div> <div class="text-center border-l border-neutral-300 dark:border-neutral-700" data-astro-cid-ufwobukd> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1" data-astro-cid-ufwobukd> <svg width="1em" height="1em" class="text-base" data-astro-cid-ufwobukd="true" data-icon="material-symbols:format-list-numbered"> <symbol id="ai:material-symbols:format-list-numbered" viewBox="0 0 24 24"><path fill="currentColor" d="M3 22v-1.5h2.5v-.75H4v-1.5h1.5v-.75H3V16h3q.425 0 .713.288T7 17v1q0 .425-.288.713T6 19q.425 0 .713.288T7 20v1q0 .425-.288.713T6 22zm0-7v-2.75q0-.425.288-.712T4 11.25h1.5v-.75H3V9h3q.425 0 .713.288T7 10v1.75q0 .425-.288.713T6 12.75H4.5v.75H7V15zm1.5-7V3.5H3V2h3v6zM9 19v-2h12v2zm0-6v-2h12v2zm0-6V5h12v2z"/></symbol><use href="#ai:material-symbols:format-list-numbered"></use> </svg> <span class="text-xs" data-astro-cid-ufwobukd>总字数</span> </div> <div class="font-bold text-lg text-neutral-700 dark:text-neutral-300" data-astro-cid-ufwobukd> 9.6w
</div> </div> </div> <!-- 全站访问量统计 --> <div class="grid grid-cols-2 mt-3 pt-3 border-t border-neutral-300 dark:border-neutral-700" data-astro-cid-ufwobukd> <div class="text-center" data-astro-cid-ufwobukd> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1" data-astro-cid-ufwobukd> <svg width="1em" height="1em" class="text-base" data-astro-cid-ufwobukd="true" data-icon="material-symbols:visibility-outline"> <symbol id="ai:material-symbols:visibility-outline" viewBox="0 0 24 24"><path fill="currentColor" d="M12 16q1.875 0 3.188-1.312T16.5 11.5t-1.312-3.187T12 7T8.813 8.313T7.5 11.5t1.313 3.188T12 16m0-1.8q-1.125 0-1.912-.788T9.3 11.5t.788-1.912T12 8.8t1.913.788t.787 1.912t-.787 1.913T12 14.2m0 4.8q-3.65 0-6.65-2.037T1 11.5q1.35-3.425 4.35-5.462T12 4t6.65 2.038T23 11.5q-1.35 3.425-4.35 5.463T12 19m0-2q2.825 0 5.188-1.487T20.8 11.5q-1.25-2.525-3.613-4.012T12 6T6.813 7.488T3.2 11.5q1.25 2.525 3.613 4.013T12 17"/></symbol><use href="#ai:material-symbols:visibility-outline"></use> </svg> <span class="text-xs" data-astro-cid-ufwobukd>访问量</span> </div> <div id="site-views" class="font-bold text-lg text-neutral-700 dark:text-neutral-300" data-astro-cid-ufwobukd>0</div> </div> <div class="text-center border-l border-neutral-300 dark:border-neutral-700" data-astro-cid-ufwobukd> <div class="text-xs text-neutral-500 mb-1 flex items-center justify-center gap-1" data-astro-cid-ufwobukd> <svg width="1em" height="1em" class="text-base" data-astro-cid-ufwobukd="true" data-icon="material-symbols:person"> <symbol id="ai:material-symbols:person" viewBox="0 0 24 24"><path fill="currentColor" d="M12 12q-1.65 0-2.825-1.175T8 8t1.175-2.825T12 4t2.825 1.175T16 8t-1.175 2.825T12 12m-8 8v-2.8q0-.85.438-1.562T5.6 14.55q1.55-.775 3.15-1.162T12 13t3.25.388t3.15 1.162q.725.375 1.163 1.088T20 17.2V20z"/></symbol><use href="#ai:material-symbols:person"></use> </svg> <span class="text-xs" data-astro-cid-ufwobukd>访客数</span> </div> <div id="site-visitors" class="font-bold text-lg text-neutral-700 dark:text-neutral-300" data-astro-cid-ufwobukd>0</div> </div> </div> </div> </div> <script>(function(){const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = Math.floor(progress * (end - start) + start);
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = end;
}
};
window.requestAnimationFrame(step);
}
// 获取全站访问量统计
async function loadSiteStats() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone
});
const pageviews = statsData.pageviews || 0;
const visitors = statsData.visitors || 0;
const viewsElement = document.getElementById('site-views');
const visitorsElement = document.getElementById('site-visitors');
if (viewsElement) {
const startVal = parseInt(viewsElement.textContent) || 0;
// 如果当前值已经是目标值,就不执行动画
if (startVal !== pageviews) {
animateValue(viewsElement, startVal, pageviews, 1000);
}
}
if (visitorsElement) {
const startVal = parseInt(visitorsElement.textContent) || 0;
// 如果当前值已经是目标值,就不执行动画
if (startVal !== visitors) {
animateValue(visitorsElement, startVal, visitors, 1000);
}
}
} catch (error) {
console.error('获取全站统计失败:', error);
}
}
// 页面加载完成后获取统计数据
document.addEventListener('DOMContentLoaded', loadSiteStats);
// 监听 Swup 内容替换事件,确保页面切换后重新获取数据
document.addEventListener('content:replace', loadSiteStats);
})();</script> </div> <div id="sidebar-sticky" class="transition-all duration-700 flex flex-col w-full gap-4 top-4 sticky top-4"> <!-- 赞助标 --> <div class="overflow-hidden flex justify-center"> <a href="https://secbit.ai/" target="_blank" rel="noopener noreferrer"> <img src="/aff/secbit-banner-1.gif" alt="Secbit" class="w-full max-w-[280px] rounded-lg hover:opacity-90 transition-opacity"> </a> </div> <div class="overflow-hidden flex justify-center"> <iframe src="https://support.nodeget.com/page/promotion?id=316" style="border:none; border-radius:8px; height:270px; width:100%; max-width:280px;
transform: scale(1.0); transform-origin: center top;"></iframe> </div> </div> </div> <main id="swup-container" class="transition-swup-fade row-start-1 col-span-2 lg:row-start-1 lg:col-span-1 overflow-visible min-w-0"> <div id="content-wrapper" class="onload-animation"> <!-- the overflow-hidden here prevent long text break the layout--> <!-- make id different from windows.swup global property --> <div class="notice-card mb-4 relative transition-all duration-300 hover:shadow-lg onload-animation" data-astro-cid-afjw4rzz><div class="notice-content flex items-center p-4 rounded-xl bg-[var(--card-bg)] border transition-colors" style="border-color:var(--admonitions-color-note);color:var(--admonitions-color-note)" data-astro-cid-afjw4rzz><div class="icon-wrapper mr-4 flex items-center justify-center shrink-0" data-astro-cid-afjw4rzz><svg width="1em" height="1em" class="text-2xl" data-astro-cid-afjw4rzz="true" data-icon="material-symbols:info-outline-rounded"> <symbol id="ai:material-symbols:info-outline-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M12 17q.425 0 .713-.288T13 16v-4q0-.425-.288-.712T12 11t-.712.288T11 12v4q0 .425.288.713T12 17m0-8q.425 0 .713-.288T13 8t-.288-.712T12 7t-.712.288T11 8t.288.713T12 9m0 13q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4T6.325 6.325T4 12t2.325 5.675T12 20m0-8"/></symbol><use href="#ai:material-symbols:info-outline-rounded"></use> </svg></div><div class="text-wrapper grow" data-astro-cid-afjw4rzz><div class="notice-text text-sm md:text-base font-bold" data-astro-cid-afjw4rzz>我们刚刚添加了文章更新系统,自此之后的每次文章更新都会通过右下角的小铃铛提醒您。</div></div></div><div class="absolute inset-0 rounded-xl pointer-events-none border-2 opacity-10" style="border-color:var(--admonitions-color-note)" data-astro-cid-afjw4rzz></div></div> <div class="transition flex flex-col rounded-[var(--radius-large)] bg-transparent gap-4 mb-4"> <div class="onload-animation" style="animation-delay: calc(var(--content-delay) + 0ms);"> <div class="card-base card-hover flex flex-col w-full relative" style="undefined; --coverWidth: 28%;" data-astro-cid-iyiqi2so> <div class="pl-9 pr-2 pt-6 pb-6 relative w-[calc(100%_-_var(--coverWidth)_-_12px)]" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <a href="/posts/pin/" class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90 relative
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-1 md:before:top-2 before:-left-[1.125rem] before:block
" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 加群向导 </a> <!-- description --> <div class="transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 关于如何联系二叉树树~ </div> <!-- metadata --> <div class="flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2 mb-2 md:mb-4"> <!-- publish date --> <!-- update date --> <!-- page views & visitors --> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" class="text-xl" data-icon="material-symbols:visibility-outline-rounded"> <symbol id="ai:material-symbols:visibility-outline-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M12 16q1.875 0 3.188-1.312T16.5 11.5t-1.312-3.187T12 7T8.813 8.313T7.5 11.5t1.313 3.188T12 16m0-1.8q-1.125 0-1.912-.788T9.3 11.5t.788-1.912T12 8.8t1.913.788t.787 1.912t-.787 1.913T12 14.2m0 4.8q-3.35 0-6.113-1.8t-4.362-4.75q-.125-.225-.187-.462t-.063-.488t.063-.488t.187-.462q1.6-2.95 4.363-4.75T12 4t6.113 1.8t4.362 4.75q.125.225.188.463t.062.487t-.062.488t-.188.462q-1.6 2.95-4.362 4.75T12 19m0-2q2.825 0 5.188-1.487T20.8 11.5q-1.25-2.525-3.613-4.012T12 6T6.813 7.488T3.2 11.5q1.25 2.525 3.613 4.013T12 17"/></symbol><use href="#ai:material-symbols:visibility-outline-rounded"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-views-pin">0 次</span> </div> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:person"> <use href="#ai:material-symbols:person"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-visitors-pin">0 人</span> </div> </div> <script>(function(){const slug = "pin";
const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = `${Math.floor(progress * (end - start) + start)} ${suffix}`;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = `${end} ${suffix}`;
}
};
window.requestAnimationFrame(step);
}
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone,
path: `eq./posts/${slug}/`
});
const pageViews = (statsData.pageviews && statsData.pageviews.value) || statsData.pageviews || 0;
const visits = (statsData.visitors && statsData.visitors.value) || statsData.visitors || 0;
const viewsElement = document.getElementById(`page-views-${slug}`);
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
// 如果数据来自缓存,直接显示结果,跳过动画
if (statsData._fromCache) {
if (viewsElement) viewsElement.innerHTML = `${pageViews}`;
if (visitorsElement) visitorsElement.innerHTML = `${visits}`;
return;
}
if (viewsElement) {
const currentViews = parseInt(viewsElement.textContent) || 0;
if (currentViews !== pageViews) {
if (currentViews === 0) {
animateValue(viewsElement, currentViews, pageViews, 1000, '次');
} else {
viewsElement.innerHTML = `${pageViews}`;
}
}
}
if (visitorsElement) {
const currentVisits = parseInt(visitorsElement.textContent) || 0;
if (currentVisits !== visits) {
if (currentVisits === 0) {
animateValue(visitorsElement, currentVisits, visits, 1000, '人');
} else {
visitorsElement.innerHTML = `${visits}`;
}
}
}
} catch (error) {
console.error('Error fetching page views:', error);
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
})();</script> <!-- word count, read time and page views --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2025-05-24</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">130 字</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">1 分钟</div> </div> </div> <a href="/posts/pin/" aria-label="加群向导" class="group max-h-none mx-0 mt-0 w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <!-- 封面图上的箭头 --> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center " data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl" data-astro-cid-iyiqi2so="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </div> <div class="w-full h-full overflow-hidden relative image-wrapper" style="--theme-hue: 361" data-astro-cid-i2xmscho> <img src="/random/h" alt="Cover Image of the Post" class="w-full h-full object-cover image-content" style="object-position: center" data-astro-cid-i2xmscho> </div> </a> </div> <div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <script type="module">function m(r,t,s,o){let a=null;const i=e=>{a||(a=e);const n=Math.min((e-a)/o,1);r.innerHTML=Math.floor(n*(s-t)+t),n<1?window.requestAnimationFrame(i):r.innerHTML=s};window.requestAnimationFrame(i)}async function d(){document.querySelectorAll(".post-pageviews").forEach(async t=>{if(t.classList.contains("processed")&&t.textContent!=="0")return;t.classList.add("processed");const s=t.dataset.url,o=t.dataset.umamiBaseUrl,a=t.dataset.umamiShareId,i=t.dataset.umamiTimezone;if(!(!o||!a))try{if(typeof window.fetchUmamiStats!="function")return;const e=await window.fetchUmamiStats(o,a,{url:s,timezone:i}),n=e.pageviews&&e.pageviews.value||e.pageviews||0;if(e._fromCache){t.innerHTML=n;return}const c=parseInt(t.textContent)||0;c!==n&&(c===0?m(t,c,n,1e3):t.innerHTML=n)}catch(e){console.error("Failed to load stats for",s,e)}})}document.addEventListener("DOMContentLoaded",d);document.addEventListener("swup:contentReplaced",d);</script> </div><div class="onload-animation" style="animation-delay: calc(var(--content-delay) + 50ms);"> <div class="card-base card-hover flex flex-col w-full relative" style="undefined; --coverWidth: 28%;" data-astro-cid-iyiqi2so> <div class="pl-9 pr-2 pt-6 pb-6 relative w-[calc(100%_-_var(--coverWidth)_-_12px)]" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <a href="/posts/cookie-consent/" class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90 relative
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-1 md:before:top-2 before:-left-[1.125rem] before:block
" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 想要一个Cookie管理器无需手搓 </a> <!-- description --> <div class="transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> Cookie Consent是一个开源简单实现的Cookie管理器能让您的网站GDPR合规仅需复制粘贴 </div> <!-- metadata --> <div class="flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2 mb-2 md:mb-4"> <!-- publish date --> <!-- update date --> <!-- page views & visitors --> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:visibility-outline-rounded"> <use href="#ai:material-symbols:visibility-outline-rounded"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-views-cookie-consent">0 次</span> </div> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:person"> <use href="#ai:material-symbols:person"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-visitors-cookie-consent">0 人</span> </div> </div> <script>(function(){const slug = "cookie-consent";
const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = `${Math.floor(progress * (end - start) + start)} ${suffix}`;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = `${end} ${suffix}`;
}
};
window.requestAnimationFrame(step);
}
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone,
path: `eq./posts/${slug}/`
});
const pageViews = (statsData.pageviews && statsData.pageviews.value) || statsData.pageviews || 0;
const visits = (statsData.visitors && statsData.visitors.value) || statsData.visitors || 0;
const viewsElement = document.getElementById(`page-views-${slug}`);
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
// 如果数据来自缓存,直接显示结果,跳过动画
if (statsData._fromCache) {
if (viewsElement) viewsElement.innerHTML = `${pageViews}`;
if (visitorsElement) visitorsElement.innerHTML = `${visits}`;
return;
}
if (viewsElement) {
const currentViews = parseInt(viewsElement.textContent) || 0;
if (currentViews !== pageViews) {
if (currentViews === 0) {
animateValue(viewsElement, currentViews, pageViews, 1000, '次');
} else {
viewsElement.innerHTML = `${pageViews}`;
}
}
}
if (visitorsElement) {
const currentVisits = parseInt(visitorsElement.textContent) || 0;
if (currentVisits !== visits) {
if (currentVisits === 0) {
animateValue(visitorsElement, currentVisits, visits, 1000, '人');
} else {
visitorsElement.innerHTML = `${visits}`;
}
}
}
} catch (error) {
console.error('Error fetching page views:', error);
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
})();</script> <!-- word count, read time and page views --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2026-01-30</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">1296 字</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">6 分钟</div> </div> </div> <a href="/posts/cookie-consent/" aria-label="想要一个Cookie管理器无需手搓" class="group max-h-none mx-0 mt-0 w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <!-- 封面图上的箭头 --> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center " data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl" data-astro-cid-iyiqi2so="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </div> <div class="w-full h-full overflow-hidden relative image-wrapper" style="--theme-hue: 361" data-astro-cid-i2xmscho> <img src="/_astro/cookie-consent.CeOBY4-w.png" alt="Cover Image of the Post" style="object-position: center" data-astro-cid-i2xmscho="true" width="572" height="645" loading="lazy" decoding="async" class="w-full h-full object-cover image-content"> </div> </a> </div> <div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> </div><div class="onload-animation" style="animation-delay: calc(var(--content-delay) + 100ms);"> <div class="card-base card-hover flex flex-col w-full relative" style="undefined; --coverWidth: 28%;" data-astro-cid-iyiqi2so> <div class="pl-9 pr-2 pt-6 pb-6 relative w-[calc(100%_-_var(--coverWidth)_-_12px)]" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <a href="/posts/warden-worker/" class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90 relative
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-1 md:before:top-2 before:-left-[1.125rem] before:block
" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 你可曾想过直接将BitWarden部署到Cloudflare Worker </a> <!-- description --> <div class="transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> warden-worker就是这样一个项目它将Rust编译为WASM然后部署到Cloudflare Worker无需VPS无需家里云只需点点鼠标就可免费用上自己的密码托管 </div> <!-- metadata --> <div class="flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2 mb-2 md:mb-4"> <!-- publish date --> <!-- update date --> <!-- page views & visitors --> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:visibility-outline-rounded"> <use href="#ai:material-symbols:visibility-outline-rounded"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-views-warden-worker">0 次</span> </div> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:person"> <use href="#ai:material-symbols:person"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-visitors-warden-worker">0 人</span> </div> </div> <script>(function(){const slug = "warden-worker";
const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = `${Math.floor(progress * (end - start) + start)} ${suffix}`;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = `${end} ${suffix}`;
}
};
window.requestAnimationFrame(step);
}
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone,
path: `eq./posts/${slug}/`
});
const pageViews = (statsData.pageviews && statsData.pageviews.value) || statsData.pageviews || 0;
const visits = (statsData.visitors && statsData.visitors.value) || statsData.visitors || 0;
const viewsElement = document.getElementById(`page-views-${slug}`);
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
// 如果数据来自缓存,直接显示结果,跳过动画
if (statsData._fromCache) {
if (viewsElement) viewsElement.innerHTML = `${pageViews}`;
if (visitorsElement) visitorsElement.innerHTML = `${visits}`;
return;
}
if (viewsElement) {
const currentViews = parseInt(viewsElement.textContent) || 0;
if (currentViews !== pageViews) {
if (currentViews === 0) {
animateValue(viewsElement, currentViews, pageViews, 1000, '次');
} else {
viewsElement.innerHTML = `${pageViews}`;
}
}
}
if (visitorsElement) {
const currentVisits = parseInt(visitorsElement.textContent) || 0;
if (currentVisits !== visits) {
if (currentVisits === 0) {
animateValue(visitorsElement, currentVisits, visits, 1000, '人');
} else {
visitorsElement.innerHTML = `${visits}`;
}
}
}
} catch (error) {
console.error('Error fetching page views:', error);
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
})();</script> <!-- word count, read time and page views --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2026-01-26</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">350 字</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2 分钟</div> </div> </div> <a href="/posts/warden-worker/" aria-label="你可曾想过直接将BitWarden部署到Cloudflare Worker" class="group max-h-none mx-0 mt-0 w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <!-- 封面图上的箭头 --> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center " data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl" data-astro-cid-iyiqi2so="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </div> <div class="w-full h-full overflow-hidden relative image-wrapper" style="--theme-hue: 361" data-astro-cid-i2xmscho> <img src="/_astro/warden-worker.DR0nXA8N.png" alt="Cover Image of the Post" style="object-position: center" data-astro-cid-i2xmscho="true" width="871" height="858" loading="lazy" decoding="async" class="w-full h-full object-cover image-content"> </div> </a> </div> <div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> </div><div class="onload-animation" style="animation-delay: calc(var(--content-delay) + 150ms);"> <div class="card-base card-hover flex flex-col w-full relative" style="undefined; --coverWidth: 28%;" data-astro-cid-iyiqi2so> <div class="pl-9 pr-2 pt-6 pb-6 relative w-[calc(100%_-_var(--coverWidth)_-_12px)]" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <a href="/posts/del-git-commit/" class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90 relative
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-1 md:before:top-2 before:-left-[1.125rem] before:block
" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 如何让一个文件在Git提交中永远消失如何丢掉其中一条提交并保持逻辑完整 </a> <!-- description --> <div class="transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 我们有时候可能会误操作导致提交了一个不该提交的文件,当你发现的时候,在此之后已经堆砌了很多新的提交... </div> <!-- metadata --> <div class="flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2 mb-2 md:mb-4"> <!-- publish date --> <!-- update date --> <!-- page views & visitors --> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:visibility-outline-rounded"> <use href="#ai:material-symbols:visibility-outline-rounded"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-views-del-git-commit">0 次</span> </div> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:person"> <use href="#ai:material-symbols:person"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-visitors-del-git-commit">0 人</span> </div> </div> <script>(function(){const slug = "del-git-commit";
const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = `${Math.floor(progress * (end - start) + start)} ${suffix}`;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = `${end} ${suffix}`;
}
};
window.requestAnimationFrame(step);
}
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone,
path: `eq./posts/${slug}/`
});
const pageViews = (statsData.pageviews && statsData.pageviews.value) || statsData.pageviews || 0;
const visits = (statsData.visitors && statsData.visitors.value) || statsData.visitors || 0;
const viewsElement = document.getElementById(`page-views-${slug}`);
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
// 如果数据来自缓存,直接显示结果,跳过动画
if (statsData._fromCache) {
if (viewsElement) viewsElement.innerHTML = `${pageViews}`;
if (visitorsElement) visitorsElement.innerHTML = `${visits}`;
return;
}
if (viewsElement) {
const currentViews = parseInt(viewsElement.textContent) || 0;
if (currentViews !== pageViews) {
if (currentViews === 0) {
animateValue(viewsElement, currentViews, pageViews, 1000, '次');
} else {
viewsElement.innerHTML = `${pageViews}`;
}
}
}
if (visitorsElement) {
const currentVisits = parseInt(visitorsElement.textContent) || 0;
if (currentVisits !== visits) {
if (currentVisits === 0) {
animateValue(visitorsElement, currentVisits, visits, 1000, '人');
} else {
visitorsElement.innerHTML = `${visits}`;
}
}
}
} catch (error) {
console.error('Error fetching page views:', error);
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
})();</script> <!-- word count, read time and page views --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2026-01-24</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">1377 字</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">7 分钟</div> </div> </div> <a href="/posts/del-git-commit/" aria-label="如何让一个文件在Git提交中永远消失如何丢掉其中一条提交并保持逻辑完整" class="group max-h-none mx-0 mt-0 w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <!-- 封面图上的箭头 --> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center " data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl" data-astro-cid-iyiqi2so="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </div> <div class="w-full h-full overflow-hidden relative image-wrapper" style="--theme-hue: 361" data-astro-cid-i2xmscho> <img src="/_astro/del-git-commit-1.B4hO8lNw.png" alt="Cover Image of the Post" style="object-position: center" data-astro-cid-i2xmscho="true" width="504" height="264" loading="lazy" decoding="async" class="w-full h-full object-cover image-content"> </div> </a> </div> <div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> </div><div class="onload-animation" style="animation-delay: calc(var(--content-delay) + 200ms);"> <div class="card-base card-hover flex flex-col w-full relative" style="undefined; --coverWidth: 28%;" data-astro-cid-iyiqi2so> <div class="pl-9 pr-2 pt-6 pb-6 relative w-[calc(100%_-_var(--coverWidth)_-_12px)]" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <a href="/posts/eo-umami/" class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90 relative
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-1 md:before:top-2 before:-left-[1.125rem] before:block
" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> WTF直接将Umami部署到EdgeOne Pages扔掉VPS直接跑在云函数 </a> <!-- description --> <div class="transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 我们都知道EdgeOne Pages有个完整的Node.js环境那Umami是Next.js也基于Node.js那我们是不是可以... </div> <!-- metadata --> <div class="flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2 mb-2 md:mb-4"> <!-- publish date --> <!-- update date --> <!-- page views & visitors --> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:visibility-outline-rounded"> <use href="#ai:material-symbols:visibility-outline-rounded"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-views-eo-umami">0 次</span> </div> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:person"> <use href="#ai:material-symbols:person"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-visitors-eo-umami">0 人</span> </div> </div> <script>(function(){const slug = "eo-umami";
const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = `${Math.floor(progress * (end - start) + start)} ${suffix}`;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = `${end} ${suffix}`;
}
};
window.requestAnimationFrame(step);
}
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone,
path: `eq./posts/${slug}/`
});
const pageViews = (statsData.pageviews && statsData.pageviews.value) || statsData.pageviews || 0;
const visits = (statsData.visitors && statsData.visitors.value) || statsData.visitors || 0;
const viewsElement = document.getElementById(`page-views-${slug}`);
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
// 如果数据来自缓存,直接显示结果,跳过动画
if (statsData._fromCache) {
if (viewsElement) viewsElement.innerHTML = `${pageViews}`;
if (visitorsElement) visitorsElement.innerHTML = `${visits}`;
return;
}
if (viewsElement) {
const currentViews = parseInt(viewsElement.textContent) || 0;
if (currentViews !== pageViews) {
if (currentViews === 0) {
animateValue(viewsElement, currentViews, pageViews, 1000, '次');
} else {
viewsElement.innerHTML = `${pageViews}`;
}
}
}
if (visitorsElement) {
const currentVisits = parseInt(visitorsElement.textContent) || 0;
if (currentVisits !== visits) {
if (currentVisits === 0) {
animateValue(visitorsElement, currentVisits, visits, 1000, '人');
} else {
visitorsElement.innerHTML = `${visits}`;
}
}
}
} catch (error) {
console.error('Error fetching page views:', error);
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
})();</script> <!-- word count, read time and page views --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2026-01-19</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">435 字</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2 分钟</div> </div> </div> <a href="/posts/eo-umami/" aria-label="WTF直接将Umami部署到EdgeOne Pages扔掉VPS直接跑在云函数" class="group max-h-none mx-0 mt-0 w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <!-- 封面图上的箭头 --> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center " data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl" data-astro-cid-iyiqi2so="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </div> <div class="w-full h-full overflow-hidden relative image-wrapper" style="--theme-hue: 361" data-astro-cid-i2xmscho> <img src="/_astro/eo-umami-1.CDFklV02.png" alt="Cover Image of the Post" style="object-position: center" data-astro-cid-i2xmscho="true" width="217" height="155" loading="lazy" decoding="async" class="w-full h-full object-cover image-content"> </div> </a> </div> <div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> </div><div class="onload-animation" style="animation-delay: calc(var(--content-delay) + 250ms);"> <div class="card-base card-hover flex flex-col w-full relative" style="undefined; --coverWidth: 28%;" data-astro-cid-iyiqi2so> <div class="pl-9 pr-2 pt-6 pb-6 relative w-[calc(100%_-_var(--coverWidth)_-_12px)]" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <a href="/posts/waf/" class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90 relative
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-1 md:before:top-2 before:-left-[1.125rem] before:block
" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 静态网站也需要WAFCloudflare不需要但是EdgeOne/ESA需要 </a> <!-- description --> <div class="transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 在几周前我的网站累计被DDoS了约100TB的流量哪怕我是静态网站并不会被“打死”但因为流量过大还是被EdgeOne取消接入了其实这件事本不会发生... </div> <!-- metadata --> <div class="flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2 mb-2 md:mb-4"> <!-- publish date --> <!-- update date --> <!-- page views & visitors --> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:visibility-outline-rounded"> <use href="#ai:material-symbols:visibility-outline-rounded"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-views-waf">0 次</span> </div> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:person"> <use href="#ai:material-symbols:person"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-visitors-waf">0 人</span> </div> </div> <script>(function(){const slug = "waf";
const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = `${Math.floor(progress * (end - start) + start)} ${suffix}`;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = `${end} ${suffix}`;
}
};
window.requestAnimationFrame(step);
}
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone,
path: `eq./posts/${slug}/`
});
const pageViews = (statsData.pageviews && statsData.pageviews.value) || statsData.pageviews || 0;
const visits = (statsData.visitors && statsData.visitors.value) || statsData.visitors || 0;
const viewsElement = document.getElementById(`page-views-${slug}`);
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
// 如果数据来自缓存,直接显示结果,跳过动画
if (statsData._fromCache) {
if (viewsElement) viewsElement.innerHTML = `${pageViews}`;
if (visitorsElement) visitorsElement.innerHTML = `${visits}`;
return;
}
if (viewsElement) {
const currentViews = parseInt(viewsElement.textContent) || 0;
if (currentViews !== pageViews) {
if (currentViews === 0) {
animateValue(viewsElement, currentViews, pageViews, 1000, '次');
} else {
viewsElement.innerHTML = `${pageViews}`;
}
}
}
if (visitorsElement) {
const currentVisits = parseInt(visitorsElement.textContent) || 0;
if (currentVisits !== visits) {
if (currentVisits === 0) {
animateValue(visitorsElement, currentVisits, visits, 1000, '人');
} else {
visitorsElement.innerHTML = `${visits}`;
}
}
}
} catch (error) {
console.error('Error fetching page views:', error);
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
})();</script> <!-- word count, read time and page views --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2026-01-18</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">1016 字</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">5 分钟</div> </div> </div> <a href="/posts/waf/" aria-label="静态网站也需要WAFCloudflare不需要但是EdgeOne/ESA需要" class="group max-h-none mx-0 mt-0 w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <!-- 封面图上的箭头 --> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center " data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl" data-astro-cid-iyiqi2so="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </div> <div class="w-full h-full overflow-hidden relative image-wrapper" style="--theme-hue: 361" data-astro-cid-i2xmscho> <img src="/_astro/waf.DxlwFCKv.png" alt="Cover Image of the Post" style="object-position: center" data-astro-cid-i2xmscho="true" width="572" height="441" loading="lazy" decoding="async" class="w-full h-full object-cover image-content"> </div> </a> </div> <div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> </div><div class="onload-animation" style="animation-delay: calc(var(--content-delay) + 300ms);"> <div class="card-base card-hover flex flex-col w-full relative" style="undefined; --coverWidth: 28%;" data-astro-cid-iyiqi2so> <div class="pl-9 pr-2 pt-6 pb-6 relative w-[calc(100%_-_var(--coverWidth)_-_12px)]" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <a href="/posts/aliyun-ecs-4rm/" class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90 relative
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-1 md:before:top-2 before:-left-[1.125rem] before:block
" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 什么4元每月200G的服务器还是阿里的还有联通9929精品回程 </a> <!-- description --> <div class="transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 通过创建抢占型实例+每月免费CDT 200G流量包你几乎只需要付很少的硬盘费 </div> <!-- metadata --> <div class="flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2 mb-2 md:mb-4"> <!-- publish date --> <!-- update date --> <!-- page views & visitors --> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:visibility-outline-rounded"> <use href="#ai:material-symbols:visibility-outline-rounded"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-views-aliyun-ecs-4rm">0 次</span> </div> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:person"> <use href="#ai:material-symbols:person"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-visitors-aliyun-ecs-4rm">0 人</span> </div> </div> <script>(function(){const slug = "aliyun-ecs-4rm";
const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = `${Math.floor(progress * (end - start) + start)} ${suffix}`;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = `${end} ${suffix}`;
}
};
window.requestAnimationFrame(step);
}
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone,
path: `eq./posts/${slug}/`
});
const pageViews = (statsData.pageviews && statsData.pageviews.value) || statsData.pageviews || 0;
const visits = (statsData.visitors && statsData.visitors.value) || statsData.visitors || 0;
const viewsElement = document.getElementById(`page-views-${slug}`);
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
// 如果数据来自缓存,直接显示结果,跳过动画
if (statsData._fromCache) {
if (viewsElement) viewsElement.innerHTML = `${pageViews}`;
if (visitorsElement) visitorsElement.innerHTML = `${visits}`;
return;
}
if (viewsElement) {
const currentViews = parseInt(viewsElement.textContent) || 0;
if (currentViews !== pageViews) {
if (currentViews === 0) {
animateValue(viewsElement, currentViews, pageViews, 1000, '次');
} else {
viewsElement.innerHTML = `${pageViews}`;
}
}
}
if (visitorsElement) {
const currentVisits = parseInt(visitorsElement.textContent) || 0;
if (currentVisits !== visits) {
if (currentVisits === 0) {
animateValue(visitorsElement, currentVisits, visits, 1000, '人');
} else {
visitorsElement.innerHTML = `${visits}`;
}
}
}
} catch (error) {
console.error('Error fetching page views:', error);
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
})();</script> <!-- word count, read time and page views --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2026-01-16</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">1759 字</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">9 分钟</div> </div> </div> <a href="/posts/aliyun-ecs-4rm/" aria-label="什么4元每月200G的服务器还是阿里的还有联通9929精品回程" class="group max-h-none mx-0 mt-0 w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <!-- 封面图上的箭头 --> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center " data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl" data-astro-cid-iyiqi2so="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </div> <div class="w-full h-full overflow-hidden relative image-wrapper" style="--theme-hue: 361" data-astro-cid-i2xmscho> <img src="/_astro/aliyun-ecs-4rm.DOJxMgSe.png" alt="Cover Image of the Post" style="object-position: center" data-astro-cid-i2xmscho="true" width="362" height="279" loading="lazy" decoding="async" class="w-full h-full object-cover image-content"> </div> </a> </div> <div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> </div><div class="onload-animation" style="animation-delay: calc(var(--content-delay) + 350ms);"> <div class="card-base card-hover flex flex-col w-full relative" style="undefined; --coverWidth: 28%;" data-astro-cid-iyiqi2so> <div class="pl-9 pr-2 pt-6 pb-6 relative w-[calc(100%_-_var(--coverWidth)_-_12px)]" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <a href="/posts/cf-saas/" class="transition group w-full block font-bold mb-2 md:mb-3 text-xl md:text-3xl text-90 relative
hover:text-[var(--primary)] dark:hover:text-[var(--primary)]
active:text-[var(--title-active)] dark:active:text-[var(--title-active)]
before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
before:absolute before:top-1 md:before:top-2 before:-left-[1.125rem] before:block
" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 你真的玩懂了Cloudflare SaaS吗为什么经由SaaS的流量可以做优选 </a> <!-- description --> <div class="transition text-75 mb-3.5 pr-4 line-clamp-1 md:line-clamp-2" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> 我们都知道可以通过SaaS来做优选但是为什么用SaaS就能做优选呢为什么直接CNAME就不能优选呢这篇文章带你一窥真相 </div> <!-- metadata --> <div class="flex flex-wrap text-neutral-500 dark:text-neutral-400 items-center gap-4 gap-x-4 gap-y-2 mb-2 md:mb-4"> <!-- publish date --> <!-- update date --> <!-- page views & visitors --> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:visibility-outline-rounded"> <use href="#ai:material-symbols:visibility-outline-rounded"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-views-cf-saas">0 次</span> </div> <div class="flex items-center"> <div class="meta-icon"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-xl" data-icon="material-symbols:person"> <use href="#ai:material-symbols:person"></use> </svg> </div> <span class="text-50 text-sm font-medium" id="page-visitors-cf-saas">0 人</span> </div> </div> <script>(function(){const slug = "cf-saas";
const umamiConfig = {"enable":true,"baseUrl":"https://umami.acofork.com","shareId":"CdkXbGgZr6ECKOyK","timezone":"Asia/Shanghai"};
// 数字动画函数
function animateValue(obj, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = `${Math.floor(progress * (end - start) + start)} ${suffix}`;
if (progress < 1) {
window.requestAnimationFrame(step);
} else {
obj.innerHTML = `${end} ${suffix}`;
}
};
window.requestAnimationFrame(step);
}
// 获取访问量统计
async function fetchPageViews() {
if (!umamiConfig.enable) return;
try {
const statsData = await fetchUmamiStats(umamiConfig.baseUrl, umamiConfig.shareId, {
timezone: umamiConfig.timezone,
path: `eq./posts/${slug}/`
});
const pageViews = (statsData.pageviews && statsData.pageviews.value) || statsData.pageviews || 0;
const visits = (statsData.visitors && statsData.visitors.value) || statsData.visitors || 0;
const viewsElement = document.getElementById(`page-views-${slug}`);
const visitorsElement = document.getElementById(`page-visitors-${slug}`);
// 如果数据来自缓存,直接显示结果,跳过动画
if (statsData._fromCache) {
if (viewsElement) viewsElement.innerHTML = `${pageViews}`;
if (visitorsElement) visitorsElement.innerHTML = `${visits}`;
return;
}
if (viewsElement) {
const currentViews = parseInt(viewsElement.textContent) || 0;
if (currentViews !== pageViews) {
if (currentViews === 0) {
animateValue(viewsElement, currentViews, pageViews, 1000, '次');
} else {
viewsElement.innerHTML = `${pageViews}`;
}
}
}
if (visitorsElement) {
const currentVisits = parseInt(visitorsElement.textContent) || 0;
if (currentVisits !== visits) {
if (currentVisits === 0) {
animateValue(visitorsElement, currentVisits, visits, 1000, '人');
} else {
visitorsElement.innerHTML = `${visits}`;
}
}
}
} catch (error) {
console.error('Error fetching page views:', error);
}
}
// 页面加载完成后获取统计数据
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fetchPageViews);
} else {
fetchPageViews();
}
})();</script> <!-- word count, read time and page views --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2026-01-16</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">355 字</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">|</div> <div data-astro-cid-iyiqi2so style="--coverWidth: 28%;">2 分钟</div> </div> </div> <a href="/posts/cf-saas/" aria-label="你真的玩懂了Cloudflare SaaS吗为什么经由SaaS的流量可以做优选" class="group max-h-none mx-0 mt-0 w-[var(--coverWidth)] absolute top-3 bottom-3 right-3 rounded-xl overflow-hidden active:scale-95" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> <!-- 封面图上的箭头 --> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center " data-astro-cid-iyiqi2so style="--coverWidth: 28%;"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl" data-astro-cid-iyiqi2so="true" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </div> <div class="w-full h-full overflow-hidden relative image-wrapper" style="--theme-hue: 361" data-astro-cid-i2xmscho> <img src="/_astro/cf-saas.M3AMXelU.png" alt="Cover Image of the Post" style="object-position: center" data-astro-cid-i2xmscho="true" width="1068" height="786" loading="lazy" decoding="async" class="w-full h-full object-cover image-content"> </div> </a> </div> <div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 hidden" data-astro-cid-iyiqi2so style="--coverWidth: 28%;"></div> </div> </div> <div class="mx-auto onload-animation flex flex-row gap-3 justify-center" style="animation-delay: calc(var(--content-delay) + 400ms)"> <a href class="btn-card overflow-hidden rounded-lg text-[var(--primary)] w-11 h-11 disabled"> <svg width="1em" height="1em" class="text-[1.75rem]" data-icon="material-symbols:chevron-left-rounded"> <symbol id="ai:material-symbols:chevron-left-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="m10.8 12l3.9 3.9q.275.275.275.7t-.275.7t-.7.275t-.7-.275l-4.6-4.6q-.15-.15-.212-.325T8.425 12t.063-.375t.212-.325l4.6-4.6q.275-.275.7-.275t.7.275t.275.7t-.275.7z"/></symbol><use href="#ai:material-symbols:chevron-left-rounded"></use> </svg> </a> <div class="bg-[var(--card-bg)] flex flex-row rounded-lg items-center text-neutral-700 dark:text-neutral-300 font-bold" style="backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);"> <div class="h-11 w-11 rounded-lg bg-[var(--primary)] flex items-center justify-center
font-bold text-white dark:text-black/70"> 1 </div><a href="/2/" aria-label="Page 2" class="transition flex items-center justify-center w-11 h-11 rounded-lg overflow-hidden active:scale-[0.85] hover:bg-[var(--btn-card-bg-hover)] active:bg-[var(--btn-card-bg-active)] text-black/75 dark:text-white/75">2</a><a href="/3/" aria-label="Page 3" class="transition flex items-center justify-center w-11 h-11 rounded-lg overflow-hidden active:scale-[0.85] hover:bg-[var(--btn-card-bg-hover)] active:bg-[var(--btn-card-bg-active)] text-black/75 dark:text-white/75">3</a><a href="/4/" aria-label="Page 4" class="transition flex items-center justify-center w-11 h-11 rounded-lg overflow-hidden active:scale-[0.85] hover:bg-[var(--btn-card-bg-hover)] active:bg-[var(--btn-card-bg-active)] text-black/75 dark:text-white/75">4</a><a href="/5/" aria-label="Page 5" class="transition flex items-center justify-center w-11 h-11 rounded-lg overflow-hidden active:scale-[0.85] hover:bg-[var(--btn-card-bg-hover)] active:bg-[var(--btn-card-bg-active)] text-black/75 dark:text-white/75">5</a><svg width="1em" height="1em" class="mx-1" data-icon="material-symbols:more-horiz"> <symbol id="ai:material-symbols:more-horiz" viewBox="0 0 24 24"><path fill="currentColor" d="M6 14q-.825 0-1.412-.587T4 12t.588-1.412T6 10t1.413.588T8 12t-.587 1.413T6 14m6 0q-.825 0-1.412-.587T10 12t.588-1.412T12 10t1.413.588T14 12t-.587 1.413T12 14m6 0q-.825 0-1.412-.587T16 12t.588-1.412T18 10t1.413.588T20 12t-.587 1.413T18 14"/></symbol><use href="#ai:material-symbols:more-horiz"></use> </svg><a href="/19/" aria-label="Page 19" class="transition flex items-center justify-center w-11 h-11 rounded-lg overflow-hidden active:scale-[0.85] hover:bg-[var(--btn-card-bg-hover)] active:bg-[var(--btn-card-bg-active)] text-black/75 dark:text-white/75">19</a> </div> <a href="/2/" aria-label="Next Page" class="btn-card overflow-hidden rounded-lg text-[var(--primary)] w-11 h-11"> <svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1.75rem]" data-icon="material-symbols:chevron-right-rounded"> <use href="#ai:material-symbols:chevron-right-rounded"></use> </svg> </a> </div> </div> </main> <div class="footer col-span-2 row-start-3 lg:col-span-1 lg:col-start-2 lg:row-start-2 onload-animation"> <!--<div class="border-t border-[var(--primary)] mx-16 border-dashed py-8 max-w-[var(--page-width)] flex flex-col items-center justify-center px-6">--><div class="transition border-t border-black/10 dark:border-white/15 my-10 border-dashed mx-4 md:mx-32"></div> <div class="card-base w-fit mx-auto rounded-xl mt-4 mb-4"> <div class="transition text-50 text-sm text-center p-6">
&copy; <span id="copyright-year">2024 - 2026</span> <a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://space.bilibili.com/325903362"> 二叉树树 </a>,采用
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a> 许可
<br> <a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/rss.xml">RSS</a> /
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/sitemap-index.xml">网站地图</a> /
<!-- Below is the link that users can use to open Preferences Center to change their preferences. Do not modify the ID parameter. Place it where appropriate, style it as needed. --> <a class="transition link text-[var(--primary)] font-medium" href="#" id="open_preferences_center">更新您的Cookie</a> <br>
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://astro.build">Astro</a>
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/saicaca/fuwari">Fuwari</a> 强力驱动
<br>
本网站代码
<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="https://github.com/afoim/fuwari">已开源</a> <a class="transition link text-black/30 dark:text-white/30 hover:text-[var(--primary)] text-xs ml-1" target="_blank" href="https://github.com/afoim/fuwari/commit/53e134f">(53e134f @ 2026-01-30 14:24:10)</a> <br> <a class="transition link text-[var(--primary)] font-medium inline-flex items-center" href="https://beian.miit.gov.cn/#/Integrated/index" target="_blank"><img alt="" src="/favicon/foot-icp.png" class="h-4 mr-1">皖ICP备2025099787号-2</a> <br> <div class="server-info-container flex flex-col items-center justify-center gap-1 mt-1"> <!-- Server info will be injected here --> </div> <!-- <a class="transition link text-[var(--primary)] font-medium inline-flex items-center" href="https://beian.mps.gov.cn/#/query/webSearch?code=34010302002608" target="_blank"><img alt="" src="/favicon/foot-ga.png" class="h-4 mr-1">皖公网安备34010302002608号</a>
<br> --> </div> </div> <script>(function(){const serverConfig = [{"url":"","text":"Blog"},{"url":"https://umami.acofork.com","text":"Umami"},{"url":"https://pic1.acofork.com","text":"RandomPic"}];
(() => {
if (window.__footerServerInit) return;
window.__footerServerInit = true;
function normalizeUrl(input) {
if (!input) return "";
return input.trim().replace(/:+$/, "");
}
function createServerEntry(config) {
const div = document.createElement('div');
div.className = 'flex items-center justify-center';
const label = document.createElement('span');
label.className = 'text-black/30 dark:text-white/30 text-xs mr-1';
label.innerText = `${config.text}`;
div.appendChild(label);
const valueSpan = document.createElement('span');
valueSpan.className = 'server-value text-black/30 dark:text-white/30 text-xs';
valueSpan.innerText = '检测中...';
div.appendChild(valueSpan);
const iconImg = document.createElement('img');
iconImg.className = 'server-icon h-4 ml-1 hidden';
iconImg.alt = 'Server Icon';
div.appendChild(iconImg);
return { div, valueSpan, iconImg };
}
function updateEntryDisplay(elements, server) {
const { valueSpan, iconImg } = elements;
if (server) {
const serverLower = server.toLowerCase();
let iconSrc = null;
if (serverLower.includes('esa')) {
iconSrc = '/cdn/esa.svg';
} else if (serverLower.includes('edgeone')) {
iconSrc = '/cdn/eo.svg';
} else if (serverLower.includes('cloudflare')) {
iconSrc = '/cdn/cf.svg';
} else if (serverLower.includes('github')) {
iconSrc = '/cdn/gh.svg';
}
if (iconSrc) {
iconImg.src = iconSrc;
iconImg.classList.remove('hidden');
valueSpan.classList.add('hidden');
} else {
valueSpan.innerText = server;
iconImg.classList.add('hidden');
valueSpan.classList.remove('hidden');
}
} else {
valueSpan.innerText = "未知";
iconImg.classList.add('hidden');
valueSpan.classList.remove('hidden');
}
}
async function checkServer(url) {
try {
const targetUrl = normalizeUrl(url) || window.location.href;
const response = await fetch(targetUrl, { method: "HEAD" });
return response.headers.get('server');
} catch (error) {
console.warn(`Failed to fetch server info for ${url}:`, error);
return null;
}
}
async function initServerInfo() {
const containers = document.querySelectorAll('.server-info-container');
if (containers.length === 0) return;
// Check for Dev Mode
const isDevMode = localStorage.getItem("dev-mode") === "true";
if (isDevMode) {
const devServer = localStorage.getItem("dev-server");
containers.forEach(container => {
container.innerHTML = '';
const elements = createServerEntry({ text: 'Dev Node' });
container.appendChild(elements.div);
updateEntryDisplay(elements, devServer);
});
return;
}
window.__serverInfoCache = window.__serverInfoCache || {};
const cache = window.__serverInfoCache;
const getServerInfo = (url) => {
const key = normalizeUrl(url);
if (cache[key]) return cache[key];
cache[key] = checkServer(key);
return cache[key];
};
containers.forEach(container => {
container.innerHTML = '';
if (!serverConfig || serverConfig.length === 0) {
const elements = createServerEntry({ text: '访问节点' });
container.appendChild(elements.div);
getServerInfo('').then(server => updateEntryDisplay(elements, server));
return;
}
serverConfig.forEach((config) => {
const elements = createServerEntry(config);
container.appendChild(elements.div);
getServerInfo(config.url).then(server => updateEntryDisplay(elements, server));
});
});
}
initServerInfo();
document.addEventListener('astro:after-swap', initServerInfo);
document.addEventListener('content:replace', initServerInfo);
})();
})();</script> </div> </div> <!-- There can't be a filter on parent element, or it will break `fixed` --><div class="back-to-top-wrapper hidden lg:block" data-astro-cid-eymb5ayk> <div id="go-to-comments-btn" class="back-to-top-btn hide flex items-center rounded-2xl overflow-hidden transition" onclick="goToComments()" data-astro-cid-eymb5ayk> <button aria-label="Go to Comments" class="btn-card h-[3.75rem] w-[3.75rem]" data-astro-cid-eymb5ayk> <svg width="1em" height="1em" class="mx-auto" data-astro-cid-eymb5ayk="true" data-icon="material-symbols:comment-rounded"> <symbol id="ai:material-symbols:comment-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="M7 14h10q.425 0 .713-.288T18 13t-.288-.712T17 12H7q-.425 0-.712.288T6 13t.288.713T7 14m0-3h10q.425 0 .713-.288T18 10t-.288-.712T17 9H7q-.425 0-.712.288T6 10t.288.713T7 11m0-3h10q.425 0 .713-.288T18 7t-.288-.712T17 6H7q-.425 0-.712.288T6 7t.288.713T7 8M4 18q-.825 0-1.412-.587T2 16V4q0-.825.588-1.412T4 2h16q.825 0 1.413.588T22 4v15.575q0 .675-.612.938T20.3 20.3L18 18z"/></symbol><use href="#ai:material-symbols:comment-rounded"></use> </svg> </button> </div> <div id="back-to-top-btn" class="back-to-top-btn hide flex items-center rounded-2xl overflow-hidden transition" onclick="backToTop()" data-astro-cid-eymb5ayk> <button aria-label="Back to Top" class="btn-card h-[3.75rem] w-[3.75rem]" data-astro-cid-eymb5ayk> <svg width="1em" height="1em" class="mx-auto" data-astro-cid-eymb5ayk="true" data-icon="material-symbols:keyboard-arrow-up-rounded"> <symbol id="ai:material-symbols:keyboard-arrow-up-rounded" viewBox="0 0 24 24"><path fill="currentColor" d="m12 10.8l-3.9 3.9q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7l4.6-4.6q.3-.3.7-.3t.7.3l4.6 4.6q.275.275.275.7t-.275.7t-.7.275t-.7-.275z"/></symbol><use href="#ai:material-symbols:keyboard-arrow-up-rounded"></use> </svg> </button> </div> </div> <script>
function backToTop() {
window.scroll({ top: 0, behavior: 'smooth' });
}
function goToComments() {
const comments = document.getElementById('giscus-container');
if (comments) {
comments.scrollIntoView({ behavior: 'smooth' });
}
}
</script> </div> </div> <div class="absolute w-full z-0 hidden 2xl:block"> <div class="relative max-w-[var(--page-width)] mx-auto"> <!-- TOC component --> <div id="toc-wrapper" class="hidden lg:block transition absolute top-0 -right-[var(--toc-width)] w-[var(--toc-width)] flex items-center"> <div id="toc-inner-wrapper" class="fixed top-14 w-[var(--toc-width)] h-[calc(100vh_-_20rem)] overflow-y-scroll overflow-x-hidden hide-scrollbar"> <div id="toc" class="w-full h-full transition-swup-fade "> <div class="h-8 w-full"></div> <script type="module">class o extends HTMLElement{tocEl=null;visibleClass="visible";observer;anchorNavTarget=null;headingIdxMap=new Map;headings=[];sections=[];tocEntries=[];active=[];activeIndicator=null;constructor(){super(),this.observer=new IntersectionObserver(this.markVisibleSection,{threshold:0})}markVisibleSection=t=>{if(t.forEach(e=>{const i=e.target.children[0]?.getAttribute("id"),s=i?this.headingIdxMap.get(i):void 0;s!=null&&(this.active[s]=e.isIntersecting),e.isIntersecting&&this.anchorNavTarget==e.target.firstChild&&(this.anchorNavTarget=null)}),!this.active.includes(!0)){this.activeIndicator?.setAttribute("style","display: none;"),this.fallback();return}this.update()};toggleActiveHeading=()=>{let t=this.active.length-1,e=this.active.length-1,i=0;for(;t>=0&&!this.active[t];)this.tocEntries[t].classList.remove(this.visibleClass),t--;for(;t>=0&&this.active[t];)this.tocEntries[t].classList.add(this.visibleClass),e=Math.min(e,t),i=Math.max(i,t),t--;for(;t>=0;)this.tocEntries[t].classList.remove(this.visibleClass),t--;let s=this.tocEntries[e].offsetTop,n=this.tocEntries[i].offsetTop+this.tocEntries[i].offsetHeight;this.activeIndicator?.setAttribute("style",`display: block; top: ${s}px; height: ${n-s}px`)};scrollToActiveHeading=()=>{if(this.anchorNavTarget||!this.tocEl)return;const t=document.querySelectorAll(`#toc .${this.visibleClass}`);if(!t.length)return;const e=t[0],i=t[t.length-1],s=this.tocEl.clientHeight;let n;i.getBoundingClientRect().bottom-e.getBoundingClientRect().top<.9*s?n=e.offsetTop-32:n=i.offsetTop-s*.8,this.tocEl.scrollTo({top:n,left:0,behavior:"smooth"})};update=()=>{requestAnimationFrame(()=>{this.toggleActiveHeading(),this.scrollToActiveHeading()})};fallback=()=>{if(this.sections.length)for(let t=0;t<this.sections.length;t++){let e=this.sections[t].getBoundingClientRect().top,i=this.sections[t].getBoundingClientRect().bottom;if(this.isInRange(e,0,window.innerHeight)||this.isInRange(i,0,window.innerHeight)||e<0&&i>window.innerHeight)this.markActiveHeading(t);else if(e>window.innerHeight)break}};markActiveHeading=t=>{this.active[t]=!0};handleAnchorClick=t=>{const e=t.composedPath().find(i=>i instanceof HTMLAnchorElement);if(e){const i=decodeURIComponent(e.hash?.substring(1)),s=this.headingIdxMap.get(i);s!==void 0?this.anchorNavTarget=this.headings[s]:this.anchorNavTarget=null}};isInRange(t,e,i){return e<t&&t<i}connectedCallback(){const t=document.querySelector(".prose");t?t.addEventListener("animationend",()=>{this.init()},{once:!0}):console.debug("Animation element not found")}init(){if(this.tocEl=document.getElementById("toc-inner-wrapper"),!!this.tocEl&&(this.tocEl.addEventListener("click",this.handleAnchorClick,{capture:!0}),this.activeIndicator=document.getElementById("active-indicator"),this.tocEntries=Array.from(document.querySelectorAll("#toc a[href^='#']")),this.tocEntries.length!==0)){this.sections=new Array(this.tocEntries.length),this.headings=new Array(this.tocEntries.length);for(let t=0;t<this.tocEntries.length;t++){const e=decodeURIComponent(this.tocEntries[t].hash?.substring(1)),i=document.getElementById(e),s=i?.parentElement;i instanceof HTMLElement&&s instanceof HTMLElement&&(this.headings[t]=i,this.sections[t]=s,this.headingIdxMap.set(e,t))}this.active=new Array(this.tocEntries.length).fill(!1),this.sections.forEach(t=>this.observer.observe(t)),this.fallback(),this.update()}}disconnectedCallback(){this.sections.forEach(t=>this.observer.unobserve(t)),this.observer.disconnect(),this.tocEl?.removeEventListener("click",this.handleAnchorClick)}}customElements.get("table-of-contents")||customElements.define("table-of-contents",o);</script> <div class="h-8 w-full"></div> </div> </div> </div> <!-- #toc needs to exist for Swup to work normally --> </div> </div> <!-- increase the page height during page transition to prevent the scrolling animation from jumping --> <div id="page-height-extend" class="hidden h-[300vh]" data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;"></div> <script type="module" src="/_astro/Layout.astro_astro_type_script_index_0_lang.BoB-OW5-.js"></script> <script type="module" src="/_astro/Layout.astro_astro_type_script_index_1_lang.BK9HixGO.js"></script> <svg style="position: absolute; width: 0; height: 0; overflow: hidden;; --bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;" aria-hidden="true" data-astro-cid-sckkx6r4> <defs data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;"> <filter id="liquid-glass" data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;"> <feTurbulence type="fractalNoise" baseFrequency="0.05" numOctaves="2" result="noise" data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;"></feTurbulence> <feDisplacementMap in="SourceGraphic" in2="noise" scale="20" xChannelSelector="R" yChannelSelector="G" data-astro-cid-sckkx6r4 style="--bannerOffset: 15vh;--banner-height-home: 65vh;--banner-height: 35vh;--configHue: 361;--page-width: 75rem;--bg-url: url(/random/h);--bg-enable: 1;--bg-position: center;--bg-size: cover;--bg-repeat: no-repeat;--bg-attachment: fixed;--bg-opacity: 1;--configHue: 361;"></feDisplacementMap> </filter> </defs> </svg></body></html>