Files
test-repo/docs/.vitepress/theme/components/appendix/frontend-engineering/HotReloadDemo.vue
T
sanbuphy 0eba9e87e9 fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier)
- Relaxed strict Vue/JS rules for demo code compatibility
- Fix syntax errors in ApiPlayground and VoiceCloningDemo
- Fix duplicate else-if condition in ApiPlayground
- Fix Promise executor async pattern in AutoregressiveAudioDemo
- Add TypeScript file support to ESLint config

Warnings reduced from 295 to 251 problems.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 17:38:10 +08:00

455 lines
9.1 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
HotReloadDemo.vue
热更新机制演示
用途
展示HMR热模块替换的工作原理
-->
<template>
<div class="hot-reload-demo">
<div class="demo-header">
<h3>🔥 热更新 (HMR) 演示</h3>
<p>修改代码无需刷新页面即时生效</p>
</div>
<div class="demo-content">
<!-- 对比图 -->
<div class="comparison">
<div class="method-card no-hmr">
<div class="card-header">
<span class="icon">🔄</span>
<span class="title">传统刷新</span>
</div>
<div class="card-body">
<div
v-for="(step, i) in noHmrSteps"
:key="i"
class="step"
>
<span class="step-num">{{ i + 1 }}</span>
<span class="step-text">{{ step }}</span>
</div>
</div>
<div class="card-footer">
<span class="time"> 5-10</span>
<span class="state">页面闪烁状态丢失</span>
</div>
</div>
<div class="vs-divider">
VS
</div>
<div class="method-card hmr">
<div class="card-header">
<span class="icon"></span>
<span class="title">HMR 热更新</span>
</div>
<div class="card-body">
<div
v-for="(step, i) in hmrSteps"
:key="i"
class="step"
>
<span class="step-num">{{ i + 1 }}</span>
<span class="step-text">{{ step }}</span>
</div>
</div>
<div class="card-footer">
<span class="time"> 50-200ms</span>
<span class="state">无刷新状态保持</span>
</div>
</div>
</div>
<!-- 流程图 -->
<div class="flow-diagram">
<h4>HMR 工作流程</h4>
<div class="flow-steps">
<div
v-for="(step, i) in flowSteps"
:key="i"
class="flow-step"
>
<div class="step-box">
<span class="step-icon">{{ step.icon }}</span>
<span class="step-label">{{ step.label }}</span>
</div>
<div
v-if="i < flowSteps.length - 1"
class="step-arrow"
>
</div>
</div>
</div>
</div>
<!-- 支持情况 -->
<div class="support-table">
<h4>各构建工具 HMR 支持</h4>
<table>
<thead>
<tr>
<th>构建工具</th>
<th>HMR 支持</th>
<th>更新速度</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr
v-for="tool in hmrTools"
:key="tool.name"
>
<td><strong>{{ tool.name }}</strong></td>
<td>
<span
class="badge"
:class="tool.supportClass"
>{{ tool.support }}</span>
</td>
<td>{{ tool.speed }}</td>
<td>{{ tool.feature }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="info-box">
<p>
<span class="icon">💡</span>
<strong>HMR 的核心原理</strong>
构建工具通过 WebSocket 与浏览器保持连接当文件修改后工具编译变更模块通过 WebSocket 通知浏览器
浏览器中的 HMR Runtime 接收更新替换旧模块同时保持应用状态不变
这就像是给飞行中的飞机换引擎不停机就能完成更新
</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const noHmrSteps = [
'修改代码并保存',
'手动刷新浏览器',
'页面重新加载所有资源',
'应用状态重置(登录丢失)'
]
const hmrSteps = [
'修改代码并保存',
'构建工具检测变更并编译',
'WebSocket 推送更新到浏览器',
'局部替换模块,状态保持'
]
const flowSteps = [
{ icon: '👨‍💻', label: '开发者修改代码' },
{ icon: '🛠️', label: '构建工具编译' },
{ icon: '📡', label: 'WebSocket推送' },
{ icon: '🔄', label: '浏览器替换模块' },
{ icon: '✨', label: '页面即时更新' }
]
const hmrTools = [
{
name: 'Vite',
support: '原生支持',
supportClass: 'excellent',
speed: '极快 (<100ms)',
feature: '基于 ESMHMR 速度最快'
},
{
name: 'Webpack',
support: '完全支持',
supportClass: 'good',
speed: '较快 (1-3s)',
feature: '最成熟的 HMR 实现'
},
{
name: 'Parcel',
support: '自动支持',
supportClass: 'good',
speed: '快 (500ms-1s)',
feature: '零配置,自动 HMR'
},
{
name: 'Rollup',
support: '插件支持',
supportClass: 'fair',
speed: '开发时较慢',
feature: '主要用于生产构建'
}
]
</script>
<style scoped>
.hot-reload-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background-color: var(--vp-c-bg-soft);
padding: 0.75rem;
margin: 0.5rem 0;
font-family: var(--vp-font-family-mono);
}
.demo-header h3 {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
}
.demo-header p {
margin: 0;
color: var(--vp-c-text-2);
font-size: 0.9rem;
}
.comparison {
display: flex;
gap: 1rem;
margin: 0.5rem 0;
align-items: stretch;
}
@media (max-width: 768px) {
.comparison {
flex-direction: column;
}
}
.method-card {
flex: 1;
background: var(--vp-c-bg);
border-radius: 6px;
border: 2px solid var(--vp-c-divider);
overflow: hidden;
}
.method-card.hmr {
border-color: var(--vp-c-brand);
}
.card-header {
background: var(--vp-c-bg-soft);
padding: 0.6rem 0.75rem;
display: flex;
align-items: center;
gap: 0.5rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.card-header .icon {
font-size: 1.25rem;
}
.card-header .title {
font-weight: 600;
font-size: 0.9rem;
}
.card-body {
padding: 0.75rem;
}
.step {
display: flex;
align-items: flex-start;
gap: 0.5rem;
padding: 0.4rem 0;
font-size: 0.8rem;
border-bottom: 1px solid var(--vp-c-divider-light);
}
.step:last-child {
border-bottom: none;
}
.step-num {
width: 18px;
height: 18px;
background: var(--vp-c-brand);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.65rem;
font-weight: bold;
flex-shrink: 0;
}
.step-text {
color: var(--vp-c-text-1);
line-height: 1.4;
}
.card-footer {
background: var(--vp-c-bg-soft);
padding: 0.5rem 0.75rem;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid var(--vp-c-divider);
font-size: 0.75rem;
}
.time {
font-weight: 600;
color: var(--vp-c-brand);
}
.state {
color: var(--vp-c-text-2);
}
.vs-divider {
display: flex;
align-items: center;
font-weight: bold;
color: var(--vp-c-text-3);
font-size: 0.9rem;
}
.flow-diagram {
background: var(--vp-c-bg);
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
padding: 0.75rem;
margin-bottom: 1rem;
}
.flow-diagram h4 {
margin: 0 0 0.75rem 0;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.flow-steps {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 0.25rem;
}
.flow-step {
display: flex;
align-items: center;
gap: 0.25rem;
}
.step-box {
display: flex;
flex-direction: column;
align-items: center;
padding: 0.4rem 0.5rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
min-width: 60px;
}
.step-icon {
font-size: 1.1rem;
}
.step-label {
font-size: 0.65rem;
color: var(--vp-c-text-2);
text-align: center;
margin-top: 0.1rem;
}
.step-arrow {
color: var(--vp-c-brand);
font-size: 1rem;
font-weight: bold;
}
.support-table {
background: var(--vp-c-bg);
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
padding: 0.75rem;
overflow-x: auto;
}
.support-table h4 {
margin: 0 0 0.75rem 0;
font-size: 0.9rem;
color: var(--vp-c-text-1);
}
.support-table table {
width: 100%;
border-collapse: collapse;
font-size: 0.8rem;
}
.support-table th,
.support-table td {
padding: 0.5rem 0.6rem;
text-align: left;
border-bottom: 1px solid var(--vp-c-divider);
}
.support-table th {
background: var(--vp-c-bg-soft);
font-weight: 600;
color: var(--vp-c-text-1);
}
.badge {
display: inline-block;
padding: 0.15rem 0.4rem;
border-radius: 3px;
font-size: 0.7rem;
font-weight: 500;
}
.badge.excellent {
background: rgba(34, 197, 94, 0.2);
color: #16a34a;
}
.badge.good {
background: rgba(59, 130, 246, 0.2);
color: #2563eb;
}
.badge.fair {
background: rgba(245, 158, 11, 0.2);
color: #d97706;
}
.info-box {
background-color: var(--vp-c-bg-alt);
padding: 0.75rem;
border-radius: 6px;
font-size: 0.85rem;
line-height: 1.4;
color: var(--vp-c-text-2);
margin-top: 1rem;
}
.info-box .icon {
margin-right: 0.5rem;
}
@media (max-width: 768px) {
.flow-steps {
flex-direction: column;
}
.flow-step {
flex-direction: column;
}
.step-arrow {
transform: rotate(90deg);
}
}
</style>