feat(docs): update computer fundamentals content and demos
- Refactor frontend framework demo descriptions for clarity - Remove interactive features from triad and field map demos - Add new computer organization and DSL documentation links - Split type systems and compilers into separate pages - Enhance power-on-to-web article with relay race analogy - Add new interactive demos for type systems and compilation - Improve visual presentation of boot process and hardware flow - Introduce new Vibe Coding flow demo component
This commit is contained in:
@@ -585,6 +585,10 @@ export default defineConfig({
|
||||
text: '从晶体管到 CPU',
|
||||
link: '/zh-cn/appendix/1-computer-fundamentals/transistor-to-cpu'
|
||||
},
|
||||
{
|
||||
text: '计算机组成原理',
|
||||
link: '/zh-cn/appendix/1-computer-fundamentals/computer-organization'
|
||||
},
|
||||
{
|
||||
text: '操作系统(进程 / 内存 / 文件系统)',
|
||||
link: '/zh-cn/appendix/1-computer-fundamentals/operating-systems'
|
||||
@@ -610,8 +614,12 @@ export default defineConfig({
|
||||
link: '/zh-cn/appendix/1-computer-fundamentals/programming-languages'
|
||||
},
|
||||
{
|
||||
text: '类型系统与编译原理入门',
|
||||
link: '/zh-cn/appendix/1-computer-fundamentals/type-systems-compilers'
|
||||
text: '类型系统入门',
|
||||
link: '/zh-cn/appendix/1-computer-fundamentals/type-systems'
|
||||
},
|
||||
{
|
||||
text: '编译原理入门',
|
||||
link: '/zh-cn/appendix/1-computer-fundamentals/compilers'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -798,6 +806,10 @@ export default defineConfig({
|
||||
{
|
||||
text: '后端分层架构',
|
||||
link: '/zh-cn/appendix/4-server-and-backend/backend-layered-architecture'
|
||||
},
|
||||
{
|
||||
text: '领域特定语言(DSL)',
|
||||
link: '/zh-cn/appendix/4-server-and-backend/domain-specific-languages'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
+101
-138
@@ -1,17 +1,13 @@
|
||||
<template>
|
||||
<div class="ai-vs-traditional-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🤖</span>
|
||||
<span class="title">AI 工程师 vs 传统工程师</span>
|
||||
<span class="subtitle">工作方式的差异</span>
|
||||
</div>
|
||||
|
||||
<div class="comparison-container">
|
||||
<div class="comparison-column traditional">
|
||||
<div class="column-header">
|
||||
<span class="col-icon">👨💻</span>
|
||||
<span class="col-title">传统工程师</span>
|
||||
</div>
|
||||
<div class="column-header">传统工程师</div>
|
||||
<div class="work-flow">
|
||||
<div v-for="(step, index) in traditionalSteps" :key="index" class="flow-step">
|
||||
<span class="step-num">{{ index + 1 }}</span>
|
||||
@@ -35,10 +31,7 @@
|
||||
</div>
|
||||
|
||||
<div class="comparison-column ai">
|
||||
<div class="column-header">
|
||||
<span class="col-icon">🤖</span>
|
||||
<span class="col-title">AI 工程师</span>
|
||||
</div>
|
||||
<div class="column-header">AI 工程师</div>
|
||||
<div class="work-flow">
|
||||
<div v-for="(step, index) in aiSteps" :key="index" class="flow-step">
|
||||
<span class="step-num">{{ index + 1 }}</span>
|
||||
@@ -59,7 +52,7 @@
|
||||
</div>
|
||||
|
||||
<div class="skill-shift">
|
||||
<div class="shift-title">📊 能力重心转移</div>
|
||||
<div class="shift-title">能力重心转移</div>
|
||||
<div class="shift-grid">
|
||||
<div v-for="item in skillShift" :key="item.from" class="shift-item">
|
||||
<div class="shift-from">
|
||||
@@ -76,159 +69,142 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-insight">
|
||||
<div class="insight-icon">💡</div>
|
||||
<div class="insight-text">
|
||||
<strong>AI 时代的核心竞争力:</strong>不是"会写代码",而是"会描述需求、会判断对错、会设计方案"。AI 是你的编程助手,但<strong>决策者永远是你</strong>。
|
||||
</div>
|
||||
<div class="info-box">
|
||||
<strong>AI 时代的核心竞争力:</strong>不是"会写代码",而是"会描述需求、会判断对错、会设计方案"。AI 是你的编程助手,但决策者永远是你。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const traditionalSteps = ref([
|
||||
const traditionalSteps = [
|
||||
'理解需求',
|
||||
'查阅文档学习语法',
|
||||
'手写代码实现',
|
||||
'调试修复 Bug',
|
||||
'优化代码性能',
|
||||
'编写测试用例'
|
||||
])
|
||||
]
|
||||
|
||||
const aiSteps = ref([
|
||||
const aiSteps = [
|
||||
'理解需求',
|
||||
'用自然语言描述给 AI',
|
||||
'审核 AI 生成的代码',
|
||||
'判断是否符合预期',
|
||||
'调整需求重新生成',
|
||||
'整合到项目中'
|
||||
])
|
||||
]
|
||||
|
||||
const skillShift = ref([
|
||||
const skillShift = [
|
||||
{ from: '语法记忆', to: '需求描述能力' },
|
||||
{ from: '手写代码速度', to: '代码审核能力' },
|
||||
{ from: '查文档能力', to: '架构设计能力' },
|
||||
{ from: '调试技巧', to: '问题定位能力' }
|
||||
])
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ai-vs-traditional-demo {
|
||||
background: linear-gradient(135deg, #fff7ed 0%, #ffedd5 100%);
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
margin: 20px 0;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 2px solid #fed7aa;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 28px;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
font-size: 0.78rem;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
|
||||
.comparison-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.comparison-column {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.column-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.col-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.col-title {
|
||||
font-size: 16px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px dashed var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.work-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
gap: 0.35rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
gap: 0.5rem;
|
||||
padding: 0.35rem 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #e2e8f0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
background: var(--vp-c-divider);
|
||||
color: var(--vp-c-text-3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.traditional .step-num {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
background: var(--vp-c-indigo-soft);
|
||||
color: var(--vp-c-indigo-1);
|
||||
}
|
||||
|
||||
.ai .step-num {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
background: var(--vp-c-green-soft);
|
||||
color: var(--vp-c-green-1);
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.column-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
gap: 0.35rem;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
@@ -238,14 +214,14 @@ const skillShift = ref([
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
font-size: 0.7rem;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 14px;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.vs-divider {
|
||||
@@ -255,115 +231,102 @@ const skillShift = ref([
|
||||
}
|
||||
|
||||
.vs-text {
|
||||
font-size: 18px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
color: #f97316;
|
||||
background: white;
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
color: var(--vp-c-brand-1);
|
||||
background: var(--vp-c-bg);
|
||||
padding: 0.35rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.skill-shift {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
margin-bottom: 24px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.shift-title {
|
||||
font-size: 16px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 16px;
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.shift-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.shift-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
gap: 0.35rem;
|
||||
padding: 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.shift-from,
|
||||
.shift-to {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 14px;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.shift-from .arrow {
|
||||
color: #ef4444;
|
||||
color: var(--vp-c-danger-1);
|
||||
}
|
||||
|
||||
.shift-to .arrow {
|
||||
color: #22c55e;
|
||||
color: var(--vp-c-green-1);
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.trend {
|
||||
font-size: 10px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.62rem;
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: 3px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.trend.down {
|
||||
background: #fee2e2;
|
||||
color: #dc2626;
|
||||
background: var(--vp-c-danger-soft);
|
||||
color: var(--vp-c-danger-1);
|
||||
}
|
||||
|
||||
.trend.up {
|
||||
background: #dcfce7;
|
||||
color: #16a34a;
|
||||
background: var(--vp-c-green-soft);
|
||||
color: var(--vp-c-green-1);
|
||||
}
|
||||
|
||||
.ai-insight {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
border-left: 4px solid #f97316;
|
||||
.info-box {
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
border-left: 3px solid var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.insight-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.insight-text {
|
||||
font-size: 14px;
|
||||
color: #475569;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.insight-text strong {
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: 640px) {
|
||||
.comparison-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.vs-divider {
|
||||
padding: 8px 0;
|
||||
padding: 0.35rem 0;
|
||||
}
|
||||
|
||||
.shift-grid {
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div class="ast-visualizer-demo">
|
||||
<h4>🌳 AST 可视化:看见代码的"骨架"</h4>
|
||||
<p class="desc">选择一个表达式,观察它的抽象语法树结构</p>
|
||||
|
||||
<div class="expr-selector">
|
||||
<button
|
||||
v-for="(ex, i) in expressions"
|
||||
:key="i"
|
||||
:class="['expr-btn', { active: selected === i }]"
|
||||
@click="selected = i"
|
||||
>
|
||||
<code>{{ ex.code }}</code>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="ast-container">
|
||||
<div class="tree-view">
|
||||
<div class="tree-title">语法树</div>
|
||||
<div class="tree-nodes">
|
||||
<ASTNode
|
||||
:node="expressions[selected].tree"
|
||||
:depth="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="explain-view">
|
||||
<div class="explain-title">解析说明</div>
|
||||
<div class="explain-list">
|
||||
<div v-for="(step, j) in expressions[selected].explains" :key="j" class="explain-item">
|
||||
<span class="explain-num">{{ j + 1 }}</span>
|
||||
<span>{{ step }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-tip">
|
||||
💡 试试 <a href="https://astexplorer.net/" target="_blank">AST Explorer</a> — 在线查看任意代码的 AST
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, h, defineComponent } from 'vue'
|
||||
|
||||
const selected = ref(0)
|
||||
|
||||
const ASTNode = defineComponent({
|
||||
name: 'ASTNode',
|
||||
props: { node: Object, depth: Number },
|
||||
setup(props) {
|
||||
return () => {
|
||||
const n = props.node
|
||||
const children = []
|
||||
|
||||
children.push(
|
||||
h('div', { class: 'node-box', style: { marginLeft: props.depth * 24 + 'px' } }, [
|
||||
h('span', { class: 'node-type' }, n.type),
|
||||
n.value ? h('span', { class: 'node-value' }, n.value) : null
|
||||
])
|
||||
)
|
||||
|
||||
if (n.children) {
|
||||
for (const child of n.children) {
|
||||
children.push(h(ASTNode, { node: child, depth: props.depth + 1 }))
|
||||
}
|
||||
}
|
||||
|
||||
return h('div', { class: 'node-wrapper' }, children)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const expressions = [
|
||||
{
|
||||
code: '1 + 2 * 3',
|
||||
tree: {
|
||||
type: 'BinaryExpression', value: '+',
|
||||
children: [
|
||||
{ type: 'NumericLiteral', value: '1' },
|
||||
{
|
||||
type: 'BinaryExpression', value: '*',
|
||||
children: [
|
||||
{ type: 'NumericLiteral', value: '2' },
|
||||
{ type: 'NumericLiteral', value: '3' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
explains: [
|
||||
'* 优先级高于 +,所以 2 * 3 先结合',
|
||||
'2 * 3 形成一个 BinaryExpression 子树',
|
||||
'1 和这个子树作为 + 的左右操作数',
|
||||
'最终 + 是根节点,体现了运算顺序'
|
||||
]
|
||||
},
|
||||
{
|
||||
code: 'let x = 10',
|
||||
tree: {
|
||||
type: 'VariableDeclaration', value: 'let',
|
||||
children: [
|
||||
{
|
||||
type: 'VariableDeclarator', value: '',
|
||||
children: [
|
||||
{ type: 'Identifier', value: 'x' },
|
||||
{ type: 'NumericLiteral', value: '10' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
explains: [
|
||||
'let 声明创建 VariableDeclaration 节点',
|
||||
'内部包含一个 VariableDeclarator(声明器)',
|
||||
'声明器左侧是标识符 x,右侧是初始值 10',
|
||||
'树结构清晰表达了"把 10 赋给 x"的语义'
|
||||
]
|
||||
},
|
||||
{
|
||||
code: 'add(a, b)',
|
||||
tree: {
|
||||
type: 'CallExpression', value: '',
|
||||
children: [
|
||||
{ type: 'Identifier', value: 'add' },
|
||||
{
|
||||
type: 'Arguments', value: '',
|
||||
children: [
|
||||
{ type: 'Identifier', value: 'a' },
|
||||
{ type: 'Identifier', value: 'b' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
explains: [
|
||||
'函数调用创建 CallExpression 节点',
|
||||
'被调用的函数名 add 是 Identifier',
|
||||
'参数列表 (a, b) 形成 Arguments 节点',
|
||||
'每个参数都是独立的 Identifier 子节点'
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ast-visualizer-demo {
|
||||
padding: 20px; border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px; margin: 16px 0; background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
.expr-selector { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.expr-btn {
|
||||
padding: 6px 14px; border-radius: 6px; border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg); cursor: pointer; font-size: 13px; transition: all 0.2s;
|
||||
}
|
||||
.expr-btn.active { background: var(--vp-c-brand-1); color: #fff; border-color: var(--vp-c-brand-1); }
|
||||
.expr-btn code { font-size: 13px; }
|
||||
.ast-container { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.tree-view, .explain-view {
|
||||
border: 1px solid var(--vp-c-divider); border-radius: 8px;
|
||||
background: var(--vp-c-bg); overflow: hidden;
|
||||
}
|
||||
.tree-title, .explain-title {
|
||||
padding: 8px 12px; font-weight: 600; font-size: 13px;
|
||||
background: var(--vp-c-bg-soft); border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
.tree-nodes { padding: 12px; }
|
||||
:deep(.node-box) {
|
||||
display: flex; align-items: center; gap: 6px; padding: 3px 0;
|
||||
}
|
||||
:deep(.node-type) {
|
||||
padding: 2px 8px; background: #dbeafe; color: #1e40af;
|
||||
border-radius: 4px; font-size: 12px; font-weight: 500;
|
||||
}
|
||||
:deep(.node-value) {
|
||||
padding: 2px 6px; background: #fef3c7; color: #92400e;
|
||||
border-radius: 4px; font-size: 12px; font-family: 'Fira Code', monospace;
|
||||
}
|
||||
.explain-list { padding: 10px 12px; }
|
||||
.explain-item { display: flex; align-items: flex-start; gap: 8px; padding: 4px 0; font-size: 13px; }
|
||||
.explain-num {
|
||||
width: 20px; height: 20px; border-radius: 50%; background: var(--vp-c-brand-1);
|
||||
color: #fff; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 11px; font-weight: 600; flex-shrink: 0;
|
||||
}
|
||||
.tool-tip {
|
||||
padding: 8px 12px; font-size: 12px; color: var(--vp-c-text-2);
|
||||
border-top: 1px solid var(--vp-c-divider); background: var(--vp-c-bg-soft);
|
||||
}
|
||||
.tool-tip a { color: var(--vp-c-brand-1); }
|
||||
@media (max-width: 640px) { .ast-container { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
+481
@@ -0,0 +1,481 @@
|
||||
<template>
|
||||
<div class="addressing-mode-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">寻址方式</span>
|
||||
<span class="subtitle">如何找到操作数的位置</span>
|
||||
</div>
|
||||
|
||||
<div class="mode-selector">
|
||||
<button
|
||||
v-for="mode in addressingModes"
|
||||
:key="mode.name"
|
||||
:class="['mode-btn', { active: selectedMode === mode.name }]"
|
||||
@click="selectMode(mode)"
|
||||
>
|
||||
{{ mode.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mode-details" v-if="selectedModeData">
|
||||
<div class="detail-header">
|
||||
<span class="mode-name">{{ selectedModeData.name }}</span>
|
||||
<span class="mode-english">{{ selectedModeData.english }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="detail-section">
|
||||
<div class="section-title">定义</div>
|
||||
<div class="section-content">{{ selectedModeData.definition }}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">指令格式</div>
|
||||
<div class="instruction-example">
|
||||
<code>{{ selectedModeData.format }}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">示例</div>
|
||||
<div class="example-code">
|
||||
<div class="code-line">{{ selectedModeData.example.assembly }}</div>
|
||||
<div class="code-desc">{{ selectedModeData.example.description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">执行过程</div>
|
||||
<div class="execution-flow">
|
||||
<div v-for="(step, i) in selectedModeData.steps" :key="i" class="flow-step">
|
||||
<span class="step-num">{{ i + 1 }}</span>
|
||||
<span class="step-text">{{ step }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">特点</div>
|
||||
<div class="characteristics">
|
||||
<div class="char-item" :class="selectedModeData.fast ? 'fast' : 'slow'">
|
||||
<span class="char-label">速度</span>
|
||||
<span class="char-value">{{ selectedModeData.fast ? '快' : '慢' }}</span>
|
||||
</div>
|
||||
<div class="char-item">
|
||||
<span class="char-label">灵活性</span>
|
||||
<span class="char-value">{{ selectedModeData.flexibility }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">寻址方式对比</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>寻址方式</th>
|
||||
<th>格式</th>
|
||||
<th>速度</th>
|
||||
<th>用途</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="mode in addressingModes" :key="mode.name">
|
||||
<td>{{ mode.name }}</td>
|
||||
<td><code>{{ mode.format }}</code></td>
|
||||
<td :class="mode.fast ? 'fast' : 'slow'">{{ mode.fast ? '最快' : '较快' }}</td>
|
||||
<td>{{ mode.usage }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selectedMode = ref('立即数寻址')
|
||||
const selectedModeData = ref(null)
|
||||
|
||||
const addressingModes = ref([
|
||||
{
|
||||
name: '立即数寻址',
|
||||
english: 'Immediate Addressing',
|
||||
definition: '操作数直接包含在指令中,作为指令的一部分立即可用',
|
||||
format: 'MOV R1, #100',
|
||||
usage: '常数赋值、初始化',
|
||||
fast: true,
|
||||
flexibility: '低',
|
||||
example: {
|
||||
assembly: 'MOV R1, #100 ; R1 = 100',
|
||||
description: '立即数 100 直接存在于指令中,无需访问任何寄存器或内存'
|
||||
},
|
||||
steps: [
|
||||
'CPU 从指令中直接读取立即数 100',
|
||||
'将立即数写入目标寄存器 R1',
|
||||
'执行完成,无需额外内存访问'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '寄存器寻址',
|
||||
english: 'Register Addressing',
|
||||
definition: '操作数位于 CPU 内部的寄存器中',
|
||||
format: 'MOV R1, R2',
|
||||
usage: '寄存器间数据传送',
|
||||
fast: true,
|
||||
flexibility: '中',
|
||||
example: {
|
||||
assembly: 'MOV R1, R2 ; R1 = R2',
|
||||
description: '从源寄存器 R2 读取数据,存入目标寄存器 R1'
|
||||
},
|
||||
steps: [
|
||||
'CPU 从寄存器组中读取 R2 的值',
|
||||
'将值写入目标寄存器 R1',
|
||||
'执行完成,无需访问内存'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '直接寻址',
|
||||
english: 'Direct Addressing',
|
||||
definition: '指令中直接给出操作数的内存地址',
|
||||
format: 'MOV R1, [100]',
|
||||
usage: '访问全局变量',
|
||||
fast: false,
|
||||
flexibility: '高',
|
||||
example: {
|
||||
assembly: 'MOV R1, [0x1000] ; R1 = M[0x1000]',
|
||||
description: '指令中包含内存地址 0x1000,从该地址读取数据'
|
||||
},
|
||||
steps: [
|
||||
'CPU 从指令中解析出地址 0x1000',
|
||||
'将地址送入 MAR(内存地址寄存器)',
|
||||
'访问内存,从地址 0x1000 读取数据到 MDR',
|
||||
'将数据从 MDR 写入目标寄存器 R1'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '间接寻址',
|
||||
english: 'Indirect Addressing',
|
||||
definition: '指令中给出寄存器,寄存器中存放操作数的地址',
|
||||
format: 'MOV R1, [R2]',
|
||||
usage: '指针操作、数组遍历',
|
||||
fast: false,
|
||||
flexibility: '高',
|
||||
example: {
|
||||
assembly: 'MOV R1, [R2] ; R1 = M[R2]',
|
||||
description: 'R2 中存放地址,从该地址读取数据'
|
||||
},
|
||||
steps: [
|
||||
'CPU 从寄存器 R2 中读取地址',
|
||||
'将地址送入 MAR',
|
||||
'访问内存,读取数据到 MDR',
|
||||
'将数据写入目标寄存器 R1'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '变址寻址',
|
||||
english: 'Indexed Addressing',
|
||||
definition: '指令中给出基地址加上变址寄存器的值作为操作数地址',
|
||||
format: 'MOV R1, [R2 + R3]',
|
||||
usage: '数组访问、循环',
|
||||
fast: false,
|
||||
flexibility: '高',
|
||||
example: {
|
||||
assembly: 'MOV R1, [R2 + R3] ; R1 = M[R2+R3]',
|
||||
description: '有效地址 = R2 + R3,用于数组元素访问'
|
||||
},
|
||||
steps: [
|
||||
'CPU 读取基地址寄存器 R2 的值',
|
||||
'CPU 读取变址寄存器 R3 的值',
|
||||
'ALU 计算有效地址 = R2 + R3',
|
||||
'将有效地址送入 MAR',
|
||||
'访问内存,读取数据到 MDR',
|
||||
'将数据写入目标寄存器 R1'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '基址寻址',
|
||||
english: 'Based Addressing',
|
||||
definition: '指令中给出基址寄存器加上偏移量作为操作数地址',
|
||||
format: 'MOV R1, [R2 + 100]',
|
||||
usage: '结构体访问、函数参数',
|
||||
fast: false,
|
||||
flexibility: '高',
|
||||
example: {
|
||||
assembly: 'MOV R1, [RBP - 8] ; 访问栈帧中的局部变量',
|
||||
description: '有效地址 = RBP - 8,用于访问函数栈帧中的变量'
|
||||
},
|
||||
steps: [
|
||||
'CPU 读取基址寄存器 RBP 的值',
|
||||
'计算有效地址 = RBP - 8',
|
||||
'将有效地址送入 MAR',
|
||||
'访问内存,读取数据'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '相对寻址',
|
||||
english: 'Relative Addressing',
|
||||
definition: '操作数地址是当前指令地址加上一个偏移量',
|
||||
format: 'JMP LABEL',
|
||||
usage: '循环、条件跳转',
|
||||
fast: true,
|
||||
flexibility: '高',
|
||||
example: {
|
||||
assembly: 'JMP LOOP ; 跳转到 LOOP 标签处',
|
||||
description: '跳转目标地址 = PC + 偏移量,用于循环和分支'
|
||||
},
|
||||
steps: [
|
||||
'CPU 计算跳转目标地址 = 当前 PC + 偏移量',
|
||||
'将目标地址写入 PC',
|
||||
'下一条指令从新地址开始执行'
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
const selectMode = (mode) => {
|
||||
selectedMode.value = mode.name
|
||||
selectedModeData.value = mode
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.addressing-mode-demo {
|
||||
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.mode-selector {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mode-btn {
|
||||
padding: 8px 14px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.mode-btn.active {
|
||||
border-color: #f59e0b;
|
||||
background: #fef3c7;
|
||||
}
|
||||
|
||||
.mode-details {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 2px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.mode-name {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.mode-english {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
margin-bottom: 6px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
font-size: 14px;
|
||||
color: #1e293b;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.instruction-example {
|
||||
background: #f1f5f9;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.instruction-example code {
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.example-code {
|
||||
background: #f1f5f9;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.code-line {
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
color: #0369a1;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.code-desc {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.execution-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
background: #f8fafc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #f59e0b;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.characteristics {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.char-item {
|
||||
padding: 8px 16px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.char-label {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.char-value {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.char-item.fast .char-value {
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.char-item.slow .char-value {
|
||||
color: #ea580c;
|
||||
}
|
||||
|
||||
.comparison-table {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.comparison-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.comparison-table th,
|
||||
.comparison-table td {
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.comparison-table th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.comparison-table td {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.comparison-table code {
|
||||
font-size: 11px;
|
||||
background: #f1f5f9;
|
||||
padding: 2px 6px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.fast {
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.slow {
|
||||
color: #ea580c;
|
||||
}
|
||||
</style>
|
||||
+30
-85
@@ -1,38 +1,27 @@
|
||||
<template>
|
||||
<div class="boot-demo">
|
||||
<div class="demo-label">操作系统启动流程 ── 点击逐步启动</div>
|
||||
|
||||
<div class="demo-title">操作系统启动流程</div>
|
||||
<div class="timeline">
|
||||
<div
|
||||
v-for="(step, index) in steps"
|
||||
:key="step.name"
|
||||
class="timeline-item"
|
||||
:class="{ active: currentStep >= index }"
|
||||
@click="currentStep = index"
|
||||
>
|
||||
<div class="timeline-marker">
|
||||
<span class="marker-dot" :class="{ filled: currentStep >= index }">{{ index + 1 }}</span>
|
||||
<span v-if="index < steps.length - 1" class="marker-line" :class="{ filled: currentStep >= index }"></span>
|
||||
<div v-for="(step, i) in steps" :key="step.name" class="timeline-item">
|
||||
<div class="marker">
|
||||
<span class="dot">{{ i + 1 }}</span>
|
||||
<span v-if="i < steps.length - 1" class="line"></span>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="content">
|
||||
<div class="step-name">{{ step.name }}</div>
|
||||
<div class="step-desc">{{ step.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tap-hint">👆 点击查看各步骤</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const currentStep = ref(0)
|
||||
const steps = [
|
||||
{ name: '引导程序', desc: '从硬盘读取 bootloader' },
|
||||
{ name: '内核加载', desc: '加载操作系统内核' },
|
||||
{ name: '系统服务', desc: '启动各种后台服务' },
|
||||
{ name: '桌面环境', desc: '显示登录界面和桌面' }
|
||||
{ name: '引导程序 Bootloader', desc: '从硬盘启动扇区读取引导代码,找到操作系统内核的位置' },
|
||||
{ name: '内核加载 Kernel', desc: '将内核载入内存,接管 CPU、内存、设备的控制权' },
|
||||
{ name: '系统服务启动', desc: '启动网络、安全、音频等后台服务(Windows 服务 / Linux systemd)' },
|
||||
{ name: '桌面环境显示', desc: '加载显卡驱动 → 启动显示服务 → 渲染桌面背景和图标' }
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -43,95 +32,51 @@ const steps = [
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
.demo-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
padding: 0.3rem 0;
|
||||
opacity: 0.4;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.timeline-item.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
.timeline { display: flex; flex-direction: column; }
|
||||
.timeline-item { display: flex; gap: 0.7rem; }
|
||||
.marker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 1.5rem;
|
||||
width: 1.6rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.marker-dot {
|
||||
.dot {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 50%;
|
||||
background: var(--vp-c-bg);
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-3);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.marker-dot.filled {
|
||||
background: var(--vp-c-brand);
|
||||
border-color: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.marker-line {
|
||||
.line {
|
||||
flex: 1;
|
||||
width: 2px;
|
||||
background: var(--vp-c-divider);
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
|
||||
.marker-line.filled {
|
||||
background: var(--vp-c-brand);
|
||||
opacity: 0.3;
|
||||
min-height: 1.2rem;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
flex: 1;
|
||||
padding-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.content { flex: 1; padding-bottom: 0.8rem; }
|
||||
.step-name {
|
||||
font-size: 0.78rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 0.7rem;
|
||||
font-size: 0.68rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
.tap-hint {
|
||||
text-align: center;
|
||||
font-size: 0.72rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
+55
-95
@@ -1,44 +1,41 @@
|
||||
<template>
|
||||
<div class="browser-demo">
|
||||
<div class="demo-label">浏览器架构 ── 点击查看各模块</div>
|
||||
|
||||
<div class="browser-schema">
|
||||
<div class="browser-layers">
|
||||
<div
|
||||
v-for="layer in layers"
|
||||
:key="layer.name"
|
||||
class="layer"
|
||||
:class="{ active: activeLayer === layer.name }"
|
||||
@click="activeLayer = activeLayer === layer.name ? '' : layer.name"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="layer-name">{{ layer.name }}</span>
|
||||
<span class="layer-icon">{{ layer.icon }}</span>
|
||||
</div>
|
||||
<div v-if="activeLayer === layer.name" class="layer-detail">
|
||||
<div class="detail-text">{{ layer.desc }}</div>
|
||||
<div class="detail-examples">
|
||||
<span v-for="ex in layer.examples" :key="ex" class="example-tag">{{ ex }}</span>
|
||||
<div class="demo-title">浏览器架构 ── 点击模块查看详情</div>
|
||||
<div class="arch">
|
||||
<div
|
||||
v-for="mod in modules"
|
||||
:key="mod.name"
|
||||
class="mod-card"
|
||||
:class="{ active: active === mod.name }"
|
||||
@click="active = active === mod.name ? '' : mod.name"
|
||||
>
|
||||
<div class="mod-header">
|
||||
<span class="mod-icon">{{ mod.icon }}</span>
|
||||
<span class="mod-name">{{ mod.name }}</span>
|
||||
</div>
|
||||
<transition name="expand">
|
||||
<div v-if="active === mod.name" class="mod-detail">
|
||||
<div class="mod-desc">{{ mod.desc }}</div>
|
||||
<div class="mod-tags">
|
||||
<span v-for="tag in mod.tags" :key="tag" class="tag">{{ tag }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tap-hint">👆 点击查看各模块详情</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const activeLayer = ref('')
|
||||
const layers = [
|
||||
{ name: '用户界面', icon: '🎨', desc: '你看到的地址栏、标签页、书签等', examples: ['地址栏', '标签页', '书签栏', '工具栏'] },
|
||||
{ name: '浏览器引擎', icon: '⚙️', desc: '协调 UI 和渲染引擎的桥梁', examples: ['Chrome: Blink', 'Firefox: Gecko', 'Safari: WebKit'] },
|
||||
{ name: '渲染引擎', icon: '📄', desc: '解析 HTML/CSS,把网页显示出来', examples: ['Blink', 'Gecko', 'WebKit'] },
|
||||
{ name: 'JavaScript 引擎', icon: '⚡', desc: '执行页面中的 JavaScript 代码', examples: ['V8', 'SpiderMonkey', 'JavaScriptCore'] },
|
||||
{ name: '网络模块', icon: '🌐', desc: '发送 HTTP/HTTPS 请求', examples: ['HTTP', 'HTTP/2', 'HTTP/3', 'WebSocket'] },
|
||||
{ name: '数据存储', icon: '💾', desc: '保存 Cookie、缓存等数据', examples: ['Cookie', 'LocalStorage', 'Cache'] }
|
||||
const active = ref('')
|
||||
const modules = [
|
||||
{ icon: '🎨', name: '用户界面', desc: '你直接看到和操作的部分:地址栏、标签页、书签、前进/后退按钮', tags: ['地址栏', '标签页', '书签栏'] },
|
||||
{ icon: '🔗', name: '浏览器引擎', desc: '连接用户界面和渲染引擎的桥梁,负责协调两者之间的通信', tags: ['Blink', 'Gecko', 'WebKit'] },
|
||||
{ icon: '📄', name: '渲染引擎', desc: '解析 HTML 和 CSS,将代码转换成你看到的网页画面', tags: ['HTML 解析', 'CSS 计算', '布局绘制'] },
|
||||
{ icon: '⚡', name: 'JavaScript 引擎', desc: '执行网页中的 JavaScript 代码,实现页面的动态交互效果', tags: ['V8', 'SpiderMonkey', 'JavaScriptCore'] },
|
||||
{ icon: '🌐', name: '网络模块', desc: '负责发送 HTTP 请求、接收服务器响应,是浏览器与外界通信的通道', tags: ['HTTP/2', 'HTTP/3', 'WebSocket'] },
|
||||
{ icon: '💾', name: '数据存储', desc: '在本地保存网站数据,让你下次访问更快、不用重复登录', tags: ['Cookie', 'LocalStorage', 'Cache'] }
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -49,82 +46,45 @@ const layers = [
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
.demo-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.browser-layers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.arch {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.layer {
|
||||
.mod-card {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem 0.7rem;
|
||||
transition: all 0.3s;
|
||||
padding: 0.5rem 0.6rem;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.layer.active {
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.layer-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.layer-name {
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.layer-icon {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.layer-detail {
|
||||
margin-top: 0.5rem;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.detail-text {
|
||||
font-size: 0.72rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.detail-examples {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.example-tag {
|
||||
font-size: 0.65rem;
|
||||
padding: 0.15rem 0.4rem;
|
||||
.mod-card.active { border-color: var(--vp-c-brand); }
|
||||
.mod-header { display: flex; align-items: center; gap: 0.4rem; }
|
||||
.mod-icon { font-size: 1rem; }
|
||||
.mod-name { font-size: 0.72rem; font-weight: 600; color: var(--vp-c-text-1); }
|
||||
.mod-detail { margin-top: 0.4rem; padding-top: 0.4rem; border-top: 1px solid var(--vp-c-divider); }
|
||||
.mod-desc { font-size: 0.65rem; color: var(--vp-c-text-3); line-height: 1.5; }
|
||||
.mod-tags { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-top: 0.35rem; }
|
||||
.tag {
|
||||
font-size: 0.6rem;
|
||||
padding: 0.1rem 0.35rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 3px;
|
||||
color: var(--vp-c-text-2);
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.tap-hint {
|
||||
text-align: center;
|
||||
font-size: 0.72rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.75rem;
|
||||
.expand-enter-active, .expand-leave-active { transition: all 0.2s ease; }
|
||||
.expand-enter-from, .expand-leave-to { opacity: 0; max-height: 0; }
|
||||
.expand-enter-to, .expand-leave-from { opacity: 1; max-height: 8rem; }
|
||||
@media (max-width: 480px) {
|
||||
.arch { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
<template>
|
||||
<div class="bus-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">计算机总线系统</span>
|
||||
<span class="subtitle">地址总线、数据总线、控制总线</span>
|
||||
</div>
|
||||
|
||||
<div class="bus-architecture">
|
||||
<div class="cpu-box">
|
||||
<div class="component-label">CPU</div>
|
||||
<div class="cpu-internal">
|
||||
<div class="cu">控制单元</div>
|
||||
<div class="alu">运算单元</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bus-section">
|
||||
<div class="bus-line address-bus" :class="{ active: activeBus === 'address' }">
|
||||
<span class="bus-name">地址总线</span>
|
||||
<span class="bus-width">32位</span>
|
||||
<div class="bus-data" v-if="activeBus === 'address'">{{ addressValue }}</div>
|
||||
</div>
|
||||
<div class="bus-line data-bus" :class="{ active: activeBus === 'data' }">
|
||||
<span class="bus-name">数据总线</span>
|
||||
<span class="bus-width">64位</span>
|
||||
<div class="bus-data" v-if="activeBus === 'data'">{{ dataValue }}</div>
|
||||
</div>
|
||||
<div class="bus-line ctrl-bus" :class="{ active: activeBus === 'control' }">
|
||||
<span class="bus-name">控制总线</span>
|
||||
<span class="bus-width">控制信号</span>
|
||||
<div class="bus-data" v-if="activeBus === 'control'">{{ ctrlSignal }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="memory-box">
|
||||
<div class="component-label">主存</div>
|
||||
<div class="mem-cells">
|
||||
<div v-for="i in 8" :key="i" class="mem-cell" :class="{ active: activeMem === i-1 }">
|
||||
{{ fmtAddr(i-1) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-panel">
|
||||
<div class="operation-group">
|
||||
<button class="btn" @click="simulateRead">读取内存</button>
|
||||
<button class="btn" @click="simulateWrite">写入内存</button>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input v-model.number="addressInput" type="number" placeholder="地址(0-7)" min="0" max="7" class="addr-input" />
|
||||
<input v-model.number="dataInput" type="number" placeholder="数据" class="data-input" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="operation-log">
|
||||
<div class="log-title">操作流程</div>
|
||||
<div class="log-steps">
|
||||
<div v-for="(step, i) in logSteps" :key="i" :class="['log-step', step.active ? 'active' : '']">
|
||||
<span class="step-num">{{ i + 1 }}</span>
|
||||
<span class="step-text">{{ step.text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bus-explanation">
|
||||
<div class="exp-title">总线知识点</div>
|
||||
<div class="exp-grid">
|
||||
<div class="exp-item">
|
||||
<div class="exp-label">地址总线</div>
|
||||
<div class="exp-desc">CPU 发送内存地址,单向传输</div>
|
||||
</div>
|
||||
<div class="exp-item">
|
||||
<div class="exp-label">数据总线</div>
|
||||
<div class="exp-desc">传输实际数据,双向传输</div>
|
||||
</div>
|
||||
<div class="exp-item">
|
||||
<div class="exp-label">控制总线</div>
|
||||
<div class="exp-desc">传输读/写等控制信号</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeBus = ref('')
|
||||
const activeMem = ref(-1)
|
||||
const addressValue = ref('')
|
||||
const dataValue = ref('')
|
||||
const ctrlSignal = ref('')
|
||||
|
||||
const addressInput = ref(0)
|
||||
const dataInput = ref(100)
|
||||
|
||||
const logSteps = ref([])
|
||||
|
||||
const simulateRead = async () => {
|
||||
logSteps.value = []
|
||||
addressValue.value = addressInput.value.toString(2).padStart(32, '0').slice(-8)
|
||||
|
||||
activeBus.value = 'address'
|
||||
logSteps.value.push({ text: `CPU 通过地址总线发送地址 ${addressInput.value}`, active: true })
|
||||
await wait(1000)
|
||||
|
||||
activeBus.value = 'control'
|
||||
ctrlSignal.value = 'READ'
|
||||
logSteps.value.push({ text: '控制总线发送 READ 信号', active: true })
|
||||
await wait(1000)
|
||||
|
||||
activeBus.value = 'data'
|
||||
activeMem.value = addressInput.value
|
||||
dataValue.value = Math.floor(Math.random() * 256)
|
||||
logSteps.value.push({ text: `主存通过数据总线返回数据 ${dataValue.value}`, active: true })
|
||||
await wait(1000)
|
||||
|
||||
logSteps.value.push({ text: 'CPU 接收数据到寄存器', active: true })
|
||||
}
|
||||
|
||||
const simulateWrite = async () => {
|
||||
logSteps.value = []
|
||||
addressValue.value = addressInput.value.toString(2).padStart(32, '0').slice(-8)
|
||||
dataValue.value = dataInput.value.toString(2).padStart(64, '0').slice(-8)
|
||||
|
||||
activeBus.value = 'address'
|
||||
logSteps.value.push({ text: `CPU 通过地址总线发送地址 ${addressInput.value}`, active: true })
|
||||
await wait(1000)
|
||||
|
||||
activeBus.value = 'data'
|
||||
logSteps.value.push({ text: `CPU 通过数据总线发送数据 ${dataInput.value}`, active: true })
|
||||
await wait(1000)
|
||||
|
||||
activeBus.value = 'control'
|
||||
ctrlSignal.value = 'WRITE'
|
||||
logSteps.value.push({ text: '控制总线发送 WRITE 信号', active: true })
|
||||
await wait(1000)
|
||||
|
||||
activeMem.value = addressInput.value
|
||||
logSteps.value.push({ text: `数据写入主存地址 ${addressInput.value}`, active: true })
|
||||
}
|
||||
|
||||
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
const fmtAddr = (addr) => '0x' + addr.toString(16).toUpperCase()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bus-demo {
|
||||
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.bus-architecture {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.cpu-box, .memory-box {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.component-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.cpu-internal {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cu, .alu {
|
||||
padding: 8px 12px;
|
||||
background: #e0f2fe;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.bus-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.bus-line {
|
||||
background: #f1f5f9;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.bus-line.active {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.address-bus.active { background: #fef3c7; border-left: 3px solid #f59e0b; }
|
||||
.data-bus.active { background: #dbeafe; border-left: 3px solid #3b82f6; }
|
||||
.ctrl-bus.active { background: #fce7f3; border-left: 3px solid #ec4899; }
|
||||
|
||||
.bus-name {
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.bus-width {
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.bus-data {
|
||||
margin-left: auto;
|
||||
font-family: monospace;
|
||||
font-size: 10px;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.memory-box {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.mem-cells {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mem-cell {
|
||||
padding: 4px;
|
||||
background: #f8fafc;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mem-cell.active {
|
||||
background: #dbeafe;
|
||||
border: 1px solid #3b82f6;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.operation-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.addr-input, .data-input {
|
||||
width: 80px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.operation-log {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.log-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.log-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.log-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
background: #f8fafc;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.log-step.active {
|
||||
background: #dbeafe;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #e2e8f0;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.step-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.bus-explanation {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.exp-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.exp-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.exp-item {
|
||||
padding: 8px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.exp-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.exp-desc {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div class="cisc-risc-demo">
|
||||
<h4>⚔️ 两种设计哲学:CISC vs RISC</h4>
|
||||
<p class="desc">点击对比维度,看两种指令集架构的核心差异</p>
|
||||
|
||||
<div class="arch-toggle">
|
||||
<button
|
||||
:class="['toggle-btn', { active: view === 'cisc' }]"
|
||||
@click="view = 'cisc'"
|
||||
>
|
||||
CISC (x86)
|
||||
</button>
|
||||
<button
|
||||
:class="['toggle-btn', { active: view === 'both' }]"
|
||||
@click="view = 'both'"
|
||||
>
|
||||
对比
|
||||
</button>
|
||||
<button
|
||||
:class="['toggle-btn', { active: view === 'risc' }]"
|
||||
@click="view = 'risc'"
|
||||
>
|
||||
RISC (ARM)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="view === 'both'" class="comparison-grid">
|
||||
<div v-for="dim in dimensions" :key="dim.label" class="dim-row">
|
||||
<div class="dim-cisc">{{ dim.cisc }}</div>
|
||||
<div class="dim-label">{{ dim.label }}</div>
|
||||
<div class="dim-risc">{{ dim.risc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="arch-detail">
|
||||
<div class="detail-card">
|
||||
<div class="card-header" :class="view">
|
||||
<span class="card-title">{{ archData[view].name }}</span>
|
||||
<span class="card-full">{{ archData[view].full }}</span>
|
||||
</div>
|
||||
<div class="card-philosophy">
|
||||
<span class="phi-label">设计哲学:</span>
|
||||
<span>{{ archData[view].philosophy }}</span>
|
||||
</div>
|
||||
<div class="card-analogy">
|
||||
<span class="ana-label">类比:</span>
|
||||
<span>{{ archData[view].analogy }}</span>
|
||||
</div>
|
||||
<div class="card-example">
|
||||
<div class="example-title">{{ archData[view].exampleTitle }}</div>
|
||||
<pre class="example-code">{{ archData[view].example }}</pre>
|
||||
<div class="example-note">{{ archData[view].exampleNote }}</div>
|
||||
</div>
|
||||
<div class="card-products">
|
||||
<span class="prod-label">代表产品:</span>
|
||||
<span v-for="p in archData[view].products" :key="p" class="prod-tag">{{ p }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="real-world">
|
||||
<div class="rw-title">🌍 现实中的选择</div>
|
||||
<div class="rw-items">
|
||||
<div class="rw-item">
|
||||
<span class="rw-device">💻 你的电脑</span>
|
||||
<span class="rw-arch">x86 (CISC)</span>
|
||||
<span class="rw-why">兼容几十年的软件生态</span>
|
||||
</div>
|
||||
<div class="rw-item">
|
||||
<span class="rw-device">📱 你的手机</span>
|
||||
<span class="rw-arch">ARM (RISC)</span>
|
||||
<span class="rw-why">低功耗,电池续航更久</span>
|
||||
</div>
|
||||
<div class="rw-item">
|
||||
<span class="rw-device">🍎 Apple Silicon</span>
|
||||
<span class="rw-arch">ARM (RISC)</span>
|
||||
<span class="rw-why">高性能低功耗,颠覆了笔记本市场</span>
|
||||
</div>
|
||||
<div class="rw-item">
|
||||
<span class="rw-device">🔬 RISC-V 开发板</span>
|
||||
<span class="rw-arch">RISC-V (RISC)</span>
|
||||
<span class="rw-why">开源免费,IoT 和教育领域崛起</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const view = ref('both')
|
||||
|
||||
const dimensions = [
|
||||
{ label: '指令数量', cisc: '上千条复杂指令', risc: '几十到几百条精简指令' },
|
||||
{ label: '单条指令', cisc: '一条能做很多事', risc: '一条只做一件事' },
|
||||
{ label: '指令长度', cisc: '变长(1-15字节)', risc: '定长(通常4字节)' },
|
||||
{ label: '执行速度', cisc: '复杂指令多周期', risc: '大多数单周期完成' },
|
||||
{ label: '功耗', cisc: '较高', risc: '较低' },
|
||||
{ label: '流水线', cisc: '难优化(指令长度不一)', risc: '易优化(指令整齐)' },
|
||||
{ label: '编译器负担', cisc: '轻(硬件做更多)', risc: '重(软件做更多优化)' }
|
||||
]
|
||||
|
||||
const archData = {
|
||||
cisc: {
|
||||
name: 'CISC',
|
||||
full: 'Complex Instruction Set Computer',
|
||||
philosophy: '让硬件尽可能强大,一条指令完成复杂操作,减轻编译器负担',
|
||||
analogy: '像一把瑞士军刀——功能多,但每个功能不一定最好用',
|
||||
exampleTitle: '用一条指令完成「内存加法」',
|
||||
example: 'ADD [0x1000], R1\n; 一条指令完成:读内存 → 加法 → 写回内存\n; CPU 内部拆成多个微操作执行',
|
||||
exampleNote: 'CISC 允许指令直接操作内存,一条指令背后可能是 5-6 个微操作',
|
||||
products: ['Intel Core', 'AMD Ryzen', 'x86 服务器']
|
||||
},
|
||||
risc: {
|
||||
name: 'RISC',
|
||||
full: 'Reduced Instruction Set Computer',
|
||||
philosophy: '让每条指令尽可能简单快速,复杂操作由多条简单指令组合完成',
|
||||
analogy: '像一套专业工具——每个工具只做一件事,但做得又快又好',
|
||||
exampleTitle: '用三条指令完成同样的「内存加法」',
|
||||
example: 'LOAD R2, [0x1000] ; 第1步:从内存读数据到寄存器\nADD R2, R2, R1 ; 第2步:寄存器之间做加法\nSTORE R2, [0x1000] ; 第3步:把结果写回内存',
|
||||
exampleNote: 'RISC 要求数据先加载到寄存器,运算只在寄存器间进行,结果再存回内存',
|
||||
products: ['Apple M 系列', '高通骁龙', 'AWS Graviton', 'RISC-V']
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cisc-risc-demo {
|
||||
padding: 20px; border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px; margin: 16px 0; background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
|
||||
.arch-toggle { display: flex; gap: 4px; margin-bottom: 16px; background: var(--vp-c-bg); border-radius: 8px; padding: 4px; }
|
||||
.toggle-btn {
|
||||
flex: 1; padding: 8px; border: none; border-radius: 6px;
|
||||
background: transparent; cursor: pointer; font-size: 13px; font-weight: 600; transition: all 0.2s;
|
||||
}
|
||||
.toggle-btn.active { background: var(--vp-c-brand-1); color: #fff; }
|
||||
|
||||
.comparison-grid { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
|
||||
.dim-row { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: center; }
|
||||
.dim-cisc, .dim-risc {
|
||||
padding: 8px 12px; border-radius: 6px; font-size: 12px;
|
||||
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
.dim-cisc { text-align: right; }
|
||||
.dim-label {
|
||||
padding: 4px 10px; background: var(--vp-c-brand-1); color: #fff;
|
||||
border-radius: 12px; font-size: 11px; font-weight: 600; white-space: nowrap;
|
||||
}
|
||||
|
||||
.arch-detail { margin-bottom: 16px; }
|
||||
.detail-card { border: 1px solid var(--vp-c-divider); border-radius: 8px; background: var(--vp-c-bg); overflow: hidden; }
|
||||
.card-header { padding: 10px 14px; display: flex; align-items: center; gap: 10px; }
|
||||
.card-header.cisc { background: #dbeafe; }
|
||||
.card-header.risc { background: #dcfce7; }
|
||||
.card-title { font-size: 16px; font-weight: 700; }
|
||||
.card-full { font-size: 12px; color: var(--vp-c-text-3); }
|
||||
.card-philosophy, .card-analogy { padding: 8px 14px; font-size: 13px; border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.phi-label, .ana-label { font-weight: 600; font-size: 12px; color: var(--vp-c-text-3); margin-right: 6px; }
|
||||
.card-example { padding: 12px 14px; border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.example-title { font-size: 12px; font-weight: 600; margin-bottom: 6px; }
|
||||
.example-code { padding: 8px 10px; margin: 0; font-size: 12px; line-height: 1.5; background: var(--vp-c-bg-soft); border-radius: 4px; white-space: pre-wrap; }
|
||||
.example-note { font-size: 11px; color: var(--vp-c-text-3); margin-top: 6px; }
|
||||
.card-products { padding: 10px 14px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
||||
.prod-label { font-size: 12px; color: var(--vp-c-text-3); font-weight: 600; }
|
||||
.prod-tag { font-size: 11px; padding: 2px 8px; background: var(--vp-c-brand-soft); color: var(--vp-c-brand-1); border-radius: 4px; }
|
||||
|
||||
.real-world { padding: 12px 14px; background: var(--vp-c-brand-soft); border-radius: 8px; }
|
||||
.rw-title { font-weight: 600; font-size: 13px; margin-bottom: 8px; }
|
||||
.rw-items { display: flex; flex-direction: column; gap: 6px; }
|
||||
.rw-item { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 6px 8px; background: var(--vp-c-bg); border-radius: 6px; }
|
||||
.rw-device { font-weight: 600; min-width: 110px; }
|
||||
.rw-arch { padding: 2px 8px; background: var(--vp-c-brand-soft); border-radius: 4px; font-weight: 500; white-space: nowrap; }
|
||||
.rw-why { color: var(--vp-c-text-2); }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.dim-row { grid-template-columns: 1fr; gap: 4px; }
|
||||
.dim-cisc { text-align: left; }
|
||||
.dim-label { justify-self: start; }
|
||||
.rw-item { flex-wrap: wrap; }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,544 @@
|
||||
<template>
|
||||
<div class="cache-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">缓存 (Cache) 原理</span>
|
||||
<span class="subtitle">CPU 与内存之间的"桥梁"</span>
|
||||
</div>
|
||||
|
||||
<div class="cache-visualization">
|
||||
<div class="cache-levels">
|
||||
<div class="level cpu-level">
|
||||
<div class="level-label">CPU 核心</div>
|
||||
<div class="level-icon">⚡</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-right">→</div>
|
||||
|
||||
<div class="level l1-cache" :class="{ active: activeLevel === 'L1' }">
|
||||
<div class="level-label">L1 缓存</div>
|
||||
<div class="level-info">
|
||||
<span class="size">64 KB</span>
|
||||
<span class="speed">~1ns</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-right">→</div>
|
||||
|
||||
<div class="level l2-cache" :class="{ active: activeLevel === 'L2' }">
|
||||
<div class="level-label">L2 缓存</div>
|
||||
<div class="level-info">
|
||||
<span class="size">256 KB</span>
|
||||
<span class="speed">~5ns</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-right">→</div>
|
||||
|
||||
<div class="level l3-cache" :class="{ active: activeLevel === 'L3' }">
|
||||
<div class="level-label">L3 缓存</div>
|
||||
<div class="level-info">
|
||||
<span class="size">8 MB</span>
|
||||
<span class="speed">~15ns</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-right">→</div>
|
||||
|
||||
<div class="level memory" :class="{ active: activeLevel === 'MEM' }">
|
||||
<div class="level-label">主存</div>
|
||||
<div class="level-info">
|
||||
<span class="size">16 GB</span>
|
||||
<span class="speed">~100ns</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cache-operation">
|
||||
<div class="control-panel">
|
||||
<div class="panel-title">缓存操作演示</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn" @click="simulateRead(100)">读取地址 100</button>
|
||||
<button class="btn" @click="simulateRead(104)">读取地址 104</button>
|
||||
<button class="btn" @click="simulateRead(200)">读取地址 200</button>
|
||||
<button class="btn" @click="simulateRead(108)">读取地址 108</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="operation-log">
|
||||
<div class="log-title">操作记录</div>
|
||||
<div class="log-content">
|
||||
<div v-for="(log, i) in logs" :key="i" :class="['log-item', log.type]">
|
||||
<span class="log-time">T+{{ log.time }}ns</span>
|
||||
<span class="log-text">{{ log.text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="locality-explanation">
|
||||
<div class="exp-title">为什么缓存有效?—— 局部性原理</div>
|
||||
<div class="locality-grid">
|
||||
<div class="locality-card">
|
||||
<div class="locality-icon">⏱️</div>
|
||||
<div class="locality-name">时间局部性</div>
|
||||
<div class="locality-desc">刚访问的数据很可能再次被访问</div>
|
||||
<div class="locality-example">循环中的变量</div>
|
||||
</div>
|
||||
<div class="locality-card">
|
||||
<div class="locality-icon">📦</div>
|
||||
<div class="locality-name">空间局部性</div>
|
||||
<div class="locality-desc">访问某个数据后,附近的数据也可能被访问</div>
|
||||
<div class="locality-example">数组遍历、顺序执行</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cache-mapping">
|
||||
<div class="mapping-title">缓存映射方式</div>
|
||||
<div class="mapping-tabs">
|
||||
<button
|
||||
v-for="map in mappings"
|
||||
:key="map.type"
|
||||
:class="['map-btn', { active: selectedMapping === map.type }]"
|
||||
@click="selectedMapping = map.type"
|
||||
>
|
||||
{{ map.type }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mapping-details" v-if="selectedMappingData">
|
||||
<div class="mapping-desc">{{ selectedMappingData.desc }}</div>
|
||||
<div class="mapping-compare">
|
||||
<div class="compare-item">
|
||||
<span class="compare-label">速度</span>
|
||||
<span class="compare-value fast">{{ selectedMappingData.speed }}</span>
|
||||
</div>
|
||||
<div class="compare-item">
|
||||
<span class="compare-label">命中率</span>
|
||||
<span class="compare-value">{{ selectedMappingData.hitRate }}</span>
|
||||
</div>
|
||||
<div class="compare-item">
|
||||
<span class="compare-label">实现复杂度</span>
|
||||
<span class="compare-value">{{ selectedMappingData.complexity }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hit-rate-calc">
|
||||
<div class="calc-title">命中率计算</div>
|
||||
<div class="calc-formula">
|
||||
<span class="formula">平均访问时间 = H × T<sub>c</sub> + (1-H) × T<sub>m</sub></span>
|
||||
</div>
|
||||
<div class="calc-example">
|
||||
<div class="calc-row">
|
||||
<label>缓存访问时间 (Tc):</label>
|
||||
<input type="range" v-model="tc" min="1" max="10" />
|
||||
<span>{{ tc }} ns</span>
|
||||
</div>
|
||||
<div class="calc-row">
|
||||
<label>内存访问时间 (Tm):</label>
|
||||
<input type="range" v-model="tm" min="50" max="200" />
|
||||
<span>{{ tm }} ns</span>
|
||||
</div>
|
||||
<div class="calc-row">
|
||||
<label>命中率 (H):</label>
|
||||
<input type="range" v-model="hitRate" min="0" max="100" />
|
||||
<span>{{ hitRate }}%</span>
|
||||
</div>
|
||||
<div class="calc-result">
|
||||
平均访问时间 = {{ avgTime }} ns
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const activeLevel = ref('')
|
||||
const logs = ref([])
|
||||
const tc = ref(2)
|
||||
const tm = ref(100)
|
||||
const hitRate = ref(90)
|
||||
const selectedMapping = ref('直接映射')
|
||||
|
||||
const mappings = ref([
|
||||
{
|
||||
type: '直接映射',
|
||||
desc: '每个主存块只能映射到唯一的缓存行',
|
||||
speed: '最快',
|
||||
hitRate: '较低',
|
||||
complexity: '最低'
|
||||
},
|
||||
{
|
||||
type: '组相联',
|
||||
desc: '每个主存块可以映射到 N 个缓存行(N路组相联)',
|
||||
speed: '较快',
|
||||
hitRate: '较高',
|
||||
complexity: '中等'
|
||||
},
|
||||
{
|
||||
type: '全相联',
|
||||
desc: '主存块可以放到任意缓存行中',
|
||||
speed: '最慢',
|
||||
hitRate: '最高',
|
||||
complexity: '最高'
|
||||
}
|
||||
])
|
||||
|
||||
const selectedMappingData = computed(() => {
|
||||
return mappings.value.find(m => m.type === selectedMapping.value)
|
||||
})
|
||||
|
||||
const avgTime = computed(() => {
|
||||
const h = hitRate.value / 100
|
||||
return Math.round(h * tc.value + (1 - h) * tm.value)
|
||||
})
|
||||
|
||||
const simulateRead = async (addr) => {
|
||||
logs.value = []
|
||||
|
||||
if (addr >= 100 && addr < 110) {
|
||||
logs.value.push({ time: 0, text: `读取地址 ${addr}`, type: 'read' })
|
||||
activeLevel.value = 'L1'
|
||||
logs.value.push({ time: tc.value, text: '✓ L1 缓存命中!', type: 'hit' })
|
||||
} else if (addr >= 200 && addr < 210) {
|
||||
logs.value.push({ time: 0, text: `读取地址 ${addr}`, type: 'read' })
|
||||
activeLevel.value = 'L1'
|
||||
logs.value.push({ time: tc.value, text: '✗ L1 缓存未命中', type: 'miss' })
|
||||
activeLevel.value = 'L2'
|
||||
logs.value.push({ time: tc.value + 5, text: '✗ L2 缓存未命中', type: 'miss' })
|
||||
activeLevel.value = 'MEM'
|
||||
logs.value.push({ time: tc.value + 5 + 100, text: '从主存加载数据', type: 'load' })
|
||||
logs.value.push({ time: tc.value + 5 + 100, text: '数据存入缓存', type: 'store' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cache-demo {
|
||||
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.cache-visualization {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.cache-levels {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.level {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
background: #f1f5f9;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.level.active {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.cpu-level {
|
||||
background: #fef3c7;
|
||||
}
|
||||
|
||||
.l1-cache.active { background: #dbeafe; border: 2px solid #3b82f6; }
|
||||
.l2-cache.active { background: #dbeafe; border: 2px solid #2563eb; }
|
||||
.l3-cache.active { background: #dbeafe; border: 2px solid #1d4ed8; }
|
||||
.memory.active { background: #dcfce7; border: 2px solid #16a34a; }
|
||||
|
||||
.level-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.level-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.level-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.size { color: #0369a1; font-weight: 600; }
|
||||
.speed { color: #64748b; }
|
||||
|
||||
.arrow-right {
|
||||
font-size: 18px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.cache-operation {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.control-panel, .operation-log {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.panel-title, .log-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px 12px;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.log-content {
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.log-item {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 4px 0;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
color: #64748b;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.log-item.hit .log-text { color: #16a34a; }
|
||||
.log-item.miss .log-text { color: #ea580c; }
|
||||
.log-item.load .log-text { color: #0369a1; }
|
||||
|
||||
.locality-explanation {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.exp-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.locality-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.locality-card {
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.locality-icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.locality-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.locality-desc {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.locality-example {
|
||||
font-size: 10px;
|
||||
padding: 4px 8px;
|
||||
background: #e0f2fe;
|
||||
border-radius: 4px;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.cache-mapping {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mapping-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mapping-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.map-btn {
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.map-btn.active {
|
||||
border-color: #3b82f6;
|
||||
background: #eff6ff;
|
||||
}
|
||||
|
||||
.mapping-desc {
|
||||
font-size: 12px;
|
||||
color: #475569;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mapping-compare {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.compare-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.compare-label {
|
||||
font-size: 10px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.compare-value {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.compare-value.fast { color: #16a34a; }
|
||||
|
||||
.hit-rate-calc {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.calc-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.calc-formula {
|
||||
text-align: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.formula {
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.calc-example {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.calc-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.calc-row label {
|
||||
min-width: 120px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.calc-row input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.calc-result {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
background: #dcfce7;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #166534;
|
||||
}
|
||||
</style>
|
||||
+60
-130
@@ -1,18 +1,15 @@
|
||||
<template>
|
||||
<div class="career-path-demo">
|
||||
<div class="demo-header">
|
||||
<span class="icon">🚀</span>
|
||||
<span class="title">工程师成长路径</span>
|
||||
<span class="subtitle">从入门到精通的技能演进</span>
|
||||
</div>
|
||||
|
||||
<div class="path-container">
|
||||
<div
|
||||
v-for="(stage, index) in stages"
|
||||
v-for="stage in stages"
|
||||
:key="stage.name"
|
||||
class="stage-card"
|
||||
:class="{ active: activeStage === index }"
|
||||
@click="activeStage = index"
|
||||
>
|
||||
<div class="stage-header">
|
||||
<span class="stage-icon">{{ stage.icon }}</span>
|
||||
@@ -34,31 +31,17 @@
|
||||
<span class="output-text">{{ stage.output }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="index < stages.length - 1" class="stage-arrow">→</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="path-insight">
|
||||
<div class="insight-icon">💡</div>
|
||||
<div class="insight-content">
|
||||
<div class="insight-title">成长关键点</div>
|
||||
<ul class="insight-list">
|
||||
<li>前 1-2 年:打基础,建立"能独立完成任务"的能力</li>
|
||||
<li>2-3 年:选方向,在某个领域建立深度</li>
|
||||
<li>3-5 年:横向扩展,培养架构思维和团队协作能力</li>
|
||||
<li>5 年+:技术决策、团队带领、技术影响力</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="info-box">
|
||||
<strong>成长关键点:</strong>前 1-2 年打基础,建立独立完成任务的能力;2-3 年选方向,建立深度;3-5 年横向扩展,培养架构思维;5 年+ 技术决策与团队影响力。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeStage = ref(0)
|
||||
|
||||
const stages = ref([
|
||||
const stages = [
|
||||
{
|
||||
name: '入门期',
|
||||
icon: '🌱',
|
||||
@@ -99,188 +82,135 @@ const stages = ref([
|
||||
skills: ['技术战略', '团队建设', '行业洞察', '创新引领'],
|
||||
output: '技术方向决策,培养技术团队'
|
||||
}
|
||||
])
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.career-path-demo {
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
margin: 20px 0;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 28px;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
font-size: 0.78rem;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
|
||||
.path-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.stage-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.stage-card:hover {
|
||||
transform: translateX(4px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stage-card.active {
|
||||
border-color: #3b82f6;
|
||||
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.stage-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.stage-icon {
|
||||
font-size: 24px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.stage-name {
|
||||
font-size: 18px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.stage-time {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
background: #f1f5f9;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7rem;
|
||||
color: var(--vp-c-text-3);
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 0.1rem 0.35rem;
|
||||
border-radius: 3px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.stage-content {
|
||||
padding-left: 36px;
|
||||
padding-left: 1.6rem;
|
||||
}
|
||||
|
||||
.stage-desc {
|
||||
font-size: 14px;
|
||||
color: #475569;
|
||||
margin-bottom: 12px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.stage-skills {
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.skill-label,
|
||||
.output-label {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
margin-right: 8px;
|
||||
font-size: 0.7rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-right: 0.35rem;
|
||||
}
|
||||
|
||||
.skill-tags {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 4px;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.skill-tag {
|
||||
font-size: 12px;
|
||||
background: #e0f2fe;
|
||||
color: #0369a1;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.68rem;
|
||||
background: var(--vp-c-brand-soft);
|
||||
color: var(--vp-c-brand-1);
|
||||
padding: 0.1rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.stage-output {
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
font-size: 0.72rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.output-text {
|
||||
color: #1e293b;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.stage-arrow {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 20px;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.path-insight {
|
||||
margin-top: 24px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
border-left: 4px solid #f59e0b;
|
||||
}
|
||||
|
||||
.insight-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.insight-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.insight-list {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.insight-list li {
|
||||
margin-bottom: 4px;
|
||||
.info-box {
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
border-left: 3px solid var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.stage-content {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.stage-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<div class="code-optimization-demo">
|
||||
<h4>⚡ 编译器优化:让代码自动变快</h4>
|
||||
<p class="desc">选择一种优化技术,观察编译器如何自动改进你的代码</p>
|
||||
|
||||
<div class="opt-selector">
|
||||
<button
|
||||
v-for="(opt, i) in optimizations"
|
||||
:key="i"
|
||||
:class="['opt-btn', { active: selected === i }]"
|
||||
@click="selected = i"
|
||||
>
|
||||
<span class="opt-icon">{{ opt.icon }}</span>
|
||||
<span>{{ opt.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="opt-detail">
|
||||
<div class="code-panel before">
|
||||
<div class="panel-header">📝 优化前</div>
|
||||
<pre class="code-block">{{ optimizations[selected].before }}</pre>
|
||||
</div>
|
||||
<div class="arrow-col">
|
||||
<div class="arrow-box">
|
||||
<span class="arrow-icon">→</span>
|
||||
<span class="arrow-label">编译器优化</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-panel after">
|
||||
<div class="panel-header">🚀 优化后</div>
|
||||
<pre class="code-block">{{ optimizations[selected].after }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="opt-explain">
|
||||
<div class="explain-header">{{ optimizations[selected].name }}原理</div>
|
||||
<div class="explain-text">{{ optimizations[selected].explain }}</div>
|
||||
<div class="perf-gain">
|
||||
<span class="gain-label">性能提升:</span>
|
||||
<div class="gain-bar-bg">
|
||||
<div
|
||||
class="gain-bar"
|
||||
:style="{ width: optimizations[selected].gain + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
<span class="gain-value">{{ optimizations[selected].gain }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selected = ref(0)
|
||||
|
||||
const optimizations = [
|
||||
{
|
||||
icon: '🧮',
|
||||
name: '常量折叠',
|
||||
before: `const width = 10
|
||||
const height = 20
|
||||
const area = width * height // 运行时计算
|
||||
console.log(area)`,
|
||||
after: `const area = 200 // 编译时直接算出结果
|
||||
console.log(200)`,
|
||||
explain:
|
||||
'编译器发现 width 和 height 都是常量,在编译阶段就直接计算出 10 * 20 = 200,运行时不再需要做乘法运算。这是最基础也最常见的优化。',
|
||||
gain: 30
|
||||
},
|
||||
{
|
||||
icon: '💀',
|
||||
name: '死代码消除',
|
||||
before: `function process(x) {
|
||||
const result = x * 2
|
||||
return result
|
||||
|
||||
// 以下代码永远不会执行
|
||||
console.log("debug info")
|
||||
const unused = x + 1
|
||||
return unused
|
||||
}`,
|
||||
after: `function process(x) {
|
||||
return x * 2 // 只保留有用的代码
|
||||
}`,
|
||||
explain:
|
||||
'编译器分析控制流,发现 return 之后的代码永远不会执行,直接删除。同时发现 result 变量只被赋值后立即返回,于是内联了表达式。',
|
||||
gain: 20
|
||||
},
|
||||
{
|
||||
icon: '🔄',
|
||||
name: '循环不变量外提',
|
||||
before: `const arr = [1, 2, 3, ..., 10000]
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
// arr.length 每次循环都要读取
|
||||
process(arr[i])
|
||||
}`,
|
||||
after: `const arr = [1, 2, 3, ..., 10000]
|
||||
const len = arr.length // 提到循环外,只读一次
|
||||
for (let i = 0; i < len; i++) {
|
||||
process(arr[i])
|
||||
}`,
|
||||
explain:
|
||||
'循环体内的 arr.length 每次迭代都要访问,但它的值在循环中不会改变。编译器把这个不变的计算提到循环外面,避免了 10000 次重复读取。',
|
||||
gain: 45
|
||||
},
|
||||
{
|
||||
icon: '📦',
|
||||
name: '函数内联',
|
||||
before: `function square(x) {
|
||||
return x * x
|
||||
}
|
||||
|
||||
// 调用 10000 次
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
result += square(i) // 每次都有函数调用开销
|
||||
}`,
|
||||
after: `// 消除函数调用开销
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
result += i * i // 直接展开,无调用开销
|
||||
}`,
|
||||
explain:
|
||||
'函数调用有开销(保存寄存器、跳转、返回)。对于小函数,编译器直接把函数体"粘贴"到调用处,消除调用开销。JIT 编译器(如 V8)特别擅长这个优化。',
|
||||
gain: 55
|
||||
},
|
||||
{
|
||||
icon: '🔗',
|
||||
name: '常量传播',
|
||||
before: `const x = 10
|
||||
const y = x + 5 // y = 15
|
||||
const z = y * 2 // z = 30
|
||||
console.log(z + 1) // 31`,
|
||||
after: `console.log(31) // 编译时追踪所有常量值
|
||||
// x, y, z 全部被消除`,
|
||||
explain:
|
||||
'编译器追踪每个变量的值:x=10 → y=15 → z=30 → z+1=31。当所有中间变量都是常量时,整个计算链在编译时就完成了,运行时只需要输出结果。',
|
||||
gain: 40
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.code-optimization-demo {
|
||||
padding: 20px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
margin: 16px 0;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 {
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
.desc {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 14px;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
.opt-selector {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.opt-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 14px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.opt-btn.active {
|
||||
background: var(--vp-c-brand-1);
|
||||
color: #fff;
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
.opt-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
.opt-detail {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
gap: 8px;
|
||||
margin-bottom: 14px;
|
||||
align-items: stretch;
|
||||
}
|
||||
.code-panel {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
.panel-header {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
.code-block {
|
||||
padding: 10px 12px;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.arrow-col {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.arrow-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.arrow-icon {
|
||||
font-size: 24px;
|
||||
color: var(--vp-c-brand-1);
|
||||
font-weight: 700;
|
||||
}
|
||||
.arrow-label {
|
||||
font-size: 11px;
|
||||
color: var(--vp-c-text-3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.opt-explain {
|
||||
padding: 12px 14px;
|
||||
background: var(--vp-c-brand-soft);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.explain-header {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.explain-text {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.perf-gain {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.gain-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.gain-bar-bg {
|
||||
flex: 1;
|
||||
height: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.gain-bar {
|
||||
height: 100%;
|
||||
background: var(--vp-c-brand-1);
|
||||
border-radius: 4px;
|
||||
transition: width 0.4s ease;
|
||||
}
|
||||
.gain-value {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.opt-detail {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.arrow-col {
|
||||
transform: rotate(90deg);
|
||||
padding: 4px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div class="code-to-instruction-demo">
|
||||
<h4>🔗 从代码到指令:一行代码的翻译之旅</h4>
|
||||
<p class="desc">点击每个阶段,看你写的代码如何一步步变成 CPU 能执行的指令</p>
|
||||
|
||||
<div class="example-selector">
|
||||
<button
|
||||
v-for="(ex, i) in examples"
|
||||
:key="i"
|
||||
:class="['ex-btn', { active: selectedExample === i }]"
|
||||
@click="selectedExample = i"
|
||||
>
|
||||
<code>{{ ex.code }}</code>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="translation-chain">
|
||||
<div
|
||||
v-for="(stage, j) in examples[selectedExample].stages"
|
||||
:key="j"
|
||||
:class="['stage-card', { active: activeStage === j }]"
|
||||
@click="activeStage = j"
|
||||
>
|
||||
<div class="stage-header">
|
||||
<span class="stage-num">{{ j + 1 }}</span>
|
||||
<span class="stage-name">{{ stage.name }}</span>
|
||||
</div>
|
||||
<pre class="stage-code">{{ stage.content }}</pre>
|
||||
<div v-if="activeStage === j" class="stage-explain">
|
||||
{{ stage.explain }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="j in examples[selectedExample].stages.length - 1"
|
||||
:key="'arrow-' + j"
|
||||
class="chain-arrow"
|
||||
:style="{ order: j * 2 }"
|
||||
>
|
||||
↓
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="key-insight">
|
||||
<div class="insight-title">💡 关键理解</div>
|
||||
<div class="insight-text">
|
||||
指令集就是 CPU 的「API」——它定义了 CPU 能听懂的所有命令。
|
||||
编译器的工作就是把你写的高级语言「翻译」成这套 API 的调用序列。
|
||||
不同的 CPU(x86、ARM)有不同的指令集,就像不同的服务有不同的 API。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selectedExample = ref(0)
|
||||
const activeStage = ref(0)
|
||||
|
||||
const examples = [
|
||||
{
|
||||
code: 'int a = 10 + 5;',
|
||||
stages: [
|
||||
{
|
||||
name: '你写的代码',
|
||||
content: 'int a = 10 + 5;',
|
||||
explain:
|
||||
'这是你在编辑器里写的高级语言代码。对人类来说很好懂,但 CPU 完全看不懂——它不认识 int、也不知道 + 是什么。'
|
||||
},
|
||||
{
|
||||
name: '编译器翻译成汇编',
|
||||
content: 'MOV R1, #10 ; 把 10 放入寄存器 R1\nMOV R2, #5 ; 把 5 放入寄存器 R2\nADD R3, R1, R2 ; R3 = R1 + R2\nSTORE R3, [a] ; 把结果存到变量 a 的内存地址',
|
||||
explain:
|
||||
'编译器把一行高级代码拆成了 4 条汇编指令。每条指令只做一件最简单的事:搬数据、做加法、存结果。这就是 CPU 的「能力粒度」。'
|
||||
},
|
||||
{
|
||||
name: '汇编器转成机器码',
|
||||
content: '0001 0001 0000 1010 → MOV R1, #10\n0001 0010 0000 0101 → MOV R2, #5\n0010 0011 0001 0010 → ADD R3, R1, R2\n0100 0011 1000 0000 → STORE R3, [a]',
|
||||
explain:
|
||||
'汇编器把每条汇编指令编码成二进制数字。操作码(前几位)告诉 CPU「做什么」,操作数(后面的位)告诉 CPU「对谁做」。这就是 CPU 真正执行的东西。'
|
||||
},
|
||||
{
|
||||
name: 'CPU 逐条执行',
|
||||
content: '时钟 1: 取指 → 译码 → 执行 MOV R1, #10\n时钟 2: 取指 → 译码 → 执行 MOV R2, #5\n时钟 3: 取指 → 译码 → 执行 ADD R3, R1, R2\n时钟 4: 取指 → 译码 → 执行 STORE R3, [a]',
|
||||
explain:
|
||||
'CPU 按顺序从内存取出每条指令,译码后执行。每个时钟周期处理一条指令(简化模型)。4 条指令执行完,变量 a 的值就是 15 了。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
code: 'if (x > 0) y = 1;',
|
||||
stages: [
|
||||
{
|
||||
name: '你写的代码',
|
||||
content: 'if (x > 0) y = 1;',
|
||||
explain:
|
||||
'一个简单的条件判断。人类一眼就懂,但 CPU 没有「if」的概念——它只会比较和跳转。'
|
||||
},
|
||||
{
|
||||
name: '编译器翻译成汇编',
|
||||
content: 'LOAD R1, [x] ; 从内存读取 x 的值\nCMP R1, #0 ; 比较 R1 和 0\nBLE skip ; 如果 ≤ 0,跳过下面\nMOV R2, #1 ; R2 = 1\nSTORE R2, [y] ; 把 1 存到 y\nskip: ; 跳转目标',
|
||||
explain:
|
||||
'编译器把 if 语句拆成了「比较 + 条件跳转」。CMP 指令比较两个值并设置标志位,BLE 根据标志位决定是否跳过赋值代码。这就是 CPU 实现条件逻辑的方式。'
|
||||
},
|
||||
{
|
||||
name: '汇编器转成机器码',
|
||||
content: '0011 0001 1000 0000 → LOAD R1, [x]\n0101 0001 0000 0000 → CMP R1, #0\n0110 0000 0000 0011 → BLE +3(跳过3条)\n0001 0010 0000 0001 → MOV R2, #1\n0100 0010 1000 0001 → STORE R2, [y]',
|
||||
explain:
|
||||
'注意 BLE 指令的操作数是「+3」——这是一个相对地址偏移,告诉 CPU 向前跳 3 条指令。这就是「相对寻址」的实际应用。'
|
||||
},
|
||||
{
|
||||
name: 'CPU 逐条执行',
|
||||
content: '假设 x = 5(大于 0):\n→ LOAD: 读取 x=5 到 R1\n→ CMP: 比较 5 > 0,设置标志位\n→ BLE: 条件不满足,不跳转\n→ MOV: R2 = 1\n→ STORE: y = 1 ✅',
|
||||
explain:
|
||||
'因为 x=5 大于 0,BLE 的条件不满足,所以 CPU 继续执行下面的赋值。如果 x=0,BLE 会跳过赋值,直接到 skip 标签处。'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.code-to-instruction-demo {
|
||||
padding: 20px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
margin: 16px 0;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
|
||||
.example-selector { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.ex-btn {
|
||||
padding: 6px 14px; border-radius: 6px; border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg); cursor: pointer; font-size: 13px; transition: all 0.2s;
|
||||
}
|
||||
.ex-btn.active { background: var(--vp-c-brand-1); color: #fff; border-color: var(--vp-c-brand-1); }
|
||||
.ex-btn code { font-size: 13px; }
|
||||
|
||||
.translation-chain {
|
||||
display: flex; flex-direction: column; gap: 0; margin-bottom: 16px;
|
||||
}
|
||||
.stage-card {
|
||||
border: 1px solid var(--vp-c-divider); border-radius: 8px;
|
||||
background: var(--vp-c-bg); overflow: hidden; cursor: pointer;
|
||||
transition: all 0.2s; order: 0;
|
||||
}
|
||||
.stage-card:nth-child(1) { order: 0; }
|
||||
.stage-card:nth-child(3) { order: 2; }
|
||||
.stage-card:nth-child(5) { order: 4; }
|
||||
.stage-card:nth-child(7) { order: 6; }
|
||||
.stage-card.active { border-color: var(--vp-c-brand-1); box-shadow: 0 0 0 1px var(--vp-c-brand-1); }
|
||||
.stage-header {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 8px 12px; background: var(--vp-c-bg-soft);
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
.stage-num {
|
||||
width: 22px; height: 22px; border-radius: 50%; background: var(--vp-c-brand-1);
|
||||
color: #fff; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 12px; font-weight: 600;
|
||||
}
|
||||
.stage-name { font-size: 13px; font-weight: 600; }
|
||||
.stage-code {
|
||||
padding: 10px 12px; margin: 0; font-size: 12px; line-height: 1.5;
|
||||
white-space: pre-wrap; max-height: 120px; overflow-y: auto;
|
||||
}
|
||||
.stage-explain {
|
||||
padding: 8px 12px; font-size: 12px; line-height: 1.6;
|
||||
background: var(--vp-c-brand-soft); border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
.chain-arrow {
|
||||
text-align: center; font-size: 18px; color: var(--vp-c-brand-1);
|
||||
font-weight: 700; padding: 4px 0;
|
||||
}
|
||||
|
||||
.key-insight {
|
||||
padding: 12px 14px; background: var(--vp-c-brand-soft); border-radius: 8px;
|
||||
}
|
||||
.insight-title { font-weight: 600; font-size: 13px; margin-bottom: 6px; }
|
||||
.insight-text { font-size: 13px; line-height: 1.6; }
|
||||
</style>
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="compile-vs-interpret-demo">
|
||||
<h4>🔄 编译型 vs 解释型 vs JIT</h4>
|
||||
<p class="desc">点击不同执行模式,观察代码从源码到运行的过程</p>
|
||||
|
||||
<div class="mode-selector">
|
||||
<button
|
||||
v-for="(m, i) in modes"
|
||||
:key="i"
|
||||
:class="['mode-btn', { active: selected === i }]"
|
||||
@click="selectMode(i)"
|
||||
>
|
||||
{{ m.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pipeline">
|
||||
<div
|
||||
v-for="(step, j) in modes[selected].steps"
|
||||
:key="j"
|
||||
:class="['pipe-step', { visible: visibleSteps > j }]"
|
||||
>
|
||||
<div class="step-icon">{{ step.icon }}</div>
|
||||
<div class="step-content">
|
||||
<div class="step-name">{{ step.name }}</div>
|
||||
<div class="step-desc">{{ step.desc }}</div>
|
||||
</div>
|
||||
<div v-if="j < modes[selected].steps.length - 1" class="arrow">→</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric" v-for="m in modes[selected].metrics" :key="m.label">
|
||||
<span class="metric-label">{{ m.label }}</span>
|
||||
<div class="metric-bar-bg">
|
||||
<div class="metric-bar" :style="{ width: m.value + '%', background: m.color }"></div>
|
||||
</div>
|
||||
<span class="metric-val">{{ m.text }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="examples">
|
||||
<span class="ex-label">代表语言:</span>
|
||||
<span class="ex-lang" v-for="l in modes[selected].langs" :key="l">{{ l }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selected = ref(0)
|
||||
const visibleSteps = ref(0)
|
||||
let timer = null
|
||||
|
||||
function selectMode(i) {
|
||||
selected.value = i
|
||||
visibleSteps.value = 0
|
||||
clearInterval(timer)
|
||||
timer = setInterval(() => {
|
||||
if (visibleSteps.value < modes[i].steps.length) {
|
||||
visibleSteps.value++
|
||||
} else {
|
||||
clearInterval(timer)
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const modes = [
|
||||
{
|
||||
name: '编译型',
|
||||
steps: [
|
||||
{ icon: '📝', name: '源代码', desc: 'main.c' },
|
||||
{ icon: '⚙️', name: '编译器', desc: '全量编译' },
|
||||
{ icon: '📦', name: '机器码', desc: '二进制可执行文件' },
|
||||
{ icon: '🚀', name: '直接执行', desc: 'CPU 直接运行' }
|
||||
],
|
||||
metrics: [
|
||||
{ label: '运行速度', value: 95, text: '极快', color: '#22c55e' },
|
||||
{ label: '启动速度', value: 30, text: '慢(需编译)', color: '#ef4444' },
|
||||
{ label: '跨平台', value: 20, text: '需重新编译', color: '#ef4444' }
|
||||
],
|
||||
langs: ['C', 'C++', 'Rust', 'Go']
|
||||
},
|
||||
{
|
||||
name: '解释型',
|
||||
steps: [
|
||||
{ icon: '📝', name: '源代码', desc: 'app.py' },
|
||||
{ icon: '🔍', name: '解释器', desc: '逐行读取' },
|
||||
{ icon: '🔄', name: '逐行执行', desc: '边翻译边运行' }
|
||||
],
|
||||
metrics: [
|
||||
{ label: '运行速度', value: 30, text: '较慢', color: '#ef4444' },
|
||||
{ label: '启动速度', value: 90, text: '快(直接运行)', color: '#22c55e' },
|
||||
{ label: '跨平台', value: 90, text: '天然跨平台', color: '#22c55e' }
|
||||
],
|
||||
langs: ['Python', 'Ruby', 'PHP', 'Bash']
|
||||
},
|
||||
{
|
||||
name: 'JIT 即时编译',
|
||||
steps: [
|
||||
{ icon: '📝', name: '源代码', desc: 'app.js' },
|
||||
{ icon: '🔍', name: '解释执行', desc: '先解释运行' },
|
||||
{ icon: '🔥', name: '热点检测', desc: '发现高频代码' },
|
||||
{ icon: '⚡', name: 'JIT 编译', desc: '编译为机器码' },
|
||||
{ icon: '🚀', name: '高速执行', desc: '接近原生速度' }
|
||||
],
|
||||
metrics: [
|
||||
{ label: '运行速度', value: 75, text: '快(热点接近原生)', color: '#22c55e' },
|
||||
{ label: '启动速度', value: 60, text: '中等(需预热)', color: '#eab308' },
|
||||
{ label: '跨平台', value: 85, text: '跨平台', color: '#22c55e' }
|
||||
],
|
||||
langs: ['JavaScript (V8)', 'Java (JVM)', 'C# (.NET)']
|
||||
}
|
||||
]
|
||||
|
||||
selectMode(0)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.compile-vs-interpret-demo {
|
||||
padding: 20px; border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px; margin: 16px 0; background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
.mode-selector { display: flex; gap: 8px; margin-bottom: 16px; }
|
||||
.mode-btn {
|
||||
padding: 8px 18px; border-radius: 8px; border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg); cursor: pointer; font-size: 14px; font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.mode-btn.active { background: var(--vp-c-brand-1); color: #fff; border-color: var(--vp-c-brand-1); }
|
||||
.pipeline { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin-bottom: 16px; }
|
||||
.pipe-step {
|
||||
display: flex; align-items: center; gap: 4px; opacity: 0;
|
||||
transform: translateY(8px); transition: all 0.3s;
|
||||
}
|
||||
.pipe-step.visible { opacity: 1; transform: translateY(0); }
|
||||
.step-icon { font-size: 24px; }
|
||||
.step-name { font-size: 12px; font-weight: 600; }
|
||||
.step-desc { font-size: 11px; color: var(--vp-c-text-3); }
|
||||
.arrow { color: var(--vp-c-text-3); font-size: 18px; margin: 0 4px; }
|
||||
.metrics { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
|
||||
.metric { display: flex; align-items: center; gap: 8px; font-size: 13px; }
|
||||
.metric-label { width: 70px; text-align: right; color: var(--vp-c-text-2); }
|
||||
.metric-bar-bg { flex: 1; height: 10px; background: var(--vp-c-divider); border-radius: 5px; overflow: hidden; }
|
||||
.metric-bar { height: 100%; border-radius: 5px; transition: width 0.5s; }
|
||||
.metric-val { width: 130px; font-size: 12px; color: var(--vp-c-text-3); }
|
||||
.examples { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.ex-label { font-size: 13px; color: var(--vp-c-text-2); }
|
||||
.ex-lang {
|
||||
padding: 3px 10px; background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px; font-size: 12px;
|
||||
}
|
||||
@media (max-width: 640px) { .metric-val { width: auto; } }
|
||||
</style>
|
||||
+2
-19
@@ -10,8 +10,6 @@
|
||||
v-for="field in fields"
|
||||
:key="field.name"
|
||||
class="field-card"
|
||||
:class="{ active: activeField === field.name }"
|
||||
@click="activeField = field.name"
|
||||
>
|
||||
<div class="field-name">{{ field.name }}</div>
|
||||
<div class="field-desc">{{ field.desc }}</div>
|
||||
@@ -28,11 +26,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeField = ref('前端')
|
||||
|
||||
const fields = ref([
|
||||
const fields = [
|
||||
{
|
||||
name: '前端',
|
||||
desc: '用户能看到、能交互的一切',
|
||||
@@ -63,7 +57,7 @@ const fields = ref([
|
||||
desc: '数据采集、存储、分析',
|
||||
techs: ['SQL', 'Spark', '数据仓库']
|
||||
}
|
||||
])
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -106,17 +100,6 @@ const fields = ref([
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.field-card:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.field-card.active {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.field-name {
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
<template>
|
||||
<div class="controller-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">控制器工作原理</span>
|
||||
<span class="subtitle">控制信号如何协调 CPU 各个部件</span>
|
||||
</div>
|
||||
|
||||
<div class="control-unit">
|
||||
<div class="cu-box">
|
||||
<div class="cu-title">控制单元 CU</div>
|
||||
<div class="cu-diagram">
|
||||
<div class="cu-internal">
|
||||
<div class="cu-component">指令寄存器 IR</div>
|
||||
<div class="cu-component">指令译码器</div>
|
||||
<div class="cu-component">时序发生器</div>
|
||||
</div>
|
||||
<div class="cu-output">
|
||||
<div class="output-label">输出控制信号:</div>
|
||||
<div class="control-signals">
|
||||
<div v-for="sig in controlSignals" :key="sig.name" :class="['sig-box', sig.active ? 'active' : '']">
|
||||
{{ sig.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cpu-block-diagram">
|
||||
<div class="block-row">
|
||||
<div class="cpu-block" :class="{ active: activeBlock === 'pc' }">
|
||||
<div class="block-name">PC</div>
|
||||
<div class="block-desc">程序计数器</div>
|
||||
</div>
|
||||
<div class="arrow" :class="{ active: activeBlock === 'pc' }">→</div>
|
||||
<div class="cpu-block" :class="{ active: activeBlock === 'mar' }">
|
||||
<div class="block-name">MAR</div>
|
||||
<div class="block-desc">地址寄存器</div>
|
||||
</div>
|
||||
<div class="arrow" :class="{ active: activeBlock === 'mar' }">→</div>
|
||||
<div class="cpu-block" :class="{ active: activeBlock === 'memory' }">
|
||||
<div class="block-name">Memory</div>
|
||||
<div class="block-desc">主存</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block-row">
|
||||
<div class="cpu-block" :class="{ active: activeBlock === 'mdr' }">
|
||||
<div class="block-name">MDR</div>
|
||||
<div class="block-desc">数据寄存器</div>
|
||||
</div>
|
||||
<div class="arrow" :class="{ active: activeBlock === 'mdr' }">→</div>
|
||||
<div class="cpu-block" :class="{ active: activeBlock === 'ir' }">
|
||||
<div class="block-name">IR</div>
|
||||
<div class="block-desc">指令寄存器</div>
|
||||
</div>
|
||||
<div class="arrow" :class="{ active: activeBlock === 'ir' }">→</div>
|
||||
<div class="cpu-block" :class="{ active: activeBlock === 'decoder' }">
|
||||
<div class="block-name">ID</div>
|
||||
<div class="block-desc">译码器</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block-row">
|
||||
<div class="cpu-block" :class="{ active: activeBlock === 'alu' }">
|
||||
<div class="block-name">ALU</div>
|
||||
<div class="block-desc">算术逻辑单元</div>
|
||||
</div>
|
||||
<div class="arrow" :class="{ active: activeBlock === 'alu' }">↔</div>
|
||||
<div class="cpu-block" :class="{ active: activeBlock === 'acc' }">
|
||||
<div class="block-name">ACC</div>
|
||||
<div class="block-desc">累加器</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-panel">
|
||||
<button class="btn" @click="executeFetch">执行取指周期</button>
|
||||
<button class="btn" @click="executeAdd">执行 ADD 指令</button>
|
||||
<button class="btn" @click="executeLoad">执行 LOAD 指令</button>
|
||||
</div>
|
||||
|
||||
<div class="microinstruction-panel">
|
||||
<div class="panel-title">当前微指令</div>
|
||||
<div class="micro-ops">
|
||||
<div v-for="(op, i) in microOps" :key="i" :class="['micro-op', op.active ? 'active' : '']">
|
||||
<span class="op-cycle">T{{ i + 1 }}</span>
|
||||
<span class="op-desc">{{ op.desc }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cu-explanation">
|
||||
<div class="exp-title">控制器核心概念</div>
|
||||
<div class="exp-content">
|
||||
<div class="exp-item">
|
||||
<strong>控制信号:</strong>由控制器发出的电信号,用于控制数据通路中各个部件的动作
|
||||
</div>
|
||||
<div class="exp-item">
|
||||
<strong>时序:</strong>CPU 操作按时钟节拍进行,每个节拍执行特定微操作
|
||||
</div>
|
||||
<div class="exp-item">
|
||||
<strong>硬布线 vs 微程序:</strong>硬布线控制器速度快但设计复杂;微程序控制器灵活但速度稍慢
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
const activeBlock = ref('')
|
||||
|
||||
const controlSignals = reactive([
|
||||
{ name: 'PC→MAR', active: false },
|
||||
{ name: 'MEM→MDR', active: false },
|
||||
{ name: 'MDR→IR', active: false },
|
||||
{ name: 'IR→ID', active: false },
|
||||
{ name: 'ALU→ACC', active: false },
|
||||
{ name: 'ACC→MDR', active: false },
|
||||
])
|
||||
|
||||
const microOps = reactive([])
|
||||
|
||||
const clearSignals = () => {
|
||||
controlSignals.forEach(s => s.active = false)
|
||||
activeBlock.value = ''
|
||||
}
|
||||
|
||||
const executeFetch = async () => {
|
||||
clearSignals()
|
||||
microOps.splice(0, microOps.length)
|
||||
|
||||
microOps.push({ desc: 'PC→MAR: 将PC中的地址送入MAR', active: true })
|
||||
controlSignals[0].active = true
|
||||
activeBlock.value = 'pc'
|
||||
await wait(1000)
|
||||
|
||||
microOps.push({ desc: 'MEM→MDR: 从内存读取指令到MDR', active: true })
|
||||
controlSignals[1].active = true
|
||||
activeBlock.value = 'memory'
|
||||
await wait(1000)
|
||||
|
||||
microOps.push({ desc: 'MDR→IR: 将指令送入IR', active: true })
|
||||
controlSignals[2].active = true
|
||||
activeBlock.value = 'mar'
|
||||
await wait(1000)
|
||||
|
||||
microOps.push({ desc: 'IR→ID: 指令送入译码器', active: true })
|
||||
controlSignals[3].active = true
|
||||
activeBlock.value = 'ir'
|
||||
await wait(1000)
|
||||
}
|
||||
|
||||
const executeAdd = async () => {
|
||||
clearSignals()
|
||||
microOps.splice(0, microOps.length)
|
||||
|
||||
microOps.push({ desc: '指令译码:识别为ADD指令', active: true })
|
||||
activeBlock.value = 'decoder'
|
||||
await wait(1000)
|
||||
|
||||
microOps.push({ desc: 'ALU执行加法运算', active: true })
|
||||
controlSignals[4].active = true
|
||||
activeBlock.value = 'alu'
|
||||
await wait(1000)
|
||||
|
||||
microOps.push({ desc: '结果写入ACC', active: true })
|
||||
activeBlock.value = 'acc'
|
||||
await wait(1000)
|
||||
}
|
||||
|
||||
const executeLoad = async () => {
|
||||
clearSignals()
|
||||
microOps.splice(0, microOps.length)
|
||||
|
||||
microOps.push({ desc: '指令译码:识别为LOAD指令', active: true })
|
||||
activeBlock.value = 'decoder'
|
||||
await wait(1000)
|
||||
|
||||
microOps.push({ desc: 'PC→MAR: 取操作数地址', active: true })
|
||||
controlSignals[0].active = true
|
||||
activeBlock.value = 'pc'
|
||||
await wait(1000)
|
||||
|
||||
microOps.push({ desc: 'MEM→MDR: 读取数据', active: true })
|
||||
controlSignals[1].active = true
|
||||
activeBlock.value = 'memory'
|
||||
await wait(1000)
|
||||
|
||||
microOps.push({ desc: 'MDR→ACC: 数据送入ACC', active: true })
|
||||
controlSignals[5].active = true
|
||||
activeBlock.value = 'mdr'
|
||||
await wait(1000)
|
||||
}
|
||||
|
||||
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.controller-demo {
|
||||
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.control-unit {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.cu-box {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.cu-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cu-diagram {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.cu-internal {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cu-component {
|
||||
padding: 8px 12px;
|
||||
background: #e0f2fe;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
color: #0369a1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cu-output {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.output-label {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.control-signals {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.sig-box {
|
||||
padding: 6px 10px;
|
||||
background: #f1f5f9;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.sig-box.active {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cpu-block-diagram {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.block-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.cpu-block {
|
||||
padding: 10px 16px;
|
||||
background: #f1f5f9;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.cpu-block.active {
|
||||
background: #dbeafe;
|
||||
border: 2px solid #3b82f6;
|
||||
}
|
||||
|
||||
.block-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.block-desc {
|
||||
font-size: 10px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 16px;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.arrow.active {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.microinstruction-panel {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.micro-ops {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.micro-op {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
background: #f8fafc;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.micro-op.active {
|
||||
background: #dbeafe;
|
||||
}
|
||||
|
||||
.op-cycle {
|
||||
font-weight: 600;
|
||||
color: #3b82f6;
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
.op-desc {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.cu-explanation {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.exp-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.exp-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.exp-item {
|
||||
font-size: 12px;
|
||||
color: #475569;
|
||||
padding: 8px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,53 +1,85 @@
|
||||
<template>
|
||||
<div class="desktop-demo">
|
||||
<div class="demo-label">桌面是如何出现的 ── 点击加载</div>
|
||||
|
||||
<div class="desktop-view" @click="loading = !loading">
|
||||
<div class="screen" :class="{ loaded: loading }">
|
||||
<div class="boot-screen" v-if="!loading">
|
||||
<div class="boot-logo">⚙️</div>
|
||||
<div class="boot-text">正在启动...</div>
|
||||
<div class="demo-title">从开机到桌面</div>
|
||||
<div class="screen-wrapper">
|
||||
<div class="screen">
|
||||
<div v-if="phase === 0" class="phase-bios">
|
||||
<div class="bios-text">POST 自检中...</div>
|
||||
</div>
|
||||
<div class="desktop" v-else>
|
||||
<div v-else-if="phase === 1" class="phase-boot">
|
||||
<div class="boot-spinner"></div>
|
||||
<div class="boot-text">正在加载内核...</div>
|
||||
</div>
|
||||
<div v-else-if="phase === 2" class="phase-loading">
|
||||
<div class="loading-bar-track">
|
||||
<div class="loading-bar-fill"></div>
|
||||
</div>
|
||||
<div class="loading-text">启动系统服务...</div>
|
||||
</div>
|
||||
<div v-else class="phase-desktop">
|
||||
<div class="desktop-icons">
|
||||
<div class="desktop-icon">📁</div>
|
||||
<div class="desktop-icon">🗑️</div>
|
||||
<div class="desktop-icon">⚙️</div>
|
||||
<div class="desktop-icon">🌐</div>
|
||||
<div class="icon-item" v-for="icon in icons" :key="icon.label">
|
||||
<div class="icon-box">{{ icon.emoji }}</div>
|
||||
<div class="icon-label">{{ icon.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="taskbar">
|
||||
<div class="taskbar-icon">🏠</div>
|
||||
<div class="taskbar-icon">📊</div>
|
||||
<div class="taskbar-time">{{ time }}</div>
|
||||
<span class="taskbar-start">☰</span>
|
||||
<span class="taskbar-spacer"></span>
|
||||
<span class="taskbar-clock">{{ clock }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tap-hint">👆 点击切换开机状态</div>
|
||||
<div class="phase-labels">
|
||||
<span v-for="(label, i) in labels" :key="label" class="phase-label" :class="{ active: phase >= i }">
|
||||
{{ label }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const time = ref('')
|
||||
|
||||
const phase = ref(0)
|
||||
const clock = ref('')
|
||||
let timer = null
|
||||
let phaseTimer = null
|
||||
|
||||
const updateTime = () => {
|
||||
const icons = [
|
||||
{ emoji: '📁', label: '文件' },
|
||||
{ emoji: '🌐', label: '浏览器' },
|
||||
{ emoji: '⚙️', label: '设置' },
|
||||
{ emoji: '🗑️', label: '回收站' }
|
||||
]
|
||||
const labels = ['BIOS 自检', '内核加载', '服务启动', '桌面就绪']
|
||||
|
||||
const updateClock = () => {
|
||||
const now = new Date()
|
||||
time.value = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
||||
clock.value = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
||||
}
|
||||
|
||||
const runSequence = () => {
|
||||
phase.value = 0
|
||||
const delays = [1500, 1500, 1800]
|
||||
let i = 0
|
||||
const next = () => {
|
||||
if (i < delays.length) {
|
||||
phaseTimer = setTimeout(() => { phase.value = i + 1; i++; next() }, delays[i])
|
||||
}
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateTime()
|
||||
timer = setInterval(updateTime, 1000)
|
||||
updateClock()
|
||||
timer = setInterval(updateClock, 30000)
|
||||
runSequence()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) clearInterval(timer)
|
||||
if (phaseTimer) clearTimeout(phaseTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -58,115 +90,163 @@ onUnmounted(() => {
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
.demo-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.desktop-view {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.screen-wrapper { display: flex; justify-content: center; }
|
||||
.screen {
|
||||
width: 14rem;
|
||||
height: 9rem;
|
||||
background: #1a1a1a;
|
||||
width: 16rem;
|
||||
height: 10rem;
|
||||
background: #111;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
transition: all 0.5s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.screen.loaded {
|
||||
background: linear-gradient(180deg, #2d5af5 0%, #1e3a8a 100%);
|
||||
/* BIOS phase */
|
||||
.phase-bios {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.boot-screen {
|
||||
.bios-text {
|
||||
font-size: 0.7rem;
|
||||
color: #0f0;
|
||||
font-family: monospace;
|
||||
animation: blink 1s steps(1) infinite;
|
||||
}
|
||||
@keyframes blink {
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
/* Boot phase */
|
||||
.phase-boot {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #666;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.boot-logo {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
animation: spin 2s linear infinite;
|
||||
.boot-spinner {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border: 2px solid #333;
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.boot-text {
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.65rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.desktop {
|
||||
/* Loading phase */
|
||||
.phase-loading {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.loading-bar-track {
|
||||
width: 8rem;
|
||||
height: 4px;
|
||||
background: #333;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.loading-bar-fill {
|
||||
height: 100%;
|
||||
background: #4a9eff;
|
||||
border-radius: 2px;
|
||||
animation: fill 1.6s ease-out forwards;
|
||||
}
|
||||
@keyframes fill {
|
||||
from { width: 0; }
|
||||
to { width: 100%; }
|
||||
}
|
||||
.loading-text {
|
||||
font-size: 0.65rem;
|
||||
color: #888;
|
||||
}
|
||||
/* Desktop phase */
|
||||
.phase-desktop {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: linear-gradient(135deg, #1e3a8a 0%, #2563eb 100%);
|
||||
animation: fadeIn 0.5s ease;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.desktop-icons {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
align-content: flex-start;
|
||||
}
|
||||
|
||||
.desktop-icon {
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
.icon-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 2.5rem;
|
||||
}
|
||||
.icon-box {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background: rgba(255,255,255,0.15);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.icon-label {
|
||||
font-size: 0.5rem;
|
||||
color: rgba(255,255,255,0.8);
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
.taskbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background: rgba(0,0,0,0.3);
|
||||
padding: 0.3rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.taskbar-icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.taskbar-time {
|
||||
margin-left: auto;
|
||||
.taskbar-start {
|
||||
font-size: 0.7rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tap-hint {
|
||||
text-align: center;
|
||||
font-size: 0.72rem;
|
||||
.taskbar-spacer { flex: 1; }
|
||||
.taskbar-clock {
|
||||
font-size: 0.6rem;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
/* Phase labels */
|
||||
.phase-labels {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.8rem;
|
||||
margin-top: 0.7rem;
|
||||
}
|
||||
.phase-label {
|
||||
font-size: 0.62rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.75rem;
|
||||
opacity: 0.4;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
.phase-label.active {
|
||||
opacity: 1;
|
||||
color: var(--vp-c-brand);
|
||||
}
|
||||
</style>
|
||||
|
||||
+3
-3
@@ -37,13 +37,13 @@ const eras = ref([
|
||||
{
|
||||
name: '原生时代',
|
||||
time: '1990s',
|
||||
desc: '直接操作 DOM,一切从零开始',
|
||||
desc: '直接用代码操控页面元素,一切从零开始',
|
||||
techs: ['HTML', 'CSS', 'JavaScript']
|
||||
},
|
||||
{
|
||||
name: 'jQuery 时代',
|
||||
time: '2006-2015',
|
||||
desc: '简化 DOM 操作,跨浏览器兼容',
|
||||
desc: '简化页面操控,跨浏览器兼容',
|
||||
techs: ['jQuery', 'Bootstrap']
|
||||
},
|
||||
{
|
||||
@@ -55,7 +55,7 @@ const eras = ref([
|
||||
{
|
||||
name: '组件化时代',
|
||||
time: '2013-至今',
|
||||
desc: '声明式、组件化、虚拟 DOM',
|
||||
desc: '声明式、组件化,框架自动更新页面',
|
||||
techs: ['React', 'Vue', 'Angular']
|
||||
},
|
||||
{
|
||||
|
||||
+2
-18
@@ -10,8 +10,6 @@
|
||||
v-for="tech in triad"
|
||||
:key="tech.name"
|
||||
class="tech-card"
|
||||
:class="{ active: activeTech === tech.name }"
|
||||
@click="activeTech = tech.name"
|
||||
>
|
||||
<div class="tech-name">{{ tech.name }}</div>
|
||||
<div class="tech-role">{{ tech.role }}</div>
|
||||
@@ -29,11 +27,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeTech = ref('HTML')
|
||||
|
||||
const triad = ref([
|
||||
const triad = [
|
||||
{
|
||||
name: 'HTML',
|
||||
role: '结构层',
|
||||
@@ -52,7 +46,7 @@ const triad = ref([
|
||||
analogy: '房子的智能:开关灯、开门',
|
||||
examples: ['事件', 'DOM操作', '网络请求']
|
||||
}
|
||||
])
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -95,16 +89,6 @@ const triad = ref([
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tech-card:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.tech-card.active {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.tech-name {
|
||||
|
||||
+66
-90
@@ -1,120 +1,96 @@
|
||||
<template>
|
||||
<div class="full-process-demo">
|
||||
<div class="demo-label">完整流程概览 ── 点击逐步演示</div>
|
||||
|
||||
<div class="process-flow">
|
||||
<div
|
||||
v-for="(phase, index) in phases"
|
||||
:key="phase.name"
|
||||
class="phase"
|
||||
:class="{ active: currentPhase >= index }"
|
||||
@click="currentPhase = index"
|
||||
>
|
||||
<div class="phase-icon">{{ phase.icon }}</div>
|
||||
<div class="phase-name">{{ phase.name }}</div>
|
||||
<div class="phase-steps">
|
||||
<span v-for="step in phase.steps" :key="step" class="step-tag">{{ step }}</span>
|
||||
<div class="full-demo">
|
||||
<div class="demo-title">从按下电源到看到网页 ── 完整链路</div>
|
||||
<div class="chain">
|
||||
<div v-for="(phase, i) in phases" :key="phase.name" class="chain-item">
|
||||
<div class="phase-card" :style="{ borderLeftColor: phase.color }">
|
||||
<div class="phase-head">
|
||||
<span class="phase-icon">{{ phase.icon }}</span>
|
||||
<span class="phase-name">{{ phase.name }}</span>
|
||||
</div>
|
||||
<div class="phase-steps">
|
||||
{{ phase.steps }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="i < phases.length - 1" class="chain-arrow">
|
||||
<svg width="20" height="14" viewBox="0 0 20 14">
|
||||
<path d="M0 7h14M10 2l6 5-6 5" fill="none" stroke="var(--vp-c-text-3)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tap-hint">👆 点击查看各阶段</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const currentPhase = ref(0)
|
||||
const phases = [
|
||||
{ icon: '🔌', name: '硬件启动', steps: ['电源', '主板', 'CPU', 'BIOS'] },
|
||||
{ icon: '💻', name: '系统启动', steps: ['引导程序', '内核', '服务', '桌面'] },
|
||||
{ icon: '🌐', name: '浏览器', steps: ['双击图标', '加载程序', '显示窗口'] },
|
||||
{ icon: '📡', name: '网络请求', steps: ['URL解析', 'DNS', 'TCP', 'HTTP'] },
|
||||
{ icon: '🎨', name: '页面渲染', steps: ['HTML解析', 'CSS', '布局', '绘制'] }
|
||||
{ icon: '🔌', name: '硬件启动', color: '#f59e0b', steps: '电源 → 主板 → CPU → BIOS' },
|
||||
{ icon: '🔍', name: '固件自检', color: '#ef4444', steps: 'POST → 初始化 → 找启动盘' },
|
||||
{ icon: '💻', name: '系统启动', color: '#8b5cf6', steps: '引导 → 内核 → 服务 → 桌面' },
|
||||
{ icon: '🌐', name: '浏览器启动', color: '#3b82f6', steps: '创建进程 → 加载代码 → 就绪' },
|
||||
{ icon: '📡', name: '网络请求与渲染', color: '#10b981', steps: 'DNS → TCP → HTTP → 渲染' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.full-process-demo {
|
||||
.full-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.process-flow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.phase {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0.6rem 0.4rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
opacity: 0.4;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.phase.active {
|
||||
opacity: 1;
|
||||
border-color: var(--vp-c-brand);
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.phase-icon {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.phase-name {
|
||||
font-size: 0.7rem;
|
||||
.demo-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.4rem;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.phase-steps {
|
||||
.chain {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.step-tag {
|
||||
font-size: 0.58rem;
|
||||
text-align: center;
|
||||
padding: 0.15rem 0;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 3px;
|
||||
color: var(--vp-c-text-3);
|
||||
.chain-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.phase.active .step-tag {
|
||||
.phase-card {
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-2);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-left: 3px solid;
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem 0.6rem;
|
||||
min-width: 6rem;
|
||||
}
|
||||
|
||||
.tap-hint {
|
||||
text-align: center;
|
||||
font-size: 0.72rem;
|
||||
.phase-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.phase-icon { font-size: 0.9rem; }
|
||||
.phase-name {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
.phase-steps {
|
||||
font-size: 0.58rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.75rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.chain-arrow {
|
||||
padding: 0 0.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.chain { flex-direction: column; gap: 0.15rem; }
|
||||
.chain-item { flex-direction: column; }
|
||||
.chain-arrow { transform: rotate(90deg); padding: 0.1rem 0; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div class="generic-type-demo">
|
||||
<h4>🧩 泛型:写一次,适用所有类型</h4>
|
||||
<p class="desc">点击不同场景,看泛型如何让代码既灵活又安全</p>
|
||||
|
||||
<div class="scene-selector">
|
||||
<button
|
||||
v-for="(s, i) in scenes"
|
||||
:key="i"
|
||||
:class="['scene-btn', { active: selected === i }]"
|
||||
@click="selected = i"
|
||||
>
|
||||
{{ s.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="code-comparison">
|
||||
<div class="code-panel">
|
||||
<div class="panel-tag bad">❌ 没有泛型</div>
|
||||
<pre class="code-block">{{ scenes[selected].without }}</pre>
|
||||
<div class="panel-problem">{{ scenes[selected].problem }}</div>
|
||||
</div>
|
||||
<div class="code-panel">
|
||||
<div class="panel-tag good">✅ 使用泛型</div>
|
||||
<pre class="code-block">{{ scenes[selected].withGeneric }}</pre>
|
||||
<div class="panel-benefit">{{ scenes[selected].benefit }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="type-flow">
|
||||
<div class="flow-title">类型传递过程</div>
|
||||
<div class="flow-steps">
|
||||
<span
|
||||
v-for="(step, j) in scenes[selected].flow"
|
||||
:key="j"
|
||||
class="flow-step"
|
||||
>
|
||||
<code>{{ step }}</code>
|
||||
<span v-if="j < scenes[selected].flow.length - 1" class="flow-arrow">→</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selected = ref(0)
|
||||
|
||||
const scenes = [
|
||||
{
|
||||
label: '通用函数',
|
||||
without: `// 要为每种类型写一个函数
|
||||
function getFirstNumber(arr: number[]): number {
|
||||
return arr[0]
|
||||
}
|
||||
function getFirstString(arr: string[]): string {
|
||||
return arr[0]
|
||||
}
|
||||
// 还有 boolean、object...写不完`,
|
||||
withGeneric: `// 一个泛型函数搞定所有类型
|
||||
function getFirst<T>(arr: T[]): T {
|
||||
return arr[0]
|
||||
}
|
||||
|
||||
getFirst<number>([1, 2, 3]) // → number
|
||||
getFirst<string>(["a", "b"]) // → string`,
|
||||
problem: '每种类型都要写一遍,代码重复',
|
||||
benefit: 'T 是类型参数,调用时自动替换为实际类型',
|
||||
flow: ['T = number', 'arr: number[]', '返回值: number']
|
||||
},
|
||||
{
|
||||
label: '类型安全容器',
|
||||
without: `// 用 any 失去类型安全
|
||||
class Box {
|
||||
value: any
|
||||
get(): any { return this.value }
|
||||
}
|
||||
const box = new Box()
|
||||
box.value = 42
|
||||
const v = box.get() // v 是 any,没有类型提示`,
|
||||
withGeneric: `// 泛型类保持类型安全
|
||||
class Box<T> {
|
||||
value: T
|
||||
get(): T { return this.value }
|
||||
}
|
||||
const box = new Box<number>()
|
||||
box.value = 42
|
||||
const v = box.get() // v 是 number,有完整提示`,
|
||||
problem: 'any 类型没有任何类型检查和提示',
|
||||
benefit: '泛型类在实例化时确定类型,全程类型安全',
|
||||
flow: ['Box<number>', 'value: number', 'get(): number']
|
||||
},
|
||||
{
|
||||
label: '类型约束',
|
||||
without: `// 没有约束,什么都能传
|
||||
function getLength<T>(item: T): number {
|
||||
return item.length // ❌ 编译错误!
|
||||
// T 可能没有 length 属性
|
||||
}`,
|
||||
withGeneric: `// 用 extends 约束 T 必须有 length
|
||||
interface HasLength { length: number }
|
||||
|
||||
function getLength<T extends HasLength>(item: T) {
|
||||
return item.length // ✅ 安全!
|
||||
}
|
||||
|
||||
getLength("hello") // ✅ string 有 length
|
||||
getLength([1, 2, 3]) // ✅ array 有 length
|
||||
getLength(42) // ❌ number 没有 length`,
|
||||
problem: '不加约束,泛型太"自由",无法安全访问属性',
|
||||
benefit: 'extends 约束确保 T 一定有 length 属性',
|
||||
flow: ['T extends HasLength', '确保有 .length', '安全访问']
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.generic-type-demo {
|
||||
padding: 20px; border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px; margin: 16px 0; background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
.scene-selector { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.scene-btn {
|
||||
padding: 6px 14px; border-radius: 6px; border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg); cursor: pointer; font-size: 13px; transition: all 0.2s;
|
||||
}
|
||||
.scene-btn.active { background: var(--vp-c-brand-1); color: #fff; border-color: var(--vp-c-brand-1); }
|
||||
.code-comparison { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; }
|
||||
.code-panel { border: 1px solid var(--vp-c-divider); border-radius: 8px; overflow: hidden; background: var(--vp-c-bg); }
|
||||
.panel-tag { padding: 6px 12px; font-size: 12px; font-weight: 600; border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.panel-tag.bad { background: #fef2f2; color: #991b1b; }
|
||||
.panel-tag.good { background: #f0fdf4; color: #166534; }
|
||||
.code-block { padding: 10px 12px; margin: 0; font-size: 12px; line-height: 1.5; white-space: pre-wrap; }
|
||||
.panel-problem, .panel-benefit { padding: 6px 12px; font-size: 12px; border-top: 1px solid var(--vp-c-divider); }
|
||||
.panel-problem { background: #fef2f2; color: #991b1b; }
|
||||
.panel-benefit { background: #f0fdf4; color: #166534; }
|
||||
.type-flow { padding: 12px 14px; background: var(--vp-c-brand-soft); border-radius: 8px; }
|
||||
.flow-title { font-weight: 600; font-size: 13px; margin-bottom: 8px; }
|
||||
.flow-steps { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
||||
.flow-step { display: flex; align-items: center; gap: 6px; }
|
||||
.flow-step code { padding: 3px 8px; background: var(--vp-c-bg); border-radius: 4px; font-size: 12px; }
|
||||
.flow-arrow { color: var(--vp-c-text-3); font-weight: 600; }
|
||||
@media (max-width: 640px) { .code-comparison { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
@@ -0,0 +1,591 @@
|
||||
<template>
|
||||
<div class="io-method-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">I/O 方式对比</span>
|
||||
<span class="subtitle">程序查询 · 中断方式 · DMA</span>
|
||||
</div>
|
||||
|
||||
<div class="io-tabs">
|
||||
<button
|
||||
v-for="method in ioMethods"
|
||||
:key="method.name"
|
||||
:class="['tab-btn', { active: selectedMethod === method.name }]"
|
||||
@click="selectedMethod = method.name"
|
||||
>
|
||||
{{ method.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="method-details" v-if="selectedMethodData">
|
||||
<div class="detail-header">
|
||||
<span class="method-name">{{ selectedMethodData.name }}</span>
|
||||
<span class="method-english">{{ selectedMethodData.english }}</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-flow">
|
||||
<div class="flow-title">工作流程</div>
|
||||
<div class="flow-diagram">
|
||||
<div v-for="(step, i) in selectedMethodData.steps" :key="i" class="flow-node">
|
||||
<div class="node-box" :class="{ active: activeStep === i }" @click="activeStep = i">
|
||||
<span class="node-num">{{ i + 1 }}</span>
|
||||
<span class="node-text">{{ step }}</span>
|
||||
</div>
|
||||
<div v-if="i < selectedMethodData.steps.length - 1" class="flow-arrow">↓</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-comparison">
|
||||
<div class="comp-grid">
|
||||
<div class="comp-item">
|
||||
<span class="comp-label">CPU 参与度</span>
|
||||
<span class="comp-value" :class="selectedMethodData.cpuLevel">{{ selectedMethodData.cpuLevel }}</span>
|
||||
</div>
|
||||
<div class="comp-item">
|
||||
<span class="comp-label">速度</span>
|
||||
<span class="comp-value">{{ selectedMethodData.speed }}</span>
|
||||
</div>
|
||||
<div class="comp-item">
|
||||
<span class="comp-label">复杂度</span>
|
||||
<span class="comp-value">{{ selectedMethodData.complexity }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comparison-section">
|
||||
<div class="section-title">三种 I/O 方式对比</div>
|
||||
<table class="compare-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>特性</th>
|
||||
<th>程序查询</th>
|
||||
<th>中断方式</th>
|
||||
<th>DMA</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>CPU 参与度</td>
|
||||
<td>全程参与</td>
|
||||
<td>仅处理中断</td>
|
||||
<td>几乎不参与</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>数据传输</td>
|
||||
<td>CPU 逐字节搬运</td>
|
||||
<td>CPU 逐字搬运</td>
|
||||
<td>外设直接到内存</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>优点</td>
|
||||
<td>简单、控制灵活</td>
|
||||
<td>CPU 效率高</td>
|
||||
<td>CPU 完全解放</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>缺点</td>
|
||||
<td>CPU 利用率低</td>
|
||||
<td>中断开销</td>
|
||||
<td>硬件复杂</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>适用场景</td>
|
||||
<td>简单外设、低速设备</td>
|
||||
<td>中低速设备</td>
|
||||
<td>高速批量传输</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="dma-demo" v-if="selectedMethod === 'DMA'">
|
||||
<div class="dma-title">DMA 传输过程</div>
|
||||
<div class="dma-visual">
|
||||
<div class="device cpu-device">
|
||||
<div class="device-icon">💻</div>
|
||||
<div class="device-name">CPU</div>
|
||||
</div>
|
||||
|
||||
<div class="dma-channel">
|
||||
<div class="channel-step" v-if="dmaStep >= 1">
|
||||
<span class="step-label">1. CPU 设置 DMA</span>
|
||||
<span class="step-arrow">→</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="device dma-device">
|
||||
<div class="device-icon">🔧</div>
|
||||
<div class="device-name">DMA 控制器</div>
|
||||
</div>
|
||||
|
||||
<div class="dma-channel">
|
||||
<div class="channel-step" v-if="dmaStep >= 2">
|
||||
<span class="step-label">2. DMA 直接访问内存</span>
|
||||
<span class="step-arrow">→</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="device memory-device">
|
||||
<div class="device-icon">💾</div>
|
||||
<div class="device-name">内存</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dma-controls">
|
||||
<button class="btn" @click="startDma" :disabled="dmaStep > 0">开始 DMA 传输</button>
|
||||
<button class="btn" @click="resetDma">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="interrupt-demo" v-if="selectedMethod === '中断方式'">
|
||||
<div class="interrupt-title">中断处理流程</div>
|
||||
<div class="interrupt-visual">
|
||||
<div class="timeline">
|
||||
<div class="timeline-item" v-for="(item, i) in interruptFlow" :key="i" :class="{ active: interruptStep === i }">
|
||||
<div class="timeline-num">{{ i + 1 }}</div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-title">{{ item.title }}</div>
|
||||
<div class="timeline-desc">{{ item.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="interrupt-controls">
|
||||
<button class="btn" @click="nextInterrupt" :disabled="interruptStep >= interruptFlow.length - 1">下一步</button>
|
||||
<button class="btn" @click="resetInterrupt">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const selectedMethod = ref('程序查询')
|
||||
const activeStep = ref(0)
|
||||
const dmaStep = ref(0)
|
||||
const interruptStep = ref(0)
|
||||
|
||||
const ioMethods = ref([
|
||||
{
|
||||
name: '程序查询',
|
||||
english: 'Programmed I/O',
|
||||
cpuLevel: '高',
|
||||
speed: '慢',
|
||||
complexity: '低',
|
||||
steps: [
|
||||
'CPU 轮询检查 I/O 设备状态',
|
||||
'设备忙?继续等待',
|
||||
'设备就绪,发送读写命令',
|
||||
'CPU 逐字节读取/写入数据',
|
||||
'判断是否传输完成',
|
||||
'未完成则继续查询'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: '中断方式',
|
||||
english: 'Interrupt-Driven I/O',
|
||||
cpuLevel: '中',
|
||||
speed: '中',
|
||||
complexity: '中',
|
||||
steps: [
|
||||
'CPU 启动 I/O 设备',
|
||||
'CPU 继续执行其他任务',
|
||||
'I/O 完成后发送中断请求',
|
||||
'CPU 响应中断,保存现场',
|
||||
'执行中断处理程序',
|
||||
'恢复现场,继续执行'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'DMA',
|
||||
english: 'Direct Memory Access',
|
||||
cpuLevel: '低',
|
||||
speed: '快',
|
||||
complexity: '高',
|
||||
steps: [
|
||||
'CPU 设置 DMA 控制器',
|
||||
'告诉 DMA 源地址、目标地址、传输长度',
|
||||
'CPU 去执行其他任务',
|
||||
'DMA 控制器直接与内存交换数据',
|
||||
'传输完成,DMA 发送中断通知 CPU'
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
const selectedMethodData = computed(() => {
|
||||
return ioMethods.value.find(m => m.name === selectedMethod.value)
|
||||
})
|
||||
|
||||
const interruptFlow = ref([
|
||||
{ title: '中断请求', desc: 'I/O 设备向 CPU 发送中断请求信号' },
|
||||
{ title: '中断响应', desc: 'CPU 完成当前指令后响应中断' },
|
||||
{ title: '保存现场', desc: '保存 PC、寄存器等当前状态到栈' },
|
||||
{ title: '中断处理', desc: '执行中断服务程序 ISR' },
|
||||
{ title: '恢复现场', desc: '恢复保存的寄存器值' },
|
||||
{ title: '返回执行', desc: '返回被中断的程序继续执行' }
|
||||
])
|
||||
|
||||
const startDma = () => {
|
||||
dmaStep.value = 1
|
||||
setTimeout(() => {
|
||||
dmaStep.value = 2
|
||||
setTimeout(() => {
|
||||
dmaStep.value = 3
|
||||
}, 1000)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const resetDma = () => {
|
||||
dmaStep.value = 0
|
||||
}
|
||||
|
||||
const nextInterrupt = () => {
|
||||
if (interruptStep.value < interruptFlow.value.length - 1) {
|
||||
interruptStep.value++
|
||||
}
|
||||
}
|
||||
|
||||
const resetInterrupt = () => {
|
||||
interruptStep.value = 0
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.io-method-demo {
|
||||
background: linear-gradient(135deg, #fce7f3 0%, #fbcfe8 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.io-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 10px 20px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
border-color: #ec4899;
|
||||
background: #fdf2f8;
|
||||
}
|
||||
|
||||
.method-details {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 2px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.method-name {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.method-english {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.detail-flow {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.flow-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.flow-diagram {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.flow-node {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.node-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 16px;
|
||||
background: #f8fafc;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.node-box.active {
|
||||
border-color: #ec4899;
|
||||
background: #fdf2f8;
|
||||
}
|
||||
|
||||
.node-num {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #ec4899;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.node-text {
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 18px;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.detail-comparison {
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.comp-grid {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.comp-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.comp-label {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.comp-value {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.comp-value.高 { color: #dc2626; }
|
||||
.comp-value.中 { color: #f59e0b; }
|
||||
.comp-value.低 { color: #16a34a; }
|
||||
|
||||
.comparison-section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.compare-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.compare-table th,
|
||||
.compare-table td {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.compare-table th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.compare-table td {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.dma-demo, .interrupt-demo {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.dma-title, .interrupt-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.dma-visual {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.device {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.device-icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.device-name {
|
||||
font-size: 12px;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.dma-channel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.channel-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
background: #dbeafe;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.step-arrow {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.dma-controls, .interrupt-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
||||
.interrupt-visual {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 10px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.timeline-item.active {
|
||||
border-left-color: #ec4899;
|
||||
background: #fdf2f8;
|
||||
}
|
||||
|
||||
.timeline-num {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #ec4899;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.timeline-desc {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
</style>
|
||||
+398
@@ -0,0 +1,398 @@
|
||||
<template>
|
||||
<div class="instruction-format-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">机器指令格式</span>
|
||||
<span class="subtitle">操作码 + 操作数 = 机器指令</span>
|
||||
</div>
|
||||
|
||||
<div class="format-selector">
|
||||
<button
|
||||
v-for="fmt in instructionFormats"
|
||||
:key="fmt.type"
|
||||
:class="['format-btn', { active: selectedFormat === fmt.type }]"
|
||||
@click="selectedFormat = fmt.type"
|
||||
>
|
||||
{{ fmt.type }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="format-visualization" v-if="selectedFormatData">
|
||||
<div class="format-diagram">
|
||||
<div
|
||||
v-for="(field, i) in selectedFormatData.fields"
|
||||
:key="i"
|
||||
class="field-box"
|
||||
:style="{ flex: field.bits }"
|
||||
>
|
||||
<span class="field-name">{{ field.name }}</span>
|
||||
<span class="field-bits">{{ field.bits }}位</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="format-example">
|
||||
<div class="example-title">示例指令</div>
|
||||
<div class="binary-display">
|
||||
<span
|
||||
v-for="(bit, i) in selectedFormatData.example"
|
||||
:key="i"
|
||||
class="bit"
|
||||
:class="{ highlight: isHighlight(i, selectedFormatData) }"
|
||||
>
|
||||
{{ bit }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="example-desc">{{ selectedFormatData.description }}</div>
|
||||
</div>
|
||||
|
||||
<div class="format-explanation">
|
||||
<div class="exp-title">{{ selectedFormatData.type }} 格式说明</div>
|
||||
<div class="exp-content">{{ selectedFormatData.explanation }}</div>
|
||||
|
||||
<div class="examples-list" v-if="selectedFormatData.examples">
|
||||
<div class="list-title">常见指令示例</div>
|
||||
<div v-for="ex in selectedFormatData.examples" :key="ex.name" class="example-item">
|
||||
<span class="ex-name">{{ ex.name }}</span>
|
||||
<span class="ex-desc">{{ ex.desc }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="opcode-table">
|
||||
<div class="table-title">常用操作码 (Opcode)</div>
|
||||
<div class="opcode-grid">
|
||||
<div v-for="op in opcodes" :key="op.code" class="opcode-item">
|
||||
<span class="op-code">{{ op.code }}</span>
|
||||
<span class="op-name">{{ op.name }}</span>
|
||||
<span class="op-desc">{{ op.desc }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const selectedFormat = ref('three-address')
|
||||
|
||||
const instructionFormats = ref([
|
||||
{
|
||||
type: '零地址',
|
||||
fields: [{ name: '操作码', bits: 8 }],
|
||||
example: '01101100',
|
||||
description: '操作数隐含在栈顶',
|
||||
explanation: '零地址指令只有操作码,操作数隐含在操作数栈中。常用于堆栈计算机,如 ENTER、EXIT 等。',
|
||||
examples: [
|
||||
{ name: 'POP', desc: '弹出栈顶数据' },
|
||||
{ name: 'PUSH', desc: '压入数据到栈顶' },
|
||||
{ name: 'CALL', desc: '调用子程序' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: '一地址',
|
||||
fields: [
|
||||
{ name: '操作码', bits: 8 },
|
||||
{ name: '地址', bits: 24 }
|
||||
],
|
||||
example: '01101100 00000001 00000010 00000011',
|
||||
description: '一个操作数地址,另一个隐含',
|
||||
explanation: '一地址指令有一个操作数在内存/寄存器中,另一个操作数隐含在 ACC(累加器)中。如 INC、DEC 等单操作数指令。',
|
||||
examples: [
|
||||
{ name: 'INC A', desc: 'A = A + 1' },
|
||||
{ name: 'DEC A', desc: 'A = A - 1' },
|
||||
{ name: 'NOT A', desc: 'A = ~A' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: '二地址',
|
||||
fields: [
|
||||
{ name: '操作码', bits: 8 },
|
||||
{ name: '目的地址', bits: 8 },
|
||||
{ name: '源地址', bits: 8 }
|
||||
],
|
||||
example: '01101100 00000001 00000010',
|
||||
description: '两个操作数地址,结果存目的地址',
|
||||
explanation: '最常用的指令格式。两个操作数地址,结果覆盖目的操作数。如 ADD、SUB、MOV 等。',
|
||||
examples: [
|
||||
{ name: 'MOV R1, R2', desc: 'R1 = R2' },
|
||||
{ name: 'ADD R1, R2', desc: 'R1 = R1 + R2' },
|
||||
{ name: 'SUB R1, R2', desc: 'R1 = R1 - R2' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: '三地址',
|
||||
fields: [
|
||||
{ name: '操作码', bits: 8 },
|
||||
{ name: '目的', bits: 8 },
|
||||
{ name: '源1', bits: 8 },
|
||||
{ name: '源2', bits: 8 }
|
||||
],
|
||||
example: '01101100 00000001 00000010 00000011',
|
||||
description: '结果存新地址,不破坏源操作数',
|
||||
explanation: '三个地址分别指定目的操作数和两个源操作数。结果存入目的地址,不改变源操作数。常见于复杂指令集。',
|
||||
examples: [
|
||||
{ name: 'ADD R1, R2, R3', desc: 'R1 = R2 + R3' },
|
||||
{ name: 'SUB R1, R2, R3', desc: 'R1 = R2 - R3' },
|
||||
{ name: 'MUL R1, R2, R3', desc: 'R1 = R2 × R3' }
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
const selectedFormatData = computed(() => {
|
||||
return instructionFormats.value.find(f => f.type === selectedFormat.value)
|
||||
})
|
||||
|
||||
const isHighlight = (index, formatData) => {
|
||||
const opcodeBits = 8
|
||||
return index < opcodeBits
|
||||
}
|
||||
|
||||
const opcodes = ref([
|
||||
{ code: '00000000', name: 'NOP', desc: '无操作' },
|
||||
{ code: '00000001', name: 'MOV', desc: '数据传送' },
|
||||
{ code: '00000010', name: 'ADD', desc: '加法' },
|
||||
{ code: '00000011', name: 'SUB', desc: '减法' },
|
||||
{ code: '00000100', name: 'MUL', desc: '乘法' },
|
||||
{ code: '00000101', name: 'DIV', desc: '除法' },
|
||||
{ code: '00000110', name: 'AND', desc: '逻辑与' },
|
||||
{ code: '00000111', name: 'OR', desc: '逻辑或' },
|
||||
{ code: '00001000', name: 'NOT', desc: '逻辑非' },
|
||||
{ code: '00001001', name: 'XOR', desc: '异或' },
|
||||
{ code: '00001010', name: 'SHL', desc: '左移' },
|
||||
{ code: '00001011', name: 'SHR', desc: '右移' },
|
||||
{ code: '00001100', name: 'JMP', desc: '无条件跳转' },
|
||||
{ code: '00001101', name: 'JE', desc: '相等跳转' },
|
||||
{ code: '00001110', name: 'JNE', desc: '不等跳转' },
|
||||
{ code: '00001111', name: 'CALL', desc: '调用子程序' },
|
||||
{ code: '00010000', name: 'RET', desc: '返回' },
|
||||
{ code: '00010001', name: 'PUSH', desc: '压栈' },
|
||||
{ code: '00010010', name: 'POP', desc: '出栈' },
|
||||
{ code: '00010011', name: 'LOAD', desc: '从内存加载' },
|
||||
{ code: '00010100', name: 'STORE', desc: '存入内存' }
|
||||
])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.instruction-format-demo {
|
||||
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.format-selector {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.format-btn {
|
||||
padding: 8px 16px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.format-btn.active {
|
||||
border-color: #22c55e;
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.format-visualization {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.format-diagram {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.field-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px 8px;
|
||||
background: #e0f2fe;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.field-box:first-child {
|
||||
background: #fef3c7;
|
||||
}
|
||||
|
||||
.field-name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.field-bits {
|
||||
font-size: 10px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.format-example {
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.example-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.binary-display {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.bit {
|
||||
padding: 4px 6px;
|
||||
background: #e2e8f0;
|
||||
border-radius: 2px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.bit.highlight {
|
||||
background: #fef3c7;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.example-desc {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.format-explanation {
|
||||
padding: 12px;
|
||||
background: #f0f9ff;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.exp-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.exp-content {
|
||||
font-size: 12px;
|
||||
color: #475569;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.examples-list {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.example-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 4px 0;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.ex-name {
|
||||
font-family: monospace;
|
||||
color: #0369a1;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.ex-desc {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.opcode-table {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.opcode-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.opcode-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
background: #f8fafc;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.op-code {
|
||||
font-family: monospace;
|
||||
padding: 2px 6px;
|
||||
background: #e0f2fe;
|
||||
border-radius: 2px;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.op-name {
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.op-desc {
|
||||
color: #64748b;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="lexer-token-demo">
|
||||
<h4>🔤 词法分析器:把代码拆成 Token</h4>
|
||||
<p class="desc">输入一行代码,实时看到词法分析的结果</p>
|
||||
|
||||
<div class="input-area">
|
||||
<input
|
||||
v-model="code"
|
||||
class="code-input"
|
||||
placeholder="试试输入: let x = 10 + 5;"
|
||||
@input="tokenize"
|
||||
/>
|
||||
<div class="presets">
|
||||
<button v-for="p in presets" :key="p" class="preset-btn" @click="code = p; tokenize()">
|
||||
{{ p }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="tokens.length" class="token-stream">
|
||||
<div class="stream-label">Token 流:</div>
|
||||
<div class="tokens">
|
||||
<span
|
||||
v-for="(t, i) in tokens"
|
||||
:key="i"
|
||||
:class="['token', 'token-' + t.type]"
|
||||
@mouseenter="hovered = i"
|
||||
@mouseleave="hovered = null"
|
||||
>
|
||||
{{ t.value }}
|
||||
<span v-if="hovered === i" class="token-tip">{{ t.label }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="tokens.length" class="token-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Token</th><th>类型</th><th>说明</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(t, i) in tokens" :key="i">
|
||||
<td><code>{{ t.value }}</code></td>
|
||||
<td><span :class="['type-badge', 'token-' + t.type]">{{ t.label }}</span></td>
|
||||
<td class="explain">{{ t.explain }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const code = ref('let x = 10 + 5;')
|
||||
const tokens = ref([])
|
||||
const hovered = ref(null)
|
||||
|
||||
const presets = [
|
||||
'let x = 10 + 5;',
|
||||
'if (a > b) { return a; }',
|
||||
'function add(a, b) { return a + b; }'
|
||||
]
|
||||
|
||||
const keywords = new Set(['let', 'const', 'var', 'if', 'else', 'for', 'while', 'function', 'return', 'class', 'import', 'export', 'true', 'false', 'null', 'undefined'])
|
||||
|
||||
function tokenize() {
|
||||
const result = []
|
||||
let s = code.value.trim()
|
||||
let i = 0
|
||||
while (i < s.length) {
|
||||
if (/\s/.test(s[i])) { i++; continue }
|
||||
if (/[0-9]/.test(s[i])) {
|
||||
let num = ''
|
||||
while (i < s.length && /[0-9.]/.test(s[i])) num += s[i++]
|
||||
result.push({ value: num, type: 'number', label: '数字', explain: '数值字面量' })
|
||||
} else if (/[a-zA-Z_$]/.test(s[i])) {
|
||||
let id = ''
|
||||
while (i < s.length && /[a-zA-Z0-9_$]/.test(s[i])) id += s[i++]
|
||||
if (keywords.has(id)) {
|
||||
result.push({ value: id, type: 'keyword', label: '关键字', explain: '语言保留字' })
|
||||
} else {
|
||||
result.push({ value: id, type: 'identifier', label: '标识符', explain: '变量/函数名' })
|
||||
}
|
||||
} else if (s[i] === '"' || s[i] === "'") {
|
||||
const q = s[i]; let str = q; i++
|
||||
while (i < s.length && s[i] !== q) str += s[i++]
|
||||
if (i < s.length) str += s[i++]
|
||||
result.push({ value: str, type: 'string', label: '字符串', explain: '字符串字面量' })
|
||||
} else if ('+-*/%'.includes(s[i])) {
|
||||
result.push({ value: s[i], type: 'operator', label: '运算符', explain: '算术运算' })
|
||||
i++
|
||||
} else if ('=<>!'.includes(s[i])) {
|
||||
let op = s[i]; i++
|
||||
if (i < s.length && s[i] === '=') { op += s[i]; i++ }
|
||||
if (i < s.length && s[i] === '=') { op += s[i]; i++ }
|
||||
result.push({ value: op, type: 'operator', label: '运算符', explain: '比较/赋值运算' })
|
||||
} else if ('(){}[]'.includes(s[i])) {
|
||||
result.push({ value: s[i], type: 'bracket', label: '括号', explain: '分组/作用域' })
|
||||
i++
|
||||
} else if (';,'.includes(s[i])) {
|
||||
result.push({ value: s[i], type: 'punctuation', label: '分隔符', explain: '语句/参数分隔' })
|
||||
i++
|
||||
} else {
|
||||
result.push({ value: s[i], type: 'unknown', label: '未知', explain: '无法识别' })
|
||||
i++
|
||||
}
|
||||
}
|
||||
tokens.value = result
|
||||
}
|
||||
|
||||
onMounted(tokenize)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.lexer-token-demo {
|
||||
padding: 20px; border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px; margin: 16px 0; background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
.code-input {
|
||||
width: 100%; padding: 10px 12px; border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px; font-family: 'Fira Code', monospace; font-size: 14px;
|
||||
background: var(--vp-c-bg); color: var(--vp-c-text-1); box-sizing: border-box;
|
||||
}
|
||||
.presets { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
|
||||
.preset-btn {
|
||||
padding: 4px 10px; border-radius: 4px; border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg); font-size: 11px; cursor: pointer;
|
||||
font-family: 'Fira Code', monospace; color: var(--vp-c-text-2);
|
||||
}
|
||||
.preset-btn:hover { border-color: var(--vp-c-brand-1); }
|
||||
.token-stream { margin-top: 16px; }
|
||||
.stream-label { font-size: 13px; font-weight: 600; margin-bottom: 8px; }
|
||||
.tokens { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.token {
|
||||
position: relative; padding: 4px 8px; border-radius: 4px;
|
||||
font-family: 'Fira Code', monospace; font-size: 13px; cursor: default;
|
||||
}
|
||||
.token-tip {
|
||||
position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
|
||||
padding: 2px 6px; background: #333; color: #fff; border-radius: 4px;
|
||||
font-size: 11px; white-space: nowrap; font-family: sans-serif;
|
||||
}
|
||||
.token-keyword { background: #dbeafe; color: #1e40af; }
|
||||
.token-identifier { background: #f0fdf4; color: #166534; }
|
||||
.token-number { background: #fef3c7; color: #92400e; }
|
||||
.token-string { background: #fce7f3; color: #9d174d; }
|
||||
.token-operator { background: #ede9fe; color: #5b21b6; }
|
||||
.token-bracket { background: #e0e7ff; color: #3730a3; }
|
||||
.token-punctuation { background: #f1f5f9; color: #475569; }
|
||||
.token-unknown { background: #fef2f2; color: #991b1b; }
|
||||
.token-table { margin-top: 16px; overflow-x: auto; }
|
||||
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
th { text-align: left; padding: 6px 10px; background: var(--vp-c-bg); border-bottom: 2px solid var(--vp-c-divider); }
|
||||
td { padding: 5px 10px; border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.type-badge { padding: 2px 6px; border-radius: 4px; font-size: 11px; }
|
||||
.explain { color: var(--vp-c-text-3); }
|
||||
</style>
|
||||
@@ -0,0 +1,483 @@
|
||||
<template>
|
||||
<div class="psw-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">程序状态字 (PSW)</span>
|
||||
<span class="subtitle">CPU 的"状态指示灯"</span>
|
||||
</div>
|
||||
|
||||
<div class="flags-layout">
|
||||
<div class="flags-grid">
|
||||
<div v-for="flag in flags" :key="flag.name" class="flag-card" :class="{ active: flag.value === 1 }" @click="toggleFlag(flag)">
|
||||
<div class="flag-name">{{ flag.name }}</div>
|
||||
<div class="flag-value">{{ flag.value }}</div>
|
||||
<div class="flag-fullname">{{ flag.fullName }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flag-details" v-if="selectedFlag">
|
||||
<div class="details-header">
|
||||
<span class="flag-title">{{ selectedFlag.name }} - {{ selectedFlag.fullName }}</span>
|
||||
</div>
|
||||
<div class="details-content">
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">英文名:</span>
|
||||
<span class="detail-value">{{ selectedFlag.english }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">作用:</span>
|
||||
<span class="detail-value">{{ selectedFlag.description }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">设置条件:</span>
|
||||
<span class="detail-value">{{ selectedFlag.setCondition }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">用途:</span>
|
||||
<span class="detail-value">{{ selectedFlag.usage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="operation-demo">
|
||||
<div class="demo-title">运算结果对标志位的影响</div>
|
||||
|
||||
<div class="operation-panel">
|
||||
<div class="operand-inputs">
|
||||
<div class="input-group">
|
||||
<label>操作数 A:</label>
|
||||
<input type="number" v-model.number="operandA" class="num-input" />
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>操作数 B:</label>
|
||||
<input type="number" v-model.number="operandB" class="num-input" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="operation-buttons">
|
||||
<button class="op-btn" @click="calculate('ADD')">A + B</button>
|
||||
<button class="op-btn" @click="calculate('SUB')">A - B</button>
|
||||
<button class="op-btn" @click="calculate('AND')">A AND B</button>
|
||||
<button class="op-btn" @click="calculate('OR')">A OR B</button>
|
||||
</div>
|
||||
|
||||
<div class="result-display">
|
||||
<div class="result-label">运算结果:</div>
|
||||
<div class="result-value">{{ result }}</div>
|
||||
</div>
|
||||
|
||||
<div class="flags-result">
|
||||
<div class="result-flags">
|
||||
<span v-for="f in flags" :key="f.name" :class="['result-flag', f.value === 1 ? 'set' : 'clear']">
|
||||
{{ f.name }}:{{ f.value }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="psw-usage">
|
||||
<div class="usage-title">标志位的典型用途</div>
|
||||
<div class="usage-grid">
|
||||
<div class="usage-card">
|
||||
<div class="usage-icon">🔀</div>
|
||||
<div class="usage-name">条件跳转</div>
|
||||
<div class="usage-desc">JE (相等跳转)、JNE、JG、JL 等指令根据 ZF、SF、OF 决定是否跳转</div>
|
||||
</div>
|
||||
<div class="usage-card">
|
||||
<div class="usage-icon">➕</div>
|
||||
<div class="usage-name">算术运算</div>
|
||||
<div class="usage-desc">多位数运算需要 CF 判断进位,OF 判断溢出</div>
|
||||
</div>
|
||||
<div class="usage-card">
|
||||
<div class="usage-icon">🔄</div>
|
||||
<div class="usage-name">循环控制</div>
|
||||
<div class="usage-desc">循环指令使用 ZF 判断循环结束条件</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selectedFlag = ref(null)
|
||||
const operandA = ref(10)
|
||||
const operandB = ref(5)
|
||||
const result = ref(0)
|
||||
|
||||
const flags = ref([
|
||||
{ name: 'CF', fullName: '进位标志', english: 'Carry Flag', value: 0,
|
||||
description: '无符号数运算产生进位或借位时置 1',
|
||||
setCondition: '加法产生进位,或减法产生借位',
|
||||
usage: '多位数无符号运算、循环计数' },
|
||||
{ name: 'PF', fullName: '奇偶标志', english: 'Parity Flag', value: 0,
|
||||
description: '结果的低 8 位中 1 的个数为偶数时置 1',
|
||||
setCondition: '结果低 8 位有偶数个 1',
|
||||
usage: '数据通信中的错误检测' },
|
||||
{ name: 'AF', fullName: '辅助进位', english: 'Auxiliary Carry Flag', value: 0,
|
||||
description: '低 4 位产生进位或借位时置 1',
|
||||
setCondition: '第 3 位(低 4 位)产生进位',
|
||||
usage: 'BCD 码运算调整' },
|
||||
{ name: 'ZF', fullName: '零标志', english: 'Zero Flag', value: 0,
|
||||
description: '运算结果为 0 时置 1',
|
||||
setCondition: '结果 = 0',
|
||||
usage: '条件跳转、循环控制、比较操作' },
|
||||
{ name: 'SF', fullName: '符号标志', english: 'Sign Flag', value: 0,
|
||||
description: '运算结果为负数时置 1(等于结果最高位)',
|
||||
setCondition: '结果最高位 = 1(负数)',
|
||||
usage: '有符号数大小比较、负数判断' },
|
||||
{ name: 'TF', fullName: '陷阱标志', english: 'Trap Flag', value: 0,
|
||||
description: '置 1 时 CPU 进入单步调试模式',
|
||||
setCondition: '软件设置',
|
||||
usage: '程序调试' },
|
||||
{ name: 'IF', fullName: '中断标志', english: 'Interrupt Flag', value: 1,
|
||||
description: '置 1 时 CPU 响应可屏蔽中断',
|
||||
setCondition: '软件设置',
|
||||
usage: '中断开关' },
|
||||
{ name: 'DF', fullName: '方向标志', english: 'Direction Flag', value: 0,
|
||||
description: '置 1 时字符串操作从高地址向低地址',
|
||||
setCondition: '软件设置',
|
||||
usage: '字符串操作方向控制' },
|
||||
{ name: 'OF', fullName: '溢出标志', english: 'Overflow Flag', value: 0,
|
||||
description: '有符号数运算结果超出表示范围时置 1',
|
||||
setCondition: '正溢出或负溢出',
|
||||
usage: '有符号数运算、溢出检测' }
|
||||
])
|
||||
|
||||
const toggleFlag = (flag) => {
|
||||
flag.value = flag.value === 1 ? 0 : 1
|
||||
selectedFlag.value = flag
|
||||
}
|
||||
|
||||
const calculate = (op) => {
|
||||
let res = 0
|
||||
|
||||
if (op === 'ADD') {
|
||||
res = operandA.value + operandB.value
|
||||
|
||||
const unsignedMax = Math.pow(2, 32)
|
||||
const signedMin = -Math.pow(2, 31)
|
||||
const signedMax = Math.pow(2, 31) - 1
|
||||
|
||||
flags.value.find(f => f.name === 'CF').value = res >= unsignedMax ? 1 : 0
|
||||
flags.value.find(f => f.name === 'OF').value = (res > signedMax || res < signedMin) ? 1 : 0
|
||||
flags.value.find(f => f.name === 'ZF').value = res === 0 ? 1 : 0
|
||||
flags.value.find(f => f.name === 'SF').value = res < 0 ? 1 : 0
|
||||
|
||||
const low8 = res & 0xFF
|
||||
const ones = (low8.toString(2).match(/1/g) || []).length
|
||||
flags.value.find(f => f.name === 'PF').value = ones % 2 === 0 ? 1 : 0
|
||||
|
||||
flags.value.find(f => f.name === 'AF').value = ((operandA.value & 0xF) + (operandB.value & 0xF)) >= 16 ? 1 : 0
|
||||
|
||||
} else if (op === 'SUB') {
|
||||
res = operandA.value - operandB.value
|
||||
|
||||
flags.value.find(f => f.name === 'CF').value = operandA.value < operandB.value ? 1 : 0
|
||||
flags.value.find(f => f.name === 'OF').value = (operandA.value > 0 && operandB.value < 0 && res < 0) || (operandA.value < 0 && operandB.value > 0 && res > 0) ? 1 : 0
|
||||
flags.value.find(f => f.name === 'ZF').value = res === 0 ? 1 : 0
|
||||
flags.value.find(f => f.name === 'SF').value = res < 0 ? 1 : 0
|
||||
|
||||
const low8 = res & 0xFF
|
||||
const ones = (low8.toString(2).match(/1/g) || []).length
|
||||
flags.value.find(f => f.name === 'PF').value = ones % 2 === 0 ? 1 : 0
|
||||
|
||||
flags.value.find(f => f.name === 'AF').value = ((operandA.value & 0xF) - (operandB.value & 0xF)) < 0 ? 1 : 0
|
||||
|
||||
} else if (op === 'AND') {
|
||||
res = operandA.value & operandB.value
|
||||
|
||||
flags.value.find(f => f.name === 'CF').value = 0
|
||||
flags.value.find(f => f.name === 'OF').value = 0
|
||||
flags.value.find(f => f.name === 'ZF').value = res === 0 ? 1 : 0
|
||||
flags.value.find(f => f.name === 'SF').value = (res & 0x80000000) !== 0 ? 1 : 0
|
||||
|
||||
const low8 = res & 0xFF
|
||||
const ones = (low8.toString(2).match(/1/g) || []).length
|
||||
flags.value.find(f => f.name === 'PF').value = ones % 2 === 0 ? 1 : 0
|
||||
|
||||
} else if (op === 'OR') {
|
||||
res = operandA.value | operandB.value
|
||||
|
||||
flags.value.find(f => f.name === 'CF').value = 0
|
||||
flags.value.find(f => f.name === 'OF').value = 0
|
||||
flags.value.find(f => f.name === 'ZF').value = res === 0 ? 1 : 0
|
||||
flags.value.find(f => f.name === 'SF').value = (res & 0x80000000) !== 0 ? 1 : 0
|
||||
|
||||
const low8 = res & 0xFF
|
||||
const ones = (low8.toString(2).match(/1/g) || []).length
|
||||
flags.value.find(f => f.name === 'PF').value = ones % 2 === 0 ? 1 : 0
|
||||
}
|
||||
|
||||
result.value = res
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.psw-demo {
|
||||
background: linear-gradient(135deg, #f0fdfa 0%, #ccfbf1 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.flags-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.flag-card {
|
||||
padding: 12px;
|
||||
background: white;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.flag-card:hover {
|
||||
border-color: #14b8a6;
|
||||
}
|
||||
|
||||
.flag-card.active {
|
||||
border-color: #14b8a6;
|
||||
background: #f0fdfa;
|
||||
}
|
||||
|
||||
.flag-name {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.flag-value {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #64748b;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.flag-card.active .flag-value {
|
||||
color: #14b8a6;
|
||||
}
|
||||
|
||||
.flag-fullname {
|
||||
font-size: 10px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.flag-details {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.details-header {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 2px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.flag-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.details-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 12px;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.operation-demo {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.operand-inputs {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
font-size: 12px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.num-input {
|
||||
width: 80px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.operation-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.op-btn {
|
||||
padding: 8px 16px;
|
||||
background: #14b8a6;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.result-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.result-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.result-value {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #14b8a6;
|
||||
}
|
||||
|
||||
.flags-result {
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.result-flags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.result-flag {
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.result-flag.set {
|
||||
background: #dcfce7;
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.result-flag.clear {
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.psw-usage {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.usage-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.usage-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.usage-card {
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.usage-icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.usage-name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.usage-desc {
|
||||
font-size: 10px;
|
||||
color: #64748b;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<div class="pipeline-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">CPU 指令流水线</span>
|
||||
<span class="subtitle">五级流水线:取指 → 译码 → 执行 → 访存 → 写回</span>
|
||||
</div>
|
||||
|
||||
<div class="control-panel">
|
||||
<button class="btn" @click="startPipeline" :disabled="isRunning">开始执行</button>
|
||||
<button class="btn" @click="stepPipeline" :disabled="isRunning">单步执行</button>
|
||||
<button class="btn" @click="resetPipeline">重置</button>
|
||||
<select v-model="selectedMode" class="mode-select">
|
||||
<option value="sequential">顺序执行</option>
|
||||
<option value="pipeline">流水线执行</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="pipeline-visualization">
|
||||
<div class="stage-header">
|
||||
<div v-for="stage in stages" :key="stage" class="stage-label">{{ stage }}</div>
|
||||
</div>
|
||||
|
||||
<div class="instruction-grid">
|
||||
<div v-for="(inst, i) in instructions" :key="i" class="instruction-row">
|
||||
<div class="inst-label">{{ inst }}</div>
|
||||
<div v-for="(stage, j) in stages" :key="j" class="stage-cell">
|
||||
<div v-if="isActive(i, j)" :class="['pipeline-box', currentStageClass(i, j)]">
|
||||
{{ inst }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-panel">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">总周期数</span>
|
||||
<span class="stat-value">{{ totalCycles }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">已完成指令</span>
|
||||
<span class="stat-value">{{ completedInstructions }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">CPI</span>
|
||||
<span class="stat-value">{{ cpi }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pipeline-explanation">
|
||||
<div class="explanation-title">流水线原理</div>
|
||||
<div class="explanation-content">
|
||||
<p><strong>顺序执行:</strong>每条指令执行完才执行下一条,N条指令需要 N × 5 个周期</p>
|
||||
<p><strong>流水线执行:</strong>多条指令同时处于不同阶段,理想情况下 CPI ≈ 1</p>
|
||||
<div class="hazard-warning" v-if="showHazard">
|
||||
⚠️ 流水线冒险:数据冒险、控制冒险、结构冒险
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const stages = ['取指(IF)', '译码(ID)', '执行(EX)', '访存(MEM)', '写回(WB)']
|
||||
const instructions = ref(['ADD R1,R2,R3', 'SUB R4,R1,R5', 'LOAD R6,[R4]', 'STORE R6,[R7]', 'AND R8,R1,R6'])
|
||||
|
||||
const selectedMode = ref('pipeline')
|
||||
const currentCycle = ref(-1)
|
||||
const isRunning = ref(false)
|
||||
const showHazard = ref(false)
|
||||
|
||||
const pipelineState = ref([])
|
||||
|
||||
const startPipeline = () => {
|
||||
isRunning.value = true
|
||||
currentCycle.value = -1
|
||||
runCycle()
|
||||
}
|
||||
|
||||
const runCycle = () => {
|
||||
if (currentCycle.value >= 20) {
|
||||
isRunning.value = false
|
||||
return
|
||||
}
|
||||
currentCycle.value++
|
||||
setTimeout(runCycle, 800)
|
||||
}
|
||||
|
||||
const stepPipeline = () => {
|
||||
if (currentCycle.value < 20) {
|
||||
currentCycle.value++
|
||||
}
|
||||
}
|
||||
|
||||
const resetPipeline = () => {
|
||||
currentCycle.value = -1
|
||||
isRunning.value = false
|
||||
showHazard.value = false
|
||||
}
|
||||
|
||||
const isActive = (instIdx, stageIdx) => {
|
||||
if (selectedMode.value === 'sequential') {
|
||||
return currentCycle.value >= 0 && instIdx === Math.floor(currentCycle.value / 5) && stageIdx === currentCycle.value % 5
|
||||
} else {
|
||||
const offset = instIdx * 5
|
||||
return currentCycle.value >= offset && currentCycle.value < offset + 5 && stageIdx === currentCycle.value - offset
|
||||
}
|
||||
}
|
||||
|
||||
const currentStageClass = (instIdx, stageIdx) => {
|
||||
if (!isActive(instIdx, stageIdx)) return ''
|
||||
return 'stage-' + stageIdx
|
||||
}
|
||||
|
||||
const totalCycles = computed(() => {
|
||||
if (currentCycle.value < 0) return 0
|
||||
if (selectedMode.value === 'sequential') {
|
||||
return (currentCycle.value + 1)
|
||||
} else {
|
||||
return currentCycle.value + 1
|
||||
}
|
||||
})
|
||||
|
||||
const completedInstructions = computed(() => {
|
||||
if (currentCycle.value < 0) return 0
|
||||
if (selectedMode.value === 'sequential') {
|
||||
return Math.floor((currentCycle.value + 1) / 5)
|
||||
} else {
|
||||
return Math.max(0, currentCycle.value - 4)
|
||||
}
|
||||
})
|
||||
|
||||
const cpi = computed(() => {
|
||||
if (completedInstructions.value === 0) return 0
|
||||
return (totalCycles.value / completedInstructions.value).toFixed(2)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pipeline-demo {
|
||||
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 6px 14px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
background: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mode-select {
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.pipeline-visualization {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stage-header {
|
||||
display: grid;
|
||||
grid-template-columns: 100px repeat(5, 1fr);
|
||||
gap: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stage-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.instruction-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.instruction-row {
|
||||
display: grid;
|
||||
grid-template-columns: 100px repeat(5, 1fr);
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.inst-label {
|
||||
font-size: 12px;
|
||||
color: #1e293b;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.stage-cell {
|
||||
height: 28px;
|
||||
background: #f8fafc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.pipeline-box {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stage-0 { background: #f97316; }
|
||||
.stage-1 { background: #eab308; }
|
||||
.stage-2 { background: #22c55e; }
|
||||
.stage-3 { background: #3b82f6; }
|
||||
.stage-4 { background: #8b5cf6; }
|
||||
|
||||
.stats-panel {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.pipeline-explanation {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.explanation-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.explanation-content p {
|
||||
font-size: 12px;
|
||||
color: #475569;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.hazard-warning {
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background: #fef3c7;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
color: #92400e;
|
||||
}
|
||||
</style>
|
||||
@@ -1,46 +1,30 @@
|
||||
<template>
|
||||
<div class="power-on-demo">
|
||||
<div class="demo-label">电脑启动第一步 ── 点击通电</div>
|
||||
|
||||
<div class="sequence" @click="powerOn = !powerOn">
|
||||
<div class="step" :class="{ active: powerOn }">
|
||||
<div class="step-icon">🔌</div>
|
||||
<div class="step-name">电源</div>
|
||||
<div class="step-status">{{ powerOn ? '✅ 供电中' : '⬜ 等待' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow">→</div>
|
||||
|
||||
<div class="step" :class="{ active: powerOn }">
|
||||
<div class="step-icon">🔋</div>
|
||||
<div class="step-name">主板</div>
|
||||
<div class="step-status">{{ powerOn ? '✅ 唤醒' : '⬜ 等待' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow">→</div>
|
||||
|
||||
<div class="step" :class="{ active: powerOn }">
|
||||
<div class="step-icon">⚙️</div>
|
||||
<div class="step-name">CPU</div>
|
||||
<div class="step-status">{{ powerOn ? '✅ 复位' : '⬜ 等待' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow">→</div>
|
||||
|
||||
<div class="step" :class="{ active: powerOn }">
|
||||
<div class="step-icon">💾</div>
|
||||
<div class="step-name">BIOS</div>
|
||||
<div class="step-status">{{ powerOn ? '✅ 启动' : '⬜ 等待' }}</div>
|
||||
<div class="demo-title">硬件启动链路</div>
|
||||
<div class="flow">
|
||||
<div v-for="(step, i) in steps" :key="step.name" class="flow-item">
|
||||
<div class="step-card">
|
||||
<div class="step-icon">{{ step.icon }}</div>
|
||||
<div class="step-name">{{ step.name }}</div>
|
||||
<div class="step-desc">{{ step.desc }}</div>
|
||||
</div>
|
||||
<div v-if="i < steps.length - 1" class="arrow">
|
||||
<svg width="24" height="16" viewBox="0 0 24 16">
|
||||
<path d="M0 8h18M14 3l6 5-6 5" fill="none" stroke="var(--vp-c-text-3)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tap-hint">👆 点击切换电源状态</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const powerOn = ref(false)
|
||||
const steps = [
|
||||
{ icon: '🔌', name: '电源 PSU', desc: '交流电 → 直流电' },
|
||||
{ icon: '🧩', name: '主板芯片组', desc: '协调各硬件部件' },
|
||||
{ icon: '⚙️', name: 'CPU 复位', desc: '清零寄存器,就绪' },
|
||||
{ icon: '📟', name: 'BIOS/UEFI', desc: '执行第一条指令' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -50,66 +34,55 @@ const powerOn = ref(false)
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
.demo-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.sequence {
|
||||
.flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.3rem;
|
||||
gap: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.step {
|
||||
.flow-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
}
|
||||
.step-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0.6rem;
|
||||
border-radius: 6px;
|
||||
opacity: 0.4;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.step.active {
|
||||
opacity: 1;
|
||||
padding: 0.6rem 0.8rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
min-width: 5.5rem;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.step-icon { font-size: 1.4rem; margin-bottom: 0.3rem; }
|
||||
.step-name {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
color: var(--vp-c-text-1);
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.step-status {
|
||||
font-size: 0.65rem;
|
||||
.step-desc {
|
||||
font-size: 0.62rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 0.9rem;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
|
||||
.tap-hint {
|
||||
text-align: center;
|
||||
font-size: 0.72rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
.arrow {
|
||||
padding: 0 0.3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.flow { flex-direction: column; gap: 0.2rem; }
|
||||
.flow-item { flex-direction: column; }
|
||||
.arrow { transform: rotate(90deg); padding: 0.1rem 0; }
|
||||
}
|
||||
</style>
|
||||
|
||||
+290
-139
@@ -1,194 +1,345 @@
|
||||
<template>
|
||||
<div class="register-demo">
|
||||
<div class="demo-label">1 位寄存器 ── 只在"写入"时更新存储值</div>
|
||||
<div class="cpu-registers-demo">
|
||||
<div class="demo-header">
|
||||
<span class="title">CPU 寄存器组</span>
|
||||
<span class="subtitle">CPU 内部的高速存储单元</span>
|
||||
</div>
|
||||
|
||||
<div class="reg-panel">
|
||||
<!-- Input -->
|
||||
<div class="reg-block">
|
||||
<span class="reg-title">输入</span>
|
||||
<button
|
||||
class="toggle-btn"
|
||||
:class="{ on: inputData === 1 }"
|
||||
@click="inputData = inputData === 1 ? 0 : 1"
|
||||
>
|
||||
{{ inputData }}
|
||||
</button>
|
||||
<div class="registers-layout">
|
||||
<!-- 专用寄存器 -->
|
||||
<div class="reg-section special-regs">
|
||||
<div class="section-title">专用寄存器 (Special Registers)</div>
|
||||
<div class="reg-grid">
|
||||
<div v-for="reg in specialRegisters" :key="reg.name" class="reg-card" :class="{ active: activeReg === reg.name }" @click="selectReg(reg)">
|
||||
<div class="reg-name">{{ reg.name }}</div>
|
||||
<div class="reg-value">{{ reg.value }}</div>
|
||||
<div class="reg-desc">{{ reg.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Write -->
|
||||
<button
|
||||
class="write-btn"
|
||||
:class="{ flash: isWriting }"
|
||||
@click="writeOnce"
|
||||
>
|
||||
写入 →
|
||||
</button>
|
||||
|
||||
<!-- Stored -->
|
||||
<div class="reg-block">
|
||||
<span class="reg-title">存储</span>
|
||||
<span
|
||||
class="val-box"
|
||||
:class="{ on: storedData === 1, flash: isWriting }"
|
||||
>{{ storedData }}</span>
|
||||
<!-- 通用寄存器 -->
|
||||
<div class="reg-section general-regs">
|
||||
<div class="section-title">通用寄存器 (General Purpose Registers)</div>
|
||||
<div class="reg-grid">
|
||||
<div v-for="reg in generalRegisters" :key="reg.name" class="reg-card small" :class="{ active: activeReg === reg.name }" @click="selectReg(reg)">
|
||||
<div class="reg-name">{{ reg.name }}</div>
|
||||
<div class="reg-value">{{ reg.value }}</div>
|
||||
<div class="reg-desc">{{ reg.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Output -->
|
||||
<div class="reg-block">
|
||||
<span class="reg-title">输出</span>
|
||||
<span class="val-box out" :class="{ on: storedData === 1 }">{{
|
||||
storedData
|
||||
}}</span>
|
||||
<!-- 状态寄存器 -->
|
||||
<div class="reg-section status-reg">
|
||||
<div class="section-title">程序状态字 (PSW / FLAGS)</div>
|
||||
<div class="flags-container">
|
||||
<div v-for="flag in statusFlags" :key="flag.name" class="flag-bit" :class="{ set: flag.value === 1 }">
|
||||
<span class="flag-name">{{ flag.name }}</span>
|
||||
<span class="flag-value">{{ flag.value }}</span>
|
||||
<span class="flag-desc">{{ flag.desc }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-line">
|
||||
{{
|
||||
inputData !== storedData
|
||||
? '⚡ 输入≠存储 → 点"写入"即可更新'
|
||||
: '✓ 输入与存储一致'
|
||||
}}
|
||||
<!-- 寄存器详解 -->
|
||||
<div class="reg-details" v-if="selectedReg">
|
||||
<div class="details-header">
|
||||
<span class="details-title">{{ selectedReg.name }} 寄存器</span>
|
||||
<span class="details-type">{{ selectedReg.type }}</span>
|
||||
</div>
|
||||
<div class="details-content">{{ selectedReg.detail }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 寄存器说明 -->
|
||||
<div class="register-explanation">
|
||||
<div class="exp-title">寄存器 vs 内存</div>
|
||||
<div class="exp-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>特性</th>
|
||||
<th>寄存器</th>
|
||||
<th>内存 (RAM)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>位置</td>
|
||||
<td>CPU 内部</td>
|
||||
<td>CPU 外部</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>访问速度</td>
|
||||
<td>最快 (< 1ns)</td>
|
||||
<td>较慢 (50-100ns)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>容量</td>
|
||||
<td>极小 (Bytes)</td>
|
||||
<td>大 (GB)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>作用</td>
|
||||
<td>暂存指令/操作数/结果</td>
|
||||
<td>存储程序和数据</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const inputData = ref(0)
|
||||
const storedData = ref(0)
|
||||
const isWriting = ref(false)
|
||||
const activeReg = ref('')
|
||||
const selectedReg = ref(null)
|
||||
|
||||
const writeOnce = () => {
|
||||
isWriting.value = true
|
||||
storedData.value = inputData.value
|
||||
setTimeout(() => {
|
||||
isWriting.value = false
|
||||
}, 400)
|
||||
const specialRegisters = ref([
|
||||
{ name: 'PC', value: '0x00401000', desc: '程序计数器', type: '专用寄存器', detail: 'Program Counter,存放下一条要执行的指令地址。每执行一条指令,PC 自动加 4(32位)或 8(64位),指向下一条指令。' },
|
||||
{ name: 'IR', value: '0x8B450008', desc: '指令寄存器', type: '专用寄存器', detail: 'Instruction Register,存放当前正在执行的指令。CPU 从内存取指令后,先存入 IR,再送入译码器进行解析。' },
|
||||
{ name: 'MAR', value: '0x00401000', desc: '内存地址寄存器', type: '专用寄存器', detail: 'Memory Address Register,存放要访问的内存地址。CPU 通过它向地址总线发送内存位置。' },
|
||||
{ name: 'MDR', value: '0x00000000', desc: '内存数据寄存器', type: '专用寄存器', detail: 'Memory Data Register,临时存放要写入或从内存读取的数据。是 CPU 与内存交换数据的桥梁。' },
|
||||
{ name: 'ACC', value: '0x0000001A', desc: '累加器', type: '专用寄存器', detail: 'Accumulator,传统 CPU 中最重要的寄存器,用于存放算术运算和逻辑运算的中间结果。' },
|
||||
])
|
||||
|
||||
const generalRegisters = ref([
|
||||
{ name: 'RAX', value: '0x00000000', desc: '返回值', type: '通用寄存器', detail: '64位寄存器,用于存放函数返回值。低32位为 EAX,低16位为 AX,低8位为 AL。' },
|
||||
{ name: 'RBX', value: '0x00000000', desc: '基址寄存器', type: '通用寄存器', detail: '64位通用寄存器,可用于存放数据或内存地址。' },
|
||||
{ name: 'RCX', value: '0x00000000', desc: '计数寄存器', type: '通用寄存器', detail: '64位通用寄存器,常用于循环计数。低32位为 ECX。' },
|
||||
{ name: 'RDX', value: '0x00000000', desc: '数据寄存器', type: '通用寄存器', detail: '64位通用寄存器,用于存放数据,也用于乘除法指令。' },
|
||||
{ name: 'RSI', value: '0x00000000', desc: '源索引', type: '通用寄存器', detail: 'Source Index,字符串操作中作为源地址指针。' },
|
||||
{ name: 'RDI', value: '0x00000000', desc: '目标索引', type: '通用寄存器', detail: 'Destination Index,字符串操作中作为目标地址指针。' },
|
||||
{ name: 'RBP', value: '0x00000000', desc: '栈帧指针', type: '通用寄存器', detail: 'Base Pointer,指向函数栈帧的基址,用于访问局部变量和函数参数。' },
|
||||
{ name: 'RSP', value: '0x7FFDE000', desc: '栈指针', type: '通用寄存器', detail: 'Stack Pointer,指向当前栈顶位置。Push 操作减 4,Pop 操作加 4。' },
|
||||
])
|
||||
|
||||
const statusFlags = ref([
|
||||
{ name: 'CF', value: 0, desc: '进位标志' },
|
||||
{ name: 'PF', value: 0, desc: '奇偶标志' },
|
||||
{ name: 'AF', value: 0, desc: '辅助进位' },
|
||||
{ name: 'ZF', value: 0, desc: '零标志' },
|
||||
{ name: 'SF', value: 0, desc: '符号标志' },
|
||||
{ name: 'OF', value: 0, desc: '溢出标志' },
|
||||
])
|
||||
|
||||
const selectReg = (reg) => {
|
||||
selectedReg.value = reg
|
||||
activeReg.value = reg.name
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.register-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
.cpu-registers-demo {
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
/* ── panel ── */
|
||||
.reg-panel {
|
||||
.demo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
flex-wrap: wrap;
|
||||
padding: 0.6rem 0.8rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.reg-block {
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.registers-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.reg-section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.reg-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.reg-card {
|
||||
padding: 10px;
|
||||
background: #f8fafc;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.reg-card:hover {
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
.reg-card.active {
|
||||
border-color: #3b82f6;
|
||||
background: #eff6ff;
|
||||
}
|
||||
|
||||
.reg-card.small {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.reg-name {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.reg-value {
|
||||
font-size: 11px;
|
||||
font-family: monospace;
|
||||
color: #0369a1;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.reg-desc {
|
||||
font-size: 10px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.status-reg {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.flags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.flag-bit {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 8px 12px;
|
||||
background: #f1f5f9;
|
||||
border-radius: 4px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.reg-title {
|
||||
font-size: 0.7rem;
|
||||
.flag-bit.set {
|
||||
background: #dbeafe;
|
||||
border: 1px solid #3b82f6;
|
||||
}
|
||||
|
||||
.flag-name {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-3);
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
border-radius: 6px;
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-alt);
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
.flag-value {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.toggle-btn.on {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
color: var(--vp-c-brand-1);
|
||||
background: var(--vp-c-brand-soft);
|
||||
.flag-bit.set .flag-value {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.write-btn {
|
||||
padding: 0.35rem 0.7rem;
|
||||
border-radius: 6px;
|
||||
border: 2px solid var(--vp-c-warning-1, #d97706);
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-warning-1, #d97706);
|
||||
font-size: 0.82rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
.flag-desc {
|
||||
font-size: 9px;
|
||||
color: #64748b;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.write-btn:hover {
|
||||
background: var(--vp-c-warning-soft, rgba(217, 119, 6, 0.1));
|
||||
.reg-details {
|
||||
margin-top: 16px;
|
||||
padding: 12px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #3b82f6;
|
||||
}
|
||||
|
||||
.write-btn.flash {
|
||||
background: var(--vp-c-warning-1, #d97706);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.val-box {
|
||||
display: inline-flex;
|
||||
.details-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
border-radius: 6px;
|
||||
border: 2px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg-alt);
|
||||
font-weight: bold;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.val-box.on {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
color: var(--vp-c-brand-1);
|
||||
background: var(--vp-c-brand-soft);
|
||||
.details-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.val-box.flash {
|
||||
box-shadow: 0 0 0 3px var(--vp-c-warning-soft, rgba(217, 119, 6, 0.25));
|
||||
.details-type {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
background: #e0f2fe;
|
||||
border-radius: 4px;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.val-box.out {
|
||||
border-style: dashed;
|
||||
.details-content {
|
||||
font-size: 12px;
|
||||
color: #475569;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.status-line {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.78rem;
|
||||
color: var(--vp-c-text-3);
|
||||
.register-explanation {
|
||||
margin-top: 16px;
|
||||
padding: 12px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.reg-panel {
|
||||
justify-content: center;
|
||||
}
|
||||
.exp-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.exp-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.exp-table th,
|
||||
.exp-table td {
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.exp-table th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.exp-table td {
|
||||
color: #475569;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,40 +1,29 @@
|
||||
<template>
|
||||
<div class="rendering-demo">
|
||||
<div class="demo-label">浏览器渲染过程 ── 点击逐步演示</div>
|
||||
|
||||
<div class="demo-title">浏览器渲染管线</div>
|
||||
<div class="pipeline">
|
||||
<div
|
||||
v-for="(stage, index) in stages"
|
||||
:key="stage.name"
|
||||
class="pipeline-stage"
|
||||
:class="{ active: currentStage >= index }"
|
||||
@click="currentStage = index"
|
||||
>
|
||||
<div class="stage-header">
|
||||
<span class="stage-num">{{ index + 1 }}</span>
|
||||
<span class="stage-name">{{ stage.name }}</span>
|
||||
</div>
|
||||
<div class="stage-desc">{{ stage.desc }}</div>
|
||||
<div v-if="currentStage >= index" class="stage-detail">
|
||||
{{ stage.detail }}
|
||||
<div v-for="(stage, i) in stages" :key="stage.name" class="pipe-item">
|
||||
<div class="pipe-card">
|
||||
<div class="pipe-num">{{ i + 1 }}</div>
|
||||
<div class="pipe-info">
|
||||
<div class="pipe-name">{{ stage.name }}</div>
|
||||
<div class="pipe-desc">{{ stage.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="i < stages.length - 1" class="pipe-arrow">↓</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tap-hint">👆 点击查看各阶段详情</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const currentStage = ref(0)
|
||||
const stages = [
|
||||
{ name: 'HTML 解析', desc: '把 HTML 变成 DOM 树', detail: '<div> → Document Object Model 树结构' },
|
||||
{ name: 'CSS 解析', desc: '把 CSS 变成样式规则', detail: '选择器 + 属性 → 样式规则表' },
|
||||
{ name: '渲染树', desc: 'DOM + CSS = 渲染树', detail: '哪些节点显示、什么样式' },
|
||||
{ name: '布局计算', desc: '计算每个元素的位置', detail: '宽高、坐标、层级' },
|
||||
{ name: '绘制', desc: '把内容画到像素缓冲区', detail: '文字、颜色、图片、边框' },
|
||||
{ name: '合成', desc: '多层合成一张图', detail: 'GPU 合成,显示到屏幕' }
|
||||
{ name: 'HTML 解析', desc: '将 HTML 文本解析为 DOM 树(文档对象模型)' },
|
||||
{ name: 'CSS 解析', desc: '将 CSS 规则解析为样式表,计算每个元素的最终样式' },
|
||||
{ name: '构建渲染树', desc: 'DOM 树 + 样式规则 = 渲染树(只包含可见元素)' },
|
||||
{ name: '布局计算', desc: '计算每个元素在页面上的精确位置和大小' },
|
||||
{ name: '绘制', desc: '将元素的文字、颜色、图片、边框等绘制到像素缓冲区' },
|
||||
{ name: '合成显示', desc: '将多个图层合成为最终画面,由 GPU 输出到屏幕' }
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -45,87 +34,63 @@ const stages = [
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.pipeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.pipeline-stage {
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem 0.6rem;
|
||||
opacity: 0.4;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.pipeline-stage.active {
|
||||
opacity: 1;
|
||||
border-color: var(--vp-c-brand);
|
||||
}
|
||||
|
||||
.stage-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.stage-num {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
background: var(--vp-c-divider);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.6rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.pipeline-stage.active .stage-num {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stage-name {
|
||||
.demo-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.stage-desc {
|
||||
font-size: 0.68rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.15rem;
|
||||
.pipeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stage-detail {
|
||||
.pipe-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.pipe-card {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.6rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
.pipe-num {
|
||||
width: 1.3rem;
|
||||
height: 1.3rem;
|
||||
border-radius: 50%;
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.65rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-top: 0.3rem;
|
||||
padding: 0.25rem 0.4rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.tap-hint {
|
||||
text-align: center;
|
||||
.pipe-info { flex: 1; }
|
||||
.pipe-name {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
.pipe-desc {
|
||||
font-size: 0.63rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.75rem;
|
||||
margin-top: 0.1rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.pipe-arrow {
|
||||
font-size: 0.7rem;
|
||||
color: var(--vp-c-text-3);
|
||||
padding: 0.15rem 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="static-vs-dynamic-demo">
|
||||
<h4>🔍 静态类型 vs 动态类型:实时对比</h4>
|
||||
<p class="desc">选择一段代码,观察两种类型系统的不同行为</p>
|
||||
|
||||
<div class="example-selector">
|
||||
<button
|
||||
v-for="(ex, i) in examples"
|
||||
:key="i"
|
||||
:class="['ex-btn', { active: selected === i }]"
|
||||
@click="selected = i"
|
||||
>
|
||||
{{ ex.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="comparison">
|
||||
<div class="panel static-panel">
|
||||
<div class="panel-header">
|
||||
<span class="badge">静态类型(TypeScript)</span>
|
||||
<span class="timing">⏱ 编译时检查</span>
|
||||
</div>
|
||||
<pre class="code-block">{{ examples[selected].staticCode }}</pre>
|
||||
<div :class="['result', examples[selected].staticOk ? 'ok' : 'err']">
|
||||
{{ examples[selected].staticResult }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vs">VS</div>
|
||||
|
||||
<div class="panel dynamic-panel">
|
||||
<div class="panel-header">
|
||||
<span class="badge dynamic">动态类型(JavaScript)</span>
|
||||
<span class="timing">⏱ 运行时检查</span>
|
||||
</div>
|
||||
<pre class="code-block">{{ examples[selected].dynamicCode }}</pre>
|
||||
<div :class="['result', examples[selected].dynamicOk ? 'ok' : 'err']">
|
||||
{{ examples[selected].dynamicResult }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="insight">
|
||||
💡 {{ examples[selected].insight }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selected = ref(0)
|
||||
|
||||
const examples = [
|
||||
{
|
||||
label: '变量赋值',
|
||||
staticCode: `let name: string = "Alice"
|
||||
name = 42 // ❌ 编译错误`,
|
||||
dynamicCode: `let name = "Alice"
|
||||
name = 42 // ✅ 没问题`,
|
||||
staticResult: '❌ Type "number" is not assignable to type "string"',
|
||||
dynamicResult: '✅ 运行正常,name 变成了 42',
|
||||
staticOk: false,
|
||||
dynamicOk: true,
|
||||
insight: '静态类型在你写代码时就发现错误,动态类型要等到运行时才知道。'
|
||||
},
|
||||
{
|
||||
label: '函数参数',
|
||||
staticCode: `function add(a: number, b: number) {
|
||||
return a + b
|
||||
}
|
||||
add("1", 2) // ❌ 编译错误`,
|
||||
dynamicCode: `function add(a, b) {
|
||||
return a + b
|
||||
}
|
||||
add("1", 2) // ✅ 返回 "12"`,
|
||||
staticResult: '❌ Argument of type "string" is not assignable to parameter of type "number"',
|
||||
dynamicResult: '✅ 返回 "12"(字符串拼接,不是数学加法!)',
|
||||
staticOk: false,
|
||||
dynamicOk: true,
|
||||
insight: '动态类型的"灵活"有时是 bug 的温床——你期望 3,却得到 "12"。'
|
||||
},
|
||||
{
|
||||
label: '属性访问',
|
||||
staticCode: `interface User { name: string }
|
||||
let user: User = { name: "Bob" }
|
||||
console.log(user.age) // ❌ 编译错误`,
|
||||
dynamicCode: `let user = { name: "Bob" }
|
||||
console.log(user.age) // ✅ 输出 undefined`,
|
||||
staticResult: '❌ Property "age" does not exist on type "User"',
|
||||
dynamicResult: '✅ 输出 undefined(不报错,但可能导致后续逻辑出错)',
|
||||
staticOk: false,
|
||||
dynamicOk: true,
|
||||
insight: '静态类型能在编译时捕获拼写错误和属性缺失,动态类型只会默默返回 undefined。'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.static-vs-dynamic-demo {
|
||||
padding: 20px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
margin: 16px 0;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
.example-selector { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.ex-btn {
|
||||
padding: 6px 14px; border-radius: 6px; border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg); cursor: pointer; font-size: 13px; transition: all 0.2s;
|
||||
}
|
||||
.ex-btn.active { background: var(--vp-c-brand-1); color: #fff; border-color: var(--vp-c-brand-1); }
|
||||
.comparison { display: flex; gap: 12px; align-items: stretch; }
|
||||
.panel { flex: 1; border-radius: 8px; border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg); overflow: hidden; }
|
||||
.panel-header { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; background: var(--vp-c-bg-soft); border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.badge { font-size: 12px; font-weight: 600; color: var(--vp-c-brand-1); }
|
||||
.badge.dynamic { color: #e5a00d; }
|
||||
.timing { font-size: 11px; color: var(--vp-c-text-3); }
|
||||
.code-block { padding: 12px; margin: 0; font-size: 12px; line-height: 1.6; white-space: pre-wrap; overflow-x: auto; }
|
||||
.result { padding: 8px 12px; font-size: 12px; border-top: 1px solid var(--vp-c-divider); }
|
||||
.result.ok { background: #f0fdf4; color: #166534; }
|
||||
.result.err { background: #fef2f2; color: #991b1b; }
|
||||
.vs { display: flex; align-items: center; font-weight: 700; color: var(--vp-c-text-3); font-size: 14px; }
|
||||
.insight { margin-top: 12px; padding: 10px 14px; background: var(--vp-c-brand-soft); border-radius: 8px; font-size: 13px; }
|
||||
@media (max-width: 640px) {
|
||||
.comparison { flex-direction: column; }
|
||||
.vs { justify-content: center; padding: 4px 0; }
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="strong-vs-weak-demo">
|
||||
<h4>⚡ 强类型 vs 弱类型:隐式转换实验室</h4>
|
||||
<p class="desc">输入一个表达式,看看不同语言怎么处理</p>
|
||||
|
||||
<div class="expr-selector">
|
||||
<button
|
||||
v-for="(ex, i) in expressions"
|
||||
:key="i"
|
||||
:class="['expr-btn', { active: selected === i }]"
|
||||
@click="selected = i"
|
||||
>
|
||||
<code>{{ ex.expr }}</code>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="results-grid">
|
||||
<div v-for="lang in expressions[selected].langs" :key="lang.name" class="lang-card">
|
||||
<div class="lang-header">
|
||||
<span class="lang-name">{{ lang.name }}</span>
|
||||
<span :class="['lang-type', lang.strong ? 'strong' : 'weak']">
|
||||
{{ lang.strong ? '强类型' : '弱类型' }}
|
||||
</span>
|
||||
</div>
|
||||
<pre class="lang-code">{{ lang.code }}</pre>
|
||||
<div :class="['lang-result', lang.error ? 'error' : 'success']">
|
||||
{{ lang.result }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="takeaway">
|
||||
📌 {{ expressions[selected].takeaway }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selected = ref(0)
|
||||
|
||||
const expressions = [
|
||||
{
|
||||
expr: '"1" + 1',
|
||||
langs: [
|
||||
{ name: 'JavaScript', strong: false, code: '"1" + 1', result: '→ "11"(字符串拼接)', error: false },
|
||||
{ name: 'Python', strong: true, code: '"1" + 1', result: '→ TypeError: can only concatenate str to str', error: true },
|
||||
{ name: 'Java', strong: false, code: '"1" + 1', result: '→ "11"(字符串拼接)', error: false },
|
||||
{ name: 'Rust', strong: true, code: '"1" + 1', result: '→ 编译错误:类型不匹配', error: true }
|
||||
],
|
||||
takeaway: '强类型语言拒绝猜测你的意图,宁可报错也不悄悄转换。弱类型语言会"好心"帮你转,但结果可能不是你想要的。'
|
||||
},
|
||||
{
|
||||
expr: 'true + 1',
|
||||
langs: [
|
||||
{ name: 'JavaScript', strong: false, code: 'true + 1', result: '→ 2(true 被转为 1)', error: false },
|
||||
{ name: 'Python', strong: true, code: 'True + 1', result: '→ 2(Python 中 bool 是 int 子类)', error: false },
|
||||
{ name: 'Java', strong: false, code: 'true + 1', result: '→ 编译错误', error: true },
|
||||
{ name: 'C', strong: false, code: '1 + 1 // true=1', result: '→ 2(C 中没有 bool,用 0/1)', error: false }
|
||||
],
|
||||
takeaway: 'bool 和数字的关系因语言而异。Python 虽是强类型,但 bool 继承自 int,这是设计选择而非弱类型。'
|
||||
},
|
||||
{
|
||||
expr: '"5" == 5',
|
||||
langs: [
|
||||
{ name: 'JavaScript', strong: false, code: '"5" == 5', result: '→ true(隐式转换后比较)', error: false },
|
||||
{ name: 'Python', strong: true, code: '"5" == 5', result: '→ False(类型不同,直接 False)', error: false },
|
||||
{ name: 'TypeScript', strong: false, code: '"5" == 5', result: '→ true(但 TSLint 会警告)', error: false },
|
||||
{ name: 'PHP', strong: false, code: '"5" == 5', result: '→ true(臭名昭著的松散比较)', error: false }
|
||||
],
|
||||
takeaway: 'JavaScript 的 == 会做隐式转换,这是无数 bug 的来源。所以社区推荐始终使用 === 严格比较。'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.strong-vs-weak-demo {
|
||||
padding: 20px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
margin: 16px 0;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
.expr-selector { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.expr-btn {
|
||||
padding: 6px 14px; border-radius: 6px; border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg); cursor: pointer; font-size: 13px; transition: all 0.2s;
|
||||
}
|
||||
.expr-btn.active { background: var(--vp-c-brand-1); color: #fff; border-color: var(--vp-c-brand-1); }
|
||||
.expr-btn code { font-size: 13px; }
|
||||
.results-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
|
||||
.lang-card { border: 1px solid var(--vp-c-divider); border-radius: 8px; background: var(--vp-c-bg); overflow: hidden; }
|
||||
.lang-header { display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; background: var(--vp-c-bg-soft); border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.lang-name { font-weight: 600; font-size: 13px; }
|
||||
.lang-type { font-size: 11px; padding: 2px 6px; border-radius: 4px; }
|
||||
.lang-type.strong { background: #dbeafe; color: #1e40af; }
|
||||
.lang-type.weak { background: #fef3c7; color: #92400e; }
|
||||
.lang-code { padding: 8px 10px; margin: 0; font-size: 12px; }
|
||||
.lang-result { padding: 6px 10px; font-size: 12px; border-top: 1px solid var(--vp-c-divider); }
|
||||
.lang-result.success { background: #f0fdf4; color: #166534; }
|
||||
.lang-result.error { background: #fef2f2; color: #991b1b; }
|
||||
.takeaway { margin-top: 12px; padding: 10px 14px; background: var(--vp-c-brand-soft); border-radius: 8px; font-size: 13px; }
|
||||
@media (max-width: 640px) { .results-grid { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div class="type-inference-demo">
|
||||
<h4>🧠 类型推断:编译器如何"猜"出类型</h4>
|
||||
<p class="desc">点击代码行,看编译器如何一步步推断类型</p>
|
||||
|
||||
<div class="code-area">
|
||||
<div
|
||||
v-for="(line, i) in codeLines"
|
||||
:key="i"
|
||||
:class="['code-line', { active: activeLine === i }]"
|
||||
@click="activeLine = i"
|
||||
>
|
||||
<span class="line-num">{{ i + 1 }}</span>
|
||||
<span class="line-code" v-html="line.code"></span>
|
||||
<span v-if="activeLine === i" class="inferred-type">
|
||||
→ {{ line.inferred }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeLine !== null" class="explanation">
|
||||
<div class="explain-header">推断过程</div>
|
||||
<div class="explain-steps">
|
||||
<div v-for="(step, j) in codeLines[activeLine].steps" :key="j" class="step">
|
||||
<span class="step-num">{{ j + 1 }}</span>
|
||||
<span>{{ step }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lang-support">
|
||||
<div class="support-title">各语言的类型推断能力</div>
|
||||
<div class="support-grid">
|
||||
<div v-for="lang in langs" :key="lang.name" class="support-item">
|
||||
<span class="support-name">{{ lang.name }}</span>
|
||||
<div class="support-bar">
|
||||
<div class="support-fill" :style="{ width: lang.level + '%' }"></div>
|
||||
</div>
|
||||
<span class="support-label">{{ lang.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeLine = ref(0)
|
||||
|
||||
const codeLines = [
|
||||
{
|
||||
code: '<span class="kw">let</span> x = <span class="num">42</span>',
|
||||
inferred: 'number',
|
||||
steps: [
|
||||
'右侧是字面量 42',
|
||||
'42 是整数,类型为 number',
|
||||
'推断 x 的类型为 number'
|
||||
]
|
||||
},
|
||||
{
|
||||
code: '<span class="kw">let</span> names = [<span class="str">"Alice"</span>, <span class="str">"Bob"</span>]',
|
||||
inferred: 'string[]',
|
||||
steps: [
|
||||
'右侧是数组字面量 [...]',
|
||||
'数组元素 "Alice"、"Bob" 都是 string',
|
||||
'推断数组类型为 string[]'
|
||||
]
|
||||
},
|
||||
{
|
||||
code: '<span class="kw">let</span> result = x > 10 ? <span class="str">"big"</span> : <span class="str">"small"</span>',
|
||||
inferred: 'string',
|
||||
steps: [
|
||||
'三元表达式的两个分支都是 string',
|
||||
'两个分支类型一致',
|
||||
'推断 result 类型为 string'
|
||||
]
|
||||
},
|
||||
{
|
||||
code: '<span class="kw">const</span> add = (a: <span class="type">number</span>, b: <span class="type">number</span>) => a + b',
|
||||
inferred: '(a: number, b: number) => number',
|
||||
steps: [
|
||||
'参数 a 和 b 显式标注为 number',
|
||||
'number + number 的结果是 number',
|
||||
'推断返回值类型为 number'
|
||||
]
|
||||
},
|
||||
{
|
||||
code: '<span class="kw">let</span> mixed = [<span class="num">1</span>, <span class="str">"two"</span>, <span class="kw">true</span>]',
|
||||
inferred: '(number | string | boolean)[]',
|
||||
steps: [
|
||||
'数组包含 number、string、boolean 三种类型',
|
||||
'取所有元素类型的联合类型',
|
||||
'推断为 (number | string | boolean)[]'
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const langs = [
|
||||
{ name: 'Rust', level: 95, label: '几乎全推断' },
|
||||
{ name: 'TypeScript', level: 85, label: '大部分可推断' },
|
||||
{ name: 'Kotlin', level: 80, label: '局部推断强' },
|
||||
{ name: 'Go', level: 50, label: '仅 := 短声明' },
|
||||
{ name: 'Java', level: 40, label: 'var 关键字(Java 10+)' },
|
||||
{ name: 'C', level: 5, label: '几乎不推断' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.type-inference-demo {
|
||||
padding: 20px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
margin: 16px 0;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
.code-area {
|
||||
background: #1e1e1e; border-radius: 8px; padding: 12px 0; font-family: 'Fira Code', monospace;
|
||||
}
|
||||
.code-line {
|
||||
display: flex; align-items: center; padding: 4px 14px; cursor: pointer;
|
||||
transition: background 0.15s; font-size: 13px; color: #d4d4d4;
|
||||
}
|
||||
.code-line:hover { background: rgba(255,255,255,0.05); }
|
||||
.code-line.active { background: rgba(100,149,237,0.15); }
|
||||
.line-num { color: #858585; width: 24px; text-align: right; margin-right: 12px; font-size: 12px; user-select: none; }
|
||||
.line-code :deep(.kw) { color: #569cd6; }
|
||||
.line-code :deep(.str) { color: #ce9178; }
|
||||
.line-code :deep(.num) { color: #b5cea8; }
|
||||
.line-code :deep(.type) { color: #4ec9b0; }
|
||||
.inferred-type {
|
||||
margin-left: auto; padding: 2px 8px; background: rgba(78,201,176,0.2);
|
||||
color: #4ec9b0; border-radius: 4px; font-size: 12px; white-space: nowrap;
|
||||
}
|
||||
.explanation {
|
||||
margin-top: 12px; border: 1px solid var(--vp-c-divider); border-radius: 8px;
|
||||
background: var(--vp-c-bg); overflow: hidden;
|
||||
}
|
||||
.explain-header { padding: 8px 12px; font-weight: 600; font-size: 13px; background: var(--vp-c-bg-soft); border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.explain-steps { padding: 10px 12px; }
|
||||
.step { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 13px; }
|
||||
.step-num {
|
||||
width: 20px; height: 20px; border-radius: 50%; background: var(--vp-c-brand-1);
|
||||
color: #fff; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 11px; font-weight: 600; flex-shrink: 0;
|
||||
}
|
||||
.lang-support { margin-top: 16px; }
|
||||
.support-title { font-weight: 600; font-size: 13px; margin-bottom: 8px; }
|
||||
.support-grid { display: flex; flex-direction: column; gap: 6px; }
|
||||
.support-item { display: flex; align-items: center; gap: 8px; font-size: 12px; }
|
||||
.support-name { width: 80px; font-weight: 500; text-align: right; }
|
||||
.support-bar { flex: 1; height: 8px; background: var(--vp-c-divider); border-radius: 4px; overflow: hidden; }
|
||||
.support-fill { height: 100%; background: var(--vp-c-brand-1); border-radius: 4px; transition: width 0.5s; }
|
||||
.support-label { width: 140px; color: var(--vp-c-text-3); font-size: 11px; }
|
||||
@media (max-width: 640px) { .support-label { display: none; } }
|
||||
</style>
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="type-safety-demo">
|
||||
<h4>🛡️ 类型安全实战:常见陷阱与防御</h4>
|
||||
<p class="desc">点击不同的陷阱场景,学习如何用类型系统保护你的代码</p>
|
||||
|
||||
<div class="trap-selector">
|
||||
<button
|
||||
v-for="(trap, i) in traps"
|
||||
:key="i"
|
||||
:class="['trap-btn', { active: selected === i }]"
|
||||
@click="selected = i"
|
||||
>
|
||||
<span class="trap-icon">{{ trap.icon }}</span>
|
||||
<span>{{ trap.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="trap-detail">
|
||||
<div class="danger-zone">
|
||||
<div class="zone-header danger">⚠️ 危险代码</div>
|
||||
<pre class="code-block">{{ traps[selected].dangerCode }}</pre>
|
||||
<div class="zone-result danger">{{ traps[selected].dangerResult }}</div>
|
||||
</div>
|
||||
|
||||
<div class="safe-zone">
|
||||
<div class="zone-header safe">✅ 安全代码</div>
|
||||
<pre class="code-block">{{ traps[selected].safeCode }}</pre>
|
||||
<div class="zone-result safe">{{ traps[selected].safeResult }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="defense-tip">
|
||||
<div class="tip-header">🔑 防御策略</div>
|
||||
<ul>
|
||||
<li v-for="(tip, j) in traps[selected].tips" :key="j">{{ tip }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selected = ref(0)
|
||||
|
||||
const traps = [
|
||||
{
|
||||
icon: '💣', name: 'null 引用',
|
||||
dangerCode: `function getLength(str) {
|
||||
return str.length // 如果 str 是 null?
|
||||
}
|
||||
getLength(null) // 💥 运行时崩溃`,
|
||||
dangerResult: '💥 TypeError: Cannot read properties of null',
|
||||
safeCode: `function getLength(str: string | null): number {
|
||||
if (str === null) return 0
|
||||
return str.length // ✅ 编译器确保此处 str 不为 null
|
||||
}`,
|
||||
safeResult: '✅ 编译器强制你处理 null 的情况',
|
||||
tips: ['使用 strictNullChecks 编译选项', '用联合类型 string | null 显式标注可空', '用可选链 ?. 安全访问属性']
|
||||
},
|
||||
{
|
||||
icon: '🎭', name: '类型断言滥用',
|
||||
dangerCode: `const data = fetchAPI() as any
|
||||
const name = data.user.profile.name
|
||||
// 如果 API 返回格式变了?`,
|
||||
dangerResult: '💥 运行时崩溃,any 绕过了所有类型检查',
|
||||
safeCode: `interface APIResponse {
|
||||
user: { profile: { name: string } }
|
||||
}
|
||||
const data: APIResponse = await fetchAPI()
|
||||
const name = data.user.profile.name`,
|
||||
safeResult: '✅ 如果 API 格式变了,编译时就能发现',
|
||||
tips: ['避免使用 any,用 unknown 代替', '为 API 响应定义明确的接口', '使用 zod 等库做运行时校验']
|
||||
},
|
||||
{
|
||||
icon: '🔄', name: '隐式转换',
|
||||
dangerCode: `if (userId == 0) {
|
||||
// 当 userId 是 "" 时也会进入!
|
||||
console.log("无效用户")
|
||||
}
|
||||
// "" == 0 → true(隐式转换)`,
|
||||
dangerResult: '💥 空字符串被当成 0,逻辑错误',
|
||||
safeCode: `if (userId === 0) {
|
||||
console.log("无效用户")
|
||||
}
|
||||
// "" === 0 → false(严格比较)`,
|
||||
safeResult: '✅ 严格比较不做隐式转换',
|
||||
tips: ['始终使用 === 而不是 ==', '开启 ESLint 的 eqeqeq 规则', '用 TypeScript 的严格模式']
|
||||
},
|
||||
{
|
||||
icon: '📦', name: '数组类型不安全',
|
||||
dangerCode: `const items = [] // any[] 类型
|
||||
items.push(1)
|
||||
items.push("hello")
|
||||
items.push({ x: 1 })
|
||||
// 数组里什么都有,取出来用时容易出错`,
|
||||
dangerResult: '💥 数组元素类型不一致,后续操作可能崩溃',
|
||||
safeCode: `const items: number[] = []
|
||||
items.push(1)
|
||||
items.push("hello") // ❌ 编译错误!
|
||||
// 编译器确保数组元素类型一致`,
|
||||
safeResult: '✅ 编译时就阻止了类型不一致的元素',
|
||||
tips: ['声明数组时指定元素类型', '使用 ReadonlyArray 防止意外修改', '用元组类型 [string, number] 表示固定结构']
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.type-safety-demo {
|
||||
padding: 20px; border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 12px; margin: 16px 0; background: var(--vp-c-bg-soft);
|
||||
}
|
||||
h4 { margin: 0 0 4px; }
|
||||
.desc { color: var(--vp-c-text-2); font-size: 14px; margin: 0 0 16px; }
|
||||
.trap-selector { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
|
||||
.trap-btn {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 6px 14px; border-radius: 6px; border: 1px solid var(--vp-c-divider);
|
||||
background: var(--vp-c-bg); cursor: pointer; font-size: 13px; transition: all 0.2s;
|
||||
}
|
||||
.trap-btn.active { background: var(--vp-c-brand-1); color: #fff; border-color: var(--vp-c-brand-1); }
|
||||
.trap-icon { font-size: 16px; }
|
||||
.trap-detail { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px; }
|
||||
.danger-zone, .safe-zone { border-radius: 8px; border: 1px solid var(--vp-c-divider); overflow: hidden; background: var(--vp-c-bg); }
|
||||
.zone-header { padding: 6px 12px; font-size: 13px; font-weight: 600; border-bottom: 1px solid var(--vp-c-divider); }
|
||||
.zone-header.danger { background: #fef2f2; color: #991b1b; }
|
||||
.zone-header.safe { background: #f0fdf4; color: #166534; }
|
||||
.code-block { padding: 10px 12px; margin: 0; font-size: 12px; line-height: 1.5; white-space: pre-wrap; }
|
||||
.zone-result { padding: 6px 12px; font-size: 12px; border-top: 1px solid var(--vp-c-divider); }
|
||||
.zone-result.danger { background: #fef2f2; color: #991b1b; }
|
||||
.zone-result.safe { background: #f0fdf4; color: #166534; }
|
||||
.defense-tip { padding: 12px 14px; background: var(--vp-c-brand-soft); border-radius: 8px; }
|
||||
.tip-header { font-weight: 600; font-size: 13px; margin-bottom: 6px; }
|
||||
.defense-tip ul { margin: 0; padding-left: 18px; }
|
||||
.defense-tip li { font-size: 13px; margin: 3px 0; }
|
||||
@media (max-width: 640px) { .trap-detail { grid-template-columns: 1fr; } }
|
||||
</style>
|
||||
+156
-96
@@ -1,43 +1,80 @@
|
||||
<template>
|
||||
<div class="url-demo">
|
||||
<div class="demo-label">URL 访问流程 ── 点击逐步演示</div>
|
||||
<div class="demo-header">
|
||||
<div class="demo-title">URL 访问全流程</div>
|
||||
<button class="play-btn" @click="autoPlay" :disabled="playing">
|
||||
{{ playing ? '播放中...' : '▶ 自动演示' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flow">
|
||||
<div
|
||||
v-for="(step, index) in steps"
|
||||
:key="step.name"
|
||||
class="flow-step"
|
||||
:class="{ active: currentStep >= index }"
|
||||
@click="currentStep = index"
|
||||
>
|
||||
<div class="step-num">{{ index + 1 }}</div>
|
||||
<div class="step-content">
|
||||
<div class="step-name">{{ step.name }}</div>
|
||||
<div class="step-desc">{{ step.desc }}</div>
|
||||
<div v-if="currentStep >= index && step.detail" class="step-detail">
|
||||
{{ step.detail }}
|
||||
<div class="flow-side client-side">
|
||||
<div class="side-label">浏览器</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-steps">
|
||||
<div
|
||||
v-for="(step, i) in steps"
|
||||
:key="step.name"
|
||||
class="step"
|
||||
:class="{ active: current >= i, highlight: current === i }"
|
||||
@click="current = i"
|
||||
>
|
||||
<div class="step-line">
|
||||
<span class="step-dot"></span>
|
||||
<span v-if="i < steps.length - 1" class="step-connector"></span>
|
||||
</div>
|
||||
<div class="step-body">
|
||||
<div class="step-head">
|
||||
<span class="step-num">{{ i + 1 }}</span>
|
||||
<span class="step-name">{{ step.name }}</span>
|
||||
<span class="step-dir" :class="step.dir">{{ step.dir === 'right' ? '→' : '←' }}</span>
|
||||
</div>
|
||||
<div v-if="current >= i" class="step-detail">{{ step.detail }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tap-hint">👆 点击查看各步骤详情</div>
|
||||
<div class="flow-side server-side">
|
||||
<div class="side-label">服务器</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const currentStep = ref(0)
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
|
||||
const current = ref(-1)
|
||||
const playing = ref(false)
|
||||
let timer = null
|
||||
|
||||
const steps = [
|
||||
{ name: 'URL 解析', desc: '提取协议、域名、路径', detail: 'https://www.example.com → 协议:https, 域名:www.example.com' },
|
||||
{ name: 'DNS 解析', desc: '域名转换为 IP 地址', detail: 'www.example.com → 93.184.216.34' },
|
||||
{ name: 'TCP 连接', desc: '建立与服务器的连接', detail: '三次握手完成' },
|
||||
{ name: 'TLS 握手', desc: '建立加密通道(HTTPS)', detail: '交换密钥,验证证书' },
|
||||
{ name: '发送请求', desc: '发送 HTTP 请求', detail: 'GET /index.html HTTP/1.1' },
|
||||
{ name: '服务器处理', desc: '后端处理请求', detail: '查询数据库,返回 HTML' },
|
||||
{ name: '返回响应', desc: '接收服务器响应', detail: 'HTTP/1.1 200 OK, Content-Type: text/html' },
|
||||
{ name: '浏览器渲染', desc: '解析并显示页面', detail: '构建 DOM 树,计算样式,绘制页面' }
|
||||
{ name: 'URL 解析', dir: 'right', detail: 'https://example.com → 协议: https, 域名: example.com, 路径: /' },
|
||||
{ name: 'DNS 解析', dir: 'right', detail: '向 DNS 服务器查询,将域名翻译为 IP 地址 93.184.216.34' },
|
||||
{ name: 'TCP 三次握手', dir: 'right', detail: 'SYN → SYN-ACK → ACK,建立可靠的传输连接' },
|
||||
{ name: 'TLS 握手', dir: 'right', detail: '交换密钥、验证证书,建立 HTTPS 加密通道' },
|
||||
{ name: '发送 HTTP 请求', dir: 'right', detail: 'GET /index.html HTTP/1.1 Host: example.com' },
|
||||
{ name: '服务器处理', dir: 'left', detail: '解析请求 → 执行业务逻辑 → 查询数据库 → 组装响应' },
|
||||
{ name: '返回 HTTP 响应', dir: 'left', detail: 'HTTP/1.1 200 OK Content-Type: text/html' },
|
||||
{ name: '浏览器渲染', dir: 'left', detail: 'HTML → DOM 树 → 样式计算 → 布局 → 绘制到屏幕' }
|
||||
]
|
||||
|
||||
const autoPlay = () => {
|
||||
current.value = -1
|
||||
playing.value = true
|
||||
let i = 0
|
||||
timer = setInterval(() => {
|
||||
current.value = i
|
||||
i++
|
||||
if (i >= steps.length) {
|
||||
clearInterval(timer)
|
||||
playing.value = false
|
||||
}
|
||||
}, 800)
|
||||
}
|
||||
|
||||
onUnmounted(() => { if (timer) clearInterval(timer) })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -47,87 +84,110 @@ const steps = [
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: bold;
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
padding: 0.4rem;
|
||||
border-radius: 6px;
|
||||
opacity: 0.4;
|
||||
transition: all 0.3s;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.flow-step.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
width: 1.3rem;
|
||||
height: 1.3rem;
|
||||
background: var(--vp-c-divider);
|
||||
border-radius: 50%;
|
||||
.demo-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.flow-step.active .step-num {
|
||||
background: var(--vp-c-brand);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-name {
|
||||
.demo-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 0.68rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.step-detail {
|
||||
.play-btn {
|
||||
font-size: 0.65rem;
|
||||
color: var(--vp-c-brand);
|
||||
margin-top: 0.3rem;
|
||||
padding: 0.3rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 0.25rem 0.6rem;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 4px;
|
||||
background: var(--vp-c-bg);
|
||||
color: var(--vp-c-text-2);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
.play-btn:hover:not(:disabled) { border-color: var(--vp-c-brand); }
|
||||
.play-btn:disabled { opacity: 0.5; cursor: default; }
|
||||
.flow {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.flow-side {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding-top: 0.3rem;
|
||||
}
|
||||
.side-label {
|
||||
writing-mode: vertical-rl;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-3);
|
||||
letter-spacing: 0.15em;
|
||||
}
|
||||
.flow-steps { flex: 1; display: flex; flex-direction: column; }
|
||||
.step {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
opacity: 0.35;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
.step.active { opacity: 1; }
|
||||
.step.highlight .step-dot { box-shadow: 0 0 0 3px rgba(var(--vp-c-brand-rgb, 100, 108, 255), 0.2); }
|
||||
.step-line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 0.8rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.step-dot {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
background: var(--vp-c-divider);
|
||||
transition: all 0.3s;
|
||||
flex-shrink: 0;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
.step.active .step-dot { background: var(--vp-c-brand); }
|
||||
.step-connector {
|
||||
flex: 1;
|
||||
width: 1px;
|
||||
background: var(--vp-c-divider);
|
||||
min-height: 0.8rem;
|
||||
}
|
||||
.step.active .step-connector { background: var(--vp-c-brand); opacity: 0.3; }
|
||||
.step-body { flex: 1; padding-bottom: 0.5rem; }
|
||||
.step-head { display: flex; align-items: center; gap: 0.35rem; }
|
||||
.step-num {
|
||||
font-size: 0.6rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-3);
|
||||
min-width: 1rem;
|
||||
}
|
||||
.step.active .step-num { color: var(--vp-c-brand); }
|
||||
.step-name {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
.step-dir {
|
||||
font-size: 0.7rem;
|
||||
color: var(--vp-c-text-3);
|
||||
}
|
||||
.step-dir.right { color: var(--vp-c-brand); }
|
||||
.step-dir.left { color: #e879a0; }
|
||||
.step-detail {
|
||||
font-size: 0.63rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.2rem;
|
||||
padding: 0.25rem 0.4rem;
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tap-hint {
|
||||
text-align: center;
|
||||
font-size: 0.72rem;
|
||||
color: var(--vp-c-text-3);
|
||||
margin-top: 0.75rem;
|
||||
@media (max-width: 480px) {
|
||||
.flow-side { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div class="flow-demo">
|
||||
<div class="flow-section">
|
||||
<div class="flow-label traditional">传统开发流程</div>
|
||||
<div class="flow-steps">
|
||||
<span v-for="(step, i) in traditionalSteps" :key="step">
|
||||
<span class="flow-step">{{ step }}</span>
|
||||
<span v-if="i < traditionalSteps.length - 1" class="flow-arrow">→</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flow-loop">↑ 反复循环 ↓</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-section">
|
||||
<div class="flow-label vibe">Vibe Coding 流程</div>
|
||||
<div class="flow-steps">
|
||||
<span v-for="(step, i) in vibeSteps" :key="step">
|
||||
<span class="flow-step" :class="{ highlight: step.highlight }">{{ step.text }}</span>
|
||||
<span v-if="i < vibeSteps.length - 1" class="flow-arrow">→</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flow-loop">↑ 快速迭代 ↓</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const traditionalSteps = ['你', '学习语法', '写代码', '调试', '查文档', '修改', '运行']
|
||||
|
||||
const vibeSteps = [
|
||||
{ text: '你', highlight: false },
|
||||
{ text: '用自然语言描述需求', highlight: true },
|
||||
{ text: 'AI 生成代码', highlight: true },
|
||||
{ text: '你审核修改', highlight: false },
|
||||
{ text: '运行', highlight: false }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.flow-demo {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 8px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
padding: 1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.flow-section {
|
||||
background: var(--vp-c-bg);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.flow-label {
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
padding-bottom: 0.35rem;
|
||||
border-bottom: 1px dashed var(--vp-c-divider);
|
||||
}
|
||||
|
||||
.flow-label.traditional {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.flow-label.vibe {
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.flow-steps {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.2rem 0.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
border-radius: 4px;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.flow-step.highlight {
|
||||
background: var(--vp-c-brand-soft);
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
color: var(--vp-c-text-3);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.flow-loop {
|
||||
font-size: 0.68rem;
|
||||
color: var(--vp-c-text-3);
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -122,6 +122,14 @@ import FunctionalUnitDemo from './components/appendix/computer-fundamentals/Func
|
||||
import CpuArchitectureDemo from './components/appendix/computer-fundamentals/CpuArchitectureDemo.vue'
|
||||
import MinCpuDemo from './components/appendix/computer-fundamentals/MinCpuDemo.vue'
|
||||
import RegisterDemo from './components/appendix/computer-fundamentals/RegisterDemo.vue'
|
||||
import PipelineDemo from './components/appendix/computer-fundamentals/PipelineDemo.vue'
|
||||
import ControllerDemo from './components/appendix/computer-fundamentals/ControllerDemo.vue'
|
||||
import BusSystemDemo from './components/appendix/computer-fundamentals/BusSystemDemo.vue'
|
||||
import InstructionFormatDemo from './components/appendix/computer-fundamentals/InstructionFormatDemo.vue'
|
||||
import AddressingModeDemo from './components/appendix/computer-fundamentals/AddressingModeDemo.vue'
|
||||
import CacheDemo from './components/appendix/computer-fundamentals/CacheDemo.vue'
|
||||
import IOMethodDemo from './components/appendix/computer-fundamentals/IOMethodDemo.vue'
|
||||
import PSWFlagDemo from './components/appendix/computer-fundamentals/PSWFlagDemo.vue'
|
||||
import FlipFlopDemo from './components/appendix/computer-fundamentals/FlipFlopDemo.vue'
|
||||
// import EvolutionFlowDemo from './components/appendix/computer-fundamentals/EvolutionFlowDemo.vue'
|
||||
import ProcessDemo from './components/appendix/computer-fundamentals/ProcessDemo.vue'
|
||||
@@ -135,6 +143,17 @@ import AlgorithmDemo from './components/appendix/computer-fundamentals/Algorithm
|
||||
import LanguageMapDemo from './components/appendix/computer-fundamentals/LanguageMapDemo.vue'
|
||||
import TypeSystemDemo from './components/appendix/computer-fundamentals/TypeSystemDemo.vue'
|
||||
import CompilerDemo from './components/appendix/computer-fundamentals/CompilerDemo.vue'
|
||||
import StaticVsDynamicDemo from './components/appendix/computer-fundamentals/StaticVsDynamicDemo.vue'
|
||||
import StrongVsWeakDemo from './components/appendix/computer-fundamentals/StrongVsWeakDemo.vue'
|
||||
import TypeInferenceFlowDemo from './components/appendix/computer-fundamentals/TypeInferenceFlowDemo.vue'
|
||||
import LexerTokenDemo from './components/appendix/computer-fundamentals/LexerTokenDemo.vue'
|
||||
import CompileVsInterpretDemo from './components/appendix/computer-fundamentals/CompileVsInterpretDemo.vue'
|
||||
import CodeToInstructionDemo from './components/appendix/computer-fundamentals/CodeToInstructionDemo.vue'
|
||||
import CISCvsRISCDemo from './components/appendix/computer-fundamentals/CISCvsRISCDemo.vue'
|
||||
import TypeSafetyPracticeDemo from './components/appendix/computer-fundamentals/TypeSafetyPracticeDemo.vue'
|
||||
import GenericTypeDemo from './components/appendix/computer-fundamentals/GenericTypeDemo.vue'
|
||||
import ASTVisualizerDemo from './components/appendix/computer-fundamentals/ASTVisualizerDemo.vue'
|
||||
import CodeOptimizationDemo from './components/appendix/computer-fundamentals/CodeOptimizationDemo.vue'
|
||||
import CFNetworkLayers from './components/appendix/computer-fundamentals/NetworkLayers.vue'
|
||||
import CFSubnetCalculator from './components/appendix/computer-fundamentals/SubnetCalculator.vue'
|
||||
import CFTcpUdpComparison from './components/appendix/computer-fundamentals/TcpUdpComparison.vue'
|
||||
@@ -184,6 +203,7 @@ import FullstackSkillDemo from './components/appendix/computer-fundamentals/Full
|
||||
import AIvsTraditionalDemo from './components/appendix/computer-fundamentals/AIvsTraditionalDemo.vue'
|
||||
import CareerPathDemo from './components/appendix/computer-fundamentals/CareerPathDemo.vue'
|
||||
import LearningStrategyDemo from './components/appendix/computer-fundamentals/LearningStrategyDemo.vue'
|
||||
import VibeCodingFlowDemo from './components/appendix/computer-fundamentals/VibeCodingFlowDemo.vue'
|
||||
import PowerOnDemo from './components/appendix/computer-fundamentals/PowerOnDemo.vue'
|
||||
import BootProcessDemo from './components/appendix/computer-fundamentals/BootProcessDemo.vue'
|
||||
import DesktopDemo from './components/appendix/computer-fundamentals/DesktopDemo.vue'
|
||||
@@ -859,6 +879,14 @@ export default {
|
||||
app.component('CpuArchitectureDemo', CpuArchitectureDemo)
|
||||
app.component('MinCpuDemo', MinCpuDemo)
|
||||
app.component('RegisterDemo', RegisterDemo)
|
||||
app.component('PipelineDemo', PipelineDemo)
|
||||
app.component('ControllerDemo', ControllerDemo)
|
||||
app.component('BusSystemDemo', BusSystemDemo)
|
||||
app.component('InstructionFormatDemo', InstructionFormatDemo)
|
||||
app.component('AddressingModeDemo', AddressingModeDemo)
|
||||
app.component('CacheDemo', CacheDemo)
|
||||
app.component('IOMethodDemo', IOMethodDemo)
|
||||
app.component('PSWFlagDemo', PSWFlagDemo)
|
||||
app.component('FlipFlopDemo', FlipFlopDemo)
|
||||
// app.component('EvolutionFlowDemo', EvolutionFlowDemo)
|
||||
app.component('ProcessDemo', ProcessDemo)
|
||||
@@ -872,6 +900,17 @@ export default {
|
||||
app.component('LanguageMapDemo', LanguageMapDemo)
|
||||
app.component('TypeSystemDemo', TypeSystemDemo)
|
||||
app.component('CompilerDemo', CompilerDemo)
|
||||
app.component('StaticVsDynamicDemo', StaticVsDynamicDemo)
|
||||
app.component('StrongVsWeakDemo', StrongVsWeakDemo)
|
||||
app.component('TypeInferenceFlowDemo', TypeInferenceFlowDemo)
|
||||
app.component('LexerTokenDemo', LexerTokenDemo)
|
||||
app.component('CompileVsInterpretDemo', CompileVsInterpretDemo)
|
||||
app.component('CodeToInstructionDemo', CodeToInstructionDemo)
|
||||
app.component('CISCvsRISCDemo', CISCvsRISCDemo)
|
||||
app.component('TypeSafetyPracticeDemo', TypeSafetyPracticeDemo)
|
||||
app.component('GenericTypeDemo', GenericTypeDemo)
|
||||
app.component('ASTVisualizerDemo', ASTVisualizerDemo)
|
||||
app.component('CodeOptimizationDemo', CodeOptimizationDemo)
|
||||
app.component('CFNetworkLayers', CFNetworkLayers)
|
||||
app.component('CFSubnetCalculator', CFSubnetCalculator)
|
||||
app.component('CFTcpUdpComparison', CFTcpUdpComparison)
|
||||
@@ -927,6 +966,7 @@ export default {
|
||||
app.component('AIvsTraditionalDemo', AIvsTraditionalDemo)
|
||||
app.component('CareerPathDemo', CareerPathDemo)
|
||||
app.component('LearningStrategyDemo', LearningStrategyDemo)
|
||||
app.component('VibeCodingFlowDemo', VibeCodingFlowDemo)
|
||||
app.component('PowerOnDemo', PowerOnDemo)
|
||||
app.component('BootProcessDemo', BootProcessDemo)
|
||||
app.component('DesktopDemo', DesktopDemo)
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
# 编译原理入门
|
||||
|
||||
::: tip 前言
|
||||
**当你按下"运行"按钮,代码是怎么变成屏幕上的结果的?** 你写的每一行代码,计算机其实都"看不懂"——它只认识 0 和 1。编译器就是那个把人类语言翻译成机器语言的"翻译官"。理解编译原理,你就能理解报错信息从哪来、为什么有些语言快有些慢、以及代码优化的底层逻辑。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **全局视野**:掌握从源代码到可执行程序的完整编译流水线
|
||||
- **词法分析**:理解编译器如何把代码拆成一个个 Token
|
||||
- **语法分析**:理解 AST(抽象语法树)的构建过程
|
||||
- **AST 可视化**:直观看到代码的树形结构
|
||||
- **语义分析与优化**:理解类型检查和代码优化的原理
|
||||
- **优化技术实战**:掌握常量折叠、死代码消除等核心优化手段
|
||||
- **执行模型**:区分编译型、解释型和 JIT 三种执行方式
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 编译器是什么 | 翻译官类比、编译流水线 |
|
||||
| **第 2 章** | 词法分析 | Token、词法规则 |
|
||||
| **第 3 章** | 语法分析 | AST、语法树、优先级 |
|
||||
| **第 4 章** | AST 可视化 | 交互式语法树、节点类型 |
|
||||
| **第 5 章** | 语义分析与优化 | 类型检查、常量折叠、死代码消除 |
|
||||
| **第 6 章** | 优化技术实战 | 函数内联、循环外提、常量传播 |
|
||||
| **第 7 章** | 编译型 vs 解释型 vs JIT | 三种执行模型对比 |
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:代码的"翻译之旅"
|
||||
|
||||
想象你是一个翻译官,要把一本中文小说翻译成英文。你不会一个字一个字地直译,而是:
|
||||
|
||||
1. **识别词语** — 把句子拆成一个个词(词法分析)
|
||||
2. **理解句法** — 判断句子结构是否正确(语法分析)
|
||||
3. **理解语义** — 确保意思通顺、没有矛盾(语义分析)
|
||||
4. **润色优化** — 让译文更地道流畅(代码优化)
|
||||
5. **输出译文** — 写出最终的英文版本(代码生成)
|
||||
|
||||
编译器做的事情完全一样,只不过它翻译的是编程语言。
|
||||
|
||||
<CompilerAnalogyDemo />
|
||||
|
||||
---
|
||||
|
||||
## 1. 编译器的六步流水线
|
||||
|
||||
编译器的工作可以分为六个阶段,像工厂流水线一样,每个阶段处理完交给下一个阶段。
|
||||
|
||||
<CompilerDemo />
|
||||
|
||||
::: tip 编译流水线
|
||||
1. **词法分析(Lexical Analysis)**:把源代码拆成一个个 Token(单词)
|
||||
2. **语法分析(Syntax Analysis)**:把 Token 组织成语法树(AST)
|
||||
3. **语义分析(Semantic Analysis)**:检查类型是否正确、变量是否声明
|
||||
4. **中间代码生成(IR Generation)**:生成与平台无关的中间表示
|
||||
5. **代码优化(Optimization)**:让中间代码更高效
|
||||
6. **代码生成(Code Generation)**:生成目标平台的机器码
|
||||
:::
|
||||
|
||||
| 阶段 | 输入 | 输出 | 类比 |
|
||||
|------|------|------|------|
|
||||
| 词法分析 | 源代码字符流 | Token 流 | 把句子拆成单词 |
|
||||
| 语法分析 | Token 流 | AST(语法树) | 分析句子结构 |
|
||||
| 语义分析 | AST | 带类型的 AST | 检查意思是否通顺 |
|
||||
| 中间代码 | 带类型的 AST | IR | 写出初稿 |
|
||||
| 代码优化 | IR | 优化后的 IR | 润色删减 |
|
||||
| 代码生成 | 优化后的 IR | 机器码 | 输出终稿 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 词法分析:把代码拆成"单词"
|
||||
|
||||
词法分析是编译的第一步。编译器从左到右扫描源代码的每个字符,把它们组合成有意义的**Token(词法单元)**。
|
||||
|
||||
<LexerTokenDemo />
|
||||
|
||||
就像读英文句子时,你的大脑会自动把字母组合成单词一样,词法分析器把字符组合成 Token:
|
||||
|
||||
```
|
||||
源代码: let x = 10 + 5;
|
||||
|
||||
Token 流:
|
||||
[let] → 关键字(语言保留字)
|
||||
[x] → 标识符(变量名)
|
||||
[=] → 运算符(赋值)
|
||||
[10] → 数字字面量
|
||||
[+] → 运算符(加法)
|
||||
[5] → 数字字面量
|
||||
[;] → 分隔符(语句结束)
|
||||
```
|
||||
|
||||
::: tip Token 的五大类型
|
||||
- **关键字**:语言保留的特殊单词,如 `let`、`if`、`return`、`function`
|
||||
- **标识符**:程序员定义的名字,如变量名、函数名
|
||||
- **字面量**:直接写在代码里的值,如数字 `42`、字符串 `"hello"`
|
||||
- **运算符**:执行运算的符号,如 `+`、`-`、`=`、`===`
|
||||
- **分隔符**:分隔代码结构的符号,如 `;`、`,`、`(`、`)`
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 语法分析:构建语法树(AST)
|
||||
|
||||
词法分析把代码拆成了 Token,但 Token 只是一个个孤立的"单词"。语法分析的任务是把这些 Token 按照语法规则组织成一棵**抽象语法树(Abstract Syntax Tree, AST)**——它反映了代码的结构和运算优先级。
|
||||
|
||||
```
|
||||
表达式: 1 + 2 * 3
|
||||
|
||||
语法树: 为什么这样?
|
||||
+ 因为 * 的优先级
|
||||
/ \ 高于 +,所以
|
||||
1 * 2 * 3 先结合
|
||||
/ \ 成为一个子树
|
||||
2 3
|
||||
```
|
||||
|
||||
::: tip AST 的重要性
|
||||
AST 是编译器的"核心数据结构",后续的语义分析、优化、代码生成都基于它进行。现代开发工具也大量使用 AST:
|
||||
- **ESLint**:解析代码为 AST,检查是否违反规则
|
||||
- **Prettier**:解析为 AST 后重新格式化输出
|
||||
- **Babel**:解析 AST → 转换 → 生成兼容代码
|
||||
- **IDE 重构**:基于 AST 进行安全的变量重命名、函数提取
|
||||
:::
|
||||
|
||||
| 语法结构 | Token 序列 | AST 节点 |
|
||||
|---------|-----------|---------|
|
||||
| 变量声明 | `let` `x` `=` `10` | VariableDeclaration → Identifier + Literal |
|
||||
| 函数调用 | `add` `(` `1` `,` `2` `)` | CallExpression → Identifier + Arguments |
|
||||
| 条件语句 | `if` `(` `a` `>` `b` `)` | IfStatement → BinaryExpression + Block |
|
||||
|
||||
---
|
||||
|
||||
## 4. AST 可视化:看见代码的"骨架"
|
||||
|
||||
上面我们用文字描述了 AST 的结构,但"看到"比"读到"更直观。下面的交互组件让你选择不同的表达式,实时观察它们的语法树长什么样。
|
||||
|
||||
<ASTVisualizerDemo />
|
||||
|
||||
通过可视化你会发现,AST 的核心规律其实很简单:
|
||||
|
||||
| 代码结构 | AST 根节点 | 子节点 |
|
||||
|---------|-----------|-------|
|
||||
| `1 + 2 * 3` | BinaryExpression (+) | 左: NumericLiteral(1),右: BinaryExpression(*) |
|
||||
| `let x = 10` | VariableDeclaration | VariableDeclarator → Identifier(x) + NumericLiteral(10) |
|
||||
| `add(a, b)` | CallExpression | Identifier(add) + Arguments(a, b) |
|
||||
|
||||
::: tip AST 在日常开发中的应用
|
||||
你可能没直接写过编译器,但你每天都在用基于 AST 的工具:
|
||||
- **ESLint / Prettier**:解析代码为 AST,检查规则或重新格式化
|
||||
- **Babel / SWC**:解析 AST → 转换语法 → 生成兼容代码
|
||||
- **IDE 重构**:基于 AST 做安全的重命名、提取函数
|
||||
- **Tree-shaking**:分析 AST 中的 import/export,删除未使用的代码
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 语义分析与代码优化
|
||||
|
||||
语法分析确保代码"结构正确",但结构正确不代表"意思正确"。语义分析负责检查代码的含义是否合法,代码优化则让程序跑得更快。
|
||||
|
||||
<CompilationPracticeDemo />
|
||||
|
||||
### 4.1 语义分析:检查"意思"对不对
|
||||
|
||||
| 检查内容 | 示例 | 结果 |
|
||||
|---------|------|------|
|
||||
| 类型检查 | `int x = "hello"` | ❌ 类型不匹配 |
|
||||
| 作用域检查 | 使用未声明的变量 `y` | ❌ 变量不存在 |
|
||||
| 类型推断 | `1 + 2.0` | ✅ 推断结果为 float |
|
||||
| 参数检查 | `add(1, 2, 3)` 但函数只接受 2 个参数 | ❌ 参数数量不匹配 |
|
||||
|
||||
::: tip 你见过的报错,大多来自语义分析
|
||||
- `TypeError: Cannot read properties of undefined` — 类型检查
|
||||
- `ReferenceError: x is not defined` — 作用域检查
|
||||
- `Expected 2 arguments, but got 3` — 参数检查
|
||||
:::
|
||||
|
||||
### 4.2 代码优化:让程序更快
|
||||
|
||||
编译器在生成最终代码前,会对中间代码做各种优化。这些优化对程序员透明,但能显著提升性能。
|
||||
|
||||
| 优化技术 | 优化前 | 优化后 | 原理 |
|
||||
|---------|-------|-------|------|
|
||||
| 常量折叠 | `x = 10 + 5` | `x = 15` | 编译时直接算出结果 |
|
||||
| 死代码消除 | `if (false) { ... }` | 直接删除 | 永远不会执行的代码 |
|
||||
| 常量传播 | `x = 15; y = x * 2` | `y = 30` | 已知值直接替换 |
|
||||
| 循环不变量外提 | 循环内重复计算 `len = arr.length` | 提到循环外 | 避免重复计算 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 优化技术实战:编译器如何让代码更快
|
||||
|
||||
上面我们提到了几种优化技术的名字,现在来深入看看编译器具体是怎么做的。下面的交互组件展示了 5 种最常见的编译器优化,你可以直观对比优化前后的代码差异。
|
||||
|
||||
<CodeOptimizationDemo />
|
||||
|
||||
现代编译器和 JIT 引擎(如 V8、GCC、LLVM)会自动应用数十种优化。作为开发者,你不需要手动做这些优化,但理解它们能帮你:
|
||||
|
||||
- **写出更容易被优化的代码**:比如用 `const` 而不是 `let`,编译器更容易做常量折叠
|
||||
- **理解性能差异**:为什么小函数比大函数快?因为编译器能内联它们
|
||||
- **避免"反优化"**:某些写法会阻止编译器优化,比如 `eval()` 和 `with`
|
||||
|
||||
| 优化技术 | 触发条件 | 性能影响 | 开发者能做什么 |
|
||||
|---------|---------|---------|-------------|
|
||||
| 常量折叠 | 表达式中全是常量 | 消除运行时计算 | 多用 const 声明 |
|
||||
| 死代码消除 | 代码不可达或结果未使用 | 减小代码体积 | 及时清理无用代码 |
|
||||
| 循环不变量外提 | 循环内有不变的计算 | 减少重复计算 | 手动提取也是好习惯 |
|
||||
| 函数内联 | 小函数被频繁调用 | 消除调用开销 | 保持函数小而专注 |
|
||||
| 常量传播 | 变量值在编译时可确定 | 整条计算链被消除 | 用常量代替魔法数字 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 编译型 vs 解释型 vs JIT
|
||||
|
||||
代码写完后,有三种"翻译方式"让它运行起来。这三种方式各有优劣,直接决定了语言的性能特征和使用场景。
|
||||
|
||||
<CompileVsInterpretDemo />
|
||||
|
||||
| 维度 | 编译型 | 解释型 | JIT 即时编译 |
|
||||
|------|-------|-------|------------|
|
||||
| 过程 | 先全量编译成机器码,再执行 | 边读边执行,逐行翻译 | 先解释执行,热点代码再编译 |
|
||||
| 运行速度 | 最快 | 最慢 | 中等(热点接近编译型) |
|
||||
| 启动速度 | 慢(需要编译) | 快(直接运行) | 中等(需要预热) |
|
||||
| 跨平台 | 需要重新编译 | 天然跨平台 | 跨平台 |
|
||||
| 代表语言 | C, Rust, Go | Python, Ruby | JavaScript (V8), Java |
|
||||
|
||||
::: tip 为什么 JavaScript 这么快?
|
||||
V8 引擎的 JIT 编译器会监测哪些代码被频繁执行(热点代码),然后把它们编译成高度优化的机器码。所以虽然 JavaScript 是"解释型语言",但在 V8 中它的性能可以接近编译型语言。这也是 Node.js 能做服务端的底气。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
编译原理不是只有编译器开发者才需要了解的知识。理解编译流程,能帮你更好地理解报错信息、选择合适的语言、写出更高效的代码。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **编译器是翻译官**:把人类可读的代码翻译成机器可执行的指令
|
||||
2. **六步流水线**:词法分析 → 语法分析 → 语义分析 → 中间代码 → 优化 → 代码生成
|
||||
3. **词法分析拆 Token**:把字符流拆成关键字、标识符、运算符等有意义的单元
|
||||
4. **语法分析建 AST**:按语法规则把 Token 组织成树形结构,反映运算优先级
|
||||
5. **语义分析保正确**:类型检查、作用域检查,你见过的大多数报错都来自这里
|
||||
6. **编译器自动优化**:常量折叠、死代码消除、函数内联等技术让代码自动变快
|
||||
7. **三种执行模型**:编译型最快、解释型最灵活、JIT 兼顾两者
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [AST Explorer](https://astexplorer.net/) - 在线查看代码的 AST 结构
|
||||
- [Crafting Interpreters](https://craftinginterpreters.com/) - 从零实现一门编程语言(免费在线书)
|
||||
- [The Super Tiny Compiler](https://github.com/jamiebuilds/the-super-tiny-compiler) - 用 JavaScript 实现的超小编译器
|
||||
- [V8 Blog](https://v8.dev/blog) - V8 引擎的 JIT 编译技术博客
|
||||
- [LLVM 官网](https://llvm.org/) - 最流行的编译器基础设施
|
||||
@@ -0,0 +1,485 @@
|
||||
# 计算机组成原理
|
||||
|
||||
::: tip 前言
|
||||
**从晶体管到 CPU 后,计算机如何组成完整系统?** 上一章我们从晶体管出发,构建了加法器、寄存器、运算单元,最终拼出了 CPU 核心。但仅有 CPU 是不够的——它需要和内存、I/O 设备协同工作,需要总线连接各个部件,需要指令系统来驱动。这一章我们将从 CPU 的内部视角转向整个计算机系统的视角,深入理解冯诺依曼架构、指令系统、存储层次、总线与 I/O 的专业原理。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **系统视角**:理解 CPU、内存、I/O 是如何协同工作的不再是孤立的硬件爱好者
|
||||
- **硬件专业术语**:掌握指令周期、流水线、CPI、缓存命中率等硬核概念
|
||||
- **性能思维**:理解计算机组成中的瓶颈与优化手段
|
||||
- **后续学习基础**:为操作系统、体系结构、嵌入式开发打下专业基础
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 冯诺依曼架构 | 存储程序、五大组成部件、数据通路 |
|
||||
| **第 2 章** | 指令系统 | 指令格式、寻址方式、CISC vs RISC |
|
||||
| **第 3 章** | CPU 控制器 | 控制单元、微操作、指令周期 |
|
||||
| **第 4 章** | 存储体系 | 缓存、主存、虚拟内存、分页机制 |
|
||||
| **第 5 章** | 总线与 I/O | 总线仲裁、DMA、中断机制 |
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:计算机硬件系统
|
||||
|
||||
在上一章"从晶体管到 CPU"中,我们已经理解了 CPU 内部是如何工作的——从取指、译码、执行到写回。但 CPU 本身只是一个执行单元,要让计算机真正"能用",还需要一系列外围部件的配合。
|
||||
|
||||
<CpuArchitectureDemo />
|
||||
|
||||
::: tip 逐层解构:计算机硬件系统
|
||||
- **第一层:CPU 核心**
|
||||
负责指令执行,包括控制单元(发出控制信号)和运算单元(执行算术逻辑运算)
|
||||
|
||||
- **第二层:寄存器组**
|
||||
CPU 内部的高速存储单元,包括通用寄存器和专用寄存器(PC、IR、MAR、MDR 等)
|
||||
|
||||
- **第三层:主存储器**
|
||||
用于存放程序和数据的内存,CPU 通过地址总线和数据总线访问
|
||||
|
||||
- **第四层:I/O 设备**
|
||||
输入输出设备通过 I/O 控制器与系统总线相连
|
||||
|
||||
- **第五层:系统总线**
|
||||
连接 CPU、内存、I/O 的数据通道,包括地址总线、数据总线、控制总线
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 冯诺依曼架构:现代计算机的"宪法"
|
||||
|
||||
### 1.1 存储程序原理
|
||||
|
||||
1945 年,数学家约翰·冯·诺依曼(John von Neumann)提出了划时代的**存储程序(Stored-program)**架构思想。这一思想奠定了现代计算机的基础。
|
||||
|
||||
::: tip 核心概念
|
||||
**存储程序**:程序本身作为一种特殊的数据,和普通数据一样存储在内存中。CPU 可以像读写数据一样读取并执行存储在内存中的程序指令。
|
||||
:::
|
||||
|
||||
这意味着:
|
||||
- **早期计算机**:程序是固定布线实现的,改变程序需要重新焊接电路
|
||||
- **冯诺依曼架构**:程序存储在内存中,改变程序只需修改内存内容
|
||||
|
||||
### 1.2 五大组成部件
|
||||
|
||||
冯诺依曼架构将计算机划分为五个核心组成部分:
|
||||
|
||||
<RegisterDemo />
|
||||
|
||||
| 部件 | 英文 | 功能 | 主要组成 |
|
||||
|------|------|------|---------|
|
||||
| **运算器** | ALU (Arithmetic Logic Unit) | 执行算术和逻辑运算 | 加法器、移位器、比较器 |
|
||||
| **控制器** | CU (Control Unit) | 指挥协调各部件工作 | 指令寄存器、译码器、时序发生器 |
|
||||
| **存储器** | Memory | 存储程序和数据 | 内存地址寄存器(MAR)、内存数据寄存器(MDR) |
|
||||
| **输入设备** | Input | 信息输入 | 键盘、鼠标、扫描仪 |
|
||||
| **输出设备** | Output | 信息输出 | 显示器、打印机 |
|
||||
|
||||
### 1.3 数据通路
|
||||
|
||||
**数据通路(Data Path)**是数据在各个功能部件之间流动的路径。在 CPU 内部,数据通路连接了:
|
||||
|
||||
- 寄存器组
|
||||
- 算术逻辑单元(ALU)
|
||||
- 内存数据寄存器(MDR)
|
||||
|
||||
数据通路的宽度(一次能传输多少位)直接影响了计算机的性能。
|
||||
|
||||
### 1.4 冯诺依曼瓶颈
|
||||
|
||||
冯诺依曼架构有一个著名的**性能瓶颈**:
|
||||
|
||||
> CPU 与内存之间的数据传输速度,远低于 CPU 的处理速度。
|
||||
|
||||
这导致 CPU 经常处于"等待数据"的空闲状态。现代计算机的很多优化技术都是围绕这个问题展开的:
|
||||
|
||||
| 优化技术 | 原理 |
|
||||
|---------|------|
|
||||
| **缓存(Cache)** | 在 CPU 附近放置小容量高速存储 |
|
||||
| **指令流水线** | 让多条指令同时处于不同阶段 |
|
||||
| **超标量** | 同一时钟周期发射多条指令 |
|
||||
| **多核并行** | 多个 CPU 核心分担计算任务 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 指令系统:CPU 与软件的接口
|
||||
|
||||
上一节我们知道了冯诺依曼架构的核心思想:**程序和数据一样存储在内存中**。但这引出了一个关键问题——存在内存里的"程序"到底长什么样?CPU 怎么读懂它?
|
||||
|
||||
答案就是**指令系统(Instruction Set Architecture, ISA)**。如果把 CPU 比作一个服务,那指令系统就是它的 **API 文档**——它定义了 CPU 能听懂的所有命令、每条命令的格式、以及命令能操作的数据范围。你写的每一行代码,最终都会被编译器翻译成这套"API"的调用序列。
|
||||
|
||||
### 2.1 从代码到指令:一行代码的翻译之旅
|
||||
|
||||
我们先建立一个全局认知:你在编辑器里写的代码,和 CPU 实际执行的东西,中间隔了好几层翻译。
|
||||
|
||||
<CodeToInstructionDemo />
|
||||
|
||||
这个翻译链路是理解指令系统的关键:
|
||||
|
||||
| 层次 | 内容 | 谁能看懂 |
|
||||
|------|------|---------|
|
||||
| 高级语言 | `int a = 10 + 5;` | 人类 |
|
||||
| 汇编语言 | `MOV R1, #10` / `ADD R3, R1, R2` | 人类(需要训练) |
|
||||
| 机器码 | `0001 0001 0000 1010` | CPU |
|
||||
|
||||
::: tip 为什么要理解这个链路?
|
||||
- 看到编译报错时,你知道错误发生在"高级语言→汇编"这一步
|
||||
- 看到运行时崩溃时,你知道问题出在 CPU 执行指令的阶段
|
||||
- 理解性能优化时,你知道编译器在"翻译"过程中做了哪些优化
|
||||
- 选择 CPU 架构时(x86 vs ARM),你知道差异在于"指令集 API"不同
|
||||
:::
|
||||
|
||||
### 2.2 一条指令长什么样?
|
||||
|
||||
知道了代码会被翻译成指令,下一个问题是:**一条指令的内部结构是什么?**
|
||||
|
||||
每条机器指令本质上就是一串二进制数字,但它有严格的内部格式。最核心的两个部分:
|
||||
|
||||
- **操作码(Opcode)**:告诉 CPU「做什么」——是加法?跳转?还是读内存?
|
||||
- **操作数(Operand)**:告诉 CPU「对谁做」——哪个寄存器?哪个内存地址?什么常数?
|
||||
|
||||
就像一句话有「动词 + 宾语」的结构,指令也有「操作 + 对象」的结构:
|
||||
|
||||
```
|
||||
指令: ADD R3, R1, R2
|
||||
─── ──────────
|
||||
操作码 操作数
|
||||
(做加法) (R3 = R1 + R2)
|
||||
```
|
||||
|
||||
根据操作数的数量,指令格式从简单到复杂分为四种:
|
||||
|
||||
<InstructionFormatDemo />
|
||||
|
||||
| 格式 | 结构 | 例子 | 使用场景 |
|
||||
|------|------|------|---------|
|
||||
| 零地址 | 只有操作码 | `RET`(返回) | 堆栈计算机,操作数隐含在栈顶 |
|
||||
| 一地址 | 操作码 + 1个地址 | `INC R1`(R1加1) | 单操作数运算 |
|
||||
| 二地址 | 操作码 + 2个地址 | `MOV R1, R2` | 最常用,数据传送和运算 |
|
||||
| 三地址 | 操作码 + 3个地址 | `ADD R3, R1, R2` | 不破坏源操作数 |
|
||||
|
||||
::: tip 为什么有这么多格式?
|
||||
这是**空间和灵活性的权衡**。零地址指令最短(省内存),但需要额外的栈操作;三地址指令最灵活(不破坏源数据),但占用更多位数。不同 CPU 架构会选择不同的指令格式组合。
|
||||
:::
|
||||
|
||||
### 2.3 CPU 怎么找到数据?——寻址方式
|
||||
|
||||
指令告诉 CPU「做加法」,但加法的两个数在哪里?可能直接写在指令里,可能在寄存器里,也可能在内存的某个地址。**寻址方式**就是告诉 CPU「去哪里找操作数」的规则。
|
||||
|
||||
用生活中「找人」来类比:
|
||||
|
||||
| 寻址方式 | 类比 | 指令示例 | 说明 |
|
||||
|---------|------|---------|------|
|
||||
| **立即数寻址** | 人就站在你面前 | `MOV R1, #100` | 数据直接写在指令里,最快 |
|
||||
| **寄存器寻址** | 打内线电话找同事 | `MOV R1, R2` | 数据在 CPU 内部的寄存器里,很快 |
|
||||
| **直接寻址** | 知道门牌号,直接上门 | `MOV R1, [0x1000]` | 指令里写了内存地址 |
|
||||
| **间接寻址** | 问前台「张三在哪个房间」 | `MOV R1, [R2]` | 寄存器里存的是地址,要多查一次 |
|
||||
| **变址寻址** | 「3号楼 + 5层」算出房间 | `MOV R1, [R2+10]` | 基地址 + 偏移量,用于数组访问 |
|
||||
|
||||
<AddressingModeDemo />
|
||||
|
||||
::: tip 为什么需要这么多寻址方式?
|
||||
不同场景需要不同的「找数据」策略:
|
||||
- **常量赋值**(`x = 100`)→ 立即数寻址,数据就在指令里
|
||||
- **变量运算**(`a + b`)→ 寄存器寻址,数据已经加载到寄存器
|
||||
- **数组访问**(`arr[i]`)→ 变址寻址,基地址 + 下标偏移
|
||||
- **指针操作**(`*ptr`)→ 间接寻址,寄存器里存的是地址
|
||||
|
||||
你写 `arr[i]` 时不会想到寻址方式,但编译器会自动选择最合适的方式。
|
||||
:::
|
||||
|
||||
### 2.4 CPU 的能力清单——指令分类
|
||||
|
||||
现在我们知道了指令的格式和寻址方式,最后一个问题:**CPU 到底能做哪些事?**
|
||||
|
||||
所有指令可以归为六大类,它们覆盖了计算机能做的一切操作:
|
||||
|
||||
| 类型 | 做什么 | 代表指令 | 对应你写的代码 |
|
||||
|------|-------|---------|-------------|
|
||||
| **数据传送** | 搬运数据 | MOV, LOAD, STORE | `let x = y`、函数传参 |
|
||||
| **算术运算** | 加减乘除 | ADD, SUB, MUL, DIV | `a + b`、`count++` |
|
||||
| **逻辑运算** | 位操作 | AND, OR, NOT, XOR | `flags & 0xFF`、权限判断 |
|
||||
| **移位操作** | 左移右移 | SHL, SHR | `x << 2`(等价于乘4) |
|
||||
| **控制转移** | 跳转和调用 | JMP, CALL, RET | `if`、`for`、函数调用 |
|
||||
| **输入输出** | 与外设通信 | IN, OUT | 读键盘、写屏幕 |
|
||||
|
||||
::: tip 一个关键洞察
|
||||
你写的所有代码——不管多复杂的业务逻辑、多炫酷的 UI 动画——最终都会被拆解成这六类基本操作的组合。CPU 的"智能"不在于它能做多复杂的事,而在于它能以每秒几十亿次的速度执行这些简单操作。
|
||||
:::
|
||||
|
||||
### 2.5 两种设计哲学:CISC vs RISC
|
||||
|
||||
指令系统的设计有一个根本性的分歧:**是让每条指令尽可能强大,还是让每条指令尽可能简单?**
|
||||
|
||||
这个分歧产生了两大阵营,直接影响了你今天用的每一台设备:
|
||||
|
||||
<CISCvsRISCDemo />
|
||||
|
||||
用一个类比来理解:
|
||||
- **CISC 像瑞士军刀**:一把刀集成了剪刀、开瓶器、螺丝刀……功能多但每个不一定最好用
|
||||
- **RISC 像专业工具套装**:每个工具只做一件事,但做得又快又好
|
||||
|
||||
::: tip 为什么你的手机用 ARM、电脑用 x86?
|
||||
- **x86 (CISC)** 统治了 PC 和服务器市场 40 年,积累了庞大的软件生态。换架构意味着所有软件都要重新编译
|
||||
- **ARM (RISC)** 凭借低功耗优势统治了移动设备。手机电池小,每一毫瓦都很珍贵
|
||||
- **Apple Silicon** 证明了 RISC 也能做到高性能——M 系列芯片在性能和功耗上同时超越了 x86 对手
|
||||
- **RISC-V** 是开源的 RISC 架构,正在 IoT、教育、AI 芯片领域快速崛起
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
> **小结**:指令系统是连接软件和硬件的桥梁。你写的代码通过编译器翻译成指令,指令通过操作码和操作数告诉 CPU 做什么、对谁做,寻址方式决定了数据从哪里来。不同的指令集设计(CISC/RISC)决定了 CPU 的性能特征和适用场景。
|
||||
>
|
||||
> 现在我们知道了指令的「静态结构」——它长什么样、有哪些类型。下一个问题是:**CPU 内部是怎么一步步执行这些指令的?** 这就是控制器的工作。
|
||||
|
||||
---
|
||||
|
||||
## 3. 控制器:CPU 的"指挥中心"
|
||||
|
||||
### 3.1 控制器的组成
|
||||
|
||||
控制器是 CPU 的"大脑",负责协调各部件按指令要求工作:
|
||||
|
||||
<ControllerDemo />
|
||||
|
||||
| 组件 | 功能 |
|
||||
|------|------|
|
||||
| **程序计数器 (PC)** | 存放下一条指令的地址 |
|
||||
| **指令寄存器 (IR)** | 存放当前正在执行的指令 |
|
||||
| **指令译码器** | 解析指令的操作码和操作数 |
|
||||
| **时序发生器** | 产生节拍信号,控制各部件时序 |
|
||||
| **微操作序列生成器** | 产生执行指令所需的一系列控制信号 |
|
||||
|
||||
<PSWFlagDemo />
|
||||
|
||||
### 3.2 指令周期
|
||||
|
||||
CPU 执行一条指令需要经历一个完整的**指令周期**,通常包括:
|
||||
|
||||
1. **取指周期 (Fetch)**: 从内存读取指令到 IR
|
||||
2. **译码周期 (Decode)**: 解析指令含义
|
||||
3. **执行周期 (Execute)**: 执行操作
|
||||
4. **访存周期 (Memory Access)**: 如果需要访存,访问内存
|
||||
5. **写回周期 (Write Back)**: 把结果写回寄存器或内存
|
||||
|
||||
### 3.3 微操作
|
||||
|
||||
**微操作**是控制信号驱动下的最基本操作。例如,"取指"这个阶段可以分解为以下微操作:
|
||||
|
||||
| 节拍 | 微操作 | 控制信号 |
|
||||
|------|--------|---------|
|
||||
| T1 | PC → MAR | PCout, MARin |
|
||||
| T2 | MEM → MDR | MEMout, MDRin |
|
||||
| T3 | MDR → IR | MDRout, IRin |
|
||||
| T4 | PC + 1 → PC | PC+1, PCin |
|
||||
|
||||
### 3.4 硬布线 vs 微程序控制器
|
||||
|
||||
| 特性 | 硬布线控制器 | 微程序控制器 |
|
||||
|------|------------|-------------|
|
||||
| **实现方式** | 组合逻辑电路 | 微指令序列(固件) |
|
||||
| **速度** | 快 | 稍慢 |
|
||||
| **设计难度** | 复杂 | 较简单 |
|
||||
| **灵活性** | 差(改动需重新设计电路) | 好(修改微程序即可) |
|
||||
| **典型应用** | RISC 处理器 | CISC 处理器早期 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 存储体系:为什么需要缓存?
|
||||
|
||||
### 4.1 存储层次结构
|
||||
|
||||
计算机的存储设备构成了一个金字塔结构:
|
||||
|
||||
<StorageHierarchyDemo />
|
||||
|
||||
| 层次 | 存储类型 | 访问时间 | 典型容量 | 位置 |
|
||||
|------|---------|---------|---------|------|
|
||||
| **寄存器** | SRAM | <1ns | 几 KB | CPU 内部 |
|
||||
| **L1 缓存** | SRAM | ~1ns | 32-64KB | CPU 核心附近 |
|
||||
| **L2 缓存** | SRAM | ~3-10ns | 256KB-1MB | CPU 芯片内 |
|
||||
| **L3 缓存** | SRAM | ~10-20ns | 2-16MB | CPU 芯片内/共享 |
|
||||
| **主存(内存)** | DRAM | ~50-100ns | 8-64GB | 主板上 |
|
||||
| **SSD** | Flash | ~10-100μs | 256GB-2TB | 主板上 |
|
||||
| **HDD** | 磁盘 | ~5-10ms | 1-10TB | 机箱内 |
|
||||
|
||||
::: tip 速度差异的比喻
|
||||
如果把 CPU 访问 L1 缓存比作**从桌上拿一张纸**:
|
||||
- 访问内存 → 坐电梯去楼下便利店买纸
|
||||
- 访问 SSD → 开车去另一个城市买纸
|
||||
- 访问 HDD → 坐飞机去另一个国家买纸
|
||||
|
||||
速度差异可达**上百万倍**!
|
||||
:::
|
||||
|
||||
### 4.2 缓存原理
|
||||
|
||||
**缓存(Cache)** 是位于 CPU 和内存之间的快速存储,其核心思想基于两个局部性原理:
|
||||
|
||||
::: tip 局部性原理
|
||||
- **时间局部性**:如果一个数据刚被访问,它很可能很快又被访问
|
||||
- **空间局部性**:如果一个数据被访问,它附近的数据很可能也被访问
|
||||
:::
|
||||
|
||||
#### 缓存的工作方式
|
||||
|
||||
1. **命中(Hit)**:CPU 要的数据在缓存中,直接读取
|
||||
2. **缺失(Miss)**:数据不在缓存中,需要从内存加载
|
||||
|
||||
```
|
||||
命中率 = 命中次数 / 总访问次数
|
||||
平均访问时间 = 命中率 × 缓存时间 + (1-命中率) × 内存时间
|
||||
```
|
||||
|
||||
<CacheDemo />
|
||||
|
||||
### 4.3 缓存映射方式
|
||||
|
||||
| 方式 | 原理 | 优点 | 缺点 |
|
||||
|------|------|------|------|
|
||||
| **直接映射** | 每个内存块只能放到一个固定位置 | 简单快速 | 冲突率高 |
|
||||
| **组相联** | 每个内存块可以放到 N 个位置(N路) | 平衡速度与命中率 | 实现复杂 |
|
||||
| **全相联** | 任意位置 | 最低冲突率 | 实现困难(需要比较所有标签) |
|
||||
|
||||
### 4.4 虚拟内存
|
||||
|
||||
**虚拟内存**是操作系统提供的重要抽象:
|
||||
|
||||
- 每个进程都认为自己拥有完整的虚拟地址空间
|
||||
- 操作系统负责把虚拟地址翻译成物理地址
|
||||
- 不常用的页面可以换出到磁盘(交换空间)
|
||||
|
||||
::: tip 虚拟内存的比喻
|
||||
把虚拟内存想象成**酒店管理房间**:
|
||||
- 你(进程)以为整栋楼都是你的
|
||||
- 实际上酒店(OS)只给你分配当前需要的房间
|
||||
- 不住的房间会被"换出"到仓库(磁盘)
|
||||
- 需要的房间可以随时"换入"
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 总线与 I/O:计算机的"血管"
|
||||
|
||||
### 5.1 系统总线
|
||||
|
||||
**总线(Bus)** 是连接计算机各部件的数据通道:
|
||||
|
||||
<BusSystemDemo />
|
||||
|
||||
| 总线类型 | 功能 | 方向 | 典型宽度 |
|
||||
|---------|------|------|---------|
|
||||
| **地址总线** | 传送内存地址 | 单向(CPU→内存) | 32位/64位 |
|
||||
| **数据总线** | 传送数据 | 双向 | 32位/64位 |
|
||||
| **控制总线** | 传送控制信号 | 双向 | 多个信号线 |
|
||||
|
||||
### 5.2 总线仲裁
|
||||
|
||||
当多个设备同时请求使用总线时,需要**仲裁**机制决定谁先使用:
|
||||
|
||||
| 仲裁方式 | 说明 |
|
||||
|---------|------|
|
||||
| **集中仲裁** | 中央仲裁器统一决定 |
|
||||
| **分布式仲裁** | 各设备自行协商 |
|
||||
|
||||
### 5.3 I/O 设备访问方式
|
||||
|
||||
| 方式 | 原理 | 优点 | 缺点 |
|
||||
|------|------|------|------|
|
||||
| **程序查询** | CPU 轮询检查 I/O 状态 | 简单 | CPU 利用率低 |
|
||||
| **中断方式** | I/O 完成后主动通知 CPU | CPU 可并行工作 | 中断处理有开销 |
|
||||
| **DMA** | I/O 设备直接访问内存 | CPU 完全不参与 | 需要 DMA 控制器 |
|
||||
|
||||
<IOMethodDemo />
|
||||
|
||||
### 5.4 DMA 原理
|
||||
|
||||
**DMA (Direct Memory Access,直接内存访问)** 允许 I/O 设备直接与内存交换数据:
|
||||
|
||||
<NetworkOverviewDemo />
|
||||
|
||||
- **无 DMA**:CPU 全程参与数据传送,CPU 无法做其他事
|
||||
- **有 DMA**:CPU 告诉 DMA 控制器"从哪里传到哪里、传多少",然后去执行其他任务,DMA 完成后通知 CPU
|
||||
|
||||
::: tip DMA 的比喻
|
||||
这就像**点外卖**:
|
||||
- **没有 DMA**:你亲自去超市买菜、回家、洗菜、炒菜(全过程参与)
|
||||
- **有 DMA**:你打电话下单,外卖小哥直接送到厨房(别人帮你搞定,你只需要最后"收货")
|
||||
:::
|
||||
|
||||
### 5.5 中断机制
|
||||
|
||||
**中断**是计算机系统中非常重要的机制:
|
||||
|
||||
1. I/O 设备完成操作后,向 CPU 发送**中断请求**
|
||||
2. CPU 正在执行指令,完成当前指令后响应中断
|
||||
3. CPU 保存当前状态,跳转到中断处理程序
|
||||
4. 处理完成后,恢复状态继续执行
|
||||
|
||||
---
|
||||
|
||||
## 6. CPU 性能优化:流水线技术
|
||||
|
||||
### 6.1 指令流水线
|
||||
|
||||
**指令流水线**是一种让 CPU 效率最大化的并行技术:
|
||||
|
||||
<PipelineDemo />
|
||||
|
||||
#### 流水线的工作原理
|
||||
|
||||
```
|
||||
顺序执行(5条指令,15个周期):
|
||||
指令1: IF→ID→EX→MEM→WB
|
||||
指令2: IF→ID→EX→MEM→WB
|
||||
指令3: IF→ID→EX→MEM→WB
|
||||
...
|
||||
|
||||
流水线执行(5条指令,9个周期):
|
||||
指令1: IF→ID→EX→MEM→WB
|
||||
指令2: IF→ID→EX→MEM→WB
|
||||
指令3: IF→ID→EX→MEM→WB
|
||||
...
|
||||
```
|
||||
|
||||
理想情况下,N 条指令的 CPI(每指令周期数) ≈ 1
|
||||
|
||||
### 6.2 流水线冒险
|
||||
|
||||
流水线虽然能提高性能,但也会带来**冒险(Hazard)** 问题:
|
||||
|
||||
| 类型 | 原因 | 解决方案 |
|
||||
|------|------|---------|
|
||||
| **结构冒险** | 硬件资源冲突 | 增加硬件/错开执行 |
|
||||
| **数据冒险** | 后面的指令需要前面的结果 | 数据转发/气泡/调度 |
|
||||
| **控制冒险** | 跳转指令改变执行流 | 延迟槽/分支预测 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 总结:计算机是如何"跑起来"的?
|
||||
|
||||
让我们用专业术语串联整个流程:
|
||||
|
||||
> **程序启动后,操作系统将可执行文件从磁盘加载到内存。CPU 的取指单元(IF)通过地址总线从内存读取指令到指令寄存器(IR)。控制器对指令进行译码(ID),识别出操作类型后产生相应的控制信号。运算单元(EX)执行算术逻辑运算,如果需要访存则通过数据总线访问内存(MEM),最后结果写回(WB)到寄存器或内存。整个过程由时钟驱动,控制器发出的微操作序列协调各部件有序工作。**
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
| 主题 | 推荐深入学习内容 |
|
||||
|------|-----------------|
|
||||
| 计算机体系结构 | 《计算机组成与设计:硬件/软件接口》- Patterson & Hennessy |
|
||||
| CPU 微架构 | 《深入理解计算机系统》- Bryant & O'Hallaron |
|
||||
| 指令集架构 | ARMv8 架构手册、Intel x64 手册 |
|
||||
| 缓存原理 | 缓存一致性协议(MESI)、缓存写策略 |
|
||||
| 操作系统 | 后续章节《操作系统》 |
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
现在你已经掌握了计算机组成原理的专业知识。接下来可以继续学习:
|
||||
|
||||
- **[操作系统](./operating-systems.md)**:了解程序是如何在操作系统上运行的,进程、线程、内存管理是如何实现的
|
||||
- **[数据的编码、存储与传输](./data-encoding-storage.md)**:深入理解数据在计算机中的表示方式
|
||||
@@ -3,15 +3,22 @@
|
||||
::: tip 前言
|
||||
你有没有想过,当你按下电脑电源键,到最终在浏览器中看到网页,这中间到底发生了什么?
|
||||
|
||||
这个过程涉及**硬件启动**、**操作系统加载**、**网络通信**等多个环节。理解这个过程,能帮助你建立对计算机系统的整体认知,也是成为全栈工程师的必经之路。
|
||||
这个过程就像一场**接力赛**——硬件通电后唤醒固件,固件检查完毕后交棒给操作系统,操作系统准备好环境后才能运行浏览器,浏览器再通过网络去远方的服务器取回网页。每一个环节都**依赖上一个环节的成功完成**,任何一棒掉链子,后面的步骤都无法进行。
|
||||
|
||||
理解这条完整的链路,能帮助你建立对计算机系统的整体认知,也是成为全栈工程师的必经之路。
|
||||
:::
|
||||
|
||||
**你会学到什么?**
|
||||
|
||||
- 电脑从通电到显示桌面的完整过程
|
||||
- 操作系统是如何启动的
|
||||
- 浏览器是如何工作的
|
||||
- 当你访问一个 URL 时,网络请求是如何完成的
|
||||
这篇文章按照事件发生的真实顺序,带你走完从按下电源到看到网页的五个阶段:
|
||||
|
||||
1. **硬件启动**(第 1 节)→ 电流如何唤醒 CPU
|
||||
2. **固件自检**(第 2 节)→ BIOS/UEFI 如何确认硬件正常并找到启动设备
|
||||
3. **操作系统启动**(第 3 节)→ 内核如何加载、桌面如何出现
|
||||
4. **浏览器启动**(第 4 节)→ 应用程序如何被操作系统运行起来
|
||||
5. **网络请求**(第 5 节)→ 从输入 URL 到页面渲染的完整网络之旅
|
||||
|
||||
每一步都建立在前一步的基础上,缺一不可。
|
||||
|
||||
---
|
||||
|
||||
@@ -37,6 +44,12 @@ CPU 接收到复位信号后,把内部所有寄存器和缓存清零,从一
|
||||
|
||||
---
|
||||
|
||||
> **接力第一棒完成** ⛳ 到这里,硬件层面的工作已经完成:电源把交流电转成了稳定的直流电,主板芯片组被唤醒并开始协调各部件,CPU 也完成了复位、清空了寄存器,准备好执行第一条指令。
|
||||
>
|
||||
> 但请注意——此刻的 CPU 就像一个"刚睁开眼的婴儿"。它虽然能执行指令,却对自己所在的环境一无所知:电脑里装了多少内存?显卡能不能用?硬盘在哪里?该从哪个设备启动操作系统?这些问题 CPU 自己回答不了。
|
||||
>
|
||||
> 所以,CPU 复位后执行的第一条指令,就是跳转到一个**固定的内存地址**——这个地址指向主板上焊死的 BIOS/UEFI 固件芯片。从这一刻起,控制权从纯硬件交到了固件手中。BIOS/UEFI 的任务很明确:**检查所有硬件是否正常,然后找到操作系统并把它启动起来**。这就是接力赛的第二棒。
|
||||
|
||||
## 2. BIOS/UEFI:硬件的自检
|
||||
|
||||
### 2.1 什么是 BIOS/UEFI?
|
||||
@@ -83,6 +96,12 @@ BIOS/UEFI 会按照设定的**启动顺序**查找启动设备:
|
||||
|
||||
---
|
||||
|
||||
> **接力第二棒完成** ⛳ BIOS/UEFI 圆满完成了它的三项使命:通过 POST 自检确认内存、显卡、键盘等硬件全部工作正常;初始化各硬件的工作模式;按照启动顺序找到了硬盘上的启动扇区。
|
||||
>
|
||||
> 但 BIOS/UEFI 的角色到此为止——它本质上是一个"体检医生 + 调度员"。它能检查硬件健不健康、能决定从哪个设备启动,但它不会管理你的文件,不会运行你的应用程序,也不会给你显示一个漂亮的桌面。这些复杂的任务,需要一个更强大的软件来接管——那就是**操作系统**。
|
||||
>
|
||||
> 交接的方式很具体:BIOS/UEFI 读取硬盘第一个扇区(启动扇区)里的引导程序代码,把它加载到内存中,然后让 CPU 跳转到这段代码开始执行。从这一刻起,控制权正式从固件交给了操作系统的引导程序。引导程序会一步步把操作系统内核加载进来,启动系统服务,最终呈现出你熟悉的桌面。这条链路中最复杂的一棒,开始了。
|
||||
|
||||
## 3. 操作系统启动:从内核到桌面
|
||||
|
||||
### 3.1 什么是操作系统?
|
||||
@@ -176,6 +195,12 @@ BIOS → GRUB → vmlinuz (内核) → systemd → 系统服务 → 桌面环境
|
||||
|
||||
---
|
||||
|
||||
> **接力第三棒完成** ⛳ 操作系统已经完全启动,桌面呈现在你眼前。回顾一下这一棒做了什么:引导程序从硬盘读取内核、内核接管了 CPU 和内存的控制权、系统服务逐个启动(网络、音频、安全中心……)、最后图形界面渲染出桌面。
|
||||
>
|
||||
> 此刻的操作系统就像一座已经通水通电、物业入驻的大楼——**进程管理**负责给每个住户(程序)分配房间,**内存管理**负责分配空间,**文件系统**负责管理仓库,**网络协议栈**负责对外通信。这些"公共服务"是所有应用程序运行的基础设施,没有它们,任何程序都无法启动。
|
||||
>
|
||||
> 现在你想上网,于是双击了桌面上的浏览器图标。这个简单的动作背后,操作系统要做一系列工作:查找浏览器的可执行文件在硬盘的哪个位置、为它创建一个独立的进程、分配内存空间、加载程序代码……这就是操作系统"进程管理"能力的直接体现。接下来,让我们看看浏览器是如何被启动起来的。
|
||||
|
||||
## 4. 打开浏览器:应用程序的启动
|
||||
|
||||
### 4.1 应用程序的启动过程
|
||||
@@ -217,6 +242,12 @@ BIOS → GRUB → vmlinuz (内核) → systemd → 系统服务 → 桌面环境
|
||||
|
||||
---
|
||||
|
||||
> **接力第四棒完成** ⛳ 浏览器已经成功启动。操作系统为它创建了独立的进程,分配了内存空间,浏览器自身的各个模块也已初始化完毕:渲染引擎准备好解析 HTML/CSS,JavaScript 引擎准备好执行脚本,网络模块准备好发送和接收数据。
|
||||
>
|
||||
> 你可以把此刻的浏览器想象成一辆已经发动的汽车——引擎在运转、仪表盘亮起、导航系统就绪,但车还停在原地,因为司机(你)还没有告诉它"去哪里"。浏览器窗口此刻是空白的,地址栏闪烁着光标,等待你的输入。
|
||||
>
|
||||
> 当你在地址栏敲入 `https://www.example.com` 并按下回车,一场跨越整个互联网的旅程就开始了。浏览器的网络模块会接管这个请求:先解析 URL 的结构,再通过 DNS 把域名翻译成 IP 地址,然后跨越网络与远方的服务器建立 TCP 连接,协商加密通道,发送 HTTP 请求,等待服务器响应,最后把收到的 HTML/CSS/JS 代码交给渲染引擎绘制成你看到的网页。这是整条接力链中步骤最多、涉及协议最丰富的一棒——也是 Web 开发者最需要理解的一段。
|
||||
|
||||
## 5. 访问 URL:网络请求的全过程
|
||||
|
||||
### 5.1 什么是 URL?
|
||||
@@ -385,6 +416,12 @@ HTTP 响应格式:
|
||||
|
||||
---
|
||||
|
||||
> **接力最后一棒完成** ⛳ 网页终于显示在你眼前了!回顾这最后一棒经历了多少环节:浏览器解析 URL 提取出协议和域名,通过 DNS 层层查询把域名翻译成 IP 地址,经过 TCP 三次握手与服务器建立可靠连接,再通过 TLS 握手建立加密通道,然后发送 HTTP 请求,服务器处理业务逻辑、查询数据库、组装响应数据返回,最后浏览器的渲染引擎把 HTML 解析成 DOM 树、CSS 计算成样式规则、两者合并成渲染树、计算布局、逐像素绘制到屏幕上。
|
||||
>
|
||||
> 现在,让我们把视角拉远,从头到尾审视这场接力赛的全貌。从按下电源键的那一刻算起:电流唤醒硬件(第 1 棒)→ 固件检查设备并找到启动盘(第 2 棒)→ 操作系统从内核到桌面完整启动(第 3 棒)→ 浏览器作为应用程序被操作系统运行起来(第 4 棒)→ 网络请求跨越互联网取回数据并渲染成页面(第 5 棒)。五棒环环相扣,每一棒都建立在前一棒的成果之上,缺少任何一个环节,你都无法看到眼前的这个网页。
|
||||
>
|
||||
> 接下来,让我们用一张完整的流程图把这五个阶段串在一起,直观地看看它们之间的依赖关系。
|
||||
|
||||
## 6. 完整流程回顾
|
||||
|
||||
让我们把整个过程串起来:
|
||||
@@ -415,6 +452,12 @@ HTTP 响应格式:
|
||||
|
||||
---
|
||||
|
||||
> 看完整条链路,你会发现一个有趣的规律:每个阶段解决的问题完全不同,背后涉及的技术领域也截然不同。第 1 棒是**电子工程**的领域——电源转换、电路设计、信号传输;第 2 棒属于**固件编程**——用底层代码直接操控硬件;第 3 棒是**操作系统**的世界——进程调度、内存管理、文件系统,这是计算机科学的核心课题;第 4 棒涉及**应用开发**——如何设计一个像浏览器这样复杂的软件架构;第 5 棒则横跨**计算机网络**和**前端开发**——从 DNS、TCP/IP、HTTP 等网络协议,到 HTML/CSS/JS 的解析与渲染。
|
||||
>
|
||||
> 这也解释了为什么"全栈工程师"需要广泛的知识面:你写的每一行前端代码,最终都要经过这整条链路才能呈现给用户。理解链路中的每一环,能帮助你在遇到问题时快速定位——是网络层的问题?是服务器的问题?还是浏览器渲染的问题?
|
||||
>
|
||||
> 下面这张知识地图把这些技术领域梳理清楚,也为你后续的深入学习指明方向。
|
||||
|
||||
## 7. 知识地图
|
||||
|
||||
这一章涉及的知识领域:
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
# 类型系统与编译原理入门
|
||||
|
||||
::: tip 前言
|
||||
当你写下 `int x = 10 + 5;` 时,编译器是如何理解每个字符、检查类型是否正确、最终生成机器指令的?本章用两个核心概念——**类型系统**和**编译流程**——帮你理解编程语言背后的"翻译机制"。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **问题诊断能力**:看到 `TypeError` 报错时,能快速定位是类型不匹配还是隐式转换惹的祸
|
||||
- **语言选择依据**:理解为什么 TypeScript 适合大型项目、Python 适合快速原型开发
|
||||
- **类型安全思维**:在写代码时就预见可能的类型错误,而不是等到运行时才发现
|
||||
- **后续学习基础**:为编译原理、语言设计等高级话题打下基础
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 类型系统 | 静态/动态类型、强/弱类型 |
|
||||
| **第 2 章** | 编译流程 | 词法分析、语法分析、代码生成 |
|
||||
|
||||
---
|
||||
|
||||
## 0. 想象你是翻译官
|
||||
|
||||
翻译一本书,你需要:
|
||||
|
||||
1. **识别单词** — 把句子拆成一个个单词(词法分析)
|
||||
2. **理解语法** — 判断句子是否符合语法规则(语法分析)
|
||||
3. **理解含义** — 确保句子意思正确,类型不冲突(语义分析)
|
||||
4. **优化表达** — 让句子更简洁流畅(代码优化)
|
||||
5. **翻译输出** — 翻译成目标语言(代码生成)
|
||||
|
||||
**编译器就是编程语言的"翻译官"**,将你写的代码转换为机器能执行的指令。而**类型系统**就是翻译过程中的"语法检查器"——确保你不会把数字当文字用。
|
||||
|
||||
---
|
||||
|
||||
## 1. 类型系统:数据的交通规则
|
||||
|
||||
👇 动手点点看:探索四种类型系统的区别
|
||||
|
||||
<TypeSystemDemo />
|
||||
|
||||
::: tip 💡 一句话总结
|
||||
类型系统在两个维度上做选择:**何时检查**(编译时 vs 运行时)和**是否允许隐式转换**(强类型 vs 弱类型)。没有最好的组合,只有最适合的场景。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
### 1.1 静态类型 vs 动态类型
|
||||
|
||||
| | 静态类型 | 动态类型 |
|
||||
|---|---|---|
|
||||
| **检查时机** | 编译时(还没运行就检查) | 运行时(跑到那行才检查) |
|
||||
| **发现 bug** | 早(写完就知道) | 晚(用户操作时才暴露) |
|
||||
| **灵活性** | 较低(类型固定) | 较高(类型可变) |
|
||||
| **IDE 支持** | 好(自动补全、重构) | 差(运行时才知道类型) |
|
||||
| **代表** | Java, TypeScript, Rust | Python, JavaScript, Ruby |
|
||||
|
||||
### 1.2 强类型 vs 弱类型
|
||||
|
||||
**核心区别**:`"1" + 1` 会发生什么?
|
||||
|
||||
- **强类型(Python)**:直接报错 `TypeError` — "你得明确告诉我怎么转"
|
||||
- **弱类型(JavaScript)**:悄悄转成 `"11"` — "我猜你想拼字符串"
|
||||
|
||||
弱类型的"好意"常常带来意想不到的 bug。
|
||||
|
||||
### 1.3 类型推断:两全其美
|
||||
|
||||
现代语言的类型推断让你**写着像动态语言,编译器检查像静态语言**:
|
||||
|
||||
```typescript
|
||||
let x = 1 // 编译器自动推断为 number
|
||||
let arr = [1, 2, 3] // 推断为 number[]
|
||||
x = "hello" // ❌ 编译错误!类型不匹配
|
||||
```
|
||||
|
||||
你不用显式写类型声明,编译器也能帮你严格检查。
|
||||
|
||||
---
|
||||
|
||||
## 2. 编译流程:从代码到机器码
|
||||
|
||||
👇 动手点点看:输入代码,观察编译器的六步翻译过程
|
||||
|
||||
<CompilerDemo />
|
||||
|
||||
::: tip 💡 一句话总结
|
||||
编译器的六步流水线:源代码 → Token(词法分析)→ AST(语法分析)→ 带类型的 AST(语义分析)→ IR(中间代码)→ 优化后的 IR → 机器码。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
### 2.1 词法分析:拆出每个"单词"
|
||||
|
||||
```
|
||||
源代码: int x = 10 + 5;
|
||||
|
||||
Token 流:
|
||||
[int] → 关键字
|
||||
[x] → 标识符
|
||||
[=] → 运算符
|
||||
[10] → 数字
|
||||
[+] → 运算符
|
||||
[5] → 数字
|
||||
[;] → 分隔符
|
||||
```
|
||||
|
||||
### 2.2 语法分析:构建语法树(AST)
|
||||
|
||||
```
|
||||
表达式: 1 + 2 * 3
|
||||
|
||||
语法树: 为什么?
|
||||
+ 因为 * 的优先级
|
||||
/ \ 高于 +,所以
|
||||
1 * 2 * 3 先结合
|
||||
/ \
|
||||
2 3
|
||||
```
|
||||
|
||||
### 2.3 语义分析:检查"意思"是否正确
|
||||
|
||||
| 检查内容 | 示例 | 结果 |
|
||||
|---|---|---|
|
||||
| 类型检查 | `int x = "hello"` | ❌ 类型不匹配 |
|
||||
| 作用域分析 | 使用未声明的变量 | ❌ 变量不存在 |
|
||||
| 类型推断 | `1 + 2.0` | ✅ 推断为 float |
|
||||
|
||||
### 2.4 代码优化:让程序跑得更快
|
||||
|
||||
| 优化技术 | 优化前 | 优化后 |
|
||||
|---|---|---|
|
||||
| 常量折叠 | `x = 10 + 5` | `x = 15` |
|
||||
| 死代码消除 | `if (false) { ... }` | 直接删除 |
|
||||
| 常量传播 | `y = x * 2`(x=15) | `y = 30` |
|
||||
|
||||
---
|
||||
|
||||
## 3. 编译型 vs 解释型 vs JIT
|
||||
|
||||
程序写完后,有三种"翻译方式"让它运行:
|
||||
|
||||
| | 编译型 | 解释型 | JIT 即时编译 |
|
||||
|---|---|---|---|
|
||||
| **过程** | 先编译成机器码,再执行 | 边读边执行 | 先解释,热点代码再编译 |
|
||||
| **速度** | 最快 | 最慢 | 中等(热点代码接近编译型) |
|
||||
| **启动** | 慢(需编译) | 快(直接运行) | 中等(需预热) |
|
||||
| **跨平台** | 需要重新编译 | 天然跨平台 | 跨平台 |
|
||||
| **代表** | C, Rust, Go | Python, Ruby | Java, JavaScript (V8) |
|
||||
|
||||
::: tip 💡 为什么 JavaScript 这么快?
|
||||
V8 引擎的 JIT 编译器会监测哪些代码被频繁执行(热点代码),然后把它们编译成高度优化的机器码。所以虽然 JavaScript 是"解释型语言",但在 V8 中它的性能可以接近编译型语言。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 总结
|
||||
|
||||
::: tip 📚 核心要点
|
||||
1. **类型系统**:静态/动态决定检查时机,强/弱决定是否允许隐式转换
|
||||
2. **编译六步**:词法分析 → 语法分析 → 语义分析 → 中间代码 → 优化 → 代码生成
|
||||
3. **三种执行**:编译型快但需编译,解释型灵活但慢,JIT 兼顾两者
|
||||
4. **类型推断**:现代语言让你享受动态语言的简洁和静态语言的安全
|
||||
:::
|
||||
|
||||
**下一步学习**:
|
||||
- [编程语言图谱](./programming-languages) - 了解更多编程语言
|
||||
- [数据结构](./data-structures) - 理解数据的组织方式
|
||||
- [算法思维入门](./algorithm-thinking) - 学习解决问题的方法
|
||||
@@ -0,0 +1,211 @@
|
||||
# 类型系统入门
|
||||
|
||||
::: tip 前言
|
||||
**为什么 `"1" + 1` 在 JavaScript 里得到 `"11"`,在 Python 里却直接报错?** 这背后就是类型系统在起作用。类型系统是编程语言的"交通规则"——它决定了数据能怎么用、能和谁运算、什么时候检查合不合法。理解类型系统,你就能理解不同语言的"性格差异"。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **分类能力**:掌握静态/动态、强/弱类型的四象限分类法
|
||||
- **问题诊断**:看到 `TypeError` 时能快速定位是类型不匹配还是隐式转换
|
||||
- **语言选择**:理解为什么 TypeScript 适合大型项目、Python 适合快速原型
|
||||
- **类型推断**:理解现代语言如何兼顾简洁和安全
|
||||
- **实践意识**:掌握类型安全的编码习惯
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 什么是类型系统 | 类型的本质、为什么需要类型 |
|
||||
| **第 2 章** | 静态类型 vs 动态类型 | 检查时机、IDE 支持、安全性 |
|
||||
| **第 3 章** | 强类型 vs 弱类型 | 隐式转换、类型安全 |
|
||||
| **第 4 章** | 类型推断 | 自动推断、两全其美 |
|
||||
| **第 5 章** | 泛型:写一次,适用所有类型 | 类型参数、类型约束、复用 |
|
||||
| **第 6 章** | 类型安全实战 | 常见陷阱、防御策略 |
|
||||
| **第 7 章** | 语言类型象限图 | 四象限分类、语言选择 |
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:类型是数据的"身份证"
|
||||
|
||||
在现实世界中,你不会把一本书塞进咖啡杯里——因为它们是不同"类型"的东西。编程世界也一样:数字、字符串、布尔值、数组……每种数据都有自己的"身份",决定了它能参与什么运算。
|
||||
|
||||
**类型系统**就是编程语言用来管理这些"身份"的规则体系。它回答两个核心问题:
|
||||
|
||||
::: tip 类型系统的两个核心问题
|
||||
- **何时检查?** 是写代码时就检查(静态类型),还是运行时才检查(动态类型)?
|
||||
- **多严格?** 是严格禁止混用(强类型),还是自动帮你转换(弱类型)?
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 什么是类型系统:数据的交通规则
|
||||
|
||||
<TypeSystemDemo />
|
||||
|
||||
类型系统的本质是一套**约束规则**,它告诉编译器或解释器:
|
||||
|
||||
- 这个变量能存什么值?
|
||||
- 这两个值能不能做加法?
|
||||
- 这个函数的参数应该是什么?
|
||||
|
||||
没有类型系统的世界就像没有交通规则的马路——任何数据都能和任何数据运算,结果完全不可预测。
|
||||
|
||||
| 类型系统的作用 | 说明 | 例子 |
|
||||
|-------------|------|------|
|
||||
| 防止非法运算 | 阻止无意义的操作 | 不能对字符串做除法 |
|
||||
| 提供文档信息 | 类型就是最好的文档 | `function add(a: number, b: number)` 一目了然 |
|
||||
| 辅助 IDE 工具 | 自动补全、重构、跳转 | 输入 `user.` 自动提示所有属性 |
|
||||
| 优化性能 | 编译器知道类型后能生成更快的代码 | 知道是整数就用整数指令 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 静态类型 vs 动态类型:什么时候检查?
|
||||
|
||||
这是类型系统最重要的分类维度——**检查时机**。
|
||||
|
||||
<StaticVsDynamicDemo />
|
||||
|
||||
::: tip 核心区别
|
||||
- **静态类型**:变量的类型在编译时就确定了,写完代码、还没运行就能发现类型错误。代表:Java、TypeScript、Rust、Go。
|
||||
- **动态类型**:变量的类型在运行时才确定,同一个变量可以先存数字再存字符串。代表:Python、JavaScript、Ruby、PHP。
|
||||
:::
|
||||
|
||||
| 维度 | 静态类型 | 动态类型 |
|
||||
|------|---------|---------|
|
||||
| 检查时机 | 编译时(还没运行就检查) | 运行时(跑到那行才检查) |
|
||||
| 发现 bug | 早(写完就知道) | 晚(用户操作时才暴露) |
|
||||
| 灵活性 | 较低(类型固定) | 较高(类型可变) |
|
||||
| IDE 支持 | 好(自动补全、重构) | 较弱(运行时才知道类型) |
|
||||
| 开发速度 | 前期慢(要写类型) | 前期快(不用管类型) |
|
||||
| 维护成本 | 低(类型即文档) | 高(缺少类型信息) |
|
||||
|
||||
::: tip 趋势:动态语言在"静态化"
|
||||
Python 加了 Type Hints,JavaScript 社区转向 TypeScript——动态语言也在拥抱静态类型的好处。这说明在大型项目中,静态类型的安全性优势越来越被认可。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 强类型 vs 弱类型:允不允许"偷偷转换"?
|
||||
|
||||
第二个分类维度是**类型转换的严格程度**。
|
||||
|
||||
<StrongVsWeakDemo />
|
||||
|
||||
::: tip 核心区别
|
||||
- **强类型**:不允许隐式类型转换,类型不匹配就报错。你必须显式地告诉语言"我要把字符串转成数字"。
|
||||
- **弱类型**:允许隐式类型转换,语言会"好心"帮你自动转。但这种"好心"经常带来意想不到的 bug。
|
||||
:::
|
||||
|
||||
| 维度 | 强类型 | 弱类型 |
|
||||
|------|-------|-------|
|
||||
| `"1" + 1` | 报错或需显式转换 | 自动转换(可能得到 `"11"` 或 `2`) |
|
||||
| 安全性 | 高(不会悄悄出错) | 低(隐式转换可能导致 bug) |
|
||||
| 便利性 | 低(需要手动转换) | 高(自动转换省事) |
|
||||
| 可预测性 | 高(行为确定) | 低(转换规则复杂) |
|
||||
|
||||
---
|
||||
|
||||
## 4. 类型推断:两全其美的现代方案
|
||||
|
||||
早期的静态类型语言(如 Java)要求你显式声明每个变量的类型,写起来很啰嗦。现代语言通过**类型推断**解决了这个问题——编译器自动推断类型,你不用写,但它帮你严格检查。
|
||||
|
||||
<TypeInferenceFlowDemo />
|
||||
|
||||
::: tip 类型推断的价值
|
||||
写着像动态语言一样简洁,编译器检查像静态语言一样严格。这是现代编程语言的主流方向。
|
||||
- **TypeScript**:`let x = 42` 自动推断为 `number`
|
||||
- **Rust**:`let v = vec![1, 2, 3]` 自动推断为 `Vec<i32>`
|
||||
- **Kotlin**:`val name = "Alice"` 自动推断为 `String`
|
||||
- **Go**:`x := 42` 短变量声明自动推断类型
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 泛型:写一次,适用所有类型
|
||||
|
||||
当你写了一个"取数组第一个元素"的函数,你会发现:数字数组要写一个、字符串数组要写一个、对象数组又要写一个……代码完全一样,只是类型不同。**泛型(Generics)**就是解决这个问题的——用一个"类型参数"代替具体类型,让一份代码适用于所有类型。
|
||||
|
||||
<GenericTypeDemo />
|
||||
|
||||
::: tip 泛型的核心价值
|
||||
- **代码复用**:一个函数/类适用于所有类型,不用重复写
|
||||
- **类型安全**:不像 `any` 那样放弃类型检查,泛型全程保持类型信息
|
||||
- **类型约束**:用 `extends` 限制泛型的范围,既灵活又安全
|
||||
:::
|
||||
|
||||
| 泛型特性 | 说明 | 示例 |
|
||||
|---------|------|------|
|
||||
| 泛型函数 | 函数的参数/返回值使用类型参数 | `function first<T>(arr: T[]): T` |
|
||||
| 泛型类 | 类的属性/方法使用类型参数 | `class Box<T> { value: T }` |
|
||||
| 泛型约束 | 用 extends 限制 T 的范围 | `<T extends HasLength>` |
|
||||
| 多个类型参数 | 同时使用多个类型变量 | `function pair<K, V>(k: K, v: V)` |
|
||||
|
||||
---
|
||||
|
||||
## 6. 类型安全实战:常见陷阱与防御
|
||||
|
||||
理论学完了,来看看实际开发中最容易踩的类型坑。这些陷阱不分语言,几乎每个开发者都会遇到。
|
||||
|
||||
<TypeSafetyPracticeDemo />
|
||||
|
||||
::: tip 类型安全的四条黄金法则
|
||||
1. **开启严格模式**:TypeScript 的 `strict: true`、Python 的 `mypy --strict`
|
||||
2. **避免 any**:用 `unknown` 代替 `any`,强制你做类型检查后再使用
|
||||
3. **显式处理 null**:用可选链 `?.` 和空值合并 `??` 安全访问
|
||||
4. **为 API 定义接口**:外部数据永远不可信,用接口 + 运行时校验双重保障
|
||||
:::
|
||||
|
||||
| 陷阱 | 危险程度 | 防御手段 |
|
||||
|------|---------|---------|
|
||||
| null/undefined 引用 | ⭐⭐⭐⭐⭐ | strictNullChecks + 可选链 |
|
||||
| any 类型滥用 | ⭐⭐⭐⭐ | 用 unknown + 类型守卫 |
|
||||
| 隐式类型转换 | ⭐⭐⭐ | 严格比较 === + ESLint |
|
||||
| 数组类型不一致 | ⭐⭐⭐ | 显式声明数组元素类型 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 语言类型象限图:给编程语言"画像"
|
||||
|
||||
把"静态/动态"和"强/弱"两个维度组合起来,就得到了一个四象限分类图。每种编程语言都可以放进这个图里。
|
||||
|
||||
<LanguageTypeModelDemo />
|
||||
|
||||
| 象限 | 特点 | 代表语言 | 适用场景 |
|
||||
|------|------|---------|---------|
|
||||
| 静态 + 强类型 | 最安全,编译时严格检查 | Rust, Java, Haskell | 大型系统、安全关键 |
|
||||
| 静态 + 弱类型 | 编译时检查但允许隐式转换 | C, C++ | 系统编程、性能敏感 |
|
||||
| 动态 + 强类型 | 运行时检查,不允许隐式转换 | Python, Ruby | 脚本、快速原型 |
|
||||
| 动态 + 弱类型 | 最灵活,也最容易出 bug | JavaScript, PHP | Web 前端、小型脚本 |
|
||||
|
||||
::: tip 没有"最好"的类型系统
|
||||
选择语言时,类型系统是重要考量因素之一:
|
||||
- **快速原型**:动态类型(Python)开发速度快
|
||||
- **大型项目**:静态类型(TypeScript、Java)维护成本低
|
||||
- **系统编程**:强类型 + 静态(Rust)安全性最高
|
||||
- **团队协作**:静态类型提供更好的代码可读性和 IDE 支持
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
类型系统是理解编程语言差异的关键视角。它不是枯燥的理论,而是直接影响你写代码的体验和代码的质量。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **类型是身份证**:每种数据都有类型,类型决定了数据能参与什么运算
|
||||
2. **静态 vs 动态**:何时检查类型——编译时还是运行时
|
||||
3. **强 vs 弱**:是否允许隐式类型转换
|
||||
4. **类型推断**:现代语言让你享受动态的简洁和静态的安全
|
||||
5. **泛型**:用类型参数实现代码复用,兼顾灵活性和类型安全
|
||||
6. **类型安全实战**:null 引用、any 滥用、隐式转换是最常见的类型陷阱
|
||||
7. **四象限分类**:没有最好的类型系统,只有最适合场景的选择
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [TypeScript 官方文档](https://www.typescriptlang.org/docs/) - 最流行的静态类型 JavaScript 超集
|
||||
- [Python Type Hints](https://docs.python.org/3/library/typing.html) - Python 的类型提示系统
|
||||
- [Rust Book - Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html) - Rust 的类型系统入门
|
||||
- [Type Systems (Wikipedia)](https://en.wikipedia.org/wiki/Type_system) - 类型系统的学术概述
|
||||
- [What To Know Before Debating Type Systems](https://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/) - 关于类型系统的经典讨论
|
||||
@@ -33,19 +33,7 @@
|
||||
|
||||
想象一下以前的软件开发:
|
||||
|
||||
```
|
||||
传统开发流程:
|
||||
你 → 学习语法 → 写代码 → 调试 → 查文档 → 修改 → 运行
|
||||
↑___________________反复循环___________________↓
|
||||
```
|
||||
|
||||
现在有了 AI 辅助:
|
||||
|
||||
```
|
||||
Vibe Coding 流程:
|
||||
你 → 用自然语言描述需求 → AI 生成代码 → 你审核修改 → 运行
|
||||
↑____________快速迭代____________↓
|
||||
```
|
||||
<VibeCodingFlowDemo />
|
||||
|
||||
**核心变化**:从"怎么写代码"变成"怎么描述需求"。
|
||||
|
||||
@@ -130,7 +118,7 @@ AI 能帮你写代码,但以下能力 AI 替代不了:
|
||||
|
||||
<FrontendFrameworkDemo />
|
||||
|
||||
**核心原因**:当页面变得复杂(比如淘宝、微信网页版),直接操作 DOM 会变得非常混乱。框架帮你"管理复杂性"。
|
||||
**核心原因**:当页面变得复杂(比如淘宝、微信网页版),直接用代码一个个操控页面元素会变得非常混乱。框架帮你"管理复杂性"。
|
||||
|
||||
### 2.4 前端工程师的一天
|
||||
|
||||
@@ -306,28 +294,30 @@ AI 能帮你写代码,但以下能力 AI 替代不了:
|
||||
### 6.2 AI 工程师的技能树
|
||||
|
||||
```
|
||||
AI 算法工程师
|
||||
AI 工程师(2025)
|
||||
│
|
||||
├── 数学基础
|
||||
│ ├── 线性代数(矩阵运算)
|
||||
│ ├── 概率统计(分布、期望)
|
||||
│ └── 微积分(梯度、优化)
|
||||
│
|
||||
├── 编程能力
|
||||
├── 基础能力
|
||||
│ ├── Python(主力语言)
|
||||
│ ├── PyTorch / TensorFlow(深度学习框架)
|
||||
│ └── 数据处理(Pandas, NumPy)
|
||||
│ ├── 数据处理(Pandas, NumPy)
|
||||
│ └── 基本数学直觉(线性代数、概率统计)
|
||||
│
|
||||
├── 机器学习
|
||||
│ ├── 监督学习(分类、回归)
|
||||
│ ├── 无监督学习(聚类、降维)
|
||||
│ └── 模型评估方法
|
||||
├── 大模型应用(最热门方向)
|
||||
│ ├── Prompt Engineering(提示词工程)
|
||||
│ ├── RAG(检索增强生成)
|
||||
│ ├── AI Agent(智能体,让 AI 自主完成任务)
|
||||
│ ├── Function Calling / MCP(让 AI 调用外部工具)
|
||||
│ └── 微调与部署(LoRA, vLLM)
|
||||
│
|
||||
└── 深度学习
|
||||
├── 神经网络基础
|
||||
├── CNN(图像)
|
||||
├── RNN / Transformer(序列)
|
||||
└── 大模型(LLM)
|
||||
├── 生成式 AI(GenAI)
|
||||
│ ├── 文本生成(GPT, Claude, Gemini)
|
||||
│ ├── 图像生成(Stable Diffusion, Midjourney, FLUX)
|
||||
│ ├── 视频生成(Sora, Kling)
|
||||
│ └── 多模态(文本 + 图像 + 音频)
|
||||
│
|
||||
└── 传统机器学习(仍然重要)
|
||||
├── 监督学习(分类、回归)
|
||||
├── 深度学习框架(PyTorch)
|
||||
└── 模型评估与优化
|
||||
```
|
||||
|
||||
### 6.3 AI 工程师的一天
|
||||
|
||||
Reference in New Issue
Block a user