You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3访问JSON数据元素出现'undefined'错误排查

三级嵌套v-for渲染JSON数据时出现Cannot read properties of undefined (reading 'threadId')错误

错误信息

Cannot read properties of undefined (reading 'threadId')
TypeError: Cannot read properties of undefined (reading 'threadId')
at eval (webpack-internal:///./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[0]!./node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[3]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/components/PageHome.vue?vue&type=template&id=c0c75584:57:318)
at renderList (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:2594:16)
at eval (webpack-internal:///./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[0]!./node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[3]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/components/PageHome.vue?vue&type=template&id=c0c75584:49:450)
at renderList (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:2594:16)
at Proxy.render (webpack-internal:///./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[0]!./node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[3]!./node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/components/PageHome.vue?vue&type=template&id=c0c75584:45:343)
at renderComponentRoot (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:887:38)
at ReactiveEffect.componentUpdateFn [as fn] (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:5064:46)
at ReactiveEffect.run (webpack-internal:///./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js:216:19)
at instance.update (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:5161:51)
at setupRenderEffect (webpack-internal:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js:5169:5)

相关数据结构

{
  "categories": [
    {
      "forums": [
        "-KpOx5Y4AqRr3sB4Ybwj",
        "-KsjO4_W3W9Q2Z2UmuPr"
      ],
      "name": "Feedback & Information",
      "slug": "feedback-and-information",
      "id": "-KpR7vRkiRPpbUd_TVAR"
    },
    ...
  ],
  "forums": [
    {
      "categoryId": "-KpR7vRkiRPpbUd_TVAR",
      "description": "Important announcements about the forum.",
      "lastPostId": "-KxxU6bYWl-6Xnu9g-56",
      "name": "Announcements",
      "slug": "announcements",
      "threads": [
        "-KvgjI6bfbWYlbYV5ndr"
      ],
      "id": "-KpOx5Y4AqRr3sB4Ybwj"
    },
    ...
  ],
  "threads": [
    {
      "contributors": [
        "FsCDAk9w8NeXEceLV87arpsXjnQ2",
        "L664y3qZSubDbT1R6npC0EEybJ73",
        "VXjpr2WHa8Ux4Bnggym8QFLdv5C3"
      ],
      "firstPostId": "-KsjWehQ--apjDBwSBCZ",
      "forumId": "-KsjPat5MWCx-dkjNVg8",
      "lastPostAt": 1594040497,
      "lastPostId": "-KsjnAAjzfpznUVh4DSa",
      "posts": [
        "-KsjWehQ--apjDBwSBCZ",
        "-KsjefUiIuNkJieBc5Rq",
        "-KsjfvciJhJnebrL4Iw7",
        "-KsjnAAjzfpznUVh4DSa"
      ],
      "publishedAt": 1594035908,
      "slug": "what-kind-of-pet-do-you-have",
      "title": "What kind of pet do you have?",
      "userId": "ALXhxjwgY9PinwNGHpfai6OWyDu2",
      "id": "-KsjWehQ--apjDBwSBCY"
    },
    ...
  ],
  "posts": [
    {
      "edited": {
        "at": 1594037546,
        "by": "ALXhxjwgY9PinwNGHpfai6OWyDu2",
        "moderated": false
      },
      "publishedAt": 1594035908,
      "text": "I'm curious to know what kind...",
      "threadId": "-KsjWehQ--apjDBwSBCY",
      "userId": "ALXhxjwgY9PinwNGHpfai6OWyDu2",
      "id": "-KsjWehQ--apjDBwSBCZ"
    },
    ...
  ],
  "users": [
    {
      "avatar": "https://...",
      "email": "email@gmail.com",
      "lastVisitAt": 1594772078,
      "name": "Chris Fritz",
      "isModerator": true,
      "registeredAt": 1594632260,
      "username": "chrisvfritz",
      "usernameLower": "chrisvfritz",
      "id": "38St7Q8Zi2N1SPa5ahzssq9kbyp1"
    },
    ...
  ]
}

对应的Vue代码

<template>
<div class="col-full">
<div v-for="category in categories" :key="category.id" class="forum-list">

            <h2 class="list-title">
                <a href="category.html">{{ category.name }}</a>
            </h2>
            <div v-for="forumId in category.forums" :key="forumId" class="forum-listing">
                <div class="forum-details">
                    <a class="text-xlarge" href="forum.html">{{ forumById(forumId).name }}</a>
                    <p>{{ forumById(forumId).description }}</p>
                </div>
    
                <div class="threads-count">
                    <p><span class="count">{{ 7 }}</span> thread</p>
                </div>
    
                <div class="last-thread">
                    <img class="avatar" :src=postById(forumById(forumId).lastPostId).avatar alt="">
                    <div class="last-thread-details">
                        <a href="thread.html">{{ threadById(postById(forumById(forumId).lastPostId).threadId).title }}</a>
                    </div>
                </div>
                
            </div>
        </div>

</template>

<script setup>
import sourceData from '@/data.json'
import { ref } from 'vue'

const categories = ref(sourceData.categories)
const forums = ref(sourceData.forums)
const threads = ref(sourceData.threads)
const posts = ref(sourceData.posts)
const users = ref(sourceData.users)

function forumById(forumId) {
return forums.value.find(f => f.id === forumId)
}

function threadById(threadId) {
return threads.value.find(t => t.id === threadId)
}

function postById(postId) {
return posts.value.find(p => p.id === postId)
}

function userById(userId) {
return users.value.find(u => u.id === userId)
}

</script>

<style></style>

问题现象

错误触发在模板第21行:threadById(postById(forumById(forumId).lastPostId).threadId).title,原因是postById(forumById(forumId).lastPostId)可能返回undefined,导致无法读取threadId属性。但用三元表达式包裹后({{ postById(forumById(forumId).lastPostId) ? threadById(postById(forumById(forumId).lastPostId).threadId).title : "oops" }}),内容能正常显示。

解决办法

1. 使用可选链操作符(推荐)

Vue模板支持ES6+的可选链操作符?.,可以简化空值判断,避免报错:

<a href="thread.html">{{ threadById(postById(forumById(forumId).lastPostId)?.threadId)?.title || "oops" }}</a>

同时头像部分也需要处理:

<img class="avatar" :src="postById(forumById(forumId).lastPostId)?.avatar || ''" alt="">

2. 优化工具函数,返回默认空对象

修改工具函数,当找不到对应数据时返回空对象,避免后续属性访问报错:

function forumById(forumId) {
  return forums.value.find(f => f.id === forumId) || {}
}

function threadById(threadId) {
  return threads.value.find(t => t.id === threadId) || {}
}

function postById(postId) {
  return posts.value.find(p => p.id === postId) || {}
}

这样即使找不到数据,后续访问属性也不会报错,再配合空值合并运算符显示默认内容:

<a href="thread.html">{{ threadById(postById(forumById(forumId).lastPostId).threadId).title || "oops" }}</a>

3. 提前计算嵌套数据(性能更优)

在组件初始化或数据更新时,提前将嵌套数据关联好,避免模板中多次调用查找函数,同时减少空值判断:

import { computed } from 'vue'

const enrichedCategories = computed(() => {
  return categories.value.map(category => ({
    ...category,
    forums: category.forums.map(forumId => {
      const forum = forumById(forumId)
      if (!forum) return {}
      const lastPost = postById(forum.lastPostId)
      const lastThread = lastPost ? threadById(lastPost.threadId) : {}
      return {
        ...forum,
        lastPost,
        lastThread
      }
    })
  }))
})

然后模板中直接使用计算好的数据:

<div v-for="category in enrichedCategories" :key="category.id" class="forum-list">
  <!-- ... -->
  <div v-for="forum in category.forums" :key="forum.id" class="forum-listing">
    <div class="forum-details">
      <a class="text-xlarge" href="forum.html">{{ forum.name }}</a>
      <p>{{ forum.description }}</p>
    </div>
    <!-- ... -->
    <div class="last-thread">
      <img class="avatar" :src="forum.lastPost?.avatar || ''" alt="">
      <div class="last-thread-details">
        <a href="thread.html">{{ forum.lastThread.title || "oops" }}</a>
      </div>
    </div>
  </div>
</div>

这种方式不仅避免了模板中的复杂嵌套调用,还提升了渲染性能,因为计算属性会缓存结果。

内容的提问来源于stack exchange,提问作者robdovDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 21:39:51