feat: 更新附录交互组件和文档

This commit is contained in:
sanbuphy
2026-02-24 00:18:09 +08:00
parent d45df3cda5
commit 94f9db0834
88 changed files with 11797 additions and 7634 deletions
@@ -72,7 +72,7 @@
@mouseenter="hoveredPart = 'card'"
@mouseleave="hoveredPart = null"
>
<div class="card">
<div class="player">
</div>
<div
class="line indent-3"
@@ -83,7 +83,7 @@
@mouseenter="hoveredPart = 'img'"
@mouseleave="hoveredPart = null"
>
&lt;img class="icon" src="castle.png" /&gt;
&lt;img class="cover" src="cat.jpg" /&gt;
</div>
<div
class="line indent-3"
@@ -94,7 +94,7 @@
@mouseenter="hoveredPart = 'title'"
@mouseleave="hoveredPart = null"
>
&lt;h2 class="title"&gt;乐高城堡&lt;/h2&gt;
&lt;h2 class="title"&gt;搞笑猫咪合集&lt;/h2&gt;
</div>
<div
class="line indent-3"
@@ -105,7 +105,7 @@
@mouseenter="hoveredPart = 'btn'"
@mouseleave="hoveredPart = null"
>
&lt;button class="btn"&gt;购买&lt;/button&gt;
&lt;button class="btn"&gt; 播放&lt;/button&gt;
</div>
<div
class="line indent-2"
@@ -154,7 +154,7 @@
@mouseenter="hoveredPart = 'card'"
@mouseleave="hoveredPart = null"
>
.card { display: flex; padding: 10px; }
.player { margin: auto; padding: 20px; }
</div>
<div
class="line"
@@ -165,7 +165,7 @@
@mouseenter="hoveredPart = 'img'"
@mouseleave="hoveredPart = null"
>
.icon { width: 50px; height: 50px; }
.cover { width: 100%; height: 200px; }
</div>
<!-- Style properties -->
<div
@@ -177,7 +177,7 @@
@mouseenter="hoveredPart = 'title'"
@mouseleave="hoveredPart = null"
>
.title { color: red; }
.title { color: #fb7299; /* B站主题色 */ }
</div>
<div
class="line"
@@ -188,7 +188,7 @@
@mouseenter="hoveredPart = 'btn'"
@mouseleave="hoveredPart = null"
>
.btn { background: blue; }
.btn { background: #00aeec; color: white; }
</div>
</div>
</div>
@@ -233,7 +233,7 @@
@mouseenter.stop="hoveredPart = 'card'"
@mouseleave="hoveredPart = null"
>
<span class="block-label">div.card</span>
<span class="block-label">div.player</span>
<!-- Image -->
<div
@@ -245,11 +245,11 @@
@mouseenter.stop="hoveredPart = 'img'"
@mouseleave="hoveredPart = null"
>
<span class="block-label">img.icon</span>
<span class="block-label">img.cover</span>
<span
v-if="currentStep >= 3"
class="content-img"
>🏰</span>
>🐈</span>
</div>
<!-- Title -->
@@ -267,7 +267,7 @@
<span
v-if="currentStep >= 3"
class="content"
>乐高城堡</span>
>搞笑猫咪合集</span>
</div>
<!-- Button -->
@@ -285,7 +285,7 @@
<span
v-if="currentStep >= 3"
class="content-btn"
>购买</span>
> 播放</span>
</div>
</div>
</div>
@@ -332,26 +332,26 @@ import { ref } from 'vue'
const steps = [
{
label: 'DOM (搭骨架)',
title: '1. 搭建骨架 (DOM)',
desc: '浏览器工头 (Parser) 解析 HTML 代码,构建出完整的文档树结构。注意:即使代码中省略了 html/body,浏览器也会自动补全。',
title: '1. 搭建骨架 (DOM 解析)',
desc: '浏览器工厂看懂了 HTML 代码,搭建好了页面的“骨架”(比如哪里是包裹盒 div,哪里是按钮 button)。',
resultTitle: 'DOM 树结构'
},
{
label: 'Style (上色)',
title: '2. 计算样式 (Recalculate Style)',
desc: '装修工 (CSS Parser) 匹配 CSS 规则。比如发现 .title 需要红色,.btn 需要蓝色背景。此时只关心"长什么样",不关心"在哪"。',
resultTitle: '附带样式的节点'
label: 'Style (看图纸)',
title: '2. 匹配样式 (CSS 解析)',
desc: '仔细看了眼配色的说明书。比如发现 .title 字体要是粉色的,.btn 背景要是蓝色的(此时只在脑子里确立样式,但不计算尺寸)。',
resultTitle: '获取了各种配置规则'
},
{
label: 'Layout (排版)',
title: '3. 布局排版 (Layout/Reflow)',
desc: '测量员 (Layout) 根据 display:flex 和 padding 等属性,计算每个盒子的精确位置和大小。图片在左,文字在右。',
resultTitle: '几何布局'
label: 'Layout (定尺寸)',
title: '3. 排版规划 (Layout)',
desc: '拿尺子量每个骨架的大小。考虑到用户的屏幕尺寸,精确计算出猫咪的图片要多高、播放按钮要挤到哪个坐标上。',
resultTitle: '排版布局盒子'
},
{
label: 'Paint (绘制)',
title: '4. 像素绘制 (Paint)',
desc: '画家 (Paint) 按照计算好的位置和样式,真正把像素点画在屏幕上。最终你看到了一个完整的商品卡片。',
title: '4. 像素上色 (Paint)',
desc: '根据前面的几何位置和颜色计划,正式拿起画笔,将一个个像素填到你的屏幕上,一个可以看视频的播放器就诞生了。',
resultTitle: '最终画面'
}
]
@@ -576,34 +576,39 @@ const hoveredPart = ref(null)
/* Step 2: Style */
.block-box.title.styled {
color: red; /* Text color applied but not painted yet */
border: 1px solid red; /* Visual cue for style applied */
background: #fee2e2;
color: #fb7299;
border: 1px solid #fb7299;
background: #fdf2f8;
}
.block-box.btn.styled {
background: blue;
background: #00aeec;
color: white;
border: 1px solid blue;
border: 1px solid #00aeec;
}
/* Step 3: Layout */
.block-box.card.layout {
display: flex;
flex-direction: row; /* Horizontal layout */
flex-direction: column;
align-items: center;
gap: 10px;
padding: 15px;
background: white;
border: 1px solid #ccc;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
.block-box.img.layout {
width: 50px;
height: 50px;
width: 100%;
height: 120px;
background: #eee;
border: none;
font-size: 3rem;
display: flex;
align-items: center;
justify-content: center;
}
.block-box.title.layout {
@@ -614,9 +619,11 @@ const hoveredPart = ref(null)
}
.block-box.btn.layout {
margin-left: auto; /* Push to right */
padding: 5px 15px;
width: 100%;
padding: 8px;
border-radius: 4px;
text-align: center;
cursor: pointer;
}
/* Content visibility for Paint step */
@@ -5,10 +5,10 @@
<strong>为什么需要 DNS(查导航)</strong>
</p>
<p class="why-desc-zh">
你知道店铺名字叫 "Shop.com"但快递员需要知道具体的经纬度坐标 (IP 地址)
你知道店铺名字叫 "bilibili.com"但快递员需要知道具体的经纬度坐标 (IP 地址)
才能送达
<br>
DNS 就像是<strong>地图导航</strong>输入店名告诉你具体的坐标
DNS 就像是<strong>地图导航</strong>输入店名通过114查号台帮你找到坐标
</p>
</div>
@@ -16,7 +16,7 @@
<div class="input-area">
<span class="label">店铺名称 (域名)</span>
<div class="fake-input">
shop.com
bilibili.com
</div>
</div>
@@ -29,10 +29,10 @@
🧭
</div>
<div class="title">
DNS (地图导航)
DNS (查号台)
</div>
<div class="desc">
正在查 shop.com 位置...
正在查 bilibili.com IP...
</div>
</div>
<div class="arrow-down">
@@ -41,9 +41,9 @@
</div>
<div class="output-area">
<span class="label">GPS 坐标 (IP 地址)</span>
<span class="label">精准坐标 (IP 地址)</span>
<div class="fake-output">
93.184.216.34
110.43.12.55
</div>
</div>
</div>
@@ -169,20 +169,20 @@ const props = defineProps({
const t = {
send: '提交订单 (发送请求)',
noRequests: '购物车是空的 (无请求)',
placeholder: '点击 "提交订单" 向店员购买玩具',
general: '订单详情 (General)',
requestUrl: '商品地址 (URL)',
noRequests: '还没发请求 (网络空闲)',
placeholder: '点击 "提交订单" 向服务器索要页面',
general: '请求概要 (General)',
requestUrl: '目标地址 (URL)',
requestMethod: '操作类型 (Method)',
statusCode: '店员回复 (Status)',
responseHeaders: '包裹标签 (Headers)',
statusCode: '服务器回复状态 (Status)',
responseHeaders: '包裹标签 / 补充说明 (Headers)',
tabs: {
headers: '订单信息',
response: '包裹内容',
preview: '玩具预览'
headers: '头部信息(Headers)',
response: '代码内容(Response)',
preview: '大致预览(Preview)'
},
cols: {
name: '商品',
name: '请求体',
status: '状态',
type: '类型',
time: '耗时'
@@ -190,7 +190,7 @@ const t = {
}
const method = ref('GET')
const path = ref('/toys/lego-castle')
const path = ref('/video/BV1xx411c7mD')
const loading = ref(false)
const requestSent = ref(false)
const activeTab = ref('headers')
@@ -210,20 +210,33 @@ const sendRequest = async () => {
loading.value = false
if (method.value === 'GET') {
responseStatus.value = '200 OK (有货)'
responseStatus.value = '200 OK (交易成功)'
responseHeaders.value = {
'Content-Type': 'application/json (积木)',
Date: new Date().toLocaleString(),
Store: '乐高官方店'
'Content-Type': 'text/html; charset=utf-8',
'Server': 'BWS/1.1 (Bilibili Web Server)',
'Date': new Date().toUTCString()
}
responseBody.value = `{\n "id": 101,\n "name": "Lego Castle",\n "pieces": 500,\n "price": "$99"\n}`
responseBody.value = `<!DOCTYPE html>
<html>
<head>
<title>【B站】超级搞笑的猫咪合集</title>
</head>
<body>
<h1>超级搞笑的猫咪合集</h1>
<div class="player">
<img src="cat_cover.jpg" alt="封面" />
<button>▶️ 播放</button>
</div>
</body>
</html>`
} else {
responseStatus.value = '201 Created (下单成功)'
responseStatus.value = '201 Created (操作成功)'
responseHeaders.value = {
'Content-Type': 'application/json',
Date: new Date().toLocaleString()
'Server': 'BWS/1.1',
'Date': new Date().toUTCString()
}
responseBody.value = `{\n "success": true,\n "message": "Order placed"\n}`
responseBody.value = `{\n "success": true,\n "message": "点赞成功!"\n}`
}
}
@@ -139,21 +139,21 @@ const props = defineProps({
// Bilingual text directly
const t = {
statusLabel: '通话状态',
connect: '拨打电话',
reset: '挂断重拨',
client: '我 (顾客)',
server: '玩具店',
statusLabel: '连接状态',
connect: '建立连接',
reset: '断开重连',
client: '我 (浏览器)',
server: '对面 (B站服务器)',
status: {
closed: '未通话',
handshaking: '正在拨号...',
established: '通话中 (连接已建立)'
closed: '未连接',
handshaking: '正在打招呼确认通道...',
established: 'TCP 通道已建立 (ESTABLISHED)'
},
steps: {
0: '点击 "拨打电话" 开始确认店铺是否营业。',
1: '步骤 1: 我问 "喂?有人在吗?" (SYN)',
2: '步骤 2: 店员答 "在的!请问有什么事" (SYN-ACK)',
3: '步骤 3: 我说 "太好了,我想买东西" (ACK)'
0: '点击 "建立连接" 开始三次握手(电话试音)。',
1: '第一次握手: "喂,服务器老哥在吗?我能发信息,你能收到吗?" (SYN)',
2: '第二次握手: "喂喂在的!我收到了!那你现在能听到我说话吗" (SYN-ACK)',
3: '第三次握手: "妥了,我也能听到!通道没问题,准备看视频" (ACK)'
}
}
@@ -109,14 +109,14 @@ const props = defineProps({
}
})
const inputUrl = ref('https://shop.com/toys/lego-castle?color=red#summary')
const inputUrl = ref('https://www.bilibili.com/video/BV1xx411c7mD?t=60#comments')
const highlightedPart = ref(null)
const icons = {
protocol: '🚛',
host: '🏢',
port: '🚪',
pathname: '🧸',
pathname: '📺',
search: '📝',
hash: '📍'
}
@@ -125,18 +125,18 @@ const labels = {
protocol: '交通方式 (Protocol)',
host: '店铺地址 (Host)',
port: '大门号 (Port)',
pathname: '商品位置 (Path)',
search: '备注要求 (Query)',
hash: '快速定位 (Hash)'
pathname: '具体货架 (Path)',
search: '特殊要求 (Search/Query)',
hash: '直接跳转 (Hash)'
}
const descriptions = {
protocol: '怎么去?(例如 https = 坐装甲车去,安全)',
host: '去哪家店?(域名例如 shop.com)',
port: '哪个门(默认 443 号)',
pathname: '商品在哪个货架?(路径)',
search: '给店员的备注 (例如 ?color=red)',
hash: '直接翻到说明书第几页 (锚点)'
protocol: '怎么去?(https = 坐押运车去,比 http 安全)',
host: '去哪家店?(域名例如 www.bilibili.com)',
port: '哪个门?(默认隐藏了 443 端口号)',
pathname: '拿什么货?(去 /video 区拿编号为 BV... 的视频)',
search: '给店员的备注说明 (例如 ?t=60 表示要求从 60 秒开始看)',
hash: '拿到货后自己做的事 (例如滚动到评论区 #comments)'
}
const parsedUrl = computed(() => {