feat(docs): enhance interactive demos and improve documentation

- Add new interactive components for frontend routing, browser rendering pipeline, and database transactions
- Improve existing demos with better visuals, explanations, and examples
- Update documentation structure and content for better clarity
- Add new utility scripts and update package.json with new commands
- Fix formatting and alignment in documentation tables
This commit is contained in:
sanbuphy
2026-02-13 22:10:03 +08:00
parent 599052b2e0
commit d174ceea32
88 changed files with 26273 additions and 15539 deletions
@@ -4,21 +4,33 @@
-->
<template>
<div class="reflow-demo">
<div class="header">
<div class="title">重排与重绘对比</div>
<div class="subtitle">观察不同操作对性能的影响</div>
<div class="demo-header">
<span class="icon"></span>
<span class="title">重排与重绘</span>
<span class="subtitle">观察不同操作对性能的影响</span>
</div>
<div class="demo-container">
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab.id"
class="tab-btn"
:class="{ active: activeTab === tab.id }"
@click="activeTab = tab.id"
>
<span class="tab-icon">{{ tab.icon }}</span>
<span class="tab-label">{{ tab.label }}</span>
</button>
</div>
<div class="demo-area">
<div class="canvas-area">
<div class="box-container">
<div
v-for="box in boxes"
:key="box.id"
class="box"
:class="box.selected ? 'selected' : ''"
:style="getBoxStyle(box)"
@click="selectBox(box.id)"
>
{{ box.id }}
</div>
@@ -51,62 +63,57 @@
</div>
<div class="controls">
<div class="control-section">
<h4>重排操作 (Reflow)</h4>
<p class="control-desc">改变元素尺寸或位置触发布局计算</p>
<button @click="changeWidth" class="btn reflow">改变宽度</button>
<button @click="changePosition" class="btn reflow">改变位置</button>
<button @click="addBox" class="btn reflow">添加元素</button>
<div v-if="activeTab === 'reflow'" class="control-group">
<button @click="changeWidth" class="btn high-impact">改变宽度</button>
<button @click="changePosition" class="btn high-impact">改变位置</button>
<button @click="addBox" class="btn high-impact">添加元素</button>
</div>
<div class="control-section">
<h4>重绘操作 (Repaint)</h4>
<p class="control-desc">只改变外观不触发布局</p>
<button @click="changeColor" class="btn repaint">改变颜色</button>
<button @click="changeBackground" class="btn repaint">
<div v-if="activeTab === 'repaint'" class="control-group">
<button @click="changeColor" class="btn medium-impact">改变颜色</button>
<button @click="changeBackground" class="btn medium-impact">
改变背景
</button>
<button @click="toggleBorder" class="btn repaint">切换边框</button>
<button @click="toggleBorder" class="btn medium-impact">切换边框</button>
</div>
<div class="control-section">
<h4>合成操作 (Composite)</h4>
<p class="control-desc">只触发合成性能最佳</p>
<button @click="transformTranslate" class="btn composite">
<div v-if="activeTab === 'composite'" class="control-group">
<button @click="transformTranslate" class="btn low-impact">
Transform 位移
</button>
<button @click="transformRotate" class="btn composite">
<button @click="transformRotate" class="btn low-impact">
Transform 旋转
</button>
<button @click="changeOpacity" class="btn composite">
<button @click="changeOpacity" class="btn low-impact">
改变透明度
</button>
</div>
</div>
</div>
<div class="info-section">
<div class="info-card">
<h4>什么是重排 (Reflow)</h4>
<p>
当元素的位置尺寸发生变化时浏览器需要重新计算布局这个过程叫重排重排开销最大因为要重新计算所有受影响元素的位置
</p>
<Transition name="fade">
<div v-if="activeTab" class="tab-info">
<div v-if="activeTab === 'reflow'" class="info-content">
<p>
<strong>重排 (Reflow)</strong>当元素的位置尺寸发生变化时浏览器需要重新计算布局重排开销最大因为要重新计算所有受影响元素的位置
</p>
</div>
<div v-if="activeTab === 'repaint'" class="info-content">
<p>
<strong>重绘 (Repaint)</strong>当元素的外观颜色背景发生变化但位置不变时浏览器只需要重新绘制像素比重排快但仍有开销
</p>
</div>
<div v-if="activeTab === 'composite'" class="info-content">
<p>
<strong>合成 (Composite)</strong>使用 transform opacity 等属性浏览器可以在合成层上完成变化完全不触发布局和绘制性能最佳推荐优先使用
</p>
</div>
</div>
</Transition>
<div class="info-card">
<h4>什么是重绘 (Repaint)</h4>
<p>
当元素的外观颜色背景发生变化但位置不变时浏览器只需要重新绘制像素这个过程叫重绘比重排快但仍有开销
</p>
</div>
<div class="info-card">
<h4>什么是合成 (Composite)</h4>
<p>
使用 transform opacity
等属性浏览器可以在合成层上完成变化完全不触发布局和绘制性能最佳推荐优先使用
</p>
</div>
<div class="info-box">
<span class="icon">💡</span>
<strong>性能优化原则</strong>优先使用 transform opacity 进行动画避免频繁触发布局计算 widthheighttopleft可以大幅提升页面性能
</div>
</div>
</template>
@@ -114,46 +121,18 @@
<script setup>
import { ref, computed } from 'vue'
const activeTab = ref('reflow')
const tabs = [
{ id: 'reflow', icon: '🔴', label: '重排' },
{ id: 'repaint', icon: '🟡', label: '重绘' },
{ id: 'composite', icon: '🟢', label: '合成' }
]
const boxes = ref([
{
id: 1,
x: 0,
y: 0,
width: 80,
height: 80,
color: '#3b82f6',
bg: '#dbeafe',
rotation: 0,
opacity: 1,
border: false,
selected: false
},
{
id: 2,
x: 100,
y: 0,
width: 80,
height: 80,
color: '#8b5cf6',
bg: '#ede9fe',
rotation: 0,
opacity: 1,
border: false,
selected: false
},
{
id: 3,
x: 0,
y: 100,
width: 80,
height: 80,
color: '#ec4899',
bg: '#fce7f3',
rotation: 0,
opacity: 1,
border: false,
selected: false
}
{ id: 1, x: 20, y: 20, width: 80, height: 80, bg: 'var(--vp-c-brand-1)', rotation: 0, opacity: 1 },
{ id: 2, x: 120, y: 20, width: 80, height: 80, bg: 'var(--vp-c-brand-2)', rotation: 0, opacity: 1 },
{ id: 3, x: 20, y: 120, width: 80, height: 80, bg: 'var(--vp-c-brand-3)', rotation: 0, opacity: 1 }
])
const currentOperation = ref('无')
@@ -161,13 +140,9 @@ const performanceImpact = ref(0)
const affectedElements = ref(0)
const performanceLevel = computed(() => {
if (performanceImpact.value <= 33) {
return { class: 'good', text: '' }
} else if (performanceImpact.value <= 66) {
return { class: 'medium', text: '中' }
} else {
return { class: 'high', text: '高' }
}
if (performanceImpact.value <= 33) return { class: 'good', text: '低' }
if (performanceImpact.value <= 66) return { class: 'medium', text: '' }
return { class: 'high', text: '高' }
})
function getBoxStyle(box) {
@@ -177,18 +152,11 @@ function getBoxStyle(box) {
width: box.width + 'px',
height: box.height + 'px',
backgroundColor: box.bg,
borderColor: box.color,
borderWidth: box.border ? '3px' : '0px',
color: box.color,
transform: `rotate(${box.rotation}deg)`,
opacity: box.opacity
}
}
function selectBox(id) {
boxes.value.forEach((b) => (b.selected = b.id === id))
}
function updateMetrics(operation, impact, affected) {
currentOperation.value = operation
performanceImpact.value = impact
@@ -196,9 +164,7 @@ function updateMetrics(operation, impact, affected) {
}
function changeWidth() {
boxes.value.forEach((box) => {
box.width = 60 + Math.random() * 60
})
boxes.value.forEach((box) => { box.width = 60 + Math.random() * 60 })
updateMetrics('改变宽度', 90, boxes.value.length)
}
@@ -218,57 +184,41 @@ function addBox() {
y: Math.random() * 100,
width: 80,
height: 80,
color: '#10b981',
bg: '#d1fae5',
bg: 'var(--vp-c-brand)',
rotation: 0,
opacity: 1,
border: false,
selected: false
opacity: 1
})
updateMetrics('添加元素', 95, boxes.value.length)
}
function changeColor() {
const colors = ['#3b82f6', '#8b5cf6', '#ec4899', '#10b981', '#f59e0b']
boxes.value.forEach((box) => {
box.color = colors[Math.floor(Math.random() * colors.length)]
})
const colors = ['var(--vp-c-brand-1)', 'var(--vp-c-brand-2)', 'var(--vp-c-brand-3)']
boxes.value.forEach((box) => { box.bg = colors[Math.floor(Math.random() * colors.length)] })
updateMetrics('改变颜色', 50, boxes.value.length)
}
function changeBackground() {
const bgs = ['#dbeafe', '#ede9fe', '#fce7f3', '#d1fae5', '#fef3c7']
boxes.value.forEach((box) => {
box.bg = bgs[Math.floor(Math.random() * bgs.length)]
})
const bgs = ['var(--vp-c-brand-1)', 'var(--vp-c-brand-2)', 'var(--vp-c-brand-3)']
boxes.value.forEach((box) => { box.bg = bgs[Math.floor(Math.random() * bgs.length)] })
updateMetrics('改变背景', 45, boxes.value.length)
}
function toggleBorder() {
boxes.value.forEach((box) => {
box.border = !box.border
})
updateMetrics('切换边框', 55, boxes.value.length)
}
function transformTranslate() {
boxes.value.forEach((box) => {
box.x += Math.random() * 20 - 10
})
boxes.value.forEach((box) => { box.x += Math.random() * 20 - 10 })
updateMetrics('Transform 位移', 10, boxes.value.length)
}
function transformRotate() {
boxes.value.forEach((box) => {
box.rotation += Math.random() * 30 - 15
})
boxes.value.forEach((box) => { box.rotation += Math.random() * 30 - 15 })
updateMetrics('Transform 旋转', 10, boxes.value.length)
}
function changeOpacity() {
boxes.value.forEach((box) => {
box.opacity = 0.5 + Math.random() * 0.5
})
boxes.value.forEach((box) => { box.opacity = 0.5 + Math.random() * 0.5 })
updateMetrics('改变透明度', 10, boxes.value.length)
}
</script>
@@ -276,54 +226,78 @@ function changeOpacity() {
<style scoped>
.reflow-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
font-family: var(--vp-font-family-base);
padding: 1rem;
margin: 1rem 0;
}
.header {
margin-bottom: 1.5rem;
.demo-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.title {
font-weight: 700;
font-size: 1.05rem;
.demo-header .icon { font-size: 1.25rem; }
.demo-header .title { font-weight: bold; font-size: 1rem; }
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.85rem; margin-left: 0.5rem; }
.tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
.subtitle {
color: var(--vp-c-text-2);
font-size: 0.9rem;
margin-top: 0.3rem;
.tab-btn {
display: flex;
align-items: center;
gap: 0.35rem;
padding: 0.5rem 0.75rem;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
border-radius: 6px;
cursor: pointer;
font-size: 0.85rem;
transition: all 0.2s ease;
}
.demo-container {
.tab-btn:hover {
background: var(--vp-c-bg-soft);
}
.tab-btn.active {
background: var(--vp-c-brand);
color: var(--vp-c-bg-inverse);
border-color: var(--vp-c-brand);
}
.tab-icon { font-size: 1rem; }
.tab-label { font-weight: 500; }
.demo-area {
display: grid;
grid-template-columns: 1fr 300px;
gap: 1.5rem;
margin-bottom: 1.5rem;
grid-template-columns: 1fr 200px;
gap: 1rem;
}
@media (max-width: 768px) {
.demo-container {
grid-template-columns: 1fr;
}
.demo-area { grid-template-columns: 1fr; }
}
.canvas-area {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 10px;
padding: 1.5rem;
border-radius: 8px;
padding: 1rem;
}
.box-container {
position: relative;
height: 250px;
margin-bottom: 1.5rem;
height: 200px;
margin-bottom: 1rem;
border: 2px dashed var(--vp-c-divider);
border-radius: 8px;
border-radius: 6px;
}
.box {
@@ -332,95 +306,72 @@ function changeOpacity() {
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 1.2rem;
border-radius: 8px;
cursor: pointer;
font-size: 1.1rem;
color: var(--vp-c-bg-inverse);
border-radius: 6px;
transition: all 0.3s ease;
user-select: none;
}
.box:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.box.selected {
box-shadow: 0 0 0 3px var(--vp-c-brand);
}
.performance-meter {
margin-bottom: 1rem;
margin-bottom: 0.75rem;
}
.meter-label {
font-size: 0.85rem;
font-size: 0.8rem;
color: var(--vp-c-text-2);
margin-bottom: 0.5rem;
margin-bottom: 0.35rem;
}
.meter-bar {
height: 12px;
height: 10px;
background: var(--vp-c-bg-soft);
border-radius: 6px;
border-radius: 5px;
overflow: hidden;
margin-bottom: 0.5rem;
margin-bottom: 0.35rem;
}
.meter-fill {
height: 100%;
transition: all 0.5s ease;
border-radius: 6px;
border-radius: 5px;
}
.meter-fill.good {
background: linear-gradient(90deg, #22c55e, #14b8a6);
}
.meter-fill.medium {
background: linear-gradient(90deg, #f59e0b, #f97316);
}
.meter-fill.high {
background: linear-gradient(90deg, #ef4444, #dc2626);
}
.meter-fill.good { background: var(--vp-c-success-1); }
.meter-fill.medium { background: var(--vp-c-warning-1); }
.meter-fill.high { background: var(--vp-c-error-1); }
.meter-value {
font-size: 0.9rem;
font-size: 0.85rem;
font-weight: 600;
text-align: right;
}
.meter-value.good {
color: #22c55e;
}
.meter-value.medium {
color: #f59e0b;
}
.meter-value.high {
color: #ef4444;
}
.meter-value.good { color: var(--vp-c-success-1); }
.meter-value.medium { color: var(--vp-c-warning-1); }
.meter-value.high { color: var(--vp-c-error-1); }
.stats {
display: flex;
gap: 1rem;
gap: 0.75rem;
}
.stat-item {
flex: 1;
background: var(--vp-c-bg-soft);
border-radius: 8px;
padding: 0.8rem;
border-radius: 6px;
padding: 0.6rem;
text-align: center;
}
.stat-label {
font-size: 0.75rem;
font-size: 0.7rem;
color: var(--vp-c-text-2);
margin-bottom: 0.3rem;
margin-bottom: 0.25rem;
}
.stat-value {
font-size: 0.95rem;
font-size: 0.85rem;
font-weight: 600;
color: var(--vp-c-text-1);
}
@@ -428,95 +379,72 @@ function changeOpacity() {
.controls {
display: flex;
flex-direction: column;
gap: 1rem;
gap: 0.5rem;
}
.control-section {
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
}
.control-section h4 {
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 0.3rem;
color: var(--vp-c-text-1);
}
.control-desc {
font-size: 0.75rem;
color: var(--vp-c-text-2);
margin-bottom: 0.8rem;
.control-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.btn {
display: block;
width: 100%;
padding: 0.6rem;
margin-bottom: 0.5rem;
border: none;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
color: #fff;
color: var(--vp-c-bg-inverse);
}
.btn:last-child {
margin-bottom: 0;
}
.btn.high-impact { background: var(--vp-c-error-1); }
.btn.high-impact:hover { opacity: 0.9; }
.btn.reflow {
background: linear-gradient(135deg, #ef4444, #dc2626);
}
.btn.medium-impact { background: var(--vp-c-warning-1); }
.btn.medium-impact:hover { opacity: 0.9; }
.btn.reflow:hover {
background: linear-gradient(135deg, #dc2626, #b91c1c);
}
.btn.low-impact { background: var(--vp-c-success-1); }
.btn.low-impact:hover { opacity: 0.9; }
.btn.repaint {
background: linear-gradient(135deg, #f59e0b, #f97316);
}
.btn.repaint:hover {
background: linear-gradient(135deg, #f97316, #ea580c);
}
.btn.composite {
background: linear-gradient(135deg, #22c55e, #14b8a6);
}
.btn.composite:hover {
background: linear-gradient(135deg, #14b8a6, #0d9488);
}
.info-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
}
.info-card {
.tab-info {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 0.75rem;
margin-top: 0.75rem;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 1rem;
}
.info-card h4 {
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--vp-c-text-1);
}
.info-card p {
.info-content {
font-size: 0.85rem;
color: var(--vp-c-text-2);
line-height: 1.6;
margin: 0;
}
.info-content strong {
color: var(--vp-c-text-1);
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateY(-10px);
}
.info-box {
background: var(--vp-c-bg-alt);
padding: 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
color: var(--vp-c-text-2);
margin-top: 0.75rem;
}
.info-box .icon { margin-right: 0.25rem; }
</style>