关于本站
「墨 · 码」记录我在 Web 开发中的学习、实践与思考,以及对相关技术的探索与验证。

「墨 · 码」是我的个人技术博客,主要记录 Web 开发中的学习、实践与思考。这里既有从入门到实践的学习笔记,也有开发过程中遇到的问题与解决方案。
写作对我来说也是学习的一部分。把学到的知识重新梳理,再用自己的语言表达出来,往往能发现原本遗漏或理解模糊的地方,也能让零散的知识逐渐建立起联系。与此同时,自己搭建和维护博客,也让我有了更多实践 Web 技术的机会。
本站最初参考了 QuietPages 的设计思路。随着需求逐渐变化,我也在持续调整站点的设计与功能。这个博客不只是记录学习的地方,它本身也成为了我学习和实践 Web 开发的一部分。
页面概览
站点围绕内容阅读与信息展示,提供了文章浏览、内容归档、数据统计、联系交流、版权说明等页面。
内容浏览
首页 主要展示最新内容和精选文章,方便快速了解近期更新。Hero 区域提供背景切换功能,可选择 Unsplash 或 Lorem Picsum 作为图片来源。
文章列表 用于浏览和搜索文章,并可以按照分类、标签和合集进行筛选,同时提供排序和分页功能。进入文章详情页后,可以通过上下篇导航和相关文章继续延伸阅读,也可以使用 Giscus 评论区参与讨论。
归档 按照时间整理全部文章,并提供年、月和列表三种视图:年视图用于查看各年份的文章分布和更新频率;月视图通过日历展示具体日期的文章发布情况;列表视图则按照发布时间分组展示文章,适合按时间顺序浏览和回顾。
站点信息
数据统计 汇总文章数量和字数、发布活跃度、分类构成,以及访问趋势、访客地域等数据。页面中的数据图表基于 ECharts 实现,地域分布则通过地图展示。配置高德安全密钥后使用 高德地图 JS API 2.0,未配置时则使用 Leaflet 加载 CARTO 提供的地图瓦片。
联系 提供站内邮件发送功能,支持发送前预览邮件内容,并通过 Resend 完成邮件发送。
版权声明 记录站内字体、图片、纹理及第三方资源的来源与许可信息。
站外资源
收藏 用于整理常用的网站,并按照类别进行分组,支持搜索和筛选。
友链 用于展示长期关注的个人站点,同时提供友链提交入口。
视觉与浏览体验
站点的视觉效果由多组设置共同控制:皮肤负责切换主题配色,明暗模式用于调整整体明度,纹理则为页面背景增加细节与质感。此外,标题和正文可以分别选择字体,字体资源由 ZeoSeven Fonts 提供,正文字号也可以根据阅读习惯进行调整。
Rough Notation 为标题、按钮和重点内容添加手绘标注。当页面布局发生变化时,标注会重新计算位置。
站内还加入了少量 UI 音效,用于配合页面交互。音效会尽量与交互过程保持同步,但受音频加载、浏览器调度和系统播放链路等因素影响,首次播放或长时间未触发后再次播放时,可能会出现较明显的延迟。
站内导航通过 Astro ClientRouter 实现客户端路由,并基于 View Transition 定制页面切换动画,减少页面跳转时的视觉割裂感。
页面使用 Lenis 实现平滑滚动。目录、评论区、编辑器和设置面板等具有独立滚动区域的组件则保留原生滚动,避免滚动行为相互干扰。
代码实验
文章中的代码内容可以分为三种形式:行内代码、多行代码和可运行代码。
行内代码通过 rehype-pretty-code 处理。插件会读取代码中的语言标记,并调用 Shiki 进行语法解析,最终生成带有 token 样式的 HTML。
语言标记写在行内代码末尾,例如:`const count = 1{:javascript}` 会被渲染成 const count = 1。指定语言后会按对应语法进行高亮,未指定语言的行内代码则保留基础样式。
需要展示多行代码时,使用带语言标记的 Markdown 代码块,例如:```javascript。它与行内代码使用同一个高亮引擎,但处理入口不同,不会经过 rehype-pretty-code 的行内节点转换。
const count = 1;
console.log(count);
文章也可以通过 MDX 嵌入可运行的代码实验组件,读者可以直接修改代码并实时运行。这些组件均由文章页面统一注入,在 MDX 中使用时无需重复导入。
前端 Playground
前端 Playground 基于 Sandpack 实现,主要用于运行和演示 HTML、CSS、JavaScript 以及前端组件等浏览器端代码。它支持多文件编辑,并提供实时预览、控制台输出、代码格式化、布局调整等功能。每个示例还可以指定默认打开的文件,以及初始化时显示预览或控制台。
<Playground
codeId="about/frontend-playground"
title="前端实时预览"
activeFile="/App.js"
initialPane="result"
/>
下面是可编辑和运行的示例,修改左侧文件后,可以在右侧的预览界面或控制台中查看运行结果。
远程代码运行器
远程代码运行器使用 CodeMirror 编辑源码,并将运行请求转发至访客选择的远程执行平台,例如: Judge0 和 Wandbox。它适合运行 Python、Java、C++ 等需要解释器或编译器的代码。
<RemoteCodeRunner
codeId="about/python-remote-execution"
title="Python 远程执行"
language="python"
stdin="Ink · Code"
/>
下面是可编辑和运行的示例,配置标准输入并运行代码,可以在输出面板中查看运行结果。
Python 远程执行
选择平台并点击“运行”,结果会显示在这里。
为避免远程执行服务被滥用,部署时应妥善保管各平台的 API Key、Token 等凭据,不要将其暴露在浏览器端或提交到公开仓库。
不同执行平台有各自的调用配额和服务限制,达到限额后,代码可能无法正常运行,此时可以切换到其他执行平台后再次尝试。
浏览器端 SQL Playground
浏览器端 SQL Playground 基于 sql.js 和 WebAssembly 在浏览器中运行 SQLite,无需连接远程数据库。它支持连续的增删改查操作,也可以根据需要只运行当前选中的 SQL 片段。
为了便于演示常见的 MySQL 示例,我在 SQLite 执行前增加了一层轻量兼容处理,将部分 MySQL 语法转换为 SQLite 可执行的形式。对于兼容层无法处理的语句,则直接交由 SQLite 执行;如果其中包含 SQLite 不支持的语法,就会返回相应错误。
多个组件使用相同的 databaseId 时,可以共享同一个浏览器端数据库。数据库快照会保存在当前浏览器的 IndexedDB 中,适合在不同文章或章节之间连续进行实验。
未设置 databaseId 时,每个组件都会使用独立的临时数据库,适合运行相互独立、无需保留状态的 SQL 示例。
<SqlPlayground
codeId="about/sqlite-local-execution"
title="SQLite 本地执行"
/>
下面是可编辑和运行的示例,可以一次执行多条语句,也可以执行选中的部分 SQL 语句。
SQLite 本地执行
项目结构
下面列出了项目的主要目录和关键文件,并简要说明它们各自的用途。
项目结构
src站点的主要源码目录。
- assets需要经过 Astro 构建处理的图片和其他资源。
- codes文章中的可运行代码,使用与文章标题无关的稳定 codeId 进行引用。
components站点使用的各类功能组件。
- analytics访问统计相关组件。
- archive归档页面相关组件。
- blog文章列表与文章详情相关组件。
- code-runner远程代码运行器相关组件。
- common跨页面复用的基础组件、脚本和预加载逻辑。
- home首页相关组件。
- layout页面布局相关组件。
- mdx供 MDX 文章使用的交互组件及其包装层。
- playground基于 Sandpack 的前端代码编辑与实时预览组件。
- sql-playground基于 SQLite 的浏览器端 SQL 运行与实验组件。
- statistics站点内容与访问数据的统计和可视化组件。
- ui跨页面复用的按钮、图标、卡片、图片等基础 UI 组件。
- widgets相对独立的功能性小组件。
- configs站点、字体、代码主题、地图、手绘标注和音效等可编辑配置。
contentAstro Content Collections 使用的文章与结构化数据。
- authors作者资料 JSON 文件。
- blogsMarkdown/MDX 技术文章,按主题和系列组织。
- bookmarks收藏分类与站点信息 JSON 文件。
- friends友情链接信息 JSON 文件。
- file-trees供 FileTree 组件使用的文件结构数据。
- layouts页面布局、全局 Head、内容容器和客户端公共逻辑。
- lib跨页面复用的数据访问、工具函数和浏览器生命周期逻辑。
- pagesAstro 页面路由、动态路由和服务端 API。
- styles设计令牌、全局样式、主题、纹理和页面专属样式。
- types项目共享的 TypeScript 类型定义。
- content.config.tsAstro Content Collections 的集合、Schema 和数据加载配置。
- middleware.ts处理请求级安全响应头、高德地图代理和其他运行时中间件逻辑。
public无需经过构建处理、可直接发布的静态资源。
- masks用于页面视觉效果的各类遮罩图片资源。
- sounds用于增强页面交互反馈的音效文件。
- styles以静态文件形式发布的 Giscus 评论区样式。
- textures纸张、布料和砂纸等可选背景纹理。
migrationsCloudflare D1 数据库迁移文件。
- 0001_init.sql创建统计与访客数据的基础表结构。
- 0002_analytics_indexes.sql为统计查询相关字段增加索引。
- 0003_code_run_rate_limits.sql创建远程代码运行的限流表与索引。
scripts文章创建和本地开发使用的辅助脚本。
- create-doc.mjs交互式创建带有 frontmatter 的 Markdown/MDX 文章。
- .dev.vars.example本地 Worker 运行期变量与 Secret 的示例配置。
- .env.exampleAstro/Vite 构建期与浏览器端公开变量的示例配置。
- astro.config.tsAstro 项目的主配置文件,用于配置框架集成、构建选项、Markdown 处理和部署适配等功能。
- worker-configuration.d.tsCloudflare Workers 运行环境与资源绑定的 TypeScript 类型声明。
- wrangler.jsoncCloudflare Wrangler 配置文件,用于定义 Worker 的部署参数、运行环境、资源绑定和静态资源。
站点配置
站点信息
站点的基础配置信息集中在 src/configs/site.ts:
const siteConfig = {
name: "墨·码",
description: "基于 Astro 构建的个人技术博客,记录技术探索与开发日常",
url: "https://example.com",
author: {
email: "you@example.com",
location: "你的城市",
coordinates: [30.63, 104.0], // [纬度, 经度]
},
clientRouter: true,
codeTheme: {
light: "catppuccin-latte",
dark: "one-dark-pro",
},
giscus: {},
nav: [],
footerNav: [],
};
国际化
国际化配置位于 src/configs/i18n.ts。
const i18nConfig = {
enabled: true,
defaultLocale: "zh-CN",
locales: ["zh-CN", "en", "ja"],
fallback: {
en: "zh-CN",
ja: "zh-CN",
},
routing: {
prefixDefaultLocale: false,
},
};
enabled:决定是否开启国际化,关闭后,全站只使用defaultLocale;defaultLocale:决定站点的默认语言,以及未带语言前缀的 URL 使用哪种语言;locales:决定站点支持哪些语言,语言代码会用于 URL 前缀。新增语言时,需要同时扩展src/types/i18n.ts中的SUPPORTED_LOCALES和文案字典;fallback:决定某种语言缺少文章译文时跳转到哪种语言的 URL。例如{ en: "zh-CN" }表示英文译文不存在时跳转到对应的中文页面;没有配置回退映射时直接返回 404;prefixDefaultLocale:控制默认语言是否使用/<locale>/前缀;
地图服务
地图统一由 src/configs/map.ts 配置。本地开发时,浏览器需要的公开配置放在项目根目录的 .env,Worker 请求运行时使用的安全密钥放在 .dev.vars;两个文件的职责不同,不要把同一个变量重复填写。Cloudflare 生产环境的配置位置见下方 部署到 Cloudflare 一节:
.env:构建期和浏览器需要的公开配置。
PUBLIC_AMAP_KEY=你的高德 Web API Key
PUBLIC_CARTO_KEY=你的 CARTO Basemap Key
.dev.vars:本地 Worker 运行期 Secret。
AMAP_SECURITY_CODE=你的高德安全密钥
PUBLIC_AMAP_KEY 是 高德地图 JS API 2.0 的 Web 端 Key,必须随浏览器请求发送。AMAP_SECURITY_CODE 是高德安全密钥,本地和生产都由 /_AMapService 同域代理在服务端读取,不会暴露给浏览器。
没有配置 PUBLIC_AMAP_KEY 时,组件自动回退到 Leaflet 和 CARTO。如果已经配置 Key 但缺少 AMAP_SECURITY_CODE,高德代理请求会失败,需要先补齐该变量。
PUBLIC_CARTO_KEY 用于 CARTO 底图服务,地图瓦片请求会在浏览器中携带它,因此它同样属于公开 Key。建议在 CARTO 申请并配置它,以避免地图被添加水印。
部署到 Cloudflare
项目使用 @astrojs/cloudflare 构建 Cloudflare Workers,并在 wrangler.jsonc 中声明 Worker、静态资源和 D1 数据库绑定。
本项目通过 Cloudflare Workers 的 GitHub 集成,实现自动部署。连接仓库后,向指定分支推送代码,Cloudflare 会自动执行构建和部署。具体配置可以参考 Workers Builds 文档。
首次配置
-
创建属于自己的 D1 数据库
pnpm exec wrangler login pnpm exec wrangler d1 create ink-code-analytics将命令返回的
database_id和数据库名称写入wrangler.jsonc,注意保留ink_code_analytics这个 binding。 -
在 Cloudflare 控制台连接 GitHub 仓库
- 新建 Worker:
Workers & Pages→Create application→Import a repository; - 已有 Worker:进入 Worker 的
Settings→Builds→Connect; - 选择本仓库和生产分支,根目录填写
/; - 构建命令填写
pnpm run build,部署命令填写npx wrangler deploy; - 确认 Cloudflare 中的 Worker 名称为
ink-code,与wrangler.jsonc中的name一致。
- 新建 Worker:
-
配置环境变量
环境变量分为 “构建期” 和 “Worker 运行期” 两类。两类变量的读取时机不同,必须分别配置:
- 构建期变量:由
Astro/Vite在执行pnpm run build时读取import.meta.env。本地放在.env;Cloudflare 放在Worker → Settings → Builds → variables and secrets。这里配置:SITE_URL:生产站点地址;PUBLIC_AMAP_KEY、PUBLIC_CARTO_KEY、PUBLIC_AMAP_SERVICE_HOST、PUBLIC_HERO_IMAGE_PROVIDER:会进入浏览器代码或请求地址的公开配置。PUBLIC_*不能存放 Secret;
- Worker 运行期变量:Worker 收到请求后从
cloudflare:workers的env读取。本地放在.dev.vars;Cloudflare 放在Worker → Settings → Runtime variables and Secrets。这里配置:RESEND_API_KEY、RESEND_FROM_EMAIL:联系接口在请求运行时读取,API Key 保存为 Secret;AMAP_SECURITY_CODE:高德安全密钥,由/_AMapService代理读取并添加到上游请求;UNSPLASH_ACCESS_KEY:由/api/hero-image/读取,浏览器不会拿到该 Key;随机图片接口不需要UNSPLASH_SECRET_KEY;CODE_RUN_RATE_LIMIT_SALT:代码运行器限流盐值,保存为 Secret;- 代码运行平台凭据和运行时端点:完整变量名见仓库根目录的
.dev.vars.example,凭据全部保存为 Secret。
构建期变量不会自动成为 Worker 的运行时
env,运行期变量也不会参与 Astro/Vite 构建。PUBLIC_*值会被编译进浏览器代码或请求地址,只能使用公开 Key;任何 API Token、Client Secret、密码和盐值都不要添加PUBLIC_前缀。本地推荐同时使用两个文件:
.env只放构建期和公开配置,.dev.vars只放 Worker 运行期配置与 Secret。按照 Cloudflare 本地环境变量文档 的规则,存在.dev.vars时,Wrangler 不会再把.env的值合并到 Workerenv;因此同一个变量不能同时写在两个文件中。如果不创建.dev.vars,也可以只用.env作为本地构建和运行时的单一来源,但所有运行期变量都必须放进.env,不适合需要明确隔离 Secret 的配置。 - 构建期变量:由
-
首次部署前,将迁移应用到远程 D1。GitHub 自动部署只负责构建和发布 Worker,不会自动执行数据库迁移:
pnpm exec wrangler d1 migrations apply ink-code-analytics --remote本地开发时可以先使用
--local验证。后续新增迁移时,也要在确认 SQL 无误后再次执行--remote; 已应用的迁移不要修改或删除,只新增更大的编号。详细规则见 D1 migrations 文档。
日常发布
修改代码后运行 pnpm check,提交并推送到 Cloudflare 监听的分支即可:
git add .
git commit -m "更新站点"
git push origin main
Cloudflare 会自动执行构建和部署。构建失败时,先在 Worker 的 Deployments → View build history 查看日志。
资源与许可
- 网络字体来自 ZeoSeven Fonts;
- Hero 背景来源由
PUBLIC_HERO_IMAGE_PROVIDER选择,可使用 Unsplash 或 Lorem Picsum; - 背景纹理来自 Transparent Textures;
