使用Cloudflare Workers搭建一个资源导航页
引言
分享一个我自用的资源导航主页,这个项目简单易用,支持自定义链接、分类、图标(emoji或图片),还带有一个可选的搜索框。你可以根据自己的需求修改和部署。以下是详细的搭建教程。
功能支持
- 分类导航:支持多类别网站列表,每个类别下可添加多个链接。
- 自定义图标:支持emoji和图片URL两种图标类型。
- 响应式设计:适配桌面和移动端,包含悬停动画和玻璃风格UI。
- 轻量高效:部署在Cloudflare Workers上,无需服务器,响应速度快。
部署
- 复制代码
// 配置选项
const config = {
enableSearch: true, // 是否开启搜索框功能,true为开启,false为关闭
};
// 定义分类网站列表
// 每个网站支持以下图标类型:
// - iconType: 'emoji' - 使用emoji图标,需指定icon为emoji字符
// - iconType: 'url' - 使用自定义图片直链,需指定icon为图片URL
const categorizedSites = [
{
category: '🔍常用链接',
items: [
{ url: 'https://blog.mlho.net/', name: '博客', iconType: 'url', icon: 'https://blog.mlho.net/favicon.ico' },
{ url: 'https://pan.baidu.com/', name: '云盘', iconType: 'emoji', icon: '☁️' }
]
},
{
category: '🔮其他链接',
items: [
{ url: 'https://www.aconvert.com/', name: '格式转换', iconType: 'emoji', icon: '🔘' },
{ url: 'https://www.iodraw.com/mind', name: '思维导图', iconType: 'emoji', icon: '🧠' }
]
}
];
// 生成图标HTML的函数
// 根据iconType生成对应的图标HTML,仅支持emoji和自定义URL
const getIconHtml = (site) => {
if (site.iconType === 'emoji') {
return `<span class="emoji">${site.icon}</span>`; // 使用emoji图标
} else if (site.iconType === 'url') {
return `<img src="${site.icon}" alt="${site.name} icon" class="icon-image">`; // 使用自定义图片URL
} else {
return `<span class="emoji">🌐</span>`; // 默认使用emoji(向后兼容)
}
};
// 生成分类卡片HTML
const categorySections = categorizedSites.map(category => `
<section class="category-section">
<h2 class="category-title">${category.category}</h2>
<div class="card-container">
${category.items.map(site => `
<div class="card">
${getIconHtml(site)}
<a href="${site.url}" target="_blank" rel="noopener noreferrer" class="card-link">${site.name}</a>
</div>
`).join('')}
</div>
</section>
`).join('');
// 搜索框HTML
const searchHtml = config.enableSearch ? `
<div class="search-container">
<form class="search-form" action="https://www.google.com/search" method="get" target="_blank" autocomplete="off">
<input
type="text"
name="q"
placeholder="输入关键词搜索..."
class="search-input"
aria-label="搜索框"
value=""
>
<span class="search-icon">🔍</span>
<button type="submit" class="search-button">搜索</button>
</form>
</div>
` : ''; // 如果enableSearch为false,则不显示搜索框
// 完整CSS,包含所有动画效果并支持图片图标
const css = `
:root {
--primary: #4f46e5;
--primary-light: #6366f1;
--text: #1e293b;
--text-light: #64748b;
--glass: rgba(255, 255, 255, 0.65);
--glass-border: 1px solid rgba(255, 255, 255, 0.3);
--shadow-sm: 0 4px 12px rgba(31, 38, 135, 0.1);
--shadow-md: 0 8px 32px rgba(31, 38, 135, 0.15);
--shadow-lg: 0 12px 40px rgba(31, 38, 135, 0.25);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background: linear-gradient(135deg, #e0e7ff 0%, #d1d8f0 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem 1rem;
color: var(--text);
}
h1 {
background: linear-gradient(45deg, var(--primary), var(--primary-light));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-size: 2.8rem;
margin: 1.5rem 0 2rem;
font-weight: 700;
letter-spacing: -0.05em;
transition: transform 0.3s ease;
}
h1:hover {
transform: scale(1.02);
}
/* 搜索框样式 */
.search-container {
width: 100%;
max-width: 680px;
margin: 1.5rem 0 3rem;
position: relative;
}
.search-form {
position: relative;
}
.search-input {
width: 100%;
padding: 1.1rem 1.5rem 1.1rem 3.5rem;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 16px;
background: var(--glass);
backdrop-filter: blur(16px);
font-size: 1.1rem;
color: var(--text);
box-shadow: var(--shadow-sm);
transition: all 0.4s cubic-bezier(0.18, 0.89, 0.32, 1.28);
}
.search-input:focus {
outline: none;
background: rgba(255, 255, 255, 0.95);
border-color: rgba(79, 70, 229, 0.4);
box-shadow: var(--shadow-md);
transform: scale(1.02);
}
.search-icon {
position: absolute;
left: 1.2rem;
top: 50%;
transform: translateY(-50%);
opacity: 0.6;
z-index: 1;
}
.search-button {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
height: calc(100% - 16px);
padding: 0 1.8rem;
border: none;
border-radius: 12px;
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
color: white;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.2);
}
.search-button:hover {
transform: translateY(-50%) scale(1.05);
box-shadow: 0 6px 16px rgba(79, 70, 229, 0.3);
}
/* 卡片样式 */
.category-section {
width: 100%;
max-width: 1200px;
margin-bottom: 3rem;
}
.category-title {
color: var(--primary);
font-size: 1.8rem;
margin: 1.5rem 0;
padding-bottom: 0.5rem;
border-bottom: 2px solid rgba(79, 70, 229, 0.2);
transition: transform 0.3s ease;
}
.category-title:hover {
transform: translateX(5px);
}
.card-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 1.5rem;
padding: 0.5rem;
}
.card {
background: var(--glass);
backdrop-filter: blur(12px);
border-radius: 18px;
border: var(--glass-border);
padding: 1.8rem 1.5rem;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
min-height: 160px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-shadow: var(--shadow-md);
}
.card:hover {
transform: translateY(-8px) scale(1.02);
background: rgba(255, 255, 255, 0.85);
box-shadow: var(--shadow-lg);
}
.emoji {
font-size: 2.8rem;
margin-bottom: 1.2rem;
transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
display: inline-block;
}
.icon-image {
width: 48px;
height: 48px;
margin-bottom: 1.2rem;
transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
.card:hover .emoji,
.card:hover .icon-image {
transform: rotate(12deg) scale(1.1);
}
.card-link {
text-decoration: none;
color: var(--text);
font-size: 1.2rem;
font-weight: 600;
position: relative;
padding: 0.25rem 0;
transition: color 0.3s ease;
}
.card-link::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: var(--primary);
transition: width 0.4s ease;
}
.card:hover .card-link::after {
width: 100%;
}
.card-link:hover {
color: var(--primary);
}
/* 移动端适配 */
@media (max-width: 768px) {
h1 { font-size: 2.4rem; }
.search-container { margin: 2rem 0; }
}
@media (max-width: 640px) {
body { padding: 1.5rem 1rem; }
h1 { font-size: 2.2rem; margin: 1rem 0 1.5rem; }
.card-container { grid-template-columns: repeat(2, minmax(140px, 1fr)); gap: 1rem; }
.card { min-height: 140px; padding: 1.2rem; }
.card:hover { transform: translateY(-5px) scale(1.02); }
.emoji { font-size: 2.2rem; margin-bottom: 0.8rem; }
.icon-image { width: 40px; height: 40px; margin-bottom: 0.8rem; }
.card-link { font-size: 1.1rem; }
}
@media (max-width: 480px) {
.search-button { padding: 0 1.2rem; }
.search-input { padding-left: 2.8rem; }
.search-icon { left: 1rem; }
.card:hover .emoji, .card:hover .icon-image { transform: rotate(8deg) scale(1.05); }
}
`;
// HTML模板
const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>资源导航</title>
<meta name="description" content="快速访问精选资源">
<link rel="icon" href="https://blog.mlho.net/favicon.ico">
<style>${css}</style>
</head>
<body>
<h1>资源导航</h1>
${searchHtml}
${categorySections}
<script>
document.addEventListener('DOMContentLoaded', () => {
const searchInput = document.querySelector('.search-input');
if (searchInput) {
searchInput.value = '';
}
const searchForm = document.querySelector('.search-form');
if (searchForm) {
searchForm.addEventListener('submit', (e) => {
setTimeout(() => {
searchInput.value = '';
}, 100);
});
}
});
</script>
</body>
</html>`;
// Cloudflare Workers处理逻辑
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
if (url.pathname === '/') {
return new Response(html, {
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Cache-Control': 'public, max-age=3600'
}
});
}
return new Response('Not Found', { status: 404 });
}- 登录Cloudflare
进入Cloudflare仪表盘,选择左侧的“Workers和Pages”选项。 - 创建Worker
点击“创建Worker”,随便输入一个名称(例如resource-nav)。 - 粘贴代码
将修改好的代码粘贴到编辑器中,点击“保存并部署”。 - 测试访问
部署后,你会得到一个默认URL(例如resource-nav.your-username.workers.dev),打开浏览器访问即可看到页面。 - 绑定自定义域名(可选)
在“设置”选项中添加自定义域名(如nav.yourdomain.com),需确保域名已在Cloudflare中管理。
效果展示
