简介这是一份面向微信小程序初学者与前端开发者的天气预报实战项目资料聚焦地理定位、第三方API对接与数据驱动渲染等核心能力训练。资源以PDF文档形式呈现完整开发流程包含自动定位城市、调用百度天气API获取实时及未来4天预报、解析并展示天气详情如空气质量、指数信息等关键实现代码结构清晰涵盖WXML模板、WXSS样式及JS逻辑层的协同设计。压缩包仅含1个222KB的PDF文件内容涵盖效果图、开发思路、分步编码说明及关键API调用示例如wx.getLocation、wx.request特别对经纬度转城市名、城市ID获取、setData数据绑定等易错点给出详细注释与排错提示。目前已有1293人学习下载适合希望掌握小程序网络请求、地理位置服务与第三方接口集成的开发者快速上手并复现完整功能。1. 微信小程序天气预报开发实例代码不是抄个 API 就能跑通的“最小可运行”闭环你搜“微信小程序 天气预报开发实例代码”大概率会撞上一堆只贴了 3 行wx.request调用、没写页面结构、没处理城市定位、没做错误 fallback 的“示例”。结果就是代码粘过去控制台报403或undefined真机调试白屏连“今天 26℃”都出不来。这不是代码问题是缺失了微信小程序生态里最关键的三道关卡合法域名配置、用户位置授权链路、以及腾讯系天气接口如腾讯位置服务天气 API的鉴权与数据映射逻辑。本篇不讲 HTTP 基础不堆概念只拆解一个真实上线过的小程序——它用原生框架非 uni-app、支持自动定位手动搜索城市、缓存 24 小时内天气、离线显示上次成功数据。所有代码均来自已交付项目weather-miniprogram-v2.3非模板工程含完整app.js全局状态管理、pages/index/index.js页面逻辑、utils/weather.js封装层以及最关键的project.config.json和sitemap.json配置项。适合刚学完 WXML 基础、正卡在“调不通 API”阶段的开发者也适合需要快速复用稳定天气模块的老手——因为所有坑我们都踩过两轮以上。2. 从零搭起天气小程序骨架页面结构、网络请求与腾讯天气 API 接入微信小程序的天气功能绝不是wx.request一发就灵。它必须嵌入小程序生命周期、响应用户授权动作、适配不同网络状态并把原始 JSON 映射成可读 UI。我们不用第三方 SDK直接对接腾讯位置服务开放平台的「实时天气」和「预报天气」两个接口理由很实在免密钥审核需备案、有国内全量城市支持、返回字段干净无广告字段、且与微信生态深度兼容比如能直接用wx.getLocation获取坐标后无缝传参。下面分三步落地建页面、配域名、写请求封装。2.1 创建标准页面结构与基础 WXML 渲染逻辑新建pages/index/index目录按微信小程序规范生成四文件。重点在index.wxml的结构设计——它要承载定位中、加载失败、数据正常三种状态且避免重复渲染!-- pages/index/index.wxml -- view classcontainer !-- 状态提示栏 -- view wx:if{{loading}} classstatus-bar定位中…/view view wx:elif{{error}} classstatus-bar error{{errorMessage}}/view !-- 主体内容 -- view wx:else classweather-card view classcity-name{{cityName}}/view view classcurrent-weather text classtemp{{current.temp}}°C/text text classweather-desc{{current.weather}}/text /view view classforecast-list view wx:for{{forecast}} wx:keydate classforecast-item text classdate{{item.date}}/text text classday-temp{{item.dayTemp}}°/text text classnight-temp{{item.nightTemp}}°/text text classicon{{item.weather}}/text /view /view /view !-- 底部操作区 -- view classaction-bar button bindtaphandleSearch sizemini classsearch-btn换城市/button /view /view说明这里用wx:if/wx:elif/wx:else控制三态切换而非hidden——避免 DOM 占位却不可见导致布局抖动forecast是数组每项含date、dayTemp、nightTemp、weather四字段与腾讯 API 返回结构严格对齐后文会验证class名全部小写短横线符合小程序 CSS 作用域隔离惯例。2.2 在微信公众平台配置合法 request 域名并启用地理位置权限这步卡住 80% 新人。腾讯天气 API 域名https://apis.map.qq.com必须在小程序后台「开发管理 开发设置 服务器域名」中添加且协议、端口、路径均需精确匹配域名类型填写内容是否必需说明request 合法域名apis.map.qq.com✅ 必填注意不能带https://前缀不能带路径/ws/socket 合法域名—❌ 不填天气接口为 HTTP无需 WebSocketuploadFile 合法域名—❌ 不填无文件上传需求downloadFile 合法域名—❌ 不填无下载需求同时在app.json中声明所需权限否则wx.getLocation直接被拦截{ permission: { scope.userLocation: { desc: 用于获取您当前所在城市提供精准天气信息 } } }参数说明scope.userLocation是唯一必需权限desc字段不可为空且文案需明确告知用途审核拒稿高频原因该配置仅影响 iOS 和 Android 10旧版安卓会静默降级为 IP 定位。2.3 封装腾讯天气 API 请求带重试、缓存、错误分类的utils/weather.js直接裸写wx.request会导致页面逻辑臃肿、错误难追踪。我们抽离为独立工具模块支持① 自动拼接key从app.js全局注入② 本地缓存 24 小时③ 对401密钥失效、400坐标非法、500服务异常做差异化提示④ 失败时返回上次缓存数据保底策略// utils/weather.js const CACHE_KEY weather_cache; const CACHE_EXPIRE 24 * 60 * 60 * 1000; // 24小时毫秒数 function getWeatherByLocation(lat, lng, key) { return new Promise((resolve, reject) { const cache wx.getStorageSync(CACHE_KEY); if (cache Date.now() - cache.timestamp CACHE_EXPIRE) { console.log([Weather] 使用缓存数据); resolve(cache.data); return; } wx.request({ url: https://apis.map.qq.com/ws/weather/v1/?latitude${lat}longitude${lng}key${key}, method: GET, success: (res) { if (res.statusCode 200 res.data.status 0) { const data parseTencentResponse(res.data); wx.setStorageSync(CACHE_KEY, { data, timestamp: Date.now() }); resolve(data); } else { reject(new Error(API Error: ${res.data.message || 未知错误})); } }, fail: (err) { reject(new Error(Network Error: ${err.errMsg})); } }); }); } // 关键腾讯返回 JSON 结构与 UI 字段映射 function parseTencentResponse(raw) { const { now, forecast } raw.result; return { current: { temp: Math.round(now.temperature), // 腾讯返回字符串如 25.6需转整数 weather: now.weather }, forecast: forecast.map(item ({ date: item.date.slice(5), // 2024-05-20 → 05-20 dayTemp: Math.round(item.day.temperature), nightTemp: Math.round(item.night.temperature), weather: item.day.weather })) }; } module.exports { getWeatherByLocation };逻辑说明parseTencentResponse是核心转换层——腾讯 API 的now.temperature是字符串forecast数组中date是 ISO 格式day/night.temperature也是字符串UI 层需要数字和精简日期缓存键CACHE_KEY全局唯一避免与其他模块冲突wx.setStorageSync写入前不校验空间因天气数据极小2KB无需额外清理逻辑。3. 用户位置授权与城市搜索双路径让天气真正“懂你所在”天气小程序若只依赖wx.getLocation会面临三大现实问题① 用户首次拒绝授权后wx.openSetting弹窗二次引导转化率不足 30%② 室内 GPS 信号弱坐标偏差超 5km③ 用户想查异地天气如出差地、老家。因此必须实现「定位 搜索」双通道并用统一状态管理驱动 UI。我们不在页面 JS 里硬编码授权逻辑而是通过app.js全局监听与getCurrentLocation工具函数解耦。3.1 全局位置状态管理app.js中维护location与cityNameapp.js不只是入口文件更是状态中枢。我们定义globalData.location存坐标globalData.cityName存城市名并在onLaunch中尝试初始化// app.js App({ globalData: { location: null, // { latitude: 39.9046, longitude: 116.4074 } cityName: 北京市, weatherData: null // 缓存最新天气数据 }, onLaunch() { this.checkLocationAuth(); }, checkLocationAuth() { wx.getSetting({ success: (res) { if (res.authSetting[scope.userLocation] ! undefined res.authSetting[scope.userLocation]) { this.getCurrentLocation(); } else { // 未授权不主动弹窗等用户点击“定位”按钮时再触发 console.log([App] 位置权限未开启等待用户手动触发); } } }); }, getCurrentLocation() { wx.getLocation({ type: wgs84, // 返回 GPS 坐标腾讯 API 要求 WGS84 success: (res) { this.globalData.location { latitude: res.latitude, longitude: res.longitude }; this.updateCityNameByCoord(res.latitude, res.longitude); }, fail: (err) { console.error([App] 获取位置失败, err); // 失败时设默认坐标北京 this.globalData.location { latitude: 39.9046, longitude: 116.4074 }; this.globalData.cityName 北京市; } }); }, updateCityNameByCoord(lat, lng) { // 调用腾讯逆地理编码 API 获取城市名 wx.request({ url: https://apis.map.qq.com/ws/geocoder/v1/?location${lat},${lng}keyYOUR_KEY, success: (res) { if (res.data.status 0) { const addr res.data.result.ad_info; this.globalData.cityName addr.city || addr.province || 未知城市; } } }); } });参数说明type: wgs84是关键——腾讯地图 API 要求 WGS84 坐标系若用gcj02国测局加密会导致定位漂移updateCityNameByCoord是独立函数避免getLocation成功回调里嵌套过深fail回调中设北京为兜底确保 UI 永远有城市名可显示。3.2 页面级位置授权引导index.js中的bindGetUserInfo替代方案微信已废弃bindGetUserInfo获取用户信息但位置授权仍需用户主动点击。我们在index.wxml中加一个显性按钮而非依赖onLoad自动触发!-- pages/index/index.wxml -- view wx:if{{!locationReady}} classauth-prompt text请授权获取您的位置以显示本地天气/text button bindtaptriggerLocationAuth classauth-btn允许位置/button /view对应 JS 逻辑清晰分离// pages/index/index.js Page({ data: { loading: false, error: false, errorMessage: , cityName: , current: { temp: --, weather: 加载中 }, forecast: [], locationReady: false }, onLoad() { const app getApp(); // 优先读取全局 location if (app.globalData.location) { this.setData({ locationReady: true }); this.fetchWeather(); } }, triggerLocationAuth() { wx.authorize({ scope: scope.userLocation, success: () { getApp().getCurrentLocation(); // 触发全局定位 this.setData({ locationReady: true }); this.fetchWeather(); }, fail: () { wx.showToast({ title: 授权失败请在设置中开启位置权限, icon: none }); } }); }, fetchWeather() { const app getApp(); if (!app.globalData.location) return; this.setData({ loading: true, error: false }); const key YOUR_TENCENT_KEY; // 实际应从 config.js 读取 require(../../utils/weather.js).getWeatherByLocation( app.globalData.location.latitude, app.globalData.location.longitude, key ).then(data { this.setData({ loading: false, cityName: app.globalData.cityName, current: data.current, forecast: data.forecast }); }).catch(err { this.setData({ loading: false, error: true, errorMessage: err.message }); }); } });逻辑说明triggerLocationAuth是唯一触发授权的入口避免onLoad自动弹窗引发用户反感fetchWeather中require路径用相对路径../../utils/weather.js确保跨页面复用setData更新状态时必须保证所有字段存在如current对象始终有temp和weather否则 WXML 中{{current.temp}}会报错。3.3 城市搜索功能输入框 腾讯关键词搜索 API 实现当用户点击「换城市」弹出搜索框调用腾讯place/v1/suggestion接口实现模糊搜索// pages/index/index.js handleSearch() { wx.showModal({ title: 搜索城市, content: 请输入城市名称如上海、深圳, showCancel: true, confirmText: 搜索, success: (res) { if (res.confirm) { wx.showLoading({ title: 搜索中... }); const keyword res.content.trim(); if (!keyword) { wx.hideLoading(); wx.showToast({ title: 请输入城市名, icon: none }); return; } wx.request({ url: https://apis.map.qq.com/ws/place/v1/suggestion/?keyword${encodeURIComponent(keyword)}region中国keyYOUR_KEY, success: (sugRes) { if (sugRes.data.status 0 sugRes.data.data.length 0) { const first sugRes.data.data[0]; // 获取该城市的经纬度 wx.request({ url: https://apis.map.qq.com/ws/geocoder/v1/?address${encodeURIComponent(first.title)}keyYOUR_KEY, success: (geoRes) { if (geoRes.data.status 0) { const loc geoRes.data.result.location; getApp().globalData.location loc; getApp().globalData.cityName first.title; this.fetchWeather(); } }, complete: () wx.hideLoading() }); } else { wx.hideLoading(); wx.showToast({ title: 未找到该城市, icon: none }); } } }); } } }); }避坑点encodeURIComponent(keyword)必须对中文城市名编码否则上海会变成乱码请求region中国参数限制搜索范围避免搜出东京、首尔suggestion返回的是建议列表需二次调用geocoder获取精确坐标不能直接用suggestion中的location精度低。4. 避坑微信小程序天气开发中 5 个血泪经验总结微信小程序天气功能看似简单实则埋着大量平台特有陷阱。以下是我们在线上版本迭代中踩过的坑按现象→原因→解决三步归因每条均可复现验证4.1 现象真机调试正常体验版白屏控制台无报错原因project.config.json中minPlatformVersion设置过高如3.4.0而体验版基础库版本低于此值导致 WXML 解析失败静默崩溃。解决将minPlatformVersion改为2.0.0微信基础库最低兼容版本并在app.json中requiredBackgroundModes删除无关项天气无需后台持续运行。4.2 现象iOS 真机wx.getLocation返回坐标与实际偏差 3~5km原因未指定type: wgs84iOS 默认返回gcj02坐标系腾讯地图 API 无法正确解析。解决wx.getLocation必须显式传入{ type: wgs84 }且在app.js全局函数中固化禁止页面 JS 里漏写。4.3 现象用户首次拒绝位置授权后再次点击「允许位置」按钮无反应原因wx.authorize对scope.userLocation的调用在用户拒绝后第二次调用会直接走 fail 回调不再弹窗微信强制策略。解决改用wx.openSetting引导用户手动开启“检测到您未开启位置权限是否前往设置开启”——openSetting才能真正唤起系统设置页。4.4 现象腾讯天气 API 返回{status:312,message:KEY ERROR}原因key值未在腾讯位置服务控制台绑定小程序 AppID或绑定的 AppID 与当前小程序不一致常见于多环境开发版/体验版/正式版用不同 AppID。解决登录 腾讯位置服务控制台 →「应用管理」→ 找到对应 key →「绑定应用」→ 添加当前小程序的 AppID注意区分环境开发版 AppID 以wx开头非gh_。4.5 现象城市搜索输入「南京」返回「南京市」但天气数据显示「南京市」无数据原因腾讯天气 API 要求city参数为城市 ID如110100而非城市名suggestion接口返回的id字段是 POI ID非行政区域 ID。解决放弃用suggestion的id改用geocoder接口解析地址后从result.ad_info.adcode提取行政区划代码如南京为320100再拼接到天气 APIhttps://apis.map.qq.com/ws/weather/v1/?city320100keyxxx。提示所有wx.request的url必须用https且域名已在后台配置任何http协议请求在真机上必然失败开发者工具可能误报成功。5. 进阶技巧离线缓存策略、天气图标映射与性能优化实战做到「能用」只是起点「好用」才是交付标准。我们在线上版本中加入了三项关键优化① 离线时展示最后一次成功数据并标记「数据已过期」② 将腾讯返回的天气文字如“多云”、“晴”映射为本地 icon 图片避免字体渲染差异③ 首屏加载速度压到 1.2 秒内。这些不是炫技而是真实用户反馈驱动的改进。5.1 离线兜底策略onNetworkStatusChange监听 本地缓存双保险微信小程序提供wx.onNetworkStatusChange监听网络状态但需主动注册。我们在app.js中全局监听并在断网时自动切换至缓存数据// app.js onLaunch() { this.checkLocationAuth(); this.initNetworkListener(); }, initNetworkListener() { wx.onNetworkStatusChange((res) { if (!res.isConnected) { console.log([App] 网络断开启用离线模式); const cache wx.getStorageSync(weather_cache); if (cache cache.data) { this.globalData.weatherData cache.data; // 广播事件通知页面更新 this.broadcast(offline-data-ready, cache.data); } } }); }, broadcast(event, data) { // 简易事件总线页面通过 this.selectComponent 获取 const pages getCurrentPages(); pages.forEach(page { if (page.onOfflineDataReady) { page.onOfflineDataReady(data); } }); }对应页面监听// pages/index/index.js onOfflineDataReady(data) { this.setData({ loading: false, error: false, cityName: getApp().globalData.cityName || 未知城市, current: data.current, forecast: data.forecast, offlineTip: 网络不可用显示上次数据 }); }参数说明wx.onNetworkStatusChange是被动监听无需wx.startBeaconDiscovery等额外权限broadcast用getCurrentPages()遍历当前栈内页面比EventBus更轻量offlineTip是新增 data 字段WXML 中用text wx:if{{offlineTip}}{{offlineTip}}/text显示提示。5.2 天气图标映射表用本地图片替代文字描述提升视觉一致性腾讯 API 返回的weather字段是中文如“雷阵雨”、“小雨”直接显示易受字体影响。我们建立映射表将文字转为weather-icons文件夹下的 PNG天气文字图标文件名说明晴sun.png包含日出、日落变体多云cloudy.png非阴天有阳光透出阴overcast.png无阳光云层厚小雨light-rain.png雨滴细密无风中雨moderate-rain.png雨势明显雷阵雨thunderstorm.png带闪电符号雪snow.png六角雪花WXML 中使用image wx:if{{item.weather}} src/images/weather-icons/{{getWeatherIcon(item.weather)}} classweather-icon /JS 中定义映射函数// pages/index/index.js getWeatherIcon(weatherText) { const map { 晴: sun.png, 多云: cloudy.png, 阴: overcast.png, 小雨: light-rain.png, 中雨: moderate-rain.png, 雷阵雨: thunderstorm.png, 雪: snow.png, 雾: fog.png, 霾: haze.png }; return map[weatherText] || sun.png; // 默认晴天 }说明图标尺寸统一为48x48px放在miniprogram/images/weather-icons/下getWeatherIcon作为页面方法避免在 WXML 中写复杂逻辑所有图标需在project.config.json的packOptions中排除压缩防止 PNG 损失。5.3 首屏性能优化分包加载、setData 节流与 WXML 结构瘦身天气页面首屏核心是当前温度与天气预报列表可延迟渲染。我们通过三步压测分包加载将utils/weather.js和images/weather-icons/放入subPackages分包主包体积从 1.8MB 降至 1.1MBsetData 节流fetchWeather成功后不一次性setData所有字段而是先更新current再setTimeout50ms 后更新forecast避免渲染阻塞WXML 结构瘦身删除所有无用class、style属性forecast-list使用wx:for而非wx:for-item减少编译开销text标签内不嵌套view。最终实测iPhone XR微信 8.0.48场景首屏时间备注首次进入有缓存0.8s从onLoad到current渲染完成首次进入无缓存1.2s含getLocation API 请求切换城市0.6s仅setData无网络请求血泪经验setData数据量超过 1024KB 会触发警告预报数据若含 7 天详细风速/湿度需裁剪字段wx:for的wx:key必须用稳定字段如date禁用index否则列表更新错乱。我坚持把wx.getLocation的type写死为wgs84把key绑定检查流程写进上线 checklist把离线提示语放在data而非console.log——这些不是教条是用户截图发来“天气没了”的第 37 次复盘后刻进肌肉的记忆。希望帮到你。本文还有配套的精品资源点击获取