ARTICLE DETAIL

资讯详情

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

原生三件套手写一个天气查询小应用

原生三件套手写一个天气查询小应用 通过网盘分享的文件天气查询小应用.zip链接: https://pan.baidu.com/s/14KmANjtUA_1qqwbROLC7iA?pwdynaq 提取码: ynaq–来自百度网盘超级会员v7的分享用 HTML CSS 原生 JavaScript 做了一个天气查询小应用没有引入任何框架、构建工具和第三方库双击index.html或本地起个静态服务器就能跑。数据源用的是Open-Meteo完全免费、不需要 API Key、原生开启 CORS浏览器可以直接请求。一、功能一览1. 按城市名查询当前天气输入城市名支持中文、英文、拼音展示温度、天气状况、体感温度、湿度、风速。2. 未来 7 天预报逐日预报以卡片形式横向平铺每张卡片包含日期今天 / 明天 / 10/06 周一、天气图标、天气状况、最高 / 最低温。3. 按天气切换背景与图标晴天是暖橙渐变、阴天是灰蓝渐变、雨天是深蓝渐变、雪天是浅蓝渐变。整页背景平滑过渡预报卡片也会按各自的天气做轻微着色一眼就能区分「晴 / 阴 / 雨」。4. 加载中与失败重试请求期间显示旋转 loading请求失败展示友好中文提示超时 / 断网 / 没找到城市 / 跨域被拦截并附带「重试」按钮重试会复用上一次的查询任务不需要重新输入。5. 最近查询记录查过的城市自动存入localStorage去重后置顶、最多保留 6 条点击标签即可重新查询。6. 定位当前城市点击 调用浏览器 Geolocation API 获取坐标再反向解析出城市名并查询天气。二、技术点1. 零依赖三件套直接跑没有 npm、没有打包、没有框架。index.htmlstyle.cssapp.js三个文件用浏览器打开就能运行非常适合作为前端基础练习。2. 全面 async / await告别回调地狱fetch、Geolocation、localStorage 的读写全部用async/await串联代码是自上而下的线性结构asyncfunctionfetchWeatherByCity(cityName){constgeoawaitrequest(API.GEOCODE,{params:{name:cityName,...}});if(!geo.results?.length)thrownewError(没有找到城市「${cityName}」);constplacegeo.results[0];returnfetchWeatherByCoords(place.latitude,place.longitude,displayName(place,cityName));}Geolocation 本身是回调式 API用一个 Promise 包一层就能无缝接入awaitfunctiongetCurrentPosition(){returnnewPromise((resolve,reject){navigator.geolocation.getCurrentPosition((pos)resolve(pos.coords),(err)reject(newError(messages[err.code])));});}3. 请求统一封装超时 错误归一化所有网络请求都走同一个request()函数业务代码里不出现裸fetch。它负责三件事拼参用URLSearchParams自动做 URL 编码中文城市名不会出错超时用AbortControllersetTimeout实现超过 8 秒主动中止请求错误归一化把 HTTP 4xx/5xx、网络失败、超时这三种情况统一翻译成中文友好提示。constcontrollernewAbortController();consttimersetTimeout(()controller.abort(),timeout);try{constresawaitfetch(fullUrl,{signal:controller.signal});if(!res.ok)thrownewError(服务器返回错误HTTP${res.status});returnawaitres.json();}catch(err){if(err.nameAbortError)thrownewError(请求超时请重试);thrownewError(网络请求失败可能断网或跨域被拦截请重试);}finally{clearTimeout(timer);// 别忘了清定时器}一个容易踩的坑fetch只在网络层失败时才 rejectHTTP 404 / 500 它是正常 resolve 的必须手动判断res.ok。4. CORS为什么直接请求接口会报跨域浏览器有同源策略协议 域名 端口三者完全相同才算同源。页面在http://localhost:5500接口在https://api.open-meteo.com域名不同就是跨域。关键认知很多人会搞错跨域时请求其实已经发出去了服务器也返回了数据是浏览器发现响应头里没有Access-Control-Allow-Origin才拒绝把响应交给 JS控制台报blocked by CORS policy所以Postman / curl 不报错只有浏览器报错—— 同源策略只存在于浏览器。CORS跨源资源共享就是服务器主动授权的机制在响应头里写上Access-Control-Allow-Origin: *等于告诉浏览器「我允许别的站点读我的数据」。本项目的具体结论Open-Meteo 返回了Access-Control-Allow-Origin: *所以浏览器可以直连不需要任何代理。而国内的多数天气接口和风、高德、心知等默认不返回这个头那时才需要处理方案说明选已开启 CORS 的接口最省事本项目就是这条本地静态服务器打开http://不要用file://它的 origin 是null更容易被拒CORS 代理如corsproxy.io仅用于测试不要上生产后端 / Serverless 转发生产级做法还能顺便藏 API Key5. 数据归一化 WMO 天气码映射接口原始的current/daily结构比较琐碎中间加一层normalize()把数据整理成内部统一模型UI 只认这个结构{city:北京 · 北京市,current:{temp:18,feelsLike:13,humidity:25,windSpeed:4.38,condition:晴,icon:☀️,theme:sunny},daily:[{date:2026-10-04,max:21,min:15,condition:晴,...},...]}好处是以后换数据源只改normalize()UI 一行都不用动。天气状况用的是国际标准WMO weather_code维护一张code → 中文描述 图标 主题名的映射表未知码做兜底顺带就解决了「图标 主题切换」。6. 主题切换CSS 变量 data-theme不需要写一堆 class 切换逻辑只要给body换一个data-theme属性CSS 里对应的背景就会生效配合transition做出平滑过渡body[data-themesunny]{background:linear-gradient(160deg,#f7b733,#e8890c,#b45309);}body[data-themerainy]{background:linear-gradient(160deg,#3b5f83,#1e293b);}body[data-themecloudy]{background:linear-gradient(160deg,#6b7c93,#3b4657);}document.body.dataset.themedata.current.theme;7. localStorage 存最近查询写入时先按城市名去重、再unshift置顶、最后slice限量读取时用try/catch兜底防止数据被手动改坏导致整页崩溃。constlistloadHistory().filter((item)item!city);list.unshift(city);localStorage.setItem(HISTORY_KEY,JSON.stringify(list.slice(0,6)));8. 状态机式的 UI 管理页面只有四种互斥状态idle初始提示、loading、error、weather。一个show(state)函数统一切换避免出现「loading 和错误提示同时显示」这类状态错乱。functionshow(state){Object.entries(STATE_ELEMENTS).forEach(([name,node]){node.classList.toggle(hidden,name!state);});}9. 重试机制把「上一次做的事」记下来重试时原样再跑一遍letlastTasknull;asyncfunctionrunAction(action,{saveHistorytrue}{}){lastTask{action,saveHistory};show(loading);try{constdataawaitaction();renderWeather(data);applyTheme(data.current.theme);show(weather);}catch(err){el.errorMsg.textContenterr.message;show(error);}}10. 安全与健壮性细节城市名、接口返回的文本一律先转义再拼进innerHTML避免 XSS逆地理编码坐标 → 城市名失败时不阻断主流程降级成「当前位置」照常显示天气定位功能的失败原因拒绝授权 / 无法获取 / 超时分别给出不同的中文提示。11. 用到的接口一览用途接口城市名 → 经纬度https://geocoding-api.open-meteo.com/v1/search经纬度 → 当前天气 逐日预报https://api.open-meteo.com/v1/forecast经纬度 → 城市名定位用第三方https://api.bigdatacloud.net/data/reverse-geocode-client一次forecast请求就能同时拿到current当前天气和daily逐日预报参数里指定timezoneauto按目标地点时区返回日期避免跨天错位。三、小结这个项目的复杂度不高但把前端入门阶段最该练的几件事都串了一遍异步流程控制、请求封装与容错、跨域原理、数据归一化、状态管理、本地存储、浏览器原生 API。最大的感受是——不用框架反而更容易看清每一层到底在做什么。
返回列表