Vite+Vue3实战:从零构建仙剑三角色展示页,掌握前端快速成型
2026/9/2 6:23:50 网站建设 项目流程

在实际前端开发中,我们常常需要快速构建具有特定视觉风格和交互逻辑的页面组件。无论是为了验证一个创意,还是为了在面试或技术分享中展示快速原型能力,掌握一套高效的“前端效果”实现方法都至关重要。这里的“前端效果”并不仅指酷炫的动画,更涵盖了从项目初始化、UI组件搭建、数据绑定到交互逻辑实现的完整链路。本文将以一个经典的文化IP——仙剑奇侠传三的角色展示页为例,演示如何运用现代前端技术栈,高效、优雅地实现一个视觉效果出众、代码结构清晰的单页应用。整个过程将模拟一种“快速成型”的开发节奏,旨在说明如何将想法迅速转化为可运行的代码。

本文适合有一定 HTML、CSS 和 JavaScript 基础,希望提升工程化实践和组件化开发能力的前端开发者。我们将从零开始,逐步完成环境搭建、项目结构设计、UI组件开发、数据管理与交互实现,并最终部署预览。你将了解到如何利用 Vite 快速启动项目,如何使用 Vue 3 的组合式 API 构建响应式组件,以及如何通过现代 CSS(如 Flexbox/Grid、CSS 变量)和图标库来打造精致的界面。最后,我们还会探讨一些常见的样式调试、数据绑定问题及其解决方案。

1. 理解“快速成型”的前端工程化基础

在开始动手写代码之前,我们需要明确目标:不是简单地堆砌 HTML 标签,而是要构建一个可维护、易扩展的前端项目。这意味着我们需要一个高效的开发服务器、模块化的代码组织方式以及便捷的构建工具。

1.1 为什么选择 Vite + Vue 3 作为技术栈

Vite 提供了极快的冷启动和热更新速度,这非常适合我们“快速成型”的需求。它原生支持 ES 模块,在开发阶段无需打包,直接按需编译,体验流畅。Vue 3 的组合式 API 让逻辑关注点更集中,代码复用性更强,配合<script setup>语法糖,可以写出非常简洁的组件。

对于 UI 样式,我们不会引入庞大的 UI 框架,而是采用“基础样式重置 + 实用类 CSS + 按需组件”的策略。这样可以保持项目的轻量,并让我们更专注于 CSS 本身的学习与控制。图标方面,我们将使用流行的 Iconify 图标集,通过@iconify/vue按需引入,避免全量包带来的体积膨胀。

1.2 初始化项目与环境检查清单

首先,确保你的开发环境满足以下要求:

环境/工具要求检查命令说明
Node.js18.0 或更高版本node -v运行 JavaScript 和服务的基础。
npm / yarn / pnpm最新稳定版npm -vyarn -v包管理器,本文使用 pnpm。
代码编辑器VS Code-推荐安装 Vue 官方扩展 Volar。
浏览器Chrome 或 Edge 最新版-用于开发和调试。

打开终端,执行以下命令创建项目:

# 使用 pnpm 创建 Vite 项目,选择 Vue 模板 pnpm create vite pal3-frontend-demo --template vue # 进入项目目录 cd pal3-frontend-demo # 安装依赖 pnpm install # 安装额外需要的库:图标库和 CSS 预处理器(可选) pnpm add -D @iconify/vue @iconify-json/mdi

项目创建后,目录结构应如下所示:

pal3-frontend-demo/ ├── index.html # 入口 HTML 文件 ├── package.json # 项目配置和依赖 ├── vite.config.js # Vite 配置文件 ├── public/ # 静态资源 └── src/ ├── assets/ # 图片、字体等资源 ├── components/ # Vue 组件 ├── App.vue # 根组件 └── main.js # 应用入口文件

运行pnpm run dev启动开发服务器,在浏览器中打开控制台给出的本地地址(通常是http://localhost:5173),看到 Vue 的默认欢迎页面即表示环境准备成功。

注意:如果启动失败,请检查端口 5173 是否被占用,或 Node.js 版本是否过低。可以通过vite.config.js中的server.port配置修改端口。

2. 设计项目结构与核心组件

我们的目标是构建一个仙剑三角色展示页。页面主要包含:一个顶部的标题导航栏、一个侧边的角色列表、一个中央的角色详情展示区。

2.1 规划组件与数据流

我们将创建以下组件:

  • App.vue: 根组件,布局容器。
  • components/TheHeader.vue: 顶部标题栏。
  • components/SideBar.vue: 侧边角色列表。
  • components/CharacterDetail.vue: 中央角色详情卡片。
  • data/characters.js: 角色数据源,一个包含所有角色信息的数组。

数据流设计为:数据存放在characters.js中。App.vue导入该数据,并通过provide/inject或 Props 传递给子组件。SideBar组件渲染列表,当用户点击某个角色时,触发事件,更新App.vue中当前选中的角色 ID,再传递给CharacterDetail组件进行渲染。

2.2 创建角色数据源

src目录下创建data文件夹,并新建characters.js文件:

// src/data/characters.js export const characters = [ { id: 1, name: '景天', title: '飞蓬将军转世', avatar: 'https://placehold.co/400x600/4a6fa5/FFFFFF?text=景天', // 使用占位图,实际项目替换为真实图片URL description: '渝州永安当伙计,头脑灵活,重情重义。前世为神界飞蓬将军,今生虽为小人物,却心怀大侠梦。', skills: ['飞龙探云手', '洒金笺', '倾国银弹波'], colorTheme: '#4a6fa5', // 主题色,用于边框、高亮等 }, { id: 2, name: '唐雪见', title: '唐家大小姐', avatar: 'https://placehold.co/400x600/e67e22/FFFFFF?text=雪见', description: '唐家堡大小姐,娇纵任性但心地善良。实则为神树果实所化,拥有神秘的身世。', skills: ['绿波红露斩', '天灵千裂破', '御剑术'], colorTheme: '#e67e22', }, { id: 3, name: '龙葵', title: '姜国公主', avatar: 'https://placehold.co/400x600/9b59b6/FFFFFF?text=龙葵', description: '古姜国公主,魂魄依附魔剑千年。有蓝葵(温柔)和红葵(泼辣)双重人格。', skills: ['鬼降', '鬼狱还神', '乾坤一掷'], colorTheme: '#9b59b6', }, { id: 4, name: '徐长卿', title: '蜀山弟子', avatar: 'https://placehold.co/400x600/27ae60/FFFFFF?text=长卿', description: '蜀山派大弟子,稳重正直,以降妖除魔为己任。与紫萱有着三世情缘。', skills: ['万剑诀', '天剑', '剑神'], colorTheme: '#27ae60', }, { id: 5, name: '紫萱', title: '女娲后人', avatar: 'https://placehold.co/400x600/e74c3c/FFFFFF?text=紫萱', description: '女娲族后裔,拥有强大灵力。为追寻与长卿的爱情,忍受了百年的孤独。', skills: ['梦蛇', '武神', '炼狱真火'], colorTheme: '#e74c3c', }, ]; // 获取默认选中的角色(例如第一个) export const getDefaultCharacter = () => characters[0];

注意:实际项目中,图片应放在publicassets目录,并通过相对路径或导入方式引用。此处使用在线占位图仅为演示。

3. 实现基础布局与样式重置

在编写具体组件前,我们需要先搭建好页面的骨架,并设置一套基础的 CSS 样式,以消除浏览器默认样式差异,并建立统一的布局系统。

3.1 修改根组件 App.vue 的布局

首先,清理src/App.vue的默认内容,替换为以下结构:

<!-- src/App.vue --> <template> <div id="app"> <TheHeader /> <main class="main-container"> <SideBar :characters="charactersList" @select-character="handleSelectCharacter" /> <CharacterDetail :character="currentCharacter" /> </main> </div> </template> <script setup> import { ref, computed, onMounted } from 'vue'; import TheHeader from './components/TheHeader.vue'; import SideBar from './components/SideBar.vue'; import CharacterDetail from './components/CharacterDetail.vue'; import { characters, getDefaultCharacter } from './data/characters.js'; // 响应式数据 const charactersList = characters; const selectedCharacterId = ref(null); // 计算当前选中的角色对象 const currentCharacter = computed(() => { return charactersList.find(char => char.id === selectedCharacterId.value) || getDefaultCharacter(); }); // 处理角色选择事件 const handleSelectCharacter = (id) => { selectedCharacterId.value = id; }; // 组件挂载时设置默认选中角色 onMounted(() => { selectedCharacterId.value = getDefaultCharacter().id; }); </script> <style> /* 全局样式重置与基础设置 */ * { margin: 0; padding: 0; box-sizing: border-box; } html, body, #app { height: 100%; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); color: #333; } #app { display: flex; flex-direction: column; } .main-container { display: flex; flex: 1; overflow: hidden; /* 防止内容溢出导致整体布局滚动 */ } </style>

3.2 创建全局样式文件(可选但推荐)

为了更系统地管理样式,可以在src下创建styles目录,并添加base.css

/* src/styles/base.css */ /* 1. 更细致的样式重置 */ html { line-height: 1.15; /* 修正所有浏览器行高 */ -webkit-text-size-adjust: 100%; /* 防止 iOS 横屏时字体放大 */ } body { margin: 0; } /* 2. 统一媒体元素样式 */ img, picture, video, canvas, svg { display: block; max-width: 100%; } /* 3. 移除按钮和输入框的默认样式 */ button, input, optgroup, select, textarea { font-family: inherit; font-size: 100%; line-height: 1.15; margin: 0; } /* 4. 修正列表样式 */ ul, ol { list-style: none; } /* 5. 修正链接样式 */ a { background-color: transparent; text-decoration: none; color: inherit; }

然后在main.js中导入这个全局样式文件:

// src/main.js import { createApp } from 'vue' import './styles/base.css' // 导入全局基础样式 import App from './App.vue' createApp(App).mount('#app')

4. 逐个实现核心组件

接下来,我们将按照从外到内、从简到繁的顺序实现三个核心组件。

4.1 实现 TheHeader 组件

这个组件相对简单,主要展示标题和可能的导航。

<!-- src/components/TheHeader.vue --> <template> <header class="the-header"> <div class="header-content"> <h1 class="title"> <span class="title-icon">⚔️</span> 仙剑奇侠传三 · 角色图鉴 </h1> <p class="subtitle">点击左侧角色,查看详细资料与技能</p> </div> </header> </template> <script setup> // 此组件无需响应式数据,逻辑简单 </script> <style scoped> .the-header { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); border-bottom: 1px solid rgba(0, 0, 0, 0.1); padding: 1.5rem 2rem; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); } .header-content { max-width: 1200px; margin: 0 auto; display: flex; flex-direction: column; gap: 0.5rem; } .title { font-size: 2.2rem; font-weight: 700; background: linear-gradient(90deg, #4a6fa5, #9b59b6); -webkit-background-clip: text; background-clip: text; color: transparent; display: flex; align-items: center; gap: 0.75rem; } .title-icon { font-size: 2.5rem; } .subtitle { font-size: 1rem; color: #666; font-weight: 400; } </style>

4.2 实现 SideBar 组件

侧边栏需要渲染角色列表,并处理点击事件。

<!-- src/components/SideBar.vue --> <template> <aside class="side-bar"> <div class="side-bar-header"> <h2>角色列表</h2> <p class="character-count">{{ characters.length }} 位角色</p> </div> <ul class="character-list"> <li v-for="char in characters" :key="char.id" class="character-item" :class="{ active: char.id === activeId }" @click="selectCharacter(char.id)" > <div class="item-avatar"> <img :src="char.avatar" :alt="char.name" /> </div> <div class="item-info"> <h3 class="item-name">{{ char.name }}</h3> <p class="item-title">{{ char.title }}</p> </div> <div class="item-indicator" :style="{ backgroundColor: char.colorTheme }"></div> </li> </ul> </aside> </template> <script setup> import { defineProps, defineEmits, computed } from 'vue'; const props = defineProps({ characters: { type: Array, required: true, }, // 可选:从父组件传入当前激活的ID,用于高亮 activeCharacterId: { type: Number, default: null, }, }); const emit = defineEmits(['selectCharacter']); // 内部也可以维护一个激活状态,这里我们依赖父组件传递的 activeCharacterId // 计算属性,方便模板使用 const activeId = computed(() => props.activeCharacterId); const selectCharacter = (id) => { emit('selectCharacter', id); }; </script> <style scoped> .side-bar { width: 320px; background-color: rgba(255, 255, 255, 0.85); border-right: 1px solid rgba(0, 0, 0, 0.08); padding: 1.5rem; overflow-y: auto; flex-shrink: 0; } .side-bar-header { margin-bottom: 1.5rem; padding-bottom: 1rem; border-bottom: 2px solid #eee; } .side-bar-header h2 { font-size: 1.5rem; font-weight: 600; color: #2c3e50; margin-bottom: 0.25rem; } .character-count { font-size: 0.9rem; color: #7f8c8d; } .character-list { display: flex; flex-direction: column; gap: 0.75rem; } .character-item { display: flex; align-items: center; padding: 0.75rem 1rem; border-radius: 12px; cursor: pointer; transition: all 0.3s ease; position: relative; overflow: hidden; border: 2px solid transparent; } .character-item:hover { background-color: #f8f9fa; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); } .character-item.active { border-color: #4a6fa5; /* 默认激活色,实际会覆盖 */ background-color: #f0f7ff; } .item-avatar { width: 50px; height: 50px; border-radius: 10px; overflow: hidden; margin-right: 1rem; flex-shrink: 0; border: 2px solid #fff; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1); } .item-avatar img { width: 100%; height: 100%; object-fit: cover; } .item-info { flex: 1; min-width: 0; /* 防止文本溢出 */ } .item-name { font-size: 1.1rem; font-weight: 600; color: #2c3e50; margin-bottom: 0.15rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .item-title { font-size: 0.85rem; color: #7f8c8d; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .item-indicator { width: 6px; height: 6px; border-radius: 50%; margin-left: 0.5rem; flex-shrink: 0; } </style>

4.3 实现 CharacterDetail 组件

这是最复杂的组件,需要展示角色的详细信息,并应用动态主题色。

<!-- src/components/CharacterDetail.vue --> <template> <article class="character-detail" :style="detailStyle"> <div class="detail-background" :style="bgStyle"></div> <div class="detail-content"> <!-- 头像与基础信息区 --> <div class="profile-section"> <div class="avatar-wrapper"> <img :src="character.avatar" :alt="character.name" class="avatar" /> <div class="avatar-border" :style="{ borderColor: character.colorTheme }"></div> </div> <div class="profile-info"> <h2 class="character-name">{{ character.name }}</h2> <p class="character-title">{{ character.title }}</p> <div class="theme-tag" :style="{ backgroundColor: character.colorTheme }"> 主题色 </div> </div> </div> <!-- 描述区域 --> <div class="description-section"> <h3 class="section-title"> <span class="icon">📖</span>角色简介 </h3> <p class="character-description">{{ character.description }}</p> </div> <!-- 技能区域 --> <div class="skills-section"> <h3 class="section-title"> <span class="icon">⚡</span>专属技能 </h3> <ul class="skills-list"> <li v-for="(skill, index) in character.skills" :key="index" class="skill-item"> <span class="skill-icon">✨</span> <span class="skill-name">{{ skill }}</span> </li> </ul> </div> <!-- 交互区域 --> <div class="action-section"> <button class="action-btn primary" :style="buttonStyle"> <span class="btn-icon">❤️</span> 收藏角色 </button> <button class="action-btn secondary"> <span class="btn-icon">🔄</span> 随机切换 </button> </div> </div> </article> </template> <script setup> import { computed } from 'vue'; const props = defineProps({ character: { type: Object, required: true, }, }); // 计算样式:为整个详情区域添加微妙的主题色渐变背景 const detailStyle = computed(() => ({ '--theme-color': props.character.colorTheme, })); // 背景渐变样式 const bgStyle = computed(() => ({ background: `linear-gradient(135deg, ${props.character.colorTheme}22 0%, #ffffff 100%)`, })); // 按钮样式 const buttonStyle = computed(() => ({ backgroundColor: props.character.colorTheme, borderColor: props.character.colorTheme, })); </script> <style scoped> .character-detail { flex: 1; padding: 2.5rem; position: relative; overflow-y: auto; display: flex; justify-content: center; align-items: flex-start; } .detail-background { position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: -1; opacity: 0.6; } .detail-content { max-width: 800px; width: 100%; background: rgba(255, 255, 255, 0.92); backdrop-filter: blur(20px); border-radius: 24px; padding: 2.5rem; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1); border: 1px solid rgba(255, 255, 255, 0.3); } /* 头像与基础信息 */ .profile-section { display: flex; align-items: center; margin-bottom: 2.5rem; gap: 2rem; } .avatar-wrapper { position: relative; width: 160px; height: 240px; flex-shrink: 0; } .avatar { width: 100%; height: 100%; object-fit: cover; border-radius: 18px; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15); } .avatar-border { position: absolute; top: -8px; left: -8px; right: -8px; bottom: -8px; border: 4px solid; border-radius: 24px; z-index: -1; opacity: 0.5; } .profile-info { flex: 1; } .character-name { font-size: 3rem; font-weight: 800; margin-bottom: 0.5rem; background: linear-gradient(90deg, var(--theme-color, #4a6fa5), #333); -webkit-background-clip: text; background-clip: text; color: transparent; } .character-title { font-size: 1.4rem; color: #666; margin-bottom: 1.5rem; font-weight: 500; } .theme-tag { display: inline-block; padding: 0.4rem 1rem; border-radius: 20px; color: white; font-size: 0.9rem; font-weight: 600; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15); } /* 通用区域样式 */ .section-title { font-size: 1.6rem; font-weight: 700; color: #2c3e50; margin-bottom: 1.2rem; display: flex; align-items: center; gap: 0.75rem; } .section-title .icon { font-size: 1.8rem; } .description-section { margin-bottom: 2.5rem; } .character-description { font-size: 1.1rem; line-height: 1.8; color: #444; background: #f9f9f9; padding: 1.5rem; border-radius: 12px; border-left: 5px solid var(--theme-color, #4a6fa5); } /* 技能列表 */ .skills-section { margin-bottom: 2.5rem; } .skills-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1rem; } .skill-item { display: flex; align-items: center; padding: 1rem 1.2rem; background: white; border-radius: 12px; border: 2px solid #eee; transition: all 0.3s ease; } .skill-item:hover { border-color: var(--theme-color, #4a6fa5); transform: translateY(-3px); box-shadow: 0 7px 14px rgba(0, 0, 0, 0.08); } .skill-icon { font-size: 1.3rem; margin-right: 0.8rem; } .skill-name { font-size: 1.05rem; font-weight: 600; color: #2c3e50; } /* 操作按钮 */ .action-section { display: flex; gap: 1rem; padding-top: 2rem; border-top: 1px solid #eee; } .action-btn { padding: 0.9rem 1.8rem; border-radius: 50px; font-size: 1rem; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 0.6rem; transition: all 0.3s ease; border: 2px solid transparent; } .action-btn.primary { background-color: var(--theme-color, #4a6fa5); color: white; } .action-btn.primary:hover { opacity: 0.9; transform: translateY(-2px); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); } .action-btn.secondary { background-color: transparent; color: #666; border-color: #ddd; } .action-btn.secondary:hover { background-color: #f5f5f5; border-color: #ccc; } .btn-icon { font-size: 1.2rem; } </style>

5. 运行验证与交互调试

完成所有组件后,回到项目根目录,确保开发服务器正在运行(pnpm run dev)。浏览器中应呈现一个完整的单页应用。

5.1 验证功能点

请按顺序检查以下功能是否正常:

  1. 页面加载:是否显示标题、侧边栏列表和默认角色(景天)的详情。
  2. 侧边栏交互
    • 鼠标悬停在列表项上,应有上浮和阴影效果。
    • 点击不同的角色,中央详情区域的内容(头像、名字、描述、技能、主题色)应立即更新。
    • 当前选中的角色在侧边栏应有高亮边框(active类)。
  3. 样式动态性:切换角色时,详情卡片的主题色渐变背景、角色名字的渐变颜色、标签和按钮的颜色应随之变化。
  4. 响应式布局(基础):尝试稍微调整浏览器窗口宽度,布局不应严重错乱。侧边栏宽度固定,详情区域应自适应填充。

5.2 浏览器开发者工具检查

打开浏览器开发者工具(F12),进行以下检查:

  • Console(控制台):确保没有 JavaScript 报错(红色错误)。
  • Elements(元素):检查 Vue 组件是否正确挂载,DOM 结构是否符合预期。观察点击侧边栏时,CharacterDetail组件内的文本和样式属性是否动态更新。
  • Network(网络):检查图片资源(占位图)是否成功加载,没有 404 错误。
  • Vue Devtools:如果已安装 Vue Devtools 扩展,可以检查组件的 Props、State 是否正确传递和更新。

6. 常见问题排查与样式优化

在实现过程中,你可能会遇到一些典型问题。下面列出常见问题及其解决方案。

6.1 数据绑定与事件传递问题

问题现象可能原因检查与解决
侧边栏点击无反应,详情不更新。1.SideBar组件未正确触发emit
2.App.vue中未监听@select-character事件。
3.selectedCharacterId的响应式更新未触发currentCharacter计算属性。
1. 检查SideBar.vue@click是否调用了selectCharacter方法,且该方法内部执行了emit('selectCharacter', id)
2. 检查App.vue<SideBar ... @select-character="handleSelectCharacter" />绑定是否正确。
3. 在 Vue Devtools 中观察selectedCharacterIdcurrentCharacter的值是否变化。
详情组件显示undefined错误。CharacterDetail组件接收的characterprop 初始值为nullundefinedApp.vue中确保currentCharacter计算属性有默认值(我们已通过 `

6.2 样式与布局问题

问题现象可能原因检查与解决
布局混乱,元素堆叠或溢出。1. Flexbox 或 Grid 属性设置错误。
2. 容器高度未设置height: 100%
3.box-sizing: border-box未全局应用。
1. 检查App.vue和各个组件的容器样式,确保display: flexflex-direction正确。
2. 确保html, body, #app高度为100%
3. 确认在全局样式中设置了* { box-sizing: border-box; }
图片变形或显示不全。img标签未设置object-fit: cover为头像等需要固定尺寸的图片元素添加object-fit: cover;样式。
动态主题色不生效。CSS 变量未正确定义或使用。检查CharacterDetail.vue:style="detailStyle"是否在元素上正确设置了--theme-color。检查 CSS 中引用该变量的属性(如background-color: var(--theme-color))是否正确。
滚动条出现在意外位置。overflow属性设置不当。为需要滚动的区域(如.side-bar,.character-detail)设置overflow-y: auto。为外层容器(如.main-container)设置overflow: hidden防止整体滚动。

6.3 性能与最佳实践优化

  1. 图片优化:实际项目中,应将角色头像图片放入publicsrc/assets,并使用构建工具进行压缩。对于大量图片,考虑使用懒加载(如loading="lazy")。
  2. 图标优化:我们使用了 Unicode 表情作为图标,简单快捷。在生产环境中,建议使用真正的图标库(如@iconify/vue),并按需引入,以获得更好的性能和矢量清晰度。
  3. 样式作用域:所有组件都使用了<style scoped>,这很好,避免了样式污染。但要注意,Scoped CSS 中的选择器会被添加>// 示例:在组件中使用 fetch import { ref, onMounted } from 'vue'; const characters = ref([]); const isLoading = ref(true); const error = ref(null); onMounted(async () => { try { const response = await fetch('/api/characters'); if (!response.ok) throw new Error('Network response was not ok'); characters.value = await response.json(); } catch (err) { error.value = err.message; } finally { isLoading.value = false; } });

    7.4 样式系统与组件库化

    如果项目持续迭代,可以考虑:

    • 引入CSS 预处理器(如 Sass)来管理变量、混合和嵌套。
    • 将可复用的 UI 片段(如按钮.action-btn、技能标签.skill-item)抽象为更基础的无状态组件
    • 建立设计 Token(颜色、间距、字体、阴影等),并通过 CSS 变量或主题配置文件统一管理。

    7.5 构建与部署

    开发完成后,运行pnpm run build进行生产构建。Vite 会将项目打包优化,输出到dist目录。你可以将这个目录部署到任何静态网站托管服务(如 GitHub Pages, Vercel, Netlify)。

    检查vite.config.js,可以根据需要配置公共路径(base)、是否生成manifest等。对于有路由的 SPA,部署到非根路径时,需要配置路由的base和服务器重写规则,将所有请求指向index.html

    通过这个从零到一的完整流程,我们不仅实现了一个视觉上吸引人的前端界面,更实践了现代前端开发的核心工作流:工程化搭建、组件化开发、响应式数据流和样式封装。你可以基于这个框架,替换数据、修改样式主题,快速打造出属于你自己的任何主题展示页。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询