Vue3+Vite天气应用报TypeError:无法读取undefined的main属性
报错产生原因
触发本次错误的核心原因是Vue选项式API配置项拼写错误:
- 声明组件响应式状态的配置项固定为
data(),你的代码里错写成了date(),导致组件初始化时根本没有将你定义的weather、query等变量挂载到组件实例上,模板访问weather时拿到的是undefined,此时读取weather.main就会直接抛出TypeError: Cannot read properties of undefined (reading 'main')错误。
除此之外代码里还存在3个会触发同类报错的隐患:
- 初始给
weather设置的默认值结构不全:模板中用到了weather.sys.country、weather.weather[0].main字段,但初始值只定义了main.temp,即使修正拼写错误,首次渲染或接口请求异常时依然会触发属性读取错误 - 根节点
:class的短路判断存在逻辑漏洞:weather.main && xxx的判断前提是weather本身有值,一旦weather为undefined,第一步读取weather.main就会直接报错 - 天气接口请求逻辑没有做容错处理,如果请求失败、返回结构不符合预期,直接赋值可能导致
weather变为异常值,触发渲染报错
可行修复方案
1. 修正基础拼写+补全默认状态
首先把写错的date()改回正确的data(),同时补全weather的默认结构,覆盖模板中用到的所有字段,补全缺失的方法和生命周期逻辑:
export default { name: "app", // 修正拼写:之前错写为date() data() { return { api_key: '803a7cd7089cd54e3ecc37bf1b6a3340', url_base: 'https://api.openweathermap.org/data/2.5/', query: 'Taiwan', // 补全默认值结构,和接口返回字段对齐 weather: { name: '', sys: { country: '' }, main: { temp: 17 }, weather: [ { main: '' } ] }, } }, methods: { async fetchWeather(e) { // 仅回车触发搜索 if (e.key !== 'Enter') return try { const res = await fetch(`${this.url_base}weather?q=${this.query}&units=metric&APPID=${this.api_key}`) if (!res.ok) throw new Error('请求失败') this.weather = await res.json() } catch (err) { console.error('获取天气失败:', err) // 出错场景不要把weather赋值为undefined,可以保留原有数据或弹出提示 } }, dateBuilder() { const d = new Date() const months = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"] const days = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"] return `${days[d.getDay()]} ${d.getDate()} ${months[d.getMonth()]} ${d.getFullYear()}` } }, mounted() { // 页面初始化时拉取默认城市天气 this.fetchWeather({key: 'Enter'}) } }
2. 给模板加安全访问兜底
Vue3原生支持可选链操作符,将模板中深层属性的访问改为可选链写法,从渲染层避免异常场景下的报错:
<div id="app" :class="{warm: weather?.main?.temp > 17}"> <main> <div class="search-box"> <input type="text" class="search-bar" placeholder="搜索城市..." v-model="query" @keypress="fetchWeather" /> </div> <div class="weather-wrap" v-if="weather?.main && weather?.weather?.length"> <div class="location-box"> <div class="location"> {{ weather.name }}, {{ weather.sys?.country }} </div> <div class="date"> {{ dateBuilder() }} </div> </div> <div class="weather-box"> <div class="temp"> {{ Math.round(weather.main.temp) }}°c </div> <div class="weather">{{ weather.weather[0].main }}</div> </div> </div> </main> </div>
额外优化提示
- 前端代码中直接明文存储OpenWeather的API Key会被滥用,正式上线建议通过后端代理转发接口请求
- 搜索输入逻辑建议加防抖,减少无效请求触发频率
- 可以补充loading态、请求失败提示,优化交互体验
内容的提问来源于stack exchange,提问作者RossenLin
相关产品推荐
相关产品推荐

