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>
This commit is contained in:
+27
-8
@@ -21,8 +21,13 @@
|
||||
@click="activeEra = activeEra === era.id ? null : era.id"
|
||||
>
|
||||
<div class="era-marker">
|
||||
<div class="era-dot">{{ era.emoji }}</div>
|
||||
<div v-if="index < eras.length - 1" class="era-line"></div>
|
||||
<div class="era-dot">
|
||||
{{ era.emoji }}
|
||||
</div>
|
||||
<div
|
||||
v-if="index < eras.length - 1"
|
||||
class="era-line"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="era-content">
|
||||
@@ -31,12 +36,19 @@
|
||||
<span class="era-name">{{ era.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="era-brief">{{ era.brief }}</div>
|
||||
<div class="era-brief">
|
||||
{{ era.brief }}
|
||||
</div>
|
||||
|
||||
<Transition name="expand">
|
||||
<div v-if="activeEra === era.id" class="era-detail">
|
||||
<div
|
||||
v-if="activeEra === era.id"
|
||||
class="era-detail"
|
||||
>
|
||||
<div class="detail-section">
|
||||
<div class="section-title">🔑 关键技术</div>
|
||||
<div class="section-title">
|
||||
🔑 关键技术
|
||||
</div>
|
||||
<div class="tech-tags">
|
||||
<span
|
||||
v-for="tech in era.technologies.slice(0, 5)"
|
||||
@@ -46,9 +58,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section" v-if="era.metaphor">
|
||||
<div class="section-title">💡 生活比喻</div>
|
||||
<div class="metaphor-box">{{ era.metaphor }}</div>
|
||||
<div
|
||||
v-if="era.metaphor"
|
||||
class="detail-section"
|
||||
>
|
||||
<div class="section-title">
|
||||
💡 生活比喻
|
||||
</div>
|
||||
<div class="metaphor-box">
|
||||
{{ era.metaphor }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
+154
-100
@@ -25,41 +25,64 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="comparison-container">
|
||||
<!-- Imperative Side (jQuery) -->
|
||||
<div class="side imperative-side">
|
||||
<div class="side-header">
|
||||
<span class="badge imperative">jQuery / 命令式</span>
|
||||
<span class="sub-label">通俗说法: 告诉怎么做</span>
|
||||
</div>
|
||||
|
||||
<div class="demo-area">
|
||||
<!-- The UI -->
|
||||
<div class="counter-ui">
|
||||
<div class="display-value" id="jq-display">{{ jqCount }}</div>
|
||||
<div class="meters">
|
||||
<div class="meter-label">Progress:</div>
|
||||
<div class="meter-bar">
|
||||
<div
|
||||
class="meter-fill"
|
||||
id="jq-meter"
|
||||
:style="{ width: jqProgress + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="status-text" id="jq-status">
|
||||
{{ jqStatus }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<button class="btn-decrement" @click="updateJq(-1)">-</button>
|
||||
<button class="btn-increment" @click="updateJq(1)">+</button>
|
||||
</div>
|
||||
<div class="comparison-container">
|
||||
<!-- Imperative Side (jQuery) -->
|
||||
<div class="side imperative-side">
|
||||
<div class="side-header">
|
||||
<span class="badge imperative">jQuery / 命令式</span>
|
||||
<span class="sub-label">通俗说法: 告诉怎么做</span>
|
||||
</div>
|
||||
|
||||
<!-- The Code -->
|
||||
<div v-show="currentView === 'code'" class="code-panel">
|
||||
<div class="code-block imperative-code">
|
||||
<pre><code>function updateCounter(change) {
|
||||
<div class="demo-area">
|
||||
<!-- The UI -->
|
||||
<div class="counter-ui">
|
||||
<div
|
||||
id="jq-display"
|
||||
class="display-value"
|
||||
>
|
||||
{{ jqCount }}
|
||||
</div>
|
||||
<div class="meters">
|
||||
<div class="meter-label">
|
||||
Progress:
|
||||
</div>
|
||||
<div class="meter-bar">
|
||||
<div
|
||||
id="jq-meter"
|
||||
class="meter-fill"
|
||||
:style="{ width: jqProgress + '%' }"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
id="jq-status"
|
||||
class="status-text"
|
||||
>
|
||||
{{ jqStatus }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<button
|
||||
class="btn-decrement"
|
||||
@click="updateJq(-1)"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<button
|
||||
class="btn-increment"
|
||||
@click="updateJq(1)"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- The Code -->
|
||||
<div
|
||||
v-show="currentView === 'code'"
|
||||
class="code-panel"
|
||||
>
|
||||
<div class="code-block imperative-code">
|
||||
<pre><code>function updateCounter(change) {
|
||||
// 1. Get current value
|
||||
var count = parseInt($('#counter').text());
|
||||
|
||||
@@ -83,64 +106,89 @@
|
||||
// 6. Update DOM element 4...
|
||||
// Oops! Forgot to update the color indicator!
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="showAnalysis"
|
||||
class="pain-points"
|
||||
>
|
||||
<div class="pain-point">
|
||||
<span class="icon">⚠️</span>
|
||||
<span>需要手动操作多个 DOM 元素</span>
|
||||
</div>
|
||||
<div class="pain-point">
|
||||
<span class="icon">🐛</span>
|
||||
<span>容易遗漏更新,导致界面不一致</span>
|
||||
</div>
|
||||
<div class="pain-point">
|
||||
<span class="icon">🍝</span>
|
||||
<span>逻辑分散,代码难以维护</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pain-points" v-if="showAnalysis">
|
||||
<div class="pain-point">
|
||||
<span class="icon">⚠️</span>
|
||||
<span>需要手动操作多个 DOM 元素</span>
|
||||
</div>
|
||||
<div class="pain-point">
|
||||
<span class="icon">🐛</span>
|
||||
<span>容易遗漏更新,导致界面不一致</span>
|
||||
</div>
|
||||
<div class="pain-point">
|
||||
<span class="icon">🍝</span>
|
||||
<span>逻辑分散,代码难以维护</span>
|
||||
<!-- VS Divider -->
|
||||
<div class="vs-divider">
|
||||
<div class="vs-badge">
|
||||
VS
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VS Divider -->
|
||||
<div class="vs-divider">
|
||||
<div class="vs-badge">VS</div>
|
||||
</div>
|
||||
<!-- Declarative Side (Vue) -->
|
||||
<div class="side declarative-side">
|
||||
<div class="side-header">
|
||||
<span class="badge declarative">Vue / 声明式</span>
|
||||
<span class="sub-label">通俗说法: 告诉要什么</span>
|
||||
</div>
|
||||
|
||||
<!-- Declarative Side (Vue) -->
|
||||
<div class="side declarative-side">
|
||||
<div class="side-header">
|
||||
<span class="badge declarative">Vue / 声明式</span>
|
||||
<span class="sub-label">通俗说法: 告诉要什么</span>
|
||||
</div>
|
||||
|
||||
<div class="demo-area">
|
||||
<!-- The UI -->
|
||||
<div class="counter-ui">
|
||||
<div class="display-value">{{ vueCount }}</div>
|
||||
<div class="meters">
|
||||
<div class="meter-label">Progress:</div>
|
||||
<div class="meter-bar">
|
||||
<div class="demo-area">
|
||||
<!-- The UI -->
|
||||
<div class="counter-ui">
|
||||
<div class="display-value">
|
||||
{{ vueCount }}
|
||||
</div>
|
||||
<div class="meters">
|
||||
<div class="meter-label">
|
||||
Progress:
|
||||
</div>
|
||||
<div class="meter-bar">
|
||||
<div
|
||||
class="meter-fill"
|
||||
:style="{ width: vueProgress + '%' }"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="meter-fill"
|
||||
:style="{ width: vueProgress + '%' }"
|
||||
></div>
|
||||
class="status-text"
|
||||
:class="{ warning: vueCount > 5 }"
|
||||
>
|
||||
{{ vueStatus }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-text" :class="{ warning: vueCount > 5 }">
|
||||
{{ vueStatus }}
|
||||
<div class="controls">
|
||||
<button
|
||||
class="btn-decrement"
|
||||
@click="vueCount--"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<button
|
||||
class="btn-increment"
|
||||
@click="vueCount++"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<button class="btn-decrement" @click="vueCount--">-</button>
|
||||
<button class="btn-increment" @click="vueCount++">+</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- The Code -->
|
||||
<div v-show="currentView === 'code'" class="code-panel">
|
||||
<div class="code-block declarative-code">
|
||||
<pre><code>export default {
|
||||
<!-- The Code -->
|
||||
<div
|
||||
v-show="currentView === 'code'"
|
||||
class="code-panel"
|
||||
>
|
||||
<div class="code-block declarative-code">
|
||||
<pre><code>export default {
|
||||
data() {
|
||||
return {
|
||||
count: 0
|
||||
@@ -166,33 +214,39 @@
|
||||
{{ status }}
|
||||
</div>
|
||||
</template></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="showAnalysis"
|
||||
class="benefits"
|
||||
>
|
||||
<div class="benefit">
|
||||
<span class="icon">✅</span>
|
||||
<span>只关注数据,不用手动操作 DOM</span>
|
||||
</div>
|
||||
<div class="benefit">
|
||||
<span class="icon">🔄</span>
|
||||
<span>数据变化自动同步到所有相关视图</span>
|
||||
</div>
|
||||
<div class="benefit">
|
||||
<span class="icon">🧩</span>
|
||||
<span>代码结构清晰,易于维护</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="benefits" v-if="showAnalysis">
|
||||
<div class="benefit">
|
||||
<span class="icon">✅</span>
|
||||
<span>只关注数据,不用手动操作 DOM</span>
|
||||
</div>
|
||||
<div class="benefit">
|
||||
<span class="icon">🔄</span>
|
||||
<span>数据变化自动同步到所有相关视图</span>
|
||||
</div>
|
||||
<div class="benefit">
|
||||
<span class="icon">🧩</span>
|
||||
<span>代码结构清晰,易于维护</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部控制 -->
|
||||
<div class="demo-controls">
|
||||
<button class="toggle-btn" @click="showAnalysis = !showAnalysis">
|
||||
{{ showAnalysis ? '隐藏' : '显示' }}对比分析
|
||||
</button>
|
||||
</div>
|
||||
<!-- 底部控制 -->
|
||||
<div class="demo-controls">
|
||||
<button
|
||||
class="toggle-btn"
|
||||
@click="showAnalysis = !showAnalysis"
|
||||
>
|
||||
{{ showAnalysis ? '隐藏' : '显示' }}对比分析
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 信息框 -->
|
||||
|
||||
@@ -52,8 +52,12 @@
|
||||
class="step-item"
|
||||
:class="{ active: index === currentStep }"
|
||||
>
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<div class="step-text">{{ step }}</div>
|
||||
<div class="step-number">
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
<div class="step-text">
|
||||
{{ step }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,7 +68,10 @@
|
||||
<div class="ledger-header">
|
||||
<span class="ledger-icon">📒</span>
|
||||
<span class="ledger-title">今日账本</span>
|
||||
<span class="ledger-status" :class="mode">{{ ledgerStatus }}</span>
|
||||
<span
|
||||
class="ledger-status"
|
||||
:class="mode"
|
||||
>{{ ledgerStatus }}</span>
|
||||
</div>
|
||||
|
||||
<div class="ledger-content">
|
||||
@@ -105,8 +112,8 @@
|
||||
<div class="action-buttons">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="processOrder"
|
||||
:disabled="isProcessing || allCompleted"
|
||||
@click="processOrder"
|
||||
>
|
||||
{{ isProcessing ? '处理中...' : allCompleted ? '今日完成!' : '下一道菜' }}
|
||||
</button>
|
||||
|
||||
+257
-80
@@ -6,8 +6,12 @@
|
||||
<div class="rendering-demo">
|
||||
<!-- 故事引入 -->
|
||||
<div class="story-box">
|
||||
<div class="story-emoji">🍽️👨🍳⚡</div>
|
||||
<h4 class="story-title">小美的餐厅</h4>
|
||||
<div class="story-emoji">
|
||||
🍽️👨🍳⚡
|
||||
</div>
|
||||
<h4 class="story-title">
|
||||
小美的餐厅
|
||||
</h4>
|
||||
<p class="story-text">
|
||||
小美开了家餐厅,有三种上菜方式:<br>
|
||||
<strong>CSR(客户端渲染)</strong>:给你半成品食材包,你自己做 <br>
|
||||
@@ -35,56 +39,124 @@
|
||||
<div class="demo-container">
|
||||
<!-- 客户区 -->
|
||||
<div class="customer-area">
|
||||
<div class="customer-icon">🧑🦰</div>
|
||||
<div class="customer-label">用户(浏览器)</div>
|
||||
<div class="customer-icon">
|
||||
🧑🦰
|
||||
</div>
|
||||
<div class="customer-label">
|
||||
用户(浏览器)
|
||||
</div>
|
||||
<div class="table">
|
||||
<div v-if="activeStrategy === 'csr'" class="table-content">
|
||||
<div
|
||||
v-if="activeStrategy === 'csr'"
|
||||
class="table-content"
|
||||
>
|
||||
<div class="ingredients-pack">
|
||||
<div class="pack-label">📦 食材包</div>
|
||||
<div class="pack-content">
|
||||
<div class="ingredient">🥬 菜叶</div>
|
||||
<div class="ingredient">🥩 肉片</div>
|
||||
<div class="ingredient">🧂 调料</div>
|
||||
<div class="pack-label">
|
||||
📦 食材包
|
||||
</div>
|
||||
<div class="pack-content">
|
||||
<div class="ingredient">
|
||||
🥬 菜叶
|
||||
</div>
|
||||
<div class="ingredient">
|
||||
🥩 肉片
|
||||
</div>
|
||||
<div class="ingredient">
|
||||
🧂 调料
|
||||
</div>
|
||||
</div>
|
||||
<div class="instruction">
|
||||
↑ 请自己烹饪
|
||||
</div>
|
||||
<div class="instruction">↑ 请自己烹饪</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="table-content ready">
|
||||
<div class="dish">{{ currentStrategy.dish }}</div>
|
||||
<div class="dish-status">{{ currentStrategy.readyStatus }}</div>
|
||||
<div
|
||||
v-else
|
||||
class="table-content ready"
|
||||
>
|
||||
<div class="dish">
|
||||
{{ currentStrategy.dish }}
|
||||
</div>
|
||||
<div class="dish-status">
|
||||
{{ currentStrategy.readyStatus }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 传输区 -->
|
||||
<div class="transfer-area">
|
||||
<div v-if="isAnimating" class="transfer-animation">
|
||||
<div class="transfer-content">{{ currentStrategy.transferItem }}</div>
|
||||
<div class="transfer-arrow">→</div>
|
||||
<div
|
||||
v-if="isAnimating"
|
||||
class="transfer-animation"
|
||||
>
|
||||
<div class="transfer-content">
|
||||
{{ currentStrategy.transferItem }}
|
||||
</div>
|
||||
<div class="transfer-arrow">
|
||||
→
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="transfer-info">
|
||||
<div class="info-label">{{ currentStrategy.transferLabel }}</div>
|
||||
<div
|
||||
v-else
|
||||
class="transfer-info"
|
||||
>
|
||||
<div class="info-label">
|
||||
{{ currentStrategy.transferLabel }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 厨房/服务器 -->
|
||||
<div class="kitchen-area">
|
||||
<div class="kitchen-icon">👨🍳</div>
|
||||
<div class="kitchen-label">{{ currentStrategy.serverLabel }}</div>
|
||||
<div class="kitchen-icon">
|
||||
👨🍳
|
||||
</div>
|
||||
<div class="kitchen-label">
|
||||
{{ currentStrategy.serverLabel }}
|
||||
</div>
|
||||
<div class="kitchen-content">
|
||||
<div v-if="activeStrategy === 'csr'" class="server-station">
|
||||
<div class="station-icon">📡</div>
|
||||
<div class="station-label">配送站</div>
|
||||
<div class="station-desc">只管配送,不做菜</div>
|
||||
<div
|
||||
v-if="activeStrategy === 'csr'"
|
||||
class="server-station"
|
||||
>
|
||||
<div class="station-icon">
|
||||
📡
|
||||
</div>
|
||||
<div class="station-label">
|
||||
配送站
|
||||
</div>
|
||||
<div class="station-desc">
|
||||
只管配送,不做菜
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="activeStrategy === 'ssr'" class="server-kitchen">
|
||||
<div class="chef-action">{{ chefAction }}</div>
|
||||
<div class="cooking-pot" v-if="isCooking">🍳</div>
|
||||
<div
|
||||
v-else-if="activeStrategy === 'ssr'"
|
||||
class="server-kitchen"
|
||||
>
|
||||
<div class="chef-action">
|
||||
{{ chefAction }}
|
||||
</div>
|
||||
<div
|
||||
v-if="isCooking"
|
||||
class="cooking-pot"
|
||||
>
|
||||
🍳
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="server-cabinet">
|
||||
<div class="cabinet-icon">🗄️</div>
|
||||
<div class="cabinet-label">保温柜</div>
|
||||
<div class="cabinet-desc">{{ currentStrategy.cabinetDesc }}</div>
|
||||
<div
|
||||
v-else
|
||||
class="server-cabinet"
|
||||
>
|
||||
<div class="cabinet-icon">
|
||||
🗄️
|
||||
</div>
|
||||
<div class="cabinet-label">
|
||||
保温柜
|
||||
</div>
|
||||
<div class="cabinet-desc">
|
||||
{{ currentStrategy.cabinetDesc }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -93,96 +165,201 @@
|
||||
<!-- 性能指标 -->
|
||||
<div class="metrics-panel">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">首屏速度</div>
|
||||
<div class="metric-bar">
|
||||
<div class="metric-fill" :style="{ width: currentStrategy.firstScreenScore + '%', background: currentStrategy.color }"></div>
|
||||
<div class="metric-label">
|
||||
首屏速度
|
||||
</div>
|
||||
<div class="metric-bar">
|
||||
<div
|
||||
class="metric-fill"
|
||||
:style="{ width: currentStrategy.firstScreenScore + '%', background: currentStrategy.color }"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="metric-value"
|
||||
:style="{ color: currentStrategy.color }"
|
||||
>
|
||||
{{ currentStrategy.firstScreenText }}
|
||||
</div>
|
||||
<div class="metric-value" :style="{ color: currentStrategy.color }">{{ currentStrategy.firstScreenText }}</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">交互体验</div>
|
||||
<div class="metric-bar">
|
||||
<div class="metric-fill" :style="{ width: currentStrategy.interactionScore + '%', background: currentStrategy.color }"></div>
|
||||
<div class="metric-label">
|
||||
交互体验
|
||||
</div>
|
||||
<div class="metric-bar">
|
||||
<div
|
||||
class="metric-fill"
|
||||
:style="{ width: currentStrategy.interactionScore + '%', background: currentStrategy.color }"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="metric-value"
|
||||
:style="{ color: currentStrategy.color }"
|
||||
>
|
||||
{{ currentStrategy.interactionText }}
|
||||
</div>
|
||||
<div class="metric-value" :style="{ color: currentStrategy.color }">{{ currentStrategy.interactionText }}</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">SEO 友好度</div>
|
||||
<div class="metric-bar">
|
||||
<div class="metric-fill" :style="{ width: currentStrategy.seoScore + '%', background: currentStrategy.color }"></div>
|
||||
<div class="metric-label">
|
||||
SEO 友好度
|
||||
</div>
|
||||
<div class="metric-bar">
|
||||
<div
|
||||
class="metric-fill"
|
||||
:style="{ width: currentStrategy.seoScore + '%', background: currentStrategy.color }"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="metric-value"
|
||||
:style="{ color: currentStrategy.color }"
|
||||
>
|
||||
{{ currentStrategy.seoText }}
|
||||
</div>
|
||||
<div class="metric-value" :style="{ color: currentStrategy.color }">{{ currentStrategy.seoText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" @click="startDemo" :disabled="isAnimating">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
:disabled="isAnimating"
|
||||
@click="startDemo"
|
||||
>
|
||||
{{ isAnimating ? '演示中...' : '开始演示' }}
|
||||
</button>
|
||||
<button class="btn btn-secondary" @click="resetDemo">
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
@click="resetDemo"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 详细对比表 -->
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">📊 三种渲染方式详细对比</div>
|
||||
<div class="table-title">
|
||||
📊 三种渲染方式详细对比
|
||||
</div>
|
||||
<div class="table-content">
|
||||
<div class="comparison-row header">
|
||||
<div class="col-feature">特点</div>
|
||||
<div class="col-csr">CSR</div>
|
||||
<div class="col-ssr">SSR</div>
|
||||
<div class="col-ssg">SSG</div>
|
||||
<div class="col-feature">
|
||||
特点
|
||||
</div>
|
||||
<div class="col-csr">
|
||||
CSR
|
||||
</div>
|
||||
<div class="col-ssr">
|
||||
SSR
|
||||
</div>
|
||||
<div class="col-ssg">
|
||||
SSG
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">比喻</div>
|
||||
<div class="col-csr">给半成品食材包,自己做</div>
|
||||
<div class="col-ssr">厨房做好菜端给你</div>
|
||||
<div class="col-ssg">提前做好放保温柜</div>
|
||||
<div class="col-feature">
|
||||
比喻
|
||||
</div>
|
||||
<div class="col-csr">
|
||||
给半成品食材包,自己做
|
||||
</div>
|
||||
<div class="col-ssr">
|
||||
厨房做好菜端给你
|
||||
</div>
|
||||
<div class="col-ssg">
|
||||
提前做好放保温柜
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">首屏速度</div>
|
||||
<div class="col-csr">慢(要等 JS)</div>
|
||||
<div class="col-ssr">快(直接给 HTML)</div>
|
||||
<div class="col-ssg">最快(直接给 HTML)</div>
|
||||
<div class="col-feature">
|
||||
首屏速度
|
||||
</div>
|
||||
<div class="col-csr">
|
||||
慢(要等 JS)
|
||||
</div>
|
||||
<div class="col-ssr">
|
||||
快(直接给 HTML)
|
||||
</div>
|
||||
<div class="col-ssg">
|
||||
最快(直接给 HTML)
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">交互体验</div>
|
||||
<div class="col-csr">流畅(已在浏览器)</div>
|
||||
<div class="col-ssr">较流畅(交互仍需 JS)</div>
|
||||
<div class="col-ssg">较流畅(交互仍需 JS)</div>
|
||||
<div class="col-feature">
|
||||
交互体验
|
||||
</div>
|
||||
<div class="col-csr">
|
||||
流畅(已在浏览器)
|
||||
</div>
|
||||
<div class="col-ssr">
|
||||
较流畅(交互仍需 JS)
|
||||
</div>
|
||||
<div class="col-ssg">
|
||||
较流畅(交互仍需 JS)
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">SEO 友好度</div>
|
||||
<div class="col-csr">差(搜不到内容)</div>
|
||||
<div class="col-ssr">好(完整 HTML)</div>
|
||||
<div class="col-ssg">好(完整 HTML)</div>
|
||||
<div class="col-feature">
|
||||
SEO 友好度
|
||||
</div>
|
||||
<div class="col-csr">
|
||||
差(搜不到内容)
|
||||
</div>
|
||||
<div class="col-ssr">
|
||||
好(完整 HTML)
|
||||
</div>
|
||||
<div class="col-ssg">
|
||||
好(完整 HTML)
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">服务器压力</div>
|
||||
<div class="col-csr">小(只传 JS)</div>
|
||||
<div class="col-ssr">大(每次都渲染)</div>
|
||||
<div class="col-ssg">最小(预渲染好)</div>
|
||||
<div class="col-feature">
|
||||
服务器压力
|
||||
</div>
|
||||
<div class="col-csr">
|
||||
小(只传 JS)
|
||||
</div>
|
||||
<div class="col-ssr">
|
||||
大(每次都渲染)
|
||||
</div>
|
||||
<div class="col-ssg">
|
||||
最小(预渲染好)
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">适合场景</div>
|
||||
<div class="col-csr">后台系统、工具应用</div>
|
||||
<div class="col-ssr">新闻网站、电商首页</div>
|
||||
<div class="col-ssg">博客、文档站</div>
|
||||
<div class="col-feature">
|
||||
适合场景
|
||||
</div>
|
||||
<div class="col-csr">
|
||||
后台系统、工具应用
|
||||
</div>
|
||||
<div class="col-ssr">
|
||||
新闻网站、电商首页
|
||||
</div>
|
||||
<div class="col-ssg">
|
||||
博客、文档站
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">代表框架</div>
|
||||
<div class="col-csr">React SPA、Vue SPA</div>
|
||||
<div class="col-ssr">Next.js SSR、Nuxt SSR</div>
|
||||
<div class="col-ssg">Next.js SSG、Nuxt SSG</div>
|
||||
<div class="col-feature">
|
||||
代表框架
|
||||
</div>
|
||||
<div class="col-csr">
|
||||
React SPA、Vue SPA
|
||||
</div>
|
||||
<div class="col-ssr">
|
||||
Next.js SSR、Nuxt SSR
|
||||
</div>
|
||||
<div class="col-ssg">
|
||||
Next.js SSG、Nuxt SSG
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 核心要点 -->
|
||||
<div class="key-takeaway">
|
||||
<div class="takeaway-icon">🎯</div>
|
||||
<div class="takeaway-icon">
|
||||
🎯
|
||||
</div>
|
||||
<div class="takeaway-content">
|
||||
<strong>如何选择?</strong><br>
|
||||
<strong>CSR</strong>:适合需要复杂交互、不关心 SEO 的应用(如后台管理系统)<br>
|
||||
|
||||
+71
-24
@@ -6,8 +6,12 @@
|
||||
<div class="magic-closet">
|
||||
<!-- 故事开头 -->
|
||||
<div class="story-box">
|
||||
<div class="story-emoji">👗✨🚪</div>
|
||||
<h4 class="story-title">小美的魔法衣柜</h4>
|
||||
<div class="story-emoji">
|
||||
👗✨🚪
|
||||
</div>
|
||||
<h4 class="story-title">
|
||||
小美的魔法衣柜
|
||||
</h4>
|
||||
<p class="story-text">
|
||||
小美有一件神奇的魔法衣柜!不管你把它放在大房间还是小房间,<br>
|
||||
<strong>里面的衣服都会自动叠好、排好,完美适应空间大小!</strong>
|
||||
@@ -24,13 +28,13 @@
|
||||
<div class="slider-box">
|
||||
<span class="slider-emoji">🏠小</span>
|
||||
<input
|
||||
type="range"
|
||||
v-model="closetWidth"
|
||||
type="range"
|
||||
:min="280"
|
||||
:max="900"
|
||||
step="10"
|
||||
class="magic-slider"
|
||||
/>
|
||||
>
|
||||
<span class="slider-emoji">大🏰</span>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +44,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 魔法衣柜展示 -->
|
||||
<div class="closet-display" :style="{ width: closetWidth + 'px' }">
|
||||
<div
|
||||
class="closet-display"
|
||||
:style="{ width: closetWidth + 'px' }"
|
||||
>
|
||||
<div class="closet-header">
|
||||
<span class="closet-icon">🚪</span>
|
||||
<span class="closet-title">小美的魔法衣柜</span>
|
||||
@@ -48,7 +55,10 @@
|
||||
</div>
|
||||
|
||||
<div class="closet-interior">
|
||||
<div class="clothes-rack" :style="rackStyle">
|
||||
<div
|
||||
class="clothes-rack"
|
||||
:style="rackStyle"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in clothes"
|
||||
:key="index"
|
||||
@@ -56,10 +66,21 @@
|
||||
:class="{ 'folded': isSmallSpace }"
|
||||
:style="{ animationDelay: (index * 0.1) + 's' }"
|
||||
>
|
||||
<div class="item-hanger">🪝</div>
|
||||
<div class="item-emoji">{{ item.emoji }}</div>
|
||||
<div class="item-name">{{ item.name }}</div>
|
||||
<div class="fold-hint" v-if="isSmallSpace">叠好了!</div>
|
||||
<div class="item-hanger">
|
||||
🪝
|
||||
</div>
|
||||
<div class="item-emoji">
|
||||
{{ item.emoji }}
|
||||
</div>
|
||||
<div class="item-name">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="isSmallSpace"
|
||||
class="fold-hint"
|
||||
>
|
||||
叠好了!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,24 +93,48 @@
|
||||
|
||||
<!-- 魔法原理说明 -->
|
||||
<div class="magic-explain">
|
||||
<div class="explain-title">🔮 魔法原理揭秘</div>
|
||||
<div class="explain-title">
|
||||
🔮 魔法原理揭秘
|
||||
</div>
|
||||
<div class="explain-cards">
|
||||
<div class="explain-card">
|
||||
<div class="card-icon">📱</div>
|
||||
<div class="card-title">小房间(手机)</div>
|
||||
<div class="card-desc">衣柜只有 320px 宽,衣服会自动叠起来,<strong>1列</strong>排开</div>
|
||||
<div class="card-icon">
|
||||
📱
|
||||
</div>
|
||||
<div class="card-title">
|
||||
小房间(手机)
|
||||
</div>
|
||||
<div class="card-desc">
|
||||
衣柜只有 320px 宽,衣服会自动叠起来,<strong>1列</strong>排开
|
||||
</div>
|
||||
</div>
|
||||
<div class="explain-arrow">➡️</div>
|
||||
<div class="explain-card">
|
||||
<div class="card-icon">📲</div>
|
||||
<div class="card-title">中房间(平板)</div>
|
||||
<div class="card-desc">衣柜有 768px 宽,衣服舒展开,<strong>2列</strong>排开</div>
|
||||
<div class="explain-arrow">
|
||||
➡️
|
||||
</div>
|
||||
<div class="explain-arrow">➡️</div>
|
||||
<div class="explain-card">
|
||||
<div class="card-icon">💻</div>
|
||||
<div class="card-title">大房间(电脑)</div>
|
||||
<div class="card-desc">衣柜有 1200px 宽,衣服完全展开,<strong>3列</strong>排开</div>
|
||||
<div class="card-icon">
|
||||
📲
|
||||
</div>
|
||||
<div class="card-title">
|
||||
中房间(平板)
|
||||
</div>
|
||||
<div class="card-desc">
|
||||
衣柜有 768px 宽,衣服舒展开,<strong>2列</strong>排开
|
||||
</div>
|
||||
</div>
|
||||
<div class="explain-arrow">
|
||||
➡️
|
||||
</div>
|
||||
<div class="explain-card">
|
||||
<div class="card-icon">
|
||||
💻
|
||||
</div>
|
||||
<div class="card-title">
|
||||
大房间(电脑)
|
||||
</div>
|
||||
<div class="card-desc">
|
||||
衣柜有 1200px 宽,衣服完全展开,<strong>3列</strong>排开
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,7 +169,9 @@
|
||||
|
||||
<!-- 总结 -->
|
||||
<div class="summary-box">
|
||||
<div class="summary-icon">🎯</div>
|
||||
<div class="summary-icon">
|
||||
🎯
|
||||
</div>
|
||||
<div class="summary-content">
|
||||
<strong>关键 takeaway:</strong>
|
||||
响应式布局就像小美的魔法衣柜,<strong>同一套衣服(内容)</strong>,
|
||||
|
||||
+269
-158
@@ -22,185 +22,296 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 模式选择 -->
|
||||
<div class="mode-selector">
|
||||
<div
|
||||
class="mode-card"
|
||||
:class="{ active: mode === 'mpa' }"
|
||||
@click="switchMode('mpa')"
|
||||
>
|
||||
<div class="mode-icon">📚</div>
|
||||
<div class="mode-name">MPA 多页应用</div>
|
||||
<div class="mode-sub">通俗说法: 像翻书</div>
|
||||
<div class="mode-desc">每点一次链接,浏览器向服务器要新页面</div>
|
||||
<!-- 模式选择 -->
|
||||
<div class="mode-selector">
|
||||
<div
|
||||
class="mode-card"
|
||||
:class="{ active: mode === 'mpa' }"
|
||||
@click="switchMode('mpa')"
|
||||
>
|
||||
<div class="mode-icon">
|
||||
📚
|
||||
</div>
|
||||
<div class="mode-name">
|
||||
MPA 多页应用
|
||||
</div>
|
||||
<div class="mode-sub">
|
||||
通俗说法: 像翻书
|
||||
</div>
|
||||
<div class="mode-desc">
|
||||
每点一次链接,浏览器向服务器要新页面
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vs-divider">
|
||||
VS
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mode-card"
|
||||
:class="{ active: mode === 'spa' }"
|
||||
@click="switchMode('spa')"
|
||||
>
|
||||
<div class="mode-icon">
|
||||
📄
|
||||
</div>
|
||||
<div class="mode-name">
|
||||
SPA 单页应用
|
||||
</div>
|
||||
<div class="mode-sub">
|
||||
通俗说法: 像换纸
|
||||
</div>
|
||||
<div class="mode-desc">
|
||||
只加载一次,后续只切换内容
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vs-divider">VS</div>
|
||||
<!-- 动画演示 -->
|
||||
<div class="demo-area">
|
||||
<div class="demo-header">
|
||||
<span>当前模式:</span>
|
||||
<span
|
||||
class="mode-badge"
|
||||
:class="mode"
|
||||
>{{ mode === 'mpa' ? 'MPA 多页应用' : 'SPA 单页应用' }}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mode-card"
|
||||
:class="{ active: mode === 'spa' }"
|
||||
@click="switchMode('spa')"
|
||||
>
|
||||
<div class="mode-icon">📄</div>
|
||||
<div class="mode-name">SPA 单页应用</div>
|
||||
<div class="mode-sub">通俗说法: 像换纸</div>
|
||||
<div class="mode-desc">只加载一次,后续只切换内容</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 场景模拟 -->
|
||||
<div class="scene-container">
|
||||
<!-- 书架(服务器) -->
|
||||
<div class="server-side">
|
||||
<div class="server-icon">
|
||||
📚
|
||||
</div>
|
||||
<div class="server-label">
|
||||
书架(服务器)
|
||||
</div>
|
||||
<div class="books-shelf">
|
||||
<div
|
||||
v-for="page in pages"
|
||||
:key="page.id"
|
||||
class="book-item"
|
||||
:class="{
|
||||
active: currentPage === page.id,
|
||||
loading: mode === 'mpa' && isLoading && page.id === targetPage
|
||||
}"
|
||||
>
|
||||
{{ page.emoji }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 动画演示 -->
|
||||
<div class="demo-area">
|
||||
<div class="demo-header">
|
||||
<span>当前模式:</span>
|
||||
<span class="mode-badge" :class="mode">{{ mode === 'mpa' ? 'MPA 多页应用' : 'SPA 单页应用' }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 场景模拟 -->
|
||||
<div class="scene-container">
|
||||
<!-- 书架(服务器) -->
|
||||
<div class="server-side">
|
||||
<div class="server-icon">📚</div>
|
||||
<div class="server-label">书架(服务器)</div>
|
||||
<div class="books-shelf">
|
||||
<!-- 传输过程 -->
|
||||
<div class="transfer-area">
|
||||
<div
|
||||
v-if="mode === 'mpa' && isLoading"
|
||||
class="transfer-animation"
|
||||
>
|
||||
<div class="transfer-icon">
|
||||
{{ pages.find(p => p.id === targetPage)?.emoji }}
|
||||
</div>
|
||||
<div class="transfer-arrow">
|
||||
→
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="transfer-placeholder"
|
||||
>
|
||||
<span>{{ mode === 'mpa' ? '点击页面传输' : '无需传输' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 阅读区(浏览器) -->
|
||||
<div class="browser-side">
|
||||
<div class="browser-icon">
|
||||
📖
|
||||
</div>
|
||||
<div class="browser-label">
|
||||
阅读区(浏览器)
|
||||
</div>
|
||||
<div class="reading-paper">
|
||||
<Transition
|
||||
:name="mode === 'mpa' ? 'page-flip' : 'content-fade'"
|
||||
mode="out-in"
|
||||
>
|
||||
<div
|
||||
:key="currentPage"
|
||||
class="page-content"
|
||||
>
|
||||
<div class="page-emoji">
|
||||
{{ getCurrentPage.emoji }}
|
||||
</div>
|
||||
<div class="page-title">
|
||||
{{ getCurrentPage.title }}
|
||||
</div>
|
||||
<div class="page-text">
|
||||
{{ getCurrentPage.content }}
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 状态保留测试 -->
|
||||
<div class="state-test">
|
||||
<div class="test-label">
|
||||
✏️ 状态保留测试:
|
||||
</div>
|
||||
<input
|
||||
v-model="userInput"
|
||||
type="text"
|
||||
placeholder="在这里输入文字,然后切换页面..."
|
||||
class="test-input"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导航控制 -->
|
||||
<div class="navigation-controls">
|
||||
<div class="nav-label">
|
||||
切换页面:
|
||||
</div>
|
||||
<div class="nav-buttons">
|
||||
<button
|
||||
v-for="page in pages"
|
||||
:key="page.id"
|
||||
class="book-item"
|
||||
:class="{
|
||||
active: currentPage === page.id,
|
||||
loading: mode === 'mpa' && isLoading && page.id === targetPage
|
||||
}"
|
||||
class="nav-btn"
|
||||
:class="{ active: currentPage === page.id }"
|
||||
:disabled="isLoading"
|
||||
@click="navigateTo(page.id)"
|
||||
>
|
||||
{{ page.emoji }}
|
||||
</div>
|
||||
{{ page.emoji }} {{ page.title }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 传输过程 -->
|
||||
<div class="transfer-area">
|
||||
<div v-if="mode === 'mpa' && isLoading" class="transfer-animation">
|
||||
<div class="transfer-icon">{{ pages.find(p => p.id === targetPage)?.emoji }}</div>
|
||||
<div class="transfer-arrow">→</div>
|
||||
</div>
|
||||
<div v-else class="transfer-placeholder">
|
||||
<span>{{ mode === 'mpa' ? '点击页面传输' : '无需传输' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 阅读区(浏览器) -->
|
||||
<div class="browser-side">
|
||||
<div class="browser-icon">📖</div>
|
||||
<div class="browser-label">阅读区(浏览器)</div>
|
||||
<div class="reading-paper">
|
||||
<Transition :name="mode === 'mpa' ? 'page-flip' : 'content-fade'" mode="out-in">
|
||||
<div :key="currentPage" class="page-content">
|
||||
<div class="page-emoji">{{ getCurrentPage.emoji }}</div>
|
||||
<div class="page-title">{{ getCurrentPage.title }}</div>
|
||||
<div class="page-text">{{ getCurrentPage.content }}</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 状态保留测试 -->
|
||||
<div class="state-test">
|
||||
<div class="test-label">✏️ 状态保留测试:</div>
|
||||
<input
|
||||
v-model="userInput"
|
||||
type="text"
|
||||
placeholder="在这里输入文字,然后切换页面..."
|
||||
class="test-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导航控制 -->
|
||||
<div class="navigation-controls">
|
||||
<div class="nav-label">切换页面:</div>
|
||||
<div class="nav-buttons">
|
||||
<button
|
||||
v-for="page in pages"
|
||||
:key="page.id"
|
||||
class="nav-btn"
|
||||
:class="{ active: currentPage === page.id }"
|
||||
@click="navigateTo(page.id)"
|
||||
:disabled="isLoading"
|
||||
<!-- 状态指示 -->
|
||||
<div class="status-indicator">
|
||||
<div
|
||||
v-if="mode === 'mpa'"
|
||||
class="status-text mpa"
|
||||
>
|
||||
{{ page.emoji }} {{ page.title }}
|
||||
</button>
|
||||
<span class="status-icon">📚</span>
|
||||
<span>每次切换都要从书架拿新书(服务器请求)</span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="status-text spa"
|
||||
>
|
||||
<span class="status-icon">⚡</span>
|
||||
<span>内容已经下载好了,切换不需要再拿(前端路由)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 状态指示 -->
|
||||
<div class="status-indicator">
|
||||
<div v-if="mode === 'mpa'" class="status-text mpa">
|
||||
<span class="status-icon">📚</span>
|
||||
<span>每次切换都要从书架拿新书(服务器请求)</span>
|
||||
<!-- 对比表格 -->
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">
|
||||
📊 MPA vs SPA 对比
|
||||
</div>
|
||||
<div v-else class="status-text spa">
|
||||
<span class="status-icon">⚡</span>
|
||||
<span>内容已经下载好了,切换不需要再拿(前端路由)</span>
|
||||
<div class="table-content">
|
||||
<div class="comparison-row header">
|
||||
<div class="col-feature">
|
||||
特点
|
||||
</div>
|
||||
<div class="col-mpa">
|
||||
MPA 多页应用
|
||||
</div>
|
||||
<div class="col-spa">
|
||||
SPA 单页应用
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">
|
||||
比喻
|
||||
</div>
|
||||
<div class="col-mpa">
|
||||
翻书:每翻一页换一本书
|
||||
</div>
|
||||
<div class="col-spa">
|
||||
换纸:同一本书里换内容
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">
|
||||
页面切换
|
||||
</div>
|
||||
<div class="col-mpa">
|
||||
每次都重新加载整个页面
|
||||
</div>
|
||||
<div class="col-spa">
|
||||
只加载一次,后续只切换内容
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">
|
||||
速度体验
|
||||
</div>
|
||||
<div class="col-mpa">
|
||||
每次都有"白屏-加载"的过程
|
||||
</div>
|
||||
<div class="col-spa">
|
||||
页面切换流畅,无白屏
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">
|
||||
状态保留
|
||||
</div>
|
||||
<div class="col-mpa">
|
||||
切换页面后,输入的内容会丢失
|
||||
</div>
|
||||
<div class="col-spa">
|
||||
切换页面后,输入的内容还在
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">
|
||||
搜索引擎
|
||||
</div>
|
||||
<div class="col-mpa">
|
||||
容易被搜索到(SEO 友好)
|
||||
</div>
|
||||
<div class="col-spa">
|
||||
需要额外处理才能被搜索到
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">
|
||||
首屏加载
|
||||
</div>
|
||||
<div class="col-mpa">
|
||||
服务器直接给 HTML,首屏快
|
||||
</div>
|
||||
<div class="col-spa">
|
||||
需要先下载 JS,首屏可能慢
|
||||
</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">
|
||||
适合场景
|
||||
</div>
|
||||
<div class="col-mpa">
|
||||
博客、新闻、企业官网
|
||||
</div>
|
||||
<div class="col-spa">
|
||||
淘宝、网易云音乐、后台系统
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对比表格 -->
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">📊 MPA vs SPA 对比</div>
|
||||
<div class="table-content">
|
||||
<div class="comparison-row header">
|
||||
<div class="col-feature">特点</div>
|
||||
<div class="col-mpa">MPA 多页应用</div>
|
||||
<div class="col-spa">SPA 单页应用</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">比喻</div>
|
||||
<div class="col-mpa">翻书:每翻一页换一本书</div>
|
||||
<div class="col-spa">换纸:同一本书里换内容</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">页面切换</div>
|
||||
<div class="col-mpa">每次都重新加载整个页面</div>
|
||||
<div class="col-spa">只加载一次,后续只切换内容</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">速度体验</div>
|
||||
<div class="col-mpa">每次都有"白屏-加载"的过程</div>
|
||||
<div class="col-spa">页面切换流畅,无白屏</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">状态保留</div>
|
||||
<div class="col-mpa">切换页面后,输入的内容会丢失</div>
|
||||
<div class="col-spa">切换页面后,输入的内容还在</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">搜索引擎</div>
|
||||
<div class="col-mpa">容易被搜索到(SEO 友好)</div>
|
||||
<div class="col-spa">需要额外处理才能被搜索到</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">首屏加载</div>
|
||||
<div class="col-mpa">服务器直接给 HTML,首屏快</div>
|
||||
<div class="col-spa">需要先下载 JS,首屏可能慢</div>
|
||||
</div>
|
||||
<div class="comparison-row">
|
||||
<div class="col-feature">适合场景</div>
|
||||
<div class="col-mpa">博客、新闻、企业官网</div>
|
||||
<div class="col-spa">淘宝、网易云音乐、后台系统</div>
|
||||
</div>
|
||||
<!-- 核心要点 -->
|
||||
<div class="info-box">
|
||||
<span class="icon">💡</span>
|
||||
<strong>核心思想:</strong>
|
||||
<strong>MPA</strong> 每次切换都要"整页刷新",像翻书,适合内容为主的网站;
|
||||
<strong>SPA</strong> 只加载一次,后续"局部更新",像换纸,适合交互复杂的应用。
|
||||
关键是:<strong>状态会不会丢</strong>。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 核心要点 -->
|
||||
<div class="info-box">
|
||||
<span class="icon">💡</span>
|
||||
<strong>核心思想:</strong>
|
||||
<strong>MPA</strong> 每次切换都要"整页刷新",像翻书,适合内容为主的网站;
|
||||
<strong>SPA</strong> 只加载一次,后续"局部更新",像换纸,适合交互复杂的应用。
|
||||
关键是:<strong>状态会不会丢</strong>。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -29,23 +29,41 @@
|
||||
:class="{ active: mode === 'separate' }"
|
||||
@click="mode = 'separate'"
|
||||
>
|
||||
<div class="mode-icon">🛵</div>
|
||||
<div class="mode-name">切图模式</div>
|
||||
<div class="mode-desc">通俗说法: 一箱一趟</div>
|
||||
<div class="mode-detail">需要 6 趟运输</div>
|
||||
<div class="mode-icon">
|
||||
🛵
|
||||
</div>
|
||||
<div class="mode-name">
|
||||
切图模式
|
||||
</div>
|
||||
<div class="mode-desc">
|
||||
通俗说法: 一箱一趟
|
||||
</div>
|
||||
<div class="mode-detail">
|
||||
需要 6 趟运输
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vs-divider">VS</div>
|
||||
<div class="vs-divider">
|
||||
VS
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mode-card"
|
||||
:class="{ active: mode === 'packed' }"
|
||||
@click="mode = 'packed'"
|
||||
>
|
||||
<div class="mode-icon">🚚</div>
|
||||
<div class="mode-name">雪碧图模式</div>
|
||||
<div class="mode-desc">通俗说法: 打包一车拉</div>
|
||||
<div class="mode-detail">只需 1 趟运输</div>
|
||||
<div class="mode-icon">
|
||||
🚚
|
||||
</div>
|
||||
<div class="mode-name">
|
||||
雪碧图模式
|
||||
</div>
|
||||
<div class="mode-desc">
|
||||
通俗说法: 打包一车拉
|
||||
</div>
|
||||
<div class="mode-detail">
|
||||
只需 1 趟运输
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -53,8 +71,12 @@
|
||||
<div class="animation-area">
|
||||
<!-- 起点 -->
|
||||
<div class="location start">
|
||||
<div class="location-icon">🏠</div>
|
||||
<div class="location-label">旧家</div>
|
||||
<div class="location-icon">
|
||||
🏠
|
||||
</div>
|
||||
<div class="location-label">
|
||||
旧家
|
||||
</div>
|
||||
<div class="boxes-remaining">
|
||||
剩余箱子: <span class="count">{{ remainingBoxes }}</span>
|
||||
</div>
|
||||
@@ -62,7 +84,7 @@
|
||||
|
||||
<!-- 道路 -->
|
||||
<div class="road">
|
||||
<div class="road-line"></div>
|
||||
<div class="road-line" />
|
||||
|
||||
<!-- 运输车辆 -->
|
||||
<div
|
||||
@@ -75,7 +97,10 @@
|
||||
<div class="vehicle-body">
|
||||
{{ mode === 'separate' ? '🛵' : '🚚' }}
|
||||
</div>
|
||||
<div class="vehicle-cargo" v-if="vehicle.cargo > 0">
|
||||
<div
|
||||
v-if="vehicle.cargo > 0"
|
||||
class="vehicle-cargo"
|
||||
>
|
||||
{{ mode === 'separate' ? '📦' : '📦×' + vehicle.cargo }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,8 +108,12 @@
|
||||
|
||||
<!-- 终点 -->
|
||||
<div class="location end">
|
||||
<div class="location-icon">🏡</div>
|
||||
<div class="location-label">新家</div>
|
||||
<div class="location-icon">
|
||||
🏡
|
||||
</div>
|
||||
<div class="location-label">
|
||||
新家
|
||||
</div>
|
||||
<div class="boxes-delivered">
|
||||
已送达: <span class="count">{{ deliveredBoxes }}</span>/6
|
||||
</div>
|
||||
@@ -94,18 +123,32 @@
|
||||
<!-- 统计面板 -->
|
||||
<div class="stats-panel">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">运输趟数</div>
|
||||
<div class="stat-value" :class="{ 'good': trips <= 2, 'bad': trips > 2 }">
|
||||
<div class="stat-label">
|
||||
运输趟数
|
||||
</div>
|
||||
<div
|
||||
class="stat-value"
|
||||
:class="{ 'good': trips <= 2, 'bad': trips > 2 }"
|
||||
>
|
||||
{{ trips }} 趟
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">总耗时</div>
|
||||
<div class="stat-value">{{ totalTime.toFixed(1) }} 秒</div>
|
||||
<div class="stat-label">
|
||||
总耗时
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
{{ totalTime.toFixed(1) }} 秒
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">效率评分</div>
|
||||
<div class="stat-value" :class="efficiencyClass">
|
||||
<div class="stat-label">
|
||||
效率评分
|
||||
</div>
|
||||
<div
|
||||
class="stat-value"
|
||||
:class="efficiencyClass"
|
||||
>
|
||||
{{ efficiency }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,8 +158,8 @@
|
||||
<div class="controls">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="startSimulation"
|
||||
:disabled="isRunning"
|
||||
@click="startSimulation"
|
||||
>
|
||||
{{ isRunning ? '运输中...' : '开始搬家' }}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user