基于Vue与Scss的旅游主题网页设计源码实战解析

发布时间:2026/9/14 5:43:40

基于Vue与Scss的旅游主题网页设计源码实战解析 简介一份以Vue和Scss构建的西安游玩主题网页设计源码面向前端初学者、旅游类网站开发者及对西安文化感兴趣的网页设计者可用于快速搭建具备现代交互体验的旅游展示页面也适合作为组件化开发与预处理器样式的实践范本。资源以ZIP压缩包形式提供共24个文件包含5个Vue组件、1个SCSS样式表、4个JSON配置、3个JavaScript脚本、9张PNG图片、1个HTML页面和1个说明文件压缩包仅61KB便于快速阅读与直接改造。Vue组件对应首页、游玩、问答、个人中心等模块JSON文件负责页面与应用配置JS脚本实现跨端适配及交互逻辑Scss利用变量和混入让样式更易维护并可整体换肤、改色PNG图片则强化了西安古城与现代都市的视觉氛围。已有104人浏览学习项目中还涉及地图定位、信息查询、用户评论等典型交互既能帮助理解旅游类页面如何组织信息也可在此基础上快速定制本地文旅宣传站点适合课程设计或个人作品集参考。1. 基于Vue和Scss的西安游玩主题网页设计源码到底在做什么如果你打开过一套城市文旅官网源码第一反应往往是“为什么一个看起来不复杂的页面要上Vue”。可一旦要给景点列表加筛选、给每个景点开详情页、再把整套配色从城墙灰改成樱花粉htmlcssjs三件套里就要做多次全局替换。这个标题对应的源码本质上是把“西安游玩”拆成了数据、组件和样式三层Vue负责组件渲染与路由Scss把景点卡片的颜色、间距、圆角收敛成变量数据独立放在js文件里。读者把景点数据一换页面城市就能换掉。适合刚做完静态网页想进阶的开发者也适合需要交付文旅类演示站的人按这套结构快速起步。2. 为什么旅游网页设计要选Vue而不是htmlcssjs环境配置与目录结构2.1 静态页面与组件化页面的分界点纯HTMLCSSJS也能做西安游玩首页甚至在首屏渲染速度上还占优势。问题出在内容重复上。旅行页面里最常见的区块是“热门景点”“美食推荐”“三日游路线”三个区块的结构几乎一样一张图、一个标题、一句描述。把这三块分别复制到三处代码量会线性增长Vue的做法则是把卡片抽成SpotCard组件再通过v-for传入不同数据。后面要调整卡片的样式只改组件一侧三处同时生效。我一般会在决定组件拆分时看一条分界线页面里有没有一个结构相似、只靠参数区分的区域。如果只是首页一块固定宣传banner没必要强行封装如果有列表要循环、详情要跳转组件化收益才明显。西安游玩主题的典型场景恰恰是景点清单和详情页所以采用Vue是合适的而不是为了迎合框架趋势。2.2 Vue安装及环境配置npm的3个关键命令在开始读源码之前先把环境跑起来。使用官方脚手架是常见做法目的是得到一个标准的vite项目结构再往里填页面。下面这条命令在终端执行时需要Node环境项目名为xian-tour。node -v npm -v npm create vuelatest xian-tour cd xian-tour npm install npm install -D sass npm run dev命令说明node -v和npm -v用来确认版本Node版本低于16时create-vue会报错npm create vuelatest创建的是Vue3Vite项目过程中会问要不要装vue-router、pinia这个项目选vue-router即可npm install安装基础依赖npm install -D sass单独装Scss预处理器npm run dev把开发服务器跑在5173端口。如果窗口提示端口被占用Vite会自动切到5174浏览器访问地址以终端输出为准。环境或依赖建议版本说明Node.js18.12 或 20 LTScreate-vue对旧版本支持有限npm9版本过低时可先用nvm换Nodesass1.63Dart Sass语法以新use为主这里把环境要求列成表是希望你在clone源码后先看package.json里面的engines字段往往写明了Node约束而不是等构建报错再回头查版本。提示如果你只是把Scss用于局部组件样式的编译sass是开发依赖构建产物不会携带它。真正的页面样式是编译后的CSS。2.3 源码目录结构哪些文件值得最先看拿到一套源码后我建议按这个顺序阅读先看package.json了解脚本再看src/router里有哪些路由最后打开一个.vue组件看它的样式结构。下面是一份比较典型的目录和标题里的“源码”对应。xian-tour/ ├─ index.html ├─ package.json ├─ vite.config.js └─ src/ ├─ main.js ├─ router/ │ └─ index.js ├─ data/ │ └─ spots.js ├─ styles/ │ ├─ variables.scss │ └─ mixins.scss ├─ components/ │ ├─ CityHeader.vue │ └─ SpotCard.vue └─ views/ ├─ HomeView.vue └─ DetailView.vuespots.js是唯一的数据源城市名、景点名都在里面variables.scss控制主题views目录下的两个页面分别是列表页和详情页。修改时先动数据文件再动样式文件最后才动组件模板这个顺序能减少改动面积。如果你接手的是已有源码第一步不是读全部代码而是用npm run dev把页面跑起来然后在浏览器里对照组件名称找文件效率更高。3. Scss变量与mixin用西安城墙色把全站样式管起来3.1 Scss在主题型源码里的定位网页设计里配色不一致是最容易返工的问题。西安游玩主题通常想表现的色彩是城墙砖灰色、钟楼市区的暖黄色、晚间灯火的金色这些颜色如果在每个vue文件里直接写十六进制后期换主题时就要开着编辑器逐页替换。Scss的核心作用是把颜色、字体、间距这类可复用token放到variables.scss让所有组件引用同一个变量。与CSS原生的var(--color)相比Scss变量在编译期替换不会多出运行时依赖更关键的是Scss的mixin能封装整段样式逻辑比如卡片 hover 时“上浮 阴影加深”在多个组件里出现写成mixin后一行调用。旅游主题网页里大量出现的图片圆角、标签样式、卡片阴影都适合用Scss组织。3.2 用变量和mixin定义一套城市主题常见的做法是新建src/styles/variables.scss把主题色按用途命名而不是按色值命名。比如不要写$yellow: #d9b384而要写$city-primary这样未来换城市时只看变量用途表。// variables.scss $city-dark: #3c2f25; $city-primary: #b08c5a; $city-pale: #f6f1ea; $city-accent: #c03028; $radius-card: 12px; $shadow-card: 0 4px 12px rgba(60, 47, 37, 0.08);变量命名说明$city-dark用于正文和深色底部$city-primary用于按钮边框或价格标签$city-pale作为页面浅色背景$city-accent用在优惠角标和日期高亮。把颜色拆成“深、主、浅、强调”四档可以覆盖一个页面百分之八十的视觉需求。$radius-card和$shadow-card虽然不是颜色但和卡片一起变化放在同一文件里是为了减少文件数量。变量示例值典型使用位置$city-dark#3c2f25全站正文、底部深色区域$city-primary#b08c5a主按钮、时间线边框$city-pale#f6f1ea页面底、卡片底、列表条纹$city-accent#c03028角标、热门标签、价格数字mixin文件mixins.scss里可以存放两类内容一是媒体查询断点二是组件卡片的hover效果。下面的mixin responsive接受一个断点名在组件里直接include responsive(mobile)就能把某条样式套到移动端。// mixins.scss mixin responsive($breakpoint) { if $breakpoint mobile { media (max-width: 767px) { content; } } else if $breakpoint tablet { media (min-width: 768px) and (max-width: 1023px) { content; } } else { media (min-width: 1024px) { content; } } }这个mixin用content接收调用方传入的CSS块省去在每个组件里重复写media query。需要提一下旧项目常见的import在Dart Sass里已不建议使用新代码一律用use因为use会按文件只加载一次并自动命名空间减少重复代码。3.3 把Scss全局注入到所有Vue组件如果每个.vue文件都写一行use /styles/variables.scss as *;很容易在复制组件时漏掉。Vite提供css.preprocessorOptions.scss.additionalData把这些公共变量统一注入这也是Vue和Scss搭配时最容易忽略的配置点。// vite.config.js import { fileURLToPath, URL } from node:url import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], css: { preprocessorOptions: { scss: { additionalData: use /styles/variables.scss as *; use /styles/mixins.scss as *; } } }, resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } } })配置说明additionalData里的两行会在每个style块编译前自动拼接这样组件里直接使用$city-primary或include responsive(mobile)即可无需手动import。resolve.alias把指向src让上面的/styles写法在js和scss里都生效。提示additionalData注入的是编译前内容不应该写体积大、带副作用的代码否则每个组件都会重复携带一份增加编译时间。它只适合放变量和mixin这类纯token。4. 景点数据、SpotCard组件与路由参数的联动4.1 把景点数据从组件里独立出来主题网页里的景点数据有名称、区域、标签、图片、简介五个核心字段。把这些字段直接写在模板里会让源码不可维护常见做法是放在src/data/spots.js中按id组织。id在这里不是数组下标而是稳定唯一的英文串方便后面做路由参数。export const spots [ { id: bing-ma-yong, name: 秦始皇帝陵博物院, district: 临潼区, tags: [兵马俑, 遗址, 亲子], image: /images/bing-ma-yong.jpg, summary: 以兵马俑陪葬坑为主体展示秦代陶俑、铜车马与青铜兵器。 }, { id: gu-cheng-qiang, name: 西安城墙, district: 碑林区, tags: [骑行, 夜景, 历史], image: /images/cheng-qiang.jpg, summary: 中国现存规模最大、保存最完整的古代城垣之一。 } ]字段说明id只用于路由识别不直接展示district用于列表页里的区域筛选tags数组在卡片上渲染成多个小标签数据驱动页面内容。图片路径用/images/..开头的绝对路径开发时在public/images里放同名图片即可后面打包部署时再讨论路径问题。字段类型用途idstring路由参数和:keynamestring卡片标题districtstring区域筛选tagsstring[]标签和热门标记imagestring卡片图片建议同时配置备选色summarystring列表摘要详情页可扩充4.2 SpotCard组件props、事件与Scss样式SpotCard是这套源码里复用性最高的组件它只做一件事接收一个spot对象渲染卡片。点击卡片时向外发送select事件把选中的景点交给父组件。把事件交给父组件处理而不是在SpotCard里直接调路由跳转能让组件在首页推荐、详情页相关推荐多个场景复用。!-- components/SpotCard.vue -- script setup defineProps({ spot: { type: Object, required: true } }) const emit defineEmits([select]) /script template article classspot-card clickemit(select, spot) img classspot-card__img :srcspot.image :altspot.name loadinglazy / div classspot-card__body span classspot-card__district{{ spot.district }}/span h3{{ spot.name }}/h3 p{{ spot.summary }}/p div classspot-card__tags span v-fortag in spot.tags :keytag classtag{{ tag }}/span /div /div /article /template style langscss scoped .spot-card { background: $city-pale; border: 1px solid rgba(60, 47, 37, 0.1); border-radius: $radius-card; overflow: hidden; cursor: pointer; transition: transform 0.2s ease, box-shadow 0.2s ease; :hover { transform: translateY(-4px); box-shadow: $shadow-card; } } .spot-card__img { width: 100%; height: 200px; object-fit: cover; } /style组件逻辑说明defineProps声明spot必须是Object模板里的v-fortag in spot.tags循环渲染标签:keytag让Vue能识别每个标签的节点差异。图片上的loadinglazy是浏览器原生懒加载不用js就能让页面滑动到视口附近再加载图片对首屏性能和流量都有帮助。样式里:hover配合Scss嵌套生成article:hover规则translateY(-4px)是最常见的卡片上浮动效能在视觉上强调可点击区域。重点提一下defineEmits([select])子组件把事件名声明为select父组件用selectgoDetail监听事件携带的payload就是当前的spot对象。这个“子组件不感知路由、只上报数据”的模式让组件在列表页和详情页都能复用。4.3 路由参数、props接收与详情页跳转列表页点击卡片后要进入详情页并让详情页知道应该展示哪条数据。Vue Router里最直接的做法是定义/detail/:id跳转时把spot.id作为参数传入。多数情况下不用手写router.push因为模板里的router-link已经足够。// router/index.js import { createRouter, createWebHashHistory } from vue-router import HomeView from /views/HomeView.vue import DetailView from /views/DetailView.vue const routes [ { path: /, name: home, component: HomeView }, { path: /detail/:id, name: detail, component: DetailView, props: true } ] export default createRouter({ history: createWebHashHistory(), routes })路由配置说明/detail/:id中的冒号是动态参数占位符:id会匹配任何字符串props: true的作用是把route.params作为组件的props下发这样DetailView里可以直接声明defineProps({ id: String })不需要在组件里写useRoute获取参数。createWebHashHistory让路由地址带#/好处是静态托管时不需要服务端配置刷新也不会404适合主题源码这种要交给别人直接预览的场景。在DetailView中通过spots.find(spot spot.id props.id)找到当前景点数据再渲染详细文案。这个查找过程的时间复杂度对数组长度不敏感景点数量在几十量级时不必优化如果后续数据量到上千条再考虑换Map缓存。5. vite构建Vue项目部署打包路径、history路由和布局异常排查5.1 先看开发环境正常、打包后布局异常的原因开发模式下Vite会把资源路径解析成服务器地址模板里写/images/x.jpg没有问题一旦执行npm run build产物要从某个根路径或子目录托管绝对路径的根/就会指向你的域名根目录导致图片、css、js全部404。表现就是页面只有文字没有排版或者图片全部裂开这就是搜索引擎里常见的“vue 打包后 布局异常”问题。解决方案是让构建产物使用相对路径。在vite.config.js里设置base: ./构建时所有静态资源路径都会从/assets/xx变成assets/xx在任意一层子目录下托管都能正确加载。如果项目部署在CDN或独立域名的根路径则可以把base改成/或完整URL但开发环境到生产环境的路径系统必须一致。// vite.config.js import { fileURLToPath, URL } from node:url import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ base: ./, plugins: [vue()], resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } }, server: { host: 0.0.0.0, port: 5173, open: true } })参数说明base: ./表示相对路径resolve.alias里的指向src目录让组件里的/styles/...写法稳定server配置只影响开发命令npm run dev其中host: 0.0.0.0允许局域网内手机直接用电脑IP访问方便在真机上看移动端样式。5.2 从build到preview的验证流程构建完成后不要直接打开dist/index.html因为文件协议下部分资源加载策略和http不同更可靠的方式是用Vite自带的预览命令起一个本地静态服务器。npm run build npm run previewnpm run build默认输出到dist目录并打印每个chunk的体积npm run preview会在4173端口启动一个和线上行为接近的静态服务器。这时打开页面如果样式正常再把这个dist目录扔到nginx、GitHub Pages或对象存储上基本都是安全的。真正部署前还要检查一遍外部图片域名是否支持跨域景点图片如果走CDN且crossorigin属性缺失部分浏览器下canvas绘制或Web字体可能被拦截。5.3 hash路由和history路由的取舍主题源码如果只作为展示页运行hash路由是最省心的。它不需要服务器端rewrite规则部署到任意静态空间都能在子页面刷新后保留当前路由。如果你在主域名根路径部署且服务器由自己控制history路由地址更干净但需要给nginx配置try_files回退到index.html。运行方式刷新行为部署要求createWebHashHistory地址带#/detail/xx刷新不报错静态托管即可无配置createWebHistory地址干净刷新可能404需服务器或rewrite规则回退index.htmlbase: ./ hash两者结合放在子目录也安全npm run build后直接上传5.4 关闭sourcemap与控制chunk体积文旅主题页面往往要放进微信公众号内嵌页面或小程序webview首包体积直接影响加载速度。vite默认产物不带sourcemap但如果有模板开过build.sourcemap构建就会额外生成.map文件部署时可能泄露源码结构。对于这类主题源码保持sourcemap: false即可。另外如果详情页和列表页高度复用可以按路由做懒加载把DetailView的import形式改为箭头函数让Vite自动切分chunk。const routes [ { path: /, name: home, component: HomeView }, { path: /detail/:id, name: detail, component: () import(/views/DetailView.vue), props: true } ]路由懒加载说明component改成箭头函数后只有访问detail页面时才加载DetailView相关代码块首页首包体积会减少。对主题源码来说这个改动不影响任何组件写法却能让打包产物从“单文件包含所有页面”变成“按路由拆分”是项目交付前最划算的优化之一。5.5 Scss资源路径错误的另一处雷区打包异常不只有路由问题。当.vue的style块里用url(../assets/bg.jpg)引用背景图时Vite在编译Scss后会重新处理url路径如果背景图放在assets里且大小超过assetsInlineLimit默认的4KB会被单独打包成带hash的文件名。此时如果Scss文件里用了相对路径且层级写错构建不会报错但图片在浏览器里显示不出来。建议背景图统一走别名或放在public目录直接写绝对路径减少Scss变量和图片路径之间的耦合。6. 在源码上扩展城市体验骨架屏与滚动加载的落地写法6.1 加载状态与骨架屏的替换时机景点图片通常体积庞大列表页从数据请求到图片渲染完成之间容易出现大面积空白。给这个空窗期套一层骨架屏会让页面看起来加载更快。骨架屏的显示完全由数据状态控制不要用setTimeout硬撑时长。常见做法是用loading配合template v-if/v-else把骨架屏和真实列表放在同一渲染位置。script setup import { ref, onMounted } from vue import { useRouter } from vue-router import { spots } from /data/spots import SpotCard from /components/SpotCard.vue const router useRouter() const spotList ref([]) const loading ref(true) onMounted(() { setTimeout(() { spotList.value spots loading.value false }, 300) }) function goDetail(spot) { router.push(/detail/${spot.id}) } /script template section classspot-grid template v-ifloading div classspot-skeleton v-forn in 6 :keyn/div /template template v-else SpotCard v-forspot in spotList :keyspot.id :spotspot selectgoDetail / /template /section /template代码里的template v-if/v-else避免v-if和v-for作用在同一元素上带来的优先级歧义。setTimeout在这里模拟接口延迟实际项目中应替换为异步请求的Promise完成回调。骨架屏数量不一定要和真实数据条数相等列表前6条的占位足以撑住首屏。对应的Scss骨架动画把渐变背景来回移动效果比纯灰色块更接近真实图片的占位感受。.spot-skeleton { height: 260px; border-radius: $radius-card; background: linear-gradient(90deg, $city-pale 25%, #e8e1d8 37%, $city-pale 63%); background-size: 400% 100%; animation: skeleton-load 1.2s ease infinite; } keyframes skeleton-load { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }样式解释background-size必须大于元素本身百分比位移才有视觉流动感animation时长1.2s太快会看起来焦躁太慢又会让人误以为页面卡住1.1到1.4秒是网页组件库常用的区间。6.2 用Network面板验证首屏改进骨架屏不是加上就结束了。打开浏览器devtools的Network面板把网络节流从Online切到Slow 3G刷新页面并观察瀑布图。图片资源通常会排到后面骨架屏的动画会在图片出现前持续运行这时页面看起来依然完整用户也不需要盯着空白页等待。要注意骨架屏并不能减少数据传输量它只是改善感知性能真正让列表变快的做法仍是图片懒加载以及把首屏用到的景点图片压缩成webp格式。如果想精细控制结束时机可以在SpotCard的img元素上绑定load事件等首屏指定的几张图全部加载完成后再把loading切回false这样进入页面时不会看到骨架屏和真实图片的明显断层。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/14 5:43:40

彩虹易支付源码部署实战:从环境配置到回调调试全流程

简介:面向个人站长与开发者,资源包提供已实测的最新版彩虹易支付源码及保姆级搭建教程,旨在快速搭建个人免签支付系统,一体化接入微信、支付宝、QQ等主流支付渠道,解决个人收款与资金归集难题。包内共1018个文件&#…

2026/9/14 5:38:40

RAG检索与重排序技术:提升AI应用精度的关键

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 5:38:40

LangChain + MCP 实战:为 Agent 打造标准化的万能工具接口

做过 Agent 的人应该都有同一种感觉:功能做得越多,工具越多,代码就越臃肿。今天接一个内部搜索引擎,明天接一个订单查询服务,后天再接一个数据库查询器,每一个都要写一遍工具封装、参数定义、鉴权逻辑、异常…

2026/9/14 6:33:42

如何用 Kortix Apps 把静态站点部署到稳定 URL 并回滚版本

如何用 Kortix Apps 把静态站点部署到稳定 URL 并回滚版本 【免费下载链接】agentpress The open-source AI Management System 项目地址: https://gitcode.com/GitHub_Trending/ag/agentpress 如果你已经有一个构建好的静态站点(纯 HTML/CSS/JS 目录&#x…

2026/9/14 6:33:42

学术论文降重工具全解析:原理、评测与实战策略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 6:28:42

游戏出海买量成本高?AI语言引擎如何破解本地化瓶颈

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 2:17:50

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/14 0:03:22

KCF目标跟踪算法与OTB工程实现:毕业设计实战解析

简介:这是一份基于KCF核相关滤波算法、融合尺度池与抗遮挡处理的目标检测跟踪MATLAB完整源码,主要面向计算机相关专业准备毕业设计、课程设计或期末大作业的学生,也适合需要项目实战练习的初学者。源码在OTB数据集上完成验证,能够…

2026/9/14 0:03:22

语音情感识别实战:Keras实现LSTM、CNN、SVM与MLP多模型对比

简介:面向语音情感识别入门与进阶开发者,这份基于Keras的项目源码完整实现了LSTM、CNN、SVM、MLP四种模型,兼容Python3.8与Keras/TensorFlow2环境。压缩包内含49个文件,大小约70.31MB,主体包括Python脚本、yaml/json配…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/13 11:18:28

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码