Vue报Cannot read properties of null (reading 'find')错误求解
问题背景
我是JavaScript和Vue.js初学者,目前通过动手开发应用熟悉技术运行逻辑,参考教程开发新闻示例应用时,已实现从纽约时报API拉取数据的功能,但运行时控制台抛出报错,以下是项目代码与报错相关信息,需要明确错误产生原因与修复方案。
项目代码
Index.html
<div class="container" id="app"> <h3 class="text-center">VueNews</h3> <div class="row" v-for="posts in processedPosts"> <div class="columns large-3 medium-6" v-for="post in posts"> <div class="card"> <div class="card-divider"> {{ post.title }} </div> <a :href="post.url" target="_blank"><img :src="post.image_url"></a> <div class="card-section"> <p>{{ post.abstract }}</p> </div> </div> </div> </div> </div>
main.js
const NYTBaseURL = 'https://api.nytimes.com/svc/topstories/v2/'; const ApiKey = 'MyApiKey'; function buildUrl(url) { return NYTBaseURL + url + '.json?api-key=' + ApiKey } const app = new Vue({ el: '#app', data: { results: [] }, mounted() { this.getPosts('home') }, methods: { getPosts(section) { let url = buildUrl(section) axios.get(url).then((response) => { this.results = response.data.results }).catch(error => { console.log(error) }) } }, computed: { processedPosts() { let posts = this.results // Add image_url_attribute posts.map(post => { let imgObj = post.multimedia.find(media => media.format === 'superJumbo') post.image_url = imgObj ? imgObj.url: 'http://placehold.it/300x200?text=N/A' }) // Put Array into Chunks let i, j, chunkedArray = [], chunk = 4 for (i = 0, j = 0; i< posts.length; i += chunk, j++) { chunkedArray[j] = posts.slice(i, i+chunk) } return chunkedArray } } })
错误原因
控制台抛出的是Cannot read property 'find' of undefined类型错误,触发原因如下:
- 纽约时报API返回的部分新闻条目,
multimedia字段可能为null、空值或非数组类型,代码未做任何判断直接调用post.multimedia.find(),相当于在undefined/null值上执行方法,直接触发报错。 - 计算属性
processedPosts直接遍历修改this.results里的原响应式对象,会触发Vue响应式更新,导致计算属性重复执行,放大报错概率。 - 接口请求为异步逻辑,页面首次渲染时
results为空数组,代码未做空值兜底,接口返回异常结构时会直接导致页面崩溃。 - 代码中
ApiKey为占位值,需替换为自己申请的有效纽约时报API密钥,无效密钥会导致接口返回错误格式数据,也会触发同类报错。
修复方案
- 给
multimedia字段加类型判断,确认其为数组后再调用find方法,不符合条件直接使用占位图 - 计算属性中先对原数组做浅拷贝,不要直接修改原响应式数据,避免不必要的重复渲染
- (可选优化)给两层
v-for绑定唯一key值,消除Vue渲染警告
修正后代码
main.js 计算属性部分修正
computed: { processedPosts() { // 浅拷贝原数组,避免直接修改原响应式数据产生副作用 let posts = [...this.results] // 拼接图片地址,增加空值兜底 posts.map(post => { let imgObj = null // 先判断multimedia是否为有效数组 if (Array.isArray(post.multimedia)) { imgObj = post.multimedia.find(media => media.format === 'superJumbo') } post.image_url = imgObj ? imgObj.url : 'http://placehold.it/300x200?text=N/A' }) // 数组分块 let i, j, chunkedArray = [], chunk = 4 for (i = 0, j = 0; i < posts.length; i += chunk, j++) { chunkedArray[j] = posts.slice(i, i + chunk) } return chunkedArray } }
Index.html 渲染部分优化(可选)
<div class="container" id="app"> <h3 class="text-center">VueNews</h3> <!-- 增加key绑定 --> <div class="row" v-for="(posts, rowIndex) in processedPosts" :key="rowIndex"> <!-- 增加key绑定 --> <div class="columns large-3 medium-6" v-for="post in posts" :key="post.url"> <div class="card"> <div class="card-divider"> {{ post.title }} </div> <a :href="post.url" target="_blank"><img :src="post.image_url"></a> <div class="card-section"> <p>{{ post.abstract }}</p> </div> </div> </div> </div> </div>
内容的提问来源于stack exchange,提问作者Ryoichi
相关产品推荐
相关产品推荐

