ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Vue的天气应用开发:组件化、数据流与HBuilder部署

基于Vue的天气应用开发:组件化、数据流与HBuilder部署 简介基于Html、JavaScript、Vue与CSS构建的天气应用设计源码面向前端初中级开发者和计算机相关专业学生定位为一个可直接运行的完整实战项目。它演示了如何从零搭建具备实时天气查询、城市切换、响应式界面等能力的Web应用内容覆盖组件化开发、前后端数据联调与项目工程化配置等关键环节。资源包共33个文件包括9个JavaScript脚本、6个Vue组件、4个JSON配置、3个CSS样式表、2个HTML模板以及SVG图标、字体文件和说明文档其中JavaScript主要用于数据请求与用户交互Vue组件实现界面模块化复用CSS负责视觉样式与响应式布局JSON与YAML配置管理路由、接口等工程参数压缩包整体仅145KB目录按stores、router、apis、views等模块划分结构清晰。目前已有450人浏览学习。通过研读全部源码可掌握Vue组件通信、前端路由配置、状态管理、异步请求封装及CSS美化等实用技能项目自带工程化配置、字体图标与说明文档特别适合作为课程设计、毕业设计或独立开发天气类应用时的参考蓝本。1. 天气应用源码真正要读的不是“天气”是组件划分“基于HtmlJavaScriptVueCSS的天气应用设计源码”这个标题乍看是给学生交作业用的前端练手项目但把关键词拆开之后会发现另一层价值Html负责语义骨架、CSS管视觉还原、JavaScript处理原生交互、Vue把数据与视图绑定起来。四者凑齐恰好组成一套完整的现代前端技术栈切片。这个项目大部分从业者30分钟就能写完但它真正值得看的是组件边界怎么切、状态放在哪一层、CSS作用域怎么控制。新手照着敲一遍能理解数据流老手则能借它快速搭出一个演示级的面板类应用换个接口就能变成监控页、报表页或设备状态页。下文按框架搭建、数据接入、交互配置、部署验证的顺序展开最终落到一个有用的调试技巧上。2. 先立框架Vue 组件树与 HTML/CSS 的承载关系2.1 为什么选 Vue 而不是原生 JavaScript天气应用的核心需求是“根据城市 ID 拿数据然后更新页面上的温度、湿度、天气图标和未来预报”。用原生 JavaScript 写也能实现核心逻辑就是fetch后操作 DOM但代码会随功能增多快速膨胀尤其是“多城市切换”和“实时刷新”这两个并发状态出现时原生写法需要手动维护 DOM 状态与数据状态的一致性非常容易漏更新。Vue 解决的是“视图与数据同步”的问题。用v-for渲染 7 天预报用v-if控制加载状态用computed推导“今天最高温最低温”这些都是模板语法层面的声明式操作。组件化把页面切成WeatherCard.vue、ForecastList.vue、CitySearch.vue三个独立文件各管各的 props 和 emit互相之间不直接操作 DOM。对于维护一个小型前端应用来说这是最省心的选型。2.2 页面骨架里 HTML 和 CSS 各自负责什么Vue 文件本身是一个单文件组件内部包含template、script、style三个模块但这不代表 HTML 和 CSS “消失了”——template编译后就是标准 HTMLstyle在构建阶段会被抽出成 CSS 文件。这个项目的源码里所谓“源码”通常就是指一组.vue文件、一个index.html、一个main.js入口和vite.config.js配置。一个典型的目录结构如下weather-app/ ├── index.html ├── package.json ├── vite.config.js └── src/ ├── main.js ├── App.vue ├── api/ │ └── weather.js ├── components/ │ ├── WeatherCard.vue │ ├── ForecastList.vue │ └── CitySearch.vue └── styles/ └── global.cssindex.html只保留一个挂载点main.js负责创建 Vue 实例App.vue作为根组件承载整体布局。所有组件的style scoped会被编译成带有>npm create vuelatest weather-app这个命令会进入交互式问答选择是否使用 TypeScript、Vue Router、Pinia、ESLint。对于纯前端练手项目推荐的选择是TypeScript 选 No、Vue Router 选 No、Pinia 选 No、ESLint 选 Yes。回答完进入项目并安装依赖cd weather-app npm install npm run devnpm run dev启动开发服务器默认端口通常是5173。在浏览器打开http://localhost:5173会看到 Vue 官方的欢迎页此时框架已经跑通。这个步骤是整个标题里“Vue”字样的落地前提。提示如果只想看最小可运行的按钮不想要 ESLint 和 Prettier直接在脚手架问答中全部选 No生成的代码量更少适合快速验证思路。3. 数据流打通天气 API 接入与 Vue 组件传值渲染3.1 天气数据从哪来天气应用必须有数据源。免费方案中最常见的是使用和风天气或 OpenWeatherMap 的开发者 API前者需要注册后获取一个 API Key后者同样需要 Key 但文档和字段更直观。本项目源码中通常使用axios或原生fetch请求当前天气与逐日预报两个接口再把返回数据交给 Vue 组件渲染。以下是src/api/weather.js中常见的封装方式import axios from axios // 创建 axios 实例统一设置基础路径 const request axios.create({ baseURL: https://api.openweathermap.org/data/2.5, timeout: 10000 // 超过 10 秒请求中断避免长时间 loading }) // 查询当前天气 export function getCurrentWeather(city) { return request.get(/weather, { params: { q: city, // 城市名如 Beijing appid: YOUR_API_KEY, // 换成你自己的密钥 units: metric, // 使用摄氏度而不是华氏度 lang: zh_cn // 返回中文描述 } }) } // 查询未来 7 天预报 export function getForecast(city) { return request.get(/forecast/daily, { params: { q: city, appid: YOUR_API_KEY, cnt: 7, // 7 天的数据 units: metric, lang: zh_cn } }) }axios.create的作用是生成一个带默认配置的请求实例后续所有请求都共用同一个 baseURL 和 timeout不需要在每个接口函数里重复写完整域名。“unitsmetric”是温度单位的关键参数缺了它返回的将是开尔文温度页面上会出现二三百度的怪数值。“langzh_cn”控制天气描述语言不传时默认为英文。3.2 App.vue 里如何协调子组件天气应用的核心交互流程是“在城市搜索框输入城市名拿到数据后把当前天气和预报分别传给两个子组件”。这个流程在 Vue 中属于典型的“父组件负责请求子组件负责展示”模式。App.vue持有城市名、当前天气数据、预报数据和加载状态这四个响应式变量数据请求成功后通过 props 分发给子组件。核心逻辑如下script setup import { ref } from vue import { getCurrentWeather, getForecast } from ./api/weather import WeatherCard from ./components/WeatherCard.vue import ForecastList from ./components/ForecastList.vue import CitySearch from ./components/CitySearch.vue const city ref(Beijing) const currentWeather ref(null) const forecastList ref([]) const loading ref(false) async function fetchWeather(cityName) { loading.value true try { const [currentRes, forecastRes] await Promise.all([ getCurrentWeather(cityName), getForecast(cityName) ]) currentWeather.value currentRes.data forecastList.value forecastRes.data.list } catch (error) { console.error(天气数据请求失败, error) currentWeather.value null forecastList.value [] } finally { loading.value false } } function handleSearch(val) { city.value val fetchWeather(val) } fetchWeather(city.value) /script template div classweather-app CitySearch :citycity searchhandleSearch / div v-ifloading classloading-message正在加载天气数据.../div WeatherCard v-ifcurrentWeather :datacurrentWeather / ForecastList v-ifforecastList.length :listforecastList / /div /template这段代码有四个关键点。第一Promise.all并行请求当前天气和预报数据总耗时取决于较慢的接口而不是两者之和。第二v-ifcurrentWeather确保子组件在数据到达之前不渲染避免空对象访问字段导致报错。第三search监听子组件派发的事件在 Vue 3 的script setup语法中不需要注册emits选项子组件用defineEmits([search])声明即可。第四finally块确保请求成功或失败都会退出加载状态否则失败后页面会一直停留在 loading 画面。3.3 子组件接收 props 并格式化输出WeatherCard.vue是页面上最显眼的卡片通常展示城市名、当前温度、天气描述和最高最低温。这个组件内部需要做两件事接收父组件传下来的data对象再把数据格式化成可读的文案。示例代码如下script setup import { computed } from vue const props defineProps({ data: { type: Object, required: true } }) // 温度四舍五入为整数去掉小数位 const temp computed(() Math.round(props.data.main.temp)) const maxTemp computed(() Math.round(props.data.main.temp_max)) const minTemp computed(() Math.round(props.data.main.temp_min)) const weatherDesc computed(() props.data.weather[0].description) /script template div classweather-card h2 classcity-name{{ data.name }}/h2 p classtemperature{{ temp }}°C/p p classdescription{{ weatherDesc }}/p p classtemp-range最高 {{ maxTemp }}°C / 最低 {{ minTemp }}°C/p /div /template style scoped .weather-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16px; color: #ffffff; padding: 24px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); } .city-name { font-size: 24px; margin: 0 0 8px; } .temperature { font-size: 64px; font-weight: 200; margin: 0; } /stylecomputed在这里的好处是父组件传入的data对象更新时所有派生值自动重算不需要额外写 setter。Math.round让温度显示更干净符合大多数天气产品的 UI 习惯。样式方面使用渐变背景和浅色文字效果接近主流天气应用卡片这是 CSS 在组件内作用域的典型用法。4. 交互落地CSS 布局、城市切换与 HBuilder 部署4.1 城市搜索组件的本地交互城市搜索是这个应用里最有交互属性的部分。用户输入城市名后点击搜索或者按回车组件把值抛给父组件父组件再触发新的数据请求。子组件内部还需要做防抖处理避免每敲一个字母就发一次请求。CitySearch.vue实现如下script setup import { ref } from vue const emit defineEmits([search]) const keyword ref() const timer ref(null) function onSearch() { const cityName keyword.value.trim() if (!cityName) return emit(search, cityName) } /script template div classcity-search input v-modelkeyword classsearch-input typetext placeholder输入城市名例如 Beijing / button classsearch-btn clickonSearch查询/button /div /template这里没有加防抖逻辑是因为“查询”按钮本身就是一次显式的用户动作。如果后续改成输入时自动搜索就需要用watch(keyword)配合 300ms 的定时器这是防抖的常见写法。emit(search, cityName)把数据传给父组件父组件在模板里通过searchhandleSearch接收。4.2 用 localStorage 记住上次搜索的城市天气应用的一个高频需求是“刷新后保留上次查询的城市”。这个需求不需要后端支持浏览器自带的localStorage就能解决。在App.vue中对city变量做持久化处理script setup import { ref, watch } from vue const city ref(localStorage.getItem(lastCity) || Beijing) watch(city, (newCity) { localStorage.setItem(lastCity, newCity) }) /scriptlocalStorage.getItem(lastCity)在初始化阶段读取缓存watch在 city 变化时把新值写回。写回操作是同步的对 tiny 应用完全够用。如果需要存储更复杂的数据结构比如用户最近搜索的 5 个城市可以改用JSON.stringify存入字符串数组读取时再用JSON.parse还原。注意localStorage是同步操作每次写入都会阻塞主线程但对于“城市名字符串”这种 KB 级数据完全感受不到。不要用它存天气接口的返回数据因为缓存过期策略需要自己实现复杂度不值得。4.3 CSS 布局的常见写法与关键参数天气应用通常是一个居中的窄卡片布局外层背景可以使用整屏渐变色或者动态背景图。布局上推荐 flex 而不是 grid因为组件数量少、层级浅flex 一行代码就能实现垂直居中。全局样式styles/global.css典型内容* { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: PingFang SC, Microsoft YaHei, Helvetica Neue, Arial, sans-serif; background: linear-gradient(180deg, #74b9ff 0%, #a29bfe 100%); min-height: 100vh; display: flex; justify-content: center; align-items: flex-start; padding: 40px 16px; } #app { width: 100%; max-width: 420px; }box-sizing: border-box让元素宽度包含内边距避免width: 100%加padding后容器超出父级。min-height: 100vh确保渐变背景铺满整个视口即使页面内容不足一屏也不会在底部露出白边。max-width: 420px让卡片在桌面浏览器里维持适合阅读的宽度在手机上则占满屏幕。4.4 用 HBuilder 运行与打包 Vue 项目热搜词里大量出现“HBuilder 配置 html、css、javascript”说明不少读者打算用 HBuilder 跑这个项目。HBuilder 主要用于 uni-app 开发但它同样可以直接管理普通的前端项目只是不承担 Vue 项目的编译任务——编译和构建仍然交给 Vite 完成。常用操作流程如下在 HBuilder 中点击“文件 – 打开目录”选择项目根目录打开内置终端输入npm run dev点击终端输出的本地地址在浏览器中查看结果HBuilder 本质上是一个编辑器加集成终端npm run dev启动的 Vite 开发服务器才是真正干活的进程。若要在 HBuilder 中调试需要提前安装 Node.js 并配置好 npm 镜像否则执行 npm 命令会报 “node 不是内部或外部命令”。生产环境的构建命令是npm run build执行后会在项目根目录生成dist/文件夹里面是编译压缩后的静态文件可直接部署到任意静态服务器。4.5 部署时的资源路径问题npm run build生成的 HTML 默认使用绝对路径/assets/xxx.js。如果把dist/目录放在服务器子目录下比如https://example.com/weather-app/资源路径会变成https://example.com/assets/xxx.js导致 404。解决办法是在vite.config.js中设置 baseexport default { base: ./ }./是相对路径构建后的 HTML 会引用./assets/xxx.js这样放在任何子目录下都能正常工作。这是部署阶段最容易踩的一个坑标题里的“CSS”相关资源也会因为路径错误一并加载失败表现是页面空白且控制台报大量Failed to load resource。5. 进阶技巧用 Vue DevTools 验证数据绑定与调试组件边界这个项目本身功能简单真正的价值在于调试过程和代码组织方式。遇到“组件没渲染”“数据没更新”的问题时不要一上来就加console.log先打开 Vue DevTools 看组件树和数据状态。5.1 安装与检查范围Vue DevTools 是浏览器扩展支持 Chrome 和 Edge 系的浏览器。安装后在地址栏旁边出现 Vue 图标。点击后会显示项目里的组件树左侧是组件结构右侧是 props、data、computed 的实时值。它比 console 更适合排查“数据更新但视图没变”一类的问题。打开开发者工具切换 Vue 面板展开App.vue节点能看到city、currentWeather、forecastList三个变量。如果currentWeather是null说明请求没成功问题在 API 层如果currentWeather有值但页面不显示问题在模板层如果数据更新了但页面还是老值大概率是v-if的判断条件写反了。组件边界问题在 DevTools 里也会暴露出常见的错误写法。有些初学者把WeatherCard里的data属性名写成props然后模板里用data.name实际应该用props.data.name。DevTools 中会看到该组件显示 “Undeclared props” 或者展开后没有对应字段此时回到代码检查 defineProps 里的声明即可。5.2 非响应式数据的处理技巧天气接口返回的某些字段可能不适合直接展示比如日出日落时间是用 Unix 时间戳表示的。在组件内部不需要新增一个 computed 属性可以在computed里直接转换script setup import { computed } from vue const props defineProps({ data: Object }) // 把 Unix 时间戳转成 HH:MM 格式 const sunriseTime computed(() { const date new Date(props.data.sys.sunrise * 1000) return date.toTimeString().slice(0, 5) }) /script这里* 1000是因为 OpenWeatherMap 返回的sys.sunrise是秒级时间戳而 JavaScript 的Date需要毫秒级。toTimeString().slice(0, 5)截取 “HH:MM” 格式避免展示完整的时区信息。如果在调试这个项目之后想扩展功能比较顺理成章的方向是增加“最近搜索城市”的历史列表把城市名数组存入localStorage并在 CitySearch 下方用v-for渲染标签。再进一步可以接入高德地图的 IP 定位接口进入页面时自动定位到当前城市省掉手动输入的步骤。这些改动都能保持现有组件结构不变只需要增加一个请求函数和一个小列表组件框定在原有四件套范畴之内。本文还有配套的精品资源点击获取
返回列表