2.0 架构解析思路
我的原有的项目在我的github里面。接下来我会从细节上一步步修改成我想要的结构,前提是能够读懂原有的结构。
我有如下几个需求:
- 首页页面的结构改成附加一些动态效果,并且左边的目录页面太宽了,想要优化一下。
- 在某个地方添加中英文版本的切换按键,并编写中英文页面。中文优先,如果有时间构建英文页面。
- 为首页中的出现的每个项目构建页面能够实现跳转。
2.0.1 解析思路
按这 3 个需求,建议按"先外壳,再内容,再路由"的顺序读。
2.0.1.1 先看全站外壳
- astro.config.mjs
- src/layouts/BaseLayout.astro
- src/components/SideBar.astro
- src/components/SideBarMenu.astro
- src/components/Header.astro
- src/styles/global.css
看什么:
- 侧边栏宽度在哪定的
- 主内容区最大宽度在哪定的
- 移动端/桌面端怎么切换
- 语言按钮放哪最合适
2.0.1.2 再看首页内容
- src/pages/index.astro
- src/components/HorizontalCard.astro
- src/components/Card.astro
看什么:
- 首页当前是静态写死,还是可以抽成数据
- 动效适合加在 hero、卡片、分区标题、滚动出现
- 每个项目卡片现在怎么跳转
2.0.1.3 再看数据和路由
- src/content/config.ts
- src/content/blog
- src/content/store
- src/pages/blog/[...page].astro
- src/pages/store/[...page].astro
看什么:
- 这项目已经有"内容集合 + 列表页 + 详情页"的模式
- 你的"项目页"可以直接照这个模式新增一套
2.0.1.4 语言切换
- 中文优先,建议先把中文做成默认
- 英文不要一开始硬翻整站,先做"核心入口页"英文化
- 最稳的方式是:
- 统一把文案抽到配置文件
- 再做
zh/en两份数据 - 切换按钮放在 Header 或 SideBarFooter
2.0.2 VSCode下载插件
作为第一次编码Astro,建议先装这几个(VS Code):
Astro(官方,必须)
- 提供
.astro语法高亮、诊断、跳转
Tailwind CSS IntelliSense
- 你这个项目大量用 Tailwind / DaisyUI,装了更好读 class
ESLint(可选)
- 读 JS/TS 逻辑更清晰,减少低级错误
Prettier(可选)
- 保持格式统一,改动更容易审查
另外命令行建议装:
powershell
npm install
npx astro check
这样能提前发现 .astro 文件里的类型/语法问题。
有关前端页面展示,你可以使用代码:
bash
npm run dev
很可惜不像html在vscode里面有插件live sever,可以点击就可以查看前端,这里你需要手动输入代码。
2.1 设置模板
由于我需要整体修改的页面功能过多,我只借助原有git下来的代码的框架,并不借鉴里面的内容。关于页面设计,主要参考daisyUI进行设计。


现在这个主页面(抽屉布局)主要看这 4 个文件:
- 布局骨架
- src/layouts/BaseLayout.astro
- 负责
drawer结构、灰色笼罩层、主内容容器。
- 左侧边栏
- src/components/SideBar.astro
- 负责头像、收缩/展开按钮、
Homepage/Settings菜单。
- 样式与动画
- src/styles/global.css
- 负责宽度滑动、文字显隐动画、灰层效果、图标尺寸统一。
- 首页内容本体
- src/pages/index.astro
- 现在是
Page Content占位内容,可替换成你的真实首页模块。
2.1.1 src/layouts/BaseLayout.astro详细解释
src/layouts/BaseLayout.astro 的意义就是:
- 规定每一页的整体外壳
- 统一头部、侧边栏、主内容区、动画、主题等
- 让不同页面只需要往里面填自己的内容
简单说:
BaseLayout= 全站模板index.astro/projects.astro这类页面 = 具体内容
你现在这套结构就是:
先用 BaseLayout 定骨架,再用各个页面往 slot 里填内容。
下面按行解释 src/layouts/BaseLayout.astro:
-
1-16Astro 前置脚本区,负责导入组件、读取全站配置、接收页面参数。
-
2导入
BaseHead,用于输出<head>里的 meta/title。 -
3导入
SideBar,用于左侧抽屉。 -
4导入 Astro 的视图切换动画组件。
-
6从配置文件读取站点默认标题、描述和动画开关。
-
8-15从页面传入参数:
image:分享图title:标题,默认SITE_TITLEdescription:描述,默认SITE_DESCRIPTIONincludeSidebar:是否显示侧栏sideBarActiveItemID:侧栏高亮项ogType:Open Graph 类型
-
18HTML5 文档声明。
-
19根
<html>:lang="zh-CN":页面语言中文data-theme="silk":DaisyUI 主题
-
20-23
<head>区域:21输出头部 meta22启用页面切换动画
-
24
<body>开始。 -
25
app-shell是整个页面外壳,drawer lg:drawer-open表示这是 DaisyUI 抽屉布局,大屏默认可开。 -
26隐藏的 checkbox,用来控制抽屉开合,
checked表示默认打开。 -
27
drawer-content:主内容区。 -
28-34小屏左上角的抽屉按钮:
28固定定位,只在小屏显示29label绑定my-drawer30-32汉堡图标。为了手机端能够流畅体验。
-
35-38顶部导航条:
navbar是 DaisyUI 的导航容器flex-1占位把文字推到右边Guoxuan是右对齐的品牌文字
-
39灰层遮罩,点击可关闭抽屉。
-
40-42主内容区:
shell-content控制整体灰感/透明度slot是页面内容插入点
-
43-82页脚:
43footer 容器44-61左侧 logo 和说明62-81三列链接区,目前href=""为空占位
-
83
drawer-content结束。 -
84条件渲染侧栏:如果
includeSidebar=true才显示。 -
85
app-shell结束。 -
86-87HTML 文档结束。
一句话总结:
这个文件就是全站骨架 ,负责把"头部、抽屉、主内容、页脚"拼起来,具体每一页只把内容塞进 slot。
2.1.2 src/components/SideBar.astro详细解释
下面按当前最新版逐行解释 src/components/SideBar.astro:
-
1-3Astro 前置脚本区。这里导入了 Astro 图片组件能力。
-
2
import { Image } from "astro:assets";使用 Astro 的图片组件来渲染头像,支持构建期图片优化。
-
5
<div class="drawer-side z-40">侧栏根容器,
drawer-side接入 DaisyUI Drawer 结构,z-40提升层级避免被主内容盖住。 -
6
<label for="my-drawer" class="drawer-overlay"></label>这是抽屉遮罩层,绑定
my-drawer。点击遮罩会触发关闭抽屉。 -
7-8
aside.sidebar-shell+div.sidebar-panel侧栏的外壳和内部面板。宽度、收缩动画、布局细节主要由全局 CSS 的这些类控制。
-
9-17顶部头像区:
sidebar-top:顶部区域容器a href="/":点击头像回首页avatar与w-11 h-11 rounded-lg ...:头像框尺寸/圆角/裁切Image src="/profile.webp":头像图片来源,alt是无障碍文本,width/height/format提供优化信息
-
19
<div class="sidebar-body">侧栏主体菜单区域开始。
-
20-27第一个菜单项(收起/展开开关):
- 用
label for="my-drawer"而不是button,直接控制抽屉开关 sidebar-item sidebar-toggle:沿用统一菜单样式并加 toggle 样式aria-label="Toggle sidebar":无障碍描述svg:收缩展开图标span.sidebar-text:文字Toggle,通常在收起状态会被 CSS 隐藏或淡出
- 用
-
28-34第二个菜单项(Homepage):
a href="/":跳转首页sidebar-icon图标 +sidebar-text文案- 当前文案是
Homepage
-
35-43第三个菜单项(Settings):
a href="#":当前是占位链接- 图标为设置样式
- 文案是
Settings - 后续可以改成真实路由(如
/settings)
-
44-47组件结构闭合:结束
sidebar-body、sidebar-panel、aside、drawer-side。
一句话总结:
这个文件只负责"左侧抽屉本体结构(头像 + 菜单)",抽屉开关状态来自 my-drawer,而宽度滑动、文字显隐、动画细节主要在 src/styles/global.css 中控制。
2.1.3 src/styles/global.css详细解释
下面按当前最新版逐行解释 src/styles/global.css:
-
1-8
.sidebar-shell定义侧栏外壳的默认状态(收起态):width: 3.5rem:收起时窄栏宽度min-height: 100vh:侧栏撑满整屏高度background: hsl(var(--b2)):使用 DaisyUI 主题底色border-right:右侧细分割线transition: width 340ms ease:宽度变化动画overflow: hidden:收起时把超出的文字/内容裁掉
-
10-12当
#my-drawer被勾选(展开)时,把侧栏宽度改为10rem。这是"抽屉滑开"核心规则之一。
-
14-19
.sidebar-panel是侧栏内部主容器:- 纵向 flex 布局
- 最小高度全屏
- 内边距
0.5rem
-
21-26
.sidebar-top是头像顶部行:- 水平 flex
- 垂直居中
- 默认
justify-content: space-between(收起/展开时会被后续规则覆盖) - 行高最小
3.25rem
-
28-32
.sidebar-toggle(Toggle 那一项):- 宽度占满
flex-shrink: 0防止被压扁
-
34-38
.sidebar-icon统一图标大小:- 宽高都
1rem - 禁止收缩,避免图标在不同状态尺寸不一致
- 宽高都
-
40-44
.sidebar-body菜单区域:display: gridgap: 0.5rem菜单项间距- 顶部外边距
1rem
-
46-54
.sidebar-item统一每个菜单项样式:- 行内 flex(图标 + 文字)
- 垂直居中
- 图标文字间距
0.75rem - 最小高度
2.75rem - 内边距
0.75rem 0.8rem - 圆角
0.75rem white-space: nowrap防止文字换行
-
56-58
.sidebar-item:hover鼠标悬停背景高亮,使用主题前景色做很淡的混合。 -
60-69
.brand-wordmark控制顶部Guoxuan文本样式:margin-left: auto把文字推到右侧- 右内边距
1rem - 字体族
Georgia/Times(衬线风格) - 字号
1.5rem、字重400、字距微压缩 - 文本颜色跟随主题前景色
-
71-73
.shell-content主内容透明度变化动画。目前只定义了过渡属性,实际透明变化由其他状态/层实现。
-
75-86
.shell-dim是非侧栏区域灰层:- 默认
display: none隐藏 position: fixed铺满屏幕left: 3.5rem:从收起侧栏右边开始,不盖住侧栏本体- 灰色背景
rgba(80, 80, 80, 0.34) - 初始
opacity: 0 - 过渡:透明度 + left(跟随侧栏宽度变化)
z-index: 20保证在主内容上层
- 默认
-
88-92抽屉展开时显示灰层:
display: blockleft: 10rem(对应展开宽度)opacity: 1(真正变灰)
-
94-96展开时把
.sidebar-top设为justify-content: center。这条后面又被更具体选择器覆盖(见
126-128),最终实际是flex-start。 -
98-100展开时
.sidebar-item左对齐。后面同样有更具体/重复规则。
-
102-109
.sidebar-text(菜单文字)默认收起态:max-width: 0+opacity: 0+translateX(-6px)overflow: hidden裁切文字- 定义宽度/透明/位移过渡
实现"收起只看图标,展开文字滑入"。
-
111-115展开时
.sidebar-text:max-width: 10remopacity: 1translateX(0)
形成文字显现动画。
-
117-119展开时
.sidebar-item左对齐(与98-100含义重复)。 -
121-124展开时
.sidebar-item:- 再次左对齐(重复)
- 额外补
padding-inline: 0.8rem
-
126-128展开时
.sidebar-top左对齐。由于选择器更具体,覆盖了
94-96的center。
一句话总结:
这个 CSS 主要做了 4 件事:侧栏宽度滑动(3.5rem -> 10rem)、文字显隐动画、非侧栏灰层覆盖、图标/菜单统一尺寸 ;其中 94-100 与 117-128 存在重复和相互覆盖,后续可以精简。
2.1.4 src/pages/index.astro详细解释
下面按当前最新版逐行解释 src/pages/index.astro:
-
1-3Astro 前置脚本区。这里只做一件事:导入页面要用的布局组件。
-
2
import BaseLayout from "../layouts/BaseLayout.astro";把全站骨架布局引入首页。后面的首页内容都会被塞进这个布局的
<slot />。 -
5
<BaseLayout sideBarActiveItemID="home">首页使用全站布局,并传入参数
sideBarActiveItemID="home"。语义上这是告诉侧栏"当前页是 home"(是否真正高亮取决于
SideBar.astro是否使用了这个参数)。 -
6
<div class="p-4 sm:p-6">首页内容外层容器,负责留白:
- 默认内边距
p-4 - 小屏以上
sm:p-6(更大留白)
- 默认内边距
-
7
<div class="text-2xl font-bold">Page Content</div>当前首页正文只是占位文案:
text-2xl:较大字号font-bold:粗体
这行就是你后续要替换成真实首页模块的位置。
-
8-9关闭内容容器和
BaseLayout。到这里,首页内容定义结束,最终渲染时会被包在全站抽屉/导航/footer 外壳中。
一句话总结:
这个文件现在非常"轻":只负责把首页内容挂到 BaseLayout 上,并放了一个 Page Content 占位块。后续你做个人主页,主要在这里替换成各个 section。
2.2 设置主页
2.2.1 需求分析
整体分为几个部分:
- 首先展示一些基本的个人信息和网页介绍,下面附加一些链接和联系方式。
- 以时间轴的形式展示学历和实习经历
- 最后着重构建项目的展示。由于这里只是一个入口,我分为四类:quant web3 AI other四类,每类仅分出三个项目供点击。每个项目的展示以卡片为基础,每个卡片有标题、描述以及图片。
- 我也需要上边栏滚动进行每个部分的显示,知道这篇文章你滑动到了哪个部分。



2.2.2 实现方法
下面逐行解释 src/pages/index.astro:
文件定位
- 这个文件负责"首页本身"的全部内容。
- 它不再依赖骨架里的上边栏,而是自己定义:
- 顶部目录栏
- 个人信息首屏
- 教育经历
- 职业经历
- 技术实践
- 顶部目录的滚动高亮逻辑
- 技术实践卡片切换逻辑
- 页面私有样式
1. 前置脚本区
1---:Astro frontmatter 开始。这里面写的是服务端/构建时执行的脚本,不是直接输出到页面的 HTML。2import BaseLayout ...:导入全站骨架布局。这个首页会被包进BaseLayout.astro。3空行:只是为了分块更清楚。
2. 职业经历数据
4const workExperiences = [:定义职业经历数组,后面时间线会循环这个数组生成。5第一条职业经历对象开始。6company:公司名Arrakis Green FinTech。7location:地点中国 香港。8role:岗位AI 独立研究员。9time:时间范围。10logoSrc:这段经历对应的 logo 图片路径。11logoAlt:图片替代文本。12第一条经历对象结束。13第二条职业经历对象开始。14第二条公司名国泰君安证券。15第二条地点中国 深圳。16第二条岗位量化研究实习生。17第二条时间范围。18第二条 logo 路径。19第二条 logo 替代文本。20第二条对象结束。21第三条职业经历对象开始。22第三条公司名Goldman Sachs。23第三条地点中国 香港。24第三条岗位量化研究实习生。25第三条时间范围。26第三条 logo 路径。27第三条 logo 替代文本。28第三条对象结束。29];:职业经历数组结束。
3. 技术实践数据
31const techPracticeGroups = [:定义技术实践四大分类的数据源。32第一组开始。33id: "quant":分组内部唯一标识,供切换逻辑使用。34title: "Quant":标签页显示标题。35description:当前分组的说明文字。36items: [:该分组下的项目卡片列表开始。37-42第一张 Quant 卡片:标题、描述、图片、图片替代文本。43-48第二张 Quant 卡片。49-54第三张 Quant 卡片。55Quant 的items结束。56Quant 分组结束。57第二组AI开始。58id: "ai":AI 分组标识。59title: "AI":标签页标题。60AI 分组说明。61AI 的卡片数组开始。62-67第一张 AI 卡片。68-73第二张 AI 卡片。74-79第三张 AI 卡片。80AI 的items结束。81AI 分组结束。82第三组Web3开始。83id: "web3"。84title: "Web3"。85Web3 分组说明。86Web3 卡片数组开始。87-92第一张 Web3 卡片。93-98第二张 Web3 卡片。99-104第三张 Web3 卡片。105Web3 的items结束。106Web3 分组结束。107第四组Others开始。108id: "others"。109title: "Others"。110Others 分组说明。111Others 卡片数组开始。112-117第一张 Others 卡片。118-123第二张 Others 卡片。124-129第三张 Others 卡片。130Others 的items结束。131Others 分组结束。132技术实践总数组结束。133---:Astro frontmatter 结束,下面开始输出 HTML。
4. 首页外壳与顶部目录栏
135<BaseLayout ...>:把当前页包进全站骨架里,同时把侧边栏高亮项设为home。136<header id="page-header" ...>:首页自己的顶部栏开始。sticky top-0表示吸顶;z-30保证层级较高。137顶栏内部容器:mx-auto max-w-6xl:限制最大宽度并居中。h-12:顶栏固定高度。justify-between:左边目录、右边名字分开。px-6 sm:px-10 lg:px-14:不同屏幕下左右内边距。
138<nav ...>:顶部目录导航开始。page-nav是你自己定义的样式钩子。overflow-x-auto表示窄屏时可以横向滚动。
139"个人信息"目录项:href="#personal-info":点击跳到id="personal-info"的区块。page-nav-link-active:默认进页面时它先高亮。data-page-link="personal-info":给脚本用来匹配区块。aria-current="true":无障碍语义上说明它是当前项。
140"教育经历"目录项,目标是#education-section。141"职业经历"目录项,目标是#work-section。142"技术实践"目录项,目标是#tech-practice-section。143</nav>:目录导航结束。144Guoxuan:顶栏右侧名字。145顶栏内部容器结束。146顶栏结束。
5. 正文总容器
148首页正文总容器开始:max-w-6xl保持和顶栏同宽。pt-12 sm:pt-16 lg:pt-24:顶部大留白,制造"导航下方留一块空间再开始正文"的效果。pb-10:底部留白。
149首屏信息区开始,同时把它作为#personal-info锚点目标。grid:大屏下左右两列。lg:grid-cols-[1fr_320px]:左边自适应,右边头像固定320px。
150左侧文字列开始。151个人大标题<h1>:text-right:右对齐。text-5xl sm:text-6xl:响应式字号。style="font-family: Georgia...":手动指定衬线字体。
152标题文字Guoxuan Sun。153</h1>:标题结束。
6. 首屏自我介绍文本
155段落容器开始:mt-8:与标题拉开距离。space-y-7:每段之间统一垂直间距。text-lg leading-10:较大的阅读字号和行高。
156-159第一段介绍:- 说明这是个人主页。
- 说明主题是职业发展、技术实践、研究兴趣。
161-163第二段介绍:- 解释左侧抽屉栏是网站架构栏。
- 解释上侧栏是当前页面的目录。
165-168第三段介绍:- 引导用户通过下方联系方式联系你。
- 引导用户查看代码仓库。
169段落容器结束。
7. 联系方式按钮区
171联系方式容器开始:flex flex-wrap gap-3:横向排列,空间不够时自动换行。
172邮箱按钮开始,href="mailto:..."点击会打开邮件客户端。173-175邮箱图标 SVG。176邮箱文字。177邮箱按钮结束。178LinkedIn 按钮开始。target="_blank":新标签页打开。rel="noreferrer":安全相关属性。
179-181LinkedIn 图标 SVG。182LinkedIn 文本。183LinkedIn 按钮结束。184GitHub 按钮开始。185-190GitHub 图标 SVG 路径定义。191SVG 结束。192GitHub 文本。193GitHub 按钮结束。194CSDN 按钮开始。195-200CSDN/博客图标 SVG。201CSDN 文本。202按钮结束。203联系方式容器结束。204左侧文字列结束。
8. 首屏头像列
206右侧头像列开始。order-first lg:order-none:小屏时头像排前面,大屏恢复到右侧。
207图片外框:rounded-xl圆角。border边框。shadow-sm小阴影。
208-213<img>标签:src="/profile.webp":头像来源。alt:无障碍替代文本。object-cover:裁剪铺满容器。loading="eager":首屏图片优先加载。
214图片外框结束。215头像列结束。216首屏两列区结束。
9. 第一条分割线
218divider:首屏和教育经历之间的分隔线。
10. 教育经历区
220教育区<section>开始:id="education-section":顶部目录点击目标。aria-labelledby="education-title":关联到标题,方便无障碍。page-anchor-section:滚动锚点修正的样式钩子。
221教育区标题。222使用 DaisyUI 的timeline timeline-vertical生成竖直时间线。223第一条教育经历<li>开始。224左侧时间文本。225-227中间时间线节点,小圆点表示时间点。228右侧内容卡片开始。229卡片内部横向布局开始。230logo 容器。231香港中文大学 logo 图片。232logo 容器结束。233文本内容容器开始。234-236学校链接,点击跳学校官网。237专业名称。238文本容器结束。239行布局结束。240卡片结束。241时间线连接线<hr />。242第一条教育经历结束。243第二条教育经历开始。244上方连接线。245河海大学时间文本。246-248第二个时间点节点。249第二张教育卡片开始。250卡片内部布局开始。251logo 容器。252河海大学 logo。253logo 容器结束。254文字容器开始。255-257河海大学官网链接。258本科专业名称。259文字容器结束。260行布局结束。261卡片结束。262第二条教育经历结束。263时间线列表结束。264教育区结束。
11. 第二条分割线
266教育和职业经历之间的分割线。
12. 职业经历区
268职业经历区开始,id="work-section"是顶部目录锚点。269职业经历区标题。270外层overflow-x-auto:如果横向时间线太宽,可以横向滚动。271timeline timeline-horizontal min-w-[960px]:横向时间线,最小宽度 960。272用workExperiences.map(...)循环渲染每条工作经历。273每一项<li>开始。274如果不是第一项,就先画左侧连接线。276timeline-start:时间线起点位置内容容器。277-292条件渲染:- 偶数项时在上方显示时间。
- 奇数项时在上方显示公司卡片。
278偶数项时间文本。280奇数项卡片开始。281卡片内部横向布局。282logo 容器。283工作 logo 图片。284logo 容器结束。285公司文字容器开始。286公司名称。287公司地点。288文字容器结束。289行布局结束。290岗位名称。291奇数项卡片结束。292条件块结束。295-297中间时间点节点。299timeline-end:时间线另一侧内容容器。300-315第二个条件渲染:- 偶数项时这里放公司卡片。
- 奇数项时这里放时间。
301-312偶数项公司卡片结构,与上方那张相同。314奇数项时间文本。315条件块结束。318如果不是最后一项,右侧再接一条连接线。319当前工作<li>结束。320map循环结束。321时间线<ul>结束。322横向滚动容器结束。323职业经历区结束。
13. 第三条分割线
325职业经历和技术实践之间的分割线。
14. 技术实践区
327技术实践区开始,id="tech-practice-section"是顶部目录跳转目标。328内层总容器开始。329顶部头部布局:- 小屏竖排。
- 大屏横向分布。
330左侧标题说明容器开始。331技术实践标题。332辅助说明文字。333左侧说明容器结束。334右侧标签页容器开始:tabs tabs-boxed是 DaisyUI 标签样式。
335开始循环techPracticeGroups渲染标签按钮。336-340每个标签按钮:type="button":防止当成表单提交按钮。class中如果index === 0,默认第一个标签激活。data-tech-tab={group.id}:把分组 id 存到按钮上,供脚本读取。
341按钮显示文本,如Quant。342按钮结束。343map结束。344标签容器结束。345顶部布局结束。347下半部分内容容器开始。348-350分组描述段落:- 默认显示第一组
Quant的描述。 - 后续会被脚本动态改写。
- 默认显示第一组
352卡片网格开始:- 小屏单列。
md两列。xl三列。
353循环当前默认分组的 3 张卡片。354-358每张卡片外层article:tech-stack-card是你自定义的卡片样式钩子。is-active让第一张初始突出。data-tech-card和data-card-index给脚本识别。
359-363整张卡片用<button>包住,点击时可以切换激活态。364图片区域外层,固定16:10比例。365-371卡片图片:data-tech-image让脚本后续替换图片时能找到它。
372图片区域结束。373文字内容区域开始。374项目标题,data-tech-title供脚本替换。375项目描述,data-tech-copy供脚本替换。376文字区域结束。377按钮结束。378卡片结束。379卡片循环结束。380网格结束。381内容容器结束。382技术实践内层容器结束。383技术实践区结束。384首页正文总容器结束。385BaseLayout结束。
15. 页面交互脚本
387<script define:vars={``{ techPracticeGroups }} is:inline>:- 在页面里写内联脚本。
define:vars把前面 Astro 里的techPracticeGroups注入到浏览器端脚本。
388获取顶部栏元素。389找到所有顶部目录链接。390-392根据目录链接上的data-page-link,去找对应的正文区块 DOM。393找到所有技术实践标签按钮。394找到所有技术实践卡片。395找到描述段落 DOM。396activeGroup初始设为第一组Quant。
16. 顶部目录与锚点修正逻辑
398定义syncHeaderOffset函数。399如果没找到头栏,直接退出。400把头栏高度写进 CSS 变量--page-header-offset。+ 18是额外补偿,避免锚点跳转时区块太贴顶。
401函数结束。403定义setActivePageLink(targetId)。404遍历每一个目录项。405判断当前链接是不是目标链接。406用类名切换当前项高亮。407同时更新aria-current状态。411目录状态切换函数结束。
17. 顶部目录滚动高亮
414如果确实找到了可观察的页面区块,就初始化滚动逻辑。415先同步一次头栏高度偏移。416窗口尺寸变化时重新同步偏移。418创建IntersectionObserver。419-423观察回调:- 过滤掉当前不可见区块。
- 按可见比例从大到小排序。
424-426如果有可见区块,就把最"显眼"的那个区块对应导航设为当前项。428-431观察器参数:rootMargin改变判断区域。threshold规定触发比例。
432观察器创建结束。434对每个页面区块开始观察。436遍历每个顶部目录链接。437-440点击目录时,先立即设置当前项状态,避免视觉延迟。441监听绑定结束。442这一整段顶部目录逻辑结束。
18. 技术实践卡片高亮逻辑
444定义activateCard(activeIndex)。445-448遍历所有卡片:- 当前卡片加
is-active - 非当前卡片去掉
is-active - 同时更新
aria-pressed
- 当前卡片加
449函数结束。
19. 技术实践分类切换逻辑
451定义renderGroup(groupId)。452在techPracticeGroups里找到对应分组。453找不到就退出。455更新当前激活的分组。456把顶部说明文字换成新分组说明。458遍历当前 3 张卡片 DOM。459拿到这个位置对应的新数据项。460找到卡片里的图片节点。461找到标题节点。462找到描述节点。464如果某个节点缺失,就跳过。465替换图片地址。466替换图片替代文本。467替换标题文字。468替换描述文字。469当前卡片更新结束。471切换分组后默认激活第一张卡片。472函数结束。
20. 技术实践交互初始化
474页面初次加载时,先激活第一张卡片。475-478给每张卡片绑定事件:- 鼠标移入时高亮
- 点击时高亮
480遍历顶部的分类标签。481-486点击标签时:- 取出目标分组 id
- 切换
tab-active - 调用
renderGroup(target)刷新 3 张卡片
487标签循环结束。488脚本结束。
21. 页面私有样式
490<style is:inline>:当前页面的局部内联样式开始。491-493page-nav:Firefox 下隐藏横向滚动条。495-497page-nav::-webkit-scrollbar:Chrome/Safari 下隐藏滚动条。499-502给#personal-info和各个大区块设置scroll-margin-top:- 锚点跳转时会预留出顶部栏的高度。
504.page-nav-link样式开始。505用inline-flex保证文字和下划线控制稳定。506垂直居中。507给目录项底部一点空间,用来放下划线。508默认下划线透明。509目录字号。510行高为 1,收紧文字高度。511字母/汉字间距略拉开。512默认文字颜色较浅。513去掉默认链接下划线。514过渡动画:颜色和边框颜色平滑变化。515当前规则结束。517-519hover 时把目录文字变深。522-524当前项样式:page-nav-link-active- 或
aria-current="true" - 显示深色文字和底部实线
526.tech-stack-card开始。527卡片的过渡动画定义。528默认降低饱和度,让非激活卡片看起来更"退后"。529鼠标形状变成手型。530当前规则结束。532-537激活卡片样式:- 饱和度恢复
- 边框变成主题色
- 阴影增强
544样式结束。
一句话总结
- 这个文件本质上做了三件事:
- 用数据数组描述首页内容
- 用 Astro 模板把这些数据渲染成首页结构
- 用一小段前端脚本实现"顶部目录滚动高亮"和"技术实践分类切换"
如果你要,我下一步可以继续做两件事之一:
- 单独把
387-488这段脚本逐行拆得更细 - 单独把
135-385这段 HTML 结构画成"页面结构图"给你看