首页
/ Union 官网 site/:Astro + Contentful + Nix 构建的营销站点工程实现

Union 官网 site/:Astro + Contentful + Nix 构建的营销站点工程实现

2026-09-04 13:51:24作者:幸俭卉

Union 的官方站点(union.build)承载了协议介绍、团队、路线图与博客等内容,其全部源码位于仓库的 site/ 目录。本文基于 site/README.md 的原始说明展开,并结合仓库中的构建配置、Contentful 集成代码与页面实现,完整讲解该站点的技术栈构成、一键本地开发流程(nix run .#site-dev-server)、生产构建管线以及 CMS 数据获取与渲染链路,帮助读者理解一个面向 DeFi 受众的营销站点是如何在 Nix 环境中被开发、类型检查与打包发布的。

站点定位:README 中的原始定义

site/README.md 对站点的定位非常直接:

  • 站点域名为 union.build,用于介绍 Union 协议并承载其 blog
  • 启动方式是一条 Nix 命令:nix run .#site-dev-server,启动后编辑 site/ 下的文件即可在浏览器中看到即时变更;
  • 技术架构一句话概括:它是一个 Astro 站点,从 Contentful CMS 拉取内容,使用 Tailwind 做样式,使用 Spline 制作 3D 模型。

下面各节将把这几句话逐一落到仓库中的真实配置与源码上。

本地开发:nix run .#site-dev-server 到底做了什么

README 给出的 Quickstart 命令由 site/site.nix 中的 apps.site-dev-server 定义(site/site.nix 第 46–61 行)。展开来看,它是一个 pkgs.writeShellApplication,执行逻辑等价于:

# 1. 确保当前处于仓库根目录(ensureAtRepositoryRoot)
cd site/
export PUPPETEER_SKIP_DOWNLOAD=1
pnpm install
pnpm dev -- --host

几个值得注意的细节:

  • ensureAtRepositoryRoot 是 flake 提供的守卫,保证无论从哪里执行 nix run .#site-dev-server,脚本都会先校验/回到仓库根目录,再进入 site/
  • PUPPETEER_SKIP_DOWNLOAD=1 跳过 Puppeteer 浏览器下载,在 CI 与离线环境下避免无谓的大文件拉取;
  • pnpm dev -- --host 对应 site/package.json 中的 "dev": "NODE_ENV='development' astro dev",加上 --host 后开发服务器会监听网络接口,便于在容器/远程开发机中访问;
  • 该应用声明了 runtimeInputs = [python3, stdenv.cc, pkg-config]deps),用于满足 pnpm 依赖中可能的原生模块构建需求。

此外 site.nix 还定义了第二个应用 site-check(第 62–72 行),执行 npm_config_yes=true npx astro check,用于对 Astro 组件做类型检查;这与 package.json 里的 check 脚本(astro check)对应。如果不走 Nix,也可以直接在 site/ 目录下用 Node 22(package.jsonengines.node 要求 22.x)运行 pnpm install && pnpm dev,这是 Nix 封装的等价替代。

site 同时是仓库 pnpm workspace 的一员:site.nixpnpmWorkspaces = [ "site" ] 与根目录的 pnpm-workspace.yaml 配合,说明该目录复用整个仓库的统一依赖管理(如 catalog: 形式的版本引用,见 site/package.jsonsvelte: "catalog:svelte")。

生产构建:buildPnpmPackage 与 Vercel serverless 产物

site.nixpackages.site(第 19–43 行)用 buildPnpmPackage 声明了可复现的构建包,其关键行为是:

buildPhase = ''
  export PUPPETEER_SKIP_DOWNLOAD=1;
  export ASTRO_TELEMETRY_DISABLED=1;
  export NODE_OPTIONS="--no-warnings";
  pnpm --filter=site build
'';
installPhase = ''
  mkdir -p $out
  cp -r ./site/.vercel/output/* $out
'';
  • 构建阶段用 pnpm --filter=site build 在 workspace 内只构建 site 这一个包,并禁用 Astro 遥测;
  • 安装阶段直接把 site/.vercel/output 拷贝为 Nix 产物 $out,这说明站点最终是按 serverless 输出格式发布的。

这一点与 site/astro.config.ts 一致:README 中描述部署为 Netlify,而当前仓库的构建配置实际使用的是 @astrojs/vercel/serverless 适配器(astro.config.ts 第 26–28 行,imageService: true),site.nix 也据此以 .vercel/output 为交付物。astro.config.ts 中与架构相关的关键配置包括:

配置项 含义
site https://union.build 站点 canonical URL,供 sitemap/SEO 使用
output "server" 混合渲染模式:默认按需服务端渲染
experimental.clientPrerender true 对服务端路由启用客户端预渲染优化
trailingSlash "ignore" 忽略 URL 尾斜杠差异
image.domains cdn.contentful.com Astro Image 允许处理的远程图源,其中两条正是 Contentful 的 CDN 域名
server { port: Number(PORT) } 开发端口默认 4321(由 PORT 环境变量覆盖)
redirects /feed/rss.xml/logo/union-logo.zip 入口级重定向
prefetch prefetchAll: true, defaultStrategy: "viewport" 视口内自动预取,提升页面跳转体验
vite.assetsInclude **/*.splinecode 将 Spline 3D 工程文件纳入 Vite 资源管线(见下文)

integrations 里注册了四个集成:astro-icon()(Iconify 图标)、tailwind()(显式指向 tailwind.config.tsapplyBaseStyles: false)、svelte()(Astro 与 Svelte 组件混用)、sitemap()(自动生成站点地图)。

Contentful 集成:客户端、查询与富文本渲染

站点内容并不写死在仓库里,而是运行时从 Contentful 拉取。相关实现集中在 site/src/lib/contentful/ 目录:

1. 客户端与环境变量site/src/lib/contentful/client.ts

export const contentfulClient = createClient({
  space: env.CONTENTFUL_SPACE_ID,
  environment: env.CONTENTFUL_ENVIRONMENT,
  host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",
  accessToken: import.meta.env.DEV ? env.CONTENTFUL_PREVIEW_TOKEN : env.CONTENTFUL_DELIVERY_TOKEN,
})

开发环境走 preview.contentful.com + Preview Token(可看到草稿),生产走 cdn.contentful.com + Delivery Token。所需的环境变量在 site/src/lib/constants/env.ts 中以 Object.freeze 集中声明:PUBLIC_CONTENTFUL_SPACE_IDPUBLIC_CONTENTFUL_ENVIRONMENTPUBLIC_CONTENTFUL_DELIVERY_TOKENPUBLIC_CONTENTFUL_PREVIEW_TOKEN 等,缺失时会在启动时直接 raise 报错,避免带病运行。

2. GraphQL 查询site/src/lib/contentful/queries.ts 定义了 blogPostsQuery(按 date_DESC 取最多 100 篇博客,返回 title/author/description/cover/content 等字段)与 blogPostQuery(按 slug 精确查询单篇,支持 preview 参数)。

3. 列表页数据流site/src/pages/blog/index.astro 中设置了 prerender = false(纯服务端渲染),通过 contentfulClient.getEntries({ content_type: "blog", order: "-fields.date", limit: 100 }) 拉取条目,并用 hidden 字段过滤——生产模式下隐藏标记为 hidden: true 的文章,开发模式则全部显示;同时响应头设置 cache-control: public, max-age=0, must-revalidate,让浏览器总是校验内容新鲜度。封面图 URL 还拼接了 Contentful 的 ?fit=fill&f=center&fm=avif&w=1344&h=706 参数,由其 CDN 按需转码为 AVIF。

4. 富文本渲染site/src/lib/contentful/render.ts 基于 @contentful/rich-text-html-rendererdocumentToHtmlString 提供 renderRichTextrenderTitlerenderTerms 等函数,通过自定义 renderMark/renderNode 把加粗、下划线、H1/H2、段落映射为带 Tailwind 类名的 HTML(例如加粗渲染为 <span class="text-accent-500">),保证 CMS 内容与站点视觉体系一致。依赖侧对应 site/package.json 中的 @contentful/rich-text-html-renderer@contentful/rich-text-links@contentful/live-preview 等包。

Markdown 渲染管线:KaTeX、mermaid 与 TOC

博客与文档类内容的 Markdown 处理统一收敛在 site/markdown.config.ts,并被 astro.config.tsmarkdown: markdownConfiguration 引用:

  • gfm: true 启用 GitHub 风格 Markdown;
  • remark 插件链:自定义 mermaid()remark-mathremark-smartypantsremark-toc(自动在 heading: "contents" 处生成目录,前缀 toc-);
  • rehype 插件链:rehypeHeadingIdsrehype-slugrehype-autolink-headingsbehavior: "wrap")、rehype-katexrehype-mathjax(数学公式双方案)。

其中 mermaid() 是一个自定义 remark 插件:遍历 AST 中的 code 节点,凡语言标记为 mermaid 的围栏代码块,就地转换为 <div class="mermaid">…</div>(内容经 escapeHTML),从而让 CMS 里的 Markdown 内容可以直接嵌入 mermaid 流程图。

Spline 3D 资产与页面结构

README 提到的 Spline 3D 模型在仓库中体现为两层:

  1. 资产层site/src/spline/ 下存放 7 个 .splinecode 工程文件,如 union-polygon.splinecodemicro-cometbls-optimized.splinecodemicro-voyager-no-zoom.splinecodelighthouse.splinecode 等,文件名可直接对应 Union 的 CometBLS、Voyager 等核心概念;astro.config.ts 通过 vite.assetsInclude: ["**/*.splinecode"] 让 Vite 能将这些文件作为静态资源处理,运行时由 @splinetool/runtime / @splinetool/viewer(见 package.json)加载渲染,组件侧由 src/components/Spline.astroSpline.svelteSplineCover.astroSplineResizing.astro 封装。
  2. 页面层site/src/pages/ 包含首页 index.astroblog/(列表与 [...slug].astro 详情页)、docs/[...slug].tslearn.astroteam.astroroadmap.astroecosystem.astroairdrop-terms-and-conditions.astro 等;首页各分区由 site/src/components/sections/landing/ 下的 ConsensusSectionZkSectionExecutionSection 等组件构成,正好对应 Union 的共识验证(CometBLS)、零知识桥(ZK)与执行层三条主线;布局统一走 src/layouts/ 中的 BaseLayout/AnimationLayout,RSS 与 robots 由 src/pages/rss.xml.tsrobots.txt.ts 生成。

小结与文件索引

site/ 目录是一个典型的 “Nix 封装 + Astro 混合渲染 + Contentful 运行时取数” 的营销站点工程:本地开发只需 nix run .#site-dev-server,生产构建由 buildPnpmPackage 产出 Vercel serverless 格式的可复现 Nix 包,内容侧则通过带预览/生产双通道的 Contentful 客户端、GraphQL 查询与可定制 rich-text 渲染器保持 CMS 内容与站点样式的一致性。核心文件索引如下:

关注点 文件
原始说明文档 site/README.md
Nix 开发/检查应用与构建包 site/site.nix
Astro 配置(适配器、端口、资源) site/astro.config.ts
依赖与脚本(Node 22) site/package.json
Markdown 管线 site/markdown.config.ts
Contentful 客户端 site/src/lib/contentful/client.ts
GraphQL 查询 site/src/lib/contentful/queries.ts
富文本渲染 site/src/lib/contentful/render.ts
环境变量声明 site/src/lib/constants/env.ts
博客列表页数据流 site/src/pages/blog/index.astro
Spline 3D 资产 site/src/spline/
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384