微信小程序天气预报开发实例:从API选型到真机调试全解析
发布时间:2026/9/6 20:16:27来源:尧图网络
简介面向微信小程序初学者的天气预报开发实例参考完整展示从自动定位、城市识别、天气数据获取到页面展示的实现链路。资源包内仅 1 个 PDF 文件大小 222KB文档以效果图、实现思路和关键代码片段为主适合用于课程设计、毕业设计或小程序入门练习。内容详细讲解了 wx.getLocation 获取经纬度再借腾讯地图逆地址解析得到城市名并通过百度天气 API 城市列表接口匹配城市 ID最终拉取实时天气和未来 4 天预报数据的完整流程同时涵盖当天详情指数、WXML 模板封装、点击跳转详情页以及 setData 刷新页面数据等关键细节。对于不熟悉小程序定位授权、网络请求和数据绑定的开发者可直接对照思路和代码片段搭建出可运行的天气原型降低入门门槛。目前已有 1290 人学习适合正在做天气类小程序实战练手的读者。 做天气类小程序是很多入门者都会选的练手项目功能边界清晰、数据源现成、页面也有固定的呈现套路。但我刷了不少号称“微信小程序天气预报开发实例代码”的教程很多只是贴一段残缺代码连API怎么申请、域名白名单怎么配都不提照着抄根本跑不起来。这篇我把一个能真正跑通的实例拆开讲从天气数据源选型、请求封装、页面渲染到真机调试每一步都会解释为什么这么做代码可以直接复制只要替换成你自己的Key就行。我用的技术栈是原生微信小程序没有引入第三方框架。整体流程不复杂小程序通过wx.getLocation拿到经纬度再调用天气API拿到实时天气、逐小时预报和未来几天数据最后渲染成一个简洁的天气页面。但功能看着简单实际开发里藏了不少坑比如接口返回的code和200对不上、iOS对ISO时间字符串解析的兼容问题、模拟器定位和真机相差很大等等。这些我都会在后面的章节里逐个说明不管你是刚学小程序还是准备拿天气功能练手这篇都能帮你少走弯路。1. 天气数据从哪来API选型与Key申请1.1 为什么选和风天气而不是高德或OpenWeatherMap做天气功能第一步不是写页面而是先把数据源定下来。市面上常用的天气接口我对比过几个各有各的问题这里直接给结论。数据源免费额度返回语言国内访问速度适合的场景和风天气开发版每天1000次中文快个人项目、Demo高德开放平台有调用配额中文快和地图功能绑定的场景OpenWeatherMap免费版每分钟60次英文不够稳定海外项目、多语言支持我最后选和风天气理由有三个。第一接口返回字段非常直观实时温度是now.temp天气现象是now.text不需要翻文档就能猜个大概。第二错误码体系清晰返回JSON里有个code字段200代表成功401代表Key无效400代表请求参数不对排错效率很高。第三免费版对个人练习完全够用不需要绑信用卡控制台申请后就能直接用。高德虽然也有天气接口但它是围绕地图服务的接口文档和权限配置都更偏地图业务单独拿来做天气有点杀鸡用牛刀。OpenWeatherMap的数据本身没问题就是国内直连有时候不稳定而且返回字段默认是英文做中文界面还得自己做翻译映射。1.2 注册、创建应用与拿到Key和风天气的使用流程是注册账号、创建应用、选择API产品、拿到Key。创建的时候要选“Web API”不要选Android或iOS SDK因为小程序本质上是通过wx.request发起HTTP请求和Web端的调用方式一致。Key是一串32位字符创建完复制保存好。你可能会问Key直接放在小程序前端代码里是不是不安全确实不安全纯前端代码里的Key是可以被扒走的。我的处理办法是开发调试阶段为了快速跑通直接放在请求头里正式上线前改成云函数转发或者把Key放在自己的服务端代理小程序只请求自己的域名。这个例子主要以跑通流程为主你在替换Key的时候要清楚这一点。1.3 接口文档里最应该先看两个点我这次用到三个接口实时天气/weather/now、24小时预报/weather/24h、未来3天预报/weather/3d。这三个接口的请求参数都是一样的关键参数是location格式是“经度,纬度”不是先纬度后经度很多人第一次在这里写反导致天气数据对不上。响应体结构也有约定核心就是code和业务数据。比如实时天气接口返回的大致结构是这样{ code: 200, now: { temp: 25, text: 多云, windDir: 东南风, windScale: 3, humidity: 40 } }注意只要code不是200整个请求都应该判定为失败。有些教程只判断HTTP状态码结果HTTP返回200但业务code是401页面就会一直白屏。所以封装请求的时候我会把statusCode 200和data.code 200同时作为成功条件。2. 环境配置与请求封装先把小程序端的网络层打牢2.1 配置app.json里的权限和页面属性要使用wx.getLocation必须在app.json里声明permission字段否则真机调用授权弹窗时直接没反应。我通常这样配置{ permission: { scope.userLocation: { desc: 你的位置信息将用于获取当地天气 } }, window: { enablePullDownRefresh: true } }desc是必填的内容会展示在授权弹窗里。enablePullDownRefresh是让页面支持下拉刷新后面章节会用到。这里还要提醒一句如果你在页面级json里关闭了窗口默认导航栏那下拉刷新的触发区域会跟着变调试时别被这点绕晕。2.2 封装request.js把wx.request变成Promise要是每个页面都写一遍wx.request的success、fail、complete页面一多代码会非常混乱。我习惯先封装一个request.js把域名、Key、错误处理集中在一个文件里。const baseURL https://devapi.qweather.com/v7 const request (url, data {}) { return new Promise((resolve, reject) { wx.showLoading({ title: 加载中 }) wx.request({ url: ${baseURL}${url}, data, header: { X-QW-Api-Key: 这里替换成你自己的Key }, timeout: 10000, success(res) { if (res.statusCode 200 res.data.code 200) { resolve(res.data) } else { wx.showToast({ title: 请求失败${res.data.code}, icon: none }) reject(res.data) } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }) reject(err) }, complete() { wx.hideLoading() } }) }) } module.exports request封装不是为了炫技而是有实际的工程收益。wx.request本身是回调风格如果以后想在请求前统一加日志、请求后统一做错误埋点回调会让嵌套越来越深。用Promise封装之后页面里就能配合async/await写出更线性的代码阅读和排错都轻松很多。2.3 配置合法域名以及开发期怎么绕过小程序对请求域名有强校验所有wx.request的域名必须在小程序管理后台配置成request合法域名而且必须是HTTPS。不配置的话真机上会直接报request:fail url not in domain list。具体操作路径是登录微信公众平台进入“开发管理”-“开发设置”-“服务器域名”在“request合法域名”里添加https://devapi.qweather.com。个人主体的小程序同样可以配置不需要企业认证。开发阶段有个捷径在微信开发者工具的“详情”-“本地设置”里勾选“不校验合法域名”。但这个设置只对开发者工具生效真机预览还是会校验。我的建议是开发调试时可以先勾选等真正需要真机调试时提前把域名配好免得临时抓瞎。3. 页面结构与天气字符方案让用户一眼看懂天气3.1 页面区块设计当前温度、逐小时、未来几天天气页面不适合用复杂布局用户打开后第一眼就应该看到当前温度。我按信息优先级把页面分成三块顶部是当前实况温度数字突出显示中间是24小时预报横向滑动查看底部是未来3天预报纵向列表排列。对应的WXML结构大概是这样的view classpage view classcurrent wx:if{{weather.now}} view classcurrent-temp{{weather.now.temp}}°/view view classcurrent-text{{weather.now.text}}/view view classcurrent-meta {{weather.now.windDir}} {{weather.now.windScale}}级 湿度{{weather.now.humidity}}% /view /view scroll-view classhourly-scroll scroll-x enable-flex view classhour-item wx:for{{hourly}} wx:keyfxTime view classhour-time{{item.time}}/view view classhour-icon{{item.iconText}}/view view classhour-temp{{item.temp}}°/view /view /scroll-view view classdaily view classdaily-item wx:for{{daily}} wx:keydate view classdaily-week{{item.week}}/view view classdaily-icon{{item.iconText}}/view view classdaily-temp{{item.tempMin}}° ~ {{item.tempMax}}°/view /view /view /viewcurrent区块用wx:if判断目的是防止接口还没返回时空数据导致页面闪烁。逐小时预报我用了scroll-view横向滚动主要考虑到24小时的数据在手机上单屏放不下横滑比换行更符合用户习惯。3.2 横向滚动列表的坑scroll-view需要明确的样式约束scroll-view做横向滚动最容易遇到的问题是子项没有横向排列成功或者排列成功但被挤压变形。我的经验是设置enable-flex属性同时给scroll-view加上display: flex子项用flex: 0 0 auto固定宽度这样最稳定。.hourly-scroll { display: flex; white-space: nowrap; padding: 24rpx 0; } .hour-item { flex: 0 0 120rpx; display: flex; flex-direction: column; align-items: center; margin-right: 16rpx; }这里有个比较坑的细节enable-flex从基础库2.7.4才开始支持老项目如果基础库版本太低这个属性不会生效。如果测试机器上发现横向滑动失效可以先检查一下基础库版本或者退回用white-space: nowrap加display: inline-block方案。3.3 用天气现象文字做轻量符号不引入图片资源很多教程会直接把天气图标做成图片引入本地图片或网络图片。但一个24小时预报列表加上3天预报图标图片数量不少调试起来又慢又费流量。我采用的方案是写一个映射函数把天气现象文本映射成简短的字符。function getWeatherSymbol(text) { if (text.includes(晴)) return 晴 if (text.includes(云)) return 云 if (text.includes(雨)) return 雨 if (text.includes(雪)) return 雪 return 天 }因为和风接口返回的text字段是标准中文天气现象比如“晴”“多云”“小雨”映射起来非常稳定。用文字符号的好处是不增加额外资源请求对天气这种低频更新的页面很友好。缺点就是视觉效果比较朴素如果要做成产品级UI建议换成图标字体或SVG雪碧图但Demo阶段完全够用。4. 页面逻辑与生命周期数据更新、下拉刷新、重复请求防护4.1 从定位到数据请求的完整流程页面逻辑的核心是把“定位-请求-渲染”串起来。我在onLoad里调用一次初始化方法先通过wx.getLocation获取经纬度再拿着经纬度去请求三个天气接口。const request require(../../utils/request) function getWeatherSymbol(text) { if (text.includes(晴)) return 晴 if (text.includes(云)) return 云 if (text.includes(雨)) return 雨 if (text.includes(雪)) return 雪 return 天 } Page({ data: { weather: {}, hourly: [], daily: [] }, onLoad() { this.getLocationAndWeather() }, getLocationAndWeather() { wx.getLocation({ type: gcj02, success: (res) { this.fetchWeather(res.longitude, res.latitude) }, fail: () { // 测试环境或用户拒绝授权时用北京坐标兜底 this.fetchWeather(116.41, 39.92) } }) }, async fetchWeather(longitude, latitude) { const location ${longitude},${latitude} const nowRes await request(/weather/now, { location }) this.setData({ weather: nowRes.now }) const hourlyRes await request(/weather/24h, { location }) const hourly hourlyRes.hourly.map((item) ({ fxTime: item.fxTime, time: this.formatHour(item.fxTime), temp: item.temp, iconText: getWeatherSymbol(item.text) })) this.setData({ hourly }) const dailyRes await request(/weather/3d, { location }) const daily dailyRes.daily.map((item) ({ date: item.fxDate, week: this.formatWeek(item.fxDate), tempMin: item.tempMin, tempMax: item.tempMax, iconText: getWeatherSymbol(item.textDay) })) this.setData({ daily }) }, formatHour(fxTime) { const date new Date(fxTime.replace(/-/g, /).replace(T, )) const h ${date.getHours()}.padStart(2, 0) return ${h}:00 }, formatWeek(fxDate) { const weekMap [日, 一, 二, 三, 四, 五, 六] const date new Date(fxDate.replace(/-/g, /)) return 周${weekMap[date.getDay()]} } })这里把getWeatherSymbol和格式化函数都放在Page外部原因是它们不依赖this放在外部可以避免每次调用都重复创建函数实例。页面里的职责只保留数据获取和setData数据转换交给工具函数代码会清爽很多。4.2 onLoad、onShow、onPullDownRefresh的取舍onLoad只在页面创建时调用一次适合做初始请求。onShow在页面每次显示时都会触发包括从后台切回小程序。很多人喜欢把请求全放在onShow里结果每次切后台回来都会刷新一次用户看到页面loading频繁跳动体验很差。我采用的策略是初始请求放在onLoad用户手动下拉刷新时通过onPullDownRefresh重新请求。如果想让数据更实时可以在onShow里加一个时间判断距离上次请求超过5分钟才重新拉取。但这个实例核心是跑通流程所以先用“初始请求加下拉刷新”的方式简单直接。4.3 下拉刷新和防重复请求下拉刷新要生效除了在app.json或页面json里开启enablePullDownRefresh还要在Page里实现onPullDownRefresh方法。这里有个常见的坑请求完成后必须手动调用wx.stopPullDownRefresh()否则顶部刷新动画会一直转。async onPullDownRefresh() { if (this.data.isRefreshing) { wx.stopPullDownRefresh() return } this.setData({ isRefreshing: true }) // 重新获取一次最新的经纬度也能覆盖用户位置变化的场景 this.getLocationAndWeather() this.setData({ isRefreshing: false }) wx.stopPullDownRefresh() }isRefreshing标记就是防重复请求的关键。用户快速连续下拉多次时如果不加判断会同时发出多个重复请求既浪费流量又容易出现数据覆盖错乱。这个思路在别的按钮点击、提交表单场景里同样适用。5. 上线前的坑域名白名单、真机差异、常见报错排查5.1 request:fail url not in domain list这个报错是遇到最多的。原因就是小程序的域名白名单校验没过。解决办法我在2.3已经说过把https://devapi.qweather.com加到request合法域名。但还有两个容易忽略的点一是配置完域名后要过几分钟才能生效不要刚保存就测试二是如果用了开发者工具“不校验合法域名”的选项工具内不会报错但真机预览时一样会失败所以真机调试前一定要确认后台域名已经配置成功。5.2 模拟器定位不准导致天气不对微信开发者工具里的定位默认返回的是腾讯北京总部的经纬度不管你在代码里怎么设置坐标最终拿到的可能都是北京。如果你在南方城市页面却一直显示北京天气就是这个原因。我的调试做法是在getLocation的fail回调里写死一个自己城市的经纬度作为兜底等真机调试时再删除或忽略。比如我想测试上海就写const DEFAULT_LOCATION { longitude: 121.47, latitude: 31.23 }这样在开发者工具里直接模拟拒绝授权就能稳定测试上海天气数据。5.3 iOS对ISO时间字符串的兼容问题和风接口返回的fxTime是ISO8601格式类似2024-06-01T00:00:0008:00。在Android和开发者工具里直接new Date(2024-06-01T00:00:0008:00)能正常解析但在iOS小程序里有可能返回Invalid Date。原因和iOS对带横线日期、带时区字符串的解析兼容性有关。我在代码里统一用replace(/-/g, /)把横线改成斜杠再替换中间的T为空格这样iOS都能识别。比如function parseDate(str) { return new Date(str.replace(/-/g, /).replace(T, )) }这个坑很容易在开发阶段被忽略因为开发工具不报错Android真机也不报错只有iOS用户会遇到属于上线后才发现的隐性bug。5.4 维护心得把配置和转换逻辑拆出去这个天气实例虽然功能不多但我还是会把API地址、Key和路径常量统一放到一个config.js里。这样以后从开发环境切到生产环境或者API域名变了只需要改一个文件不用满项目搜索baseURL。数据转换逻辑也尽量抽成纯函数和Page状态解耦。我在实际维护中最大的感受是小项目反而更容易写烂因为觉得代码少就不重视结构等需求迭代两三轮后再回来看自己都不想改了。最后再分享一个调试习惯请求失败时不要只盯着fail回调先把接口返回的JSON打出来看code。很多时候页面渲染不出来不是网络问题而是参数拼错了或者Key过期了。把“先看code再查代码”养成习惯排查报错的速度会快很多。这个天气实例里踩过的坑基本也是小程序网络请求类的通用坑搞懂了以后做其他业务页面也能少走弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网