Vue2中页面数据响应的问题

问题:

  • 数据初始为 undefinednullv-if="data"false,DOM 不渲染。
  • 数据后来通过非响应式方式 被赋值(如直接 this.data = xxxdata 不在 data() 中声明)。

1.示例:

vue 复制代码
<template>
  <div>
    <!-- 第一次渲染时 list 是 undefined,v-if 为 false,p 标签不会进入 DOM -->
    <p v-if="list">第一项:{{ list[0] }}</p>
  </div>
</template>

<script>
export default {
  // 1. 没有声明 list → 非响应式
  data() {
    return {}; // 这里缺了 list
  },
  mounted() {
    // 2. 异步拿到数据
    setTimeout(() => {
      this.list = ['a', 'b']; // 3. 直接挂一个新属性
    }, 1000);
  },
};
</script>

2. Vue2 响应式核心(Object.defineProperty)

  1. 初始化阶段
    new Vue()_init()initState()initData() 时,会遍历 data() 返回对象的 key ,用 Object.definePropertygetter/setter 劫持
    只有此刻已存在的属性 才能被追踪;后续动态新增的属性默认不会被侦测。
  2. 依赖收集
    当渲染函数执行到 v-if="list" 时,如果 list 已经是响应式属性,会触发 getter,把当前的 渲染 watcher 收集到 Dep 中;将来 list 变化时触发 setterdep.notify() → 重新渲染。
    如果 list 压根儿就不是响应式,渲染 watcher 不会被收集 ,后续你哪怕改成 this.list = xxx不会触发任何更新
  3. 数组/对象新增属性
    Vue2 对数组的 索引长度 以及 对象新增属性无能为力 (性能权衡)。
    官方提供 Vue.set / vm.$set 作为补丁。

3. 生命周期与挂载时机(Vue2)

kotlin 复制代码
beforeCreate → initState 已执行完,data 已完成响应式转换  
created      → 此时可访问响应式数据,但 DOM 尚未生成  
beforeMount  → 模板已编译成 render 函数  
mounted      → 真实 DOM 已插入页面,且 第一次渲染 watcher 已执行完毕

关键点:

  • mounted 之后,渲染 watcher 已经执行过一次
  • 如果第一次执行时 list 不存在,那么 v-if="list" 计算为 false不会把 <p> 加入 VNode 树 ,也不会对 list 做依赖收集;
  • 后续你再加 this.list = xxx,由于 没有收集到依赖 ,Vue 认为"没人关心它",于是不会触发重新渲染

4. 常见"踩坑"场景(Vue2)

场景 触发条件 结果
1. data 里漏声明 data(){ return {} } 新增属性非响应式
2. 异步赋值 axios 回调里 this.list = res 同上
3. 对象嵌套新增 this.obj.newKey = x 需要 Vue.set
4. 数组索引赋值 this.arr[0] = x 需要 Vue.setsplice
5. JSON 解析后直接替换 this.data = JSON.parse(str) 只要原 key 已声明就安全,否则一样失效
6. v-if 条件里拼错字段名 v-if="lis" 始终 false,且不会报错

5. 源码级:为什么不会触发

  1. 渲染 watcher 的执行栈
    mountComponent()new Watcher(vm, updateComponent, noop, {}, true /* isRenderWatcher */)
    updateComponent = () => vm._update(vm._render(), hydrating)
    第一次 _render() 执行时,访问 listundefined不会进入 getter ,因此 Dep.target(当前渲染 watcher)不会被收集
  2. 后续赋值
    你执行 this.list = ['a','b'] 时,只是普通属性赋值,没有 setter 通知dep.notify() 永远不会调用,组件不会重新进入 patch 阶段

6. 现场定位技巧(Vue2)

  1. Vue-DevTools
    打开组件面板,看 list 字段是否带 Getter/Setter 图标;如果图标缺失 → 非响应式。
  2. 控制台打印
    console.log(this.$data) 看是否包含 list 字段;没有 → 漏声明。
  3. 强制刷新
    在赋值后手动 this.$forceUpdate(),如果页面立刻变化 → 100% 非响应式问题。

7. 正确写法(Vue2 最佳实践)

vue 复制代码
<template>
  <div>
    <p v-if="list && list.length">第一项:{{ list[0] }}</p>
    <button @click="load">加载</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 1. 先声明,哪怕是空数组/null
      list: null,
    };
  },
  methods: {
    load() {
      // 2. 异步拿到数据
      setTimeout(() => {
        // 3. 直接替换即可,响应式已建立
        this.list = ['a', 'b'];
      }, 500);
    },
  },
};
</script>

补充

  • 如果一开始不知道结构 ,可用 Vue.set 动态加顶级属性,但更推荐先声明
  • 对于对象嵌套 使用 this.$set(this.obj, 'newKey', value)
  • 对于数组 使用 this.$set(this.arr, index, newVal)this.arr.splice(index, 1, newVal)

8. 进阶:万一已经踩坑,如何补救

场景 补救方案
1. 漏声明 this.$set(this, 'list', value) 把顶级属性补成响应式;后续就能正常追踪
2. 大量动态字段 Object.assign 前先 this.data = Object.assign({}, this.data, newObj),只要原对象 key 已声明即可
3. 必须强制刷新 this.$forceUpdate()(暴力,不推荐长期使用)
4. 监听异步数据 watchcomputed 把异步结果映射到已声明的字段

9. 总结

Vue2 的响应式只在 初始化 data() 时存在的属性 上生效;

第一次渲染时如果字段不存在,渲染 watcher 不会收集依赖

后续你再怎么 this.xxx = ...不会触发更新 ,看起来就像 v-if 失效。
先声明、后赋值,是 Vue2 的铁律。

相关推荐
知识分享小能手2 小时前
uni-app 入门学习教程,从入门到精通,uni-app基础扩展 —— 详细知识点与案例(3)
vue.js·学习·ui·微信小程序·小程序·uni-app·编程
demi_meng3 小时前
reactNative 遇到的问题记录
javascript·react native·react.js
MC丶科3 小时前
【SpringBoot 快速上手实战系列】5 分钟用 Spring Boot 搭建一个用户管理系统(含前后端分离)!新手也能一次跑通!
java·vue.js·spring boot·后端
千码君20164 小时前
React Native:从react的解构看编程众多语言中的解构
java·javascript·python·react native·react.js·解包·解构
lijun_xiao20096 小时前
前端最新Vue2+Vue3基础入门到实战项目全套教程
前端
90后的晨仔6 小时前
Pinia 状态管理原理与实战全解析
前端·vue.js
杰克尼6 小时前
JavaWeb_p165部门管理
java·开发语言·前端
EndingCoder6 小时前
WebSocket实时通信:Socket.io
服务器·javascript·网络·websocket·网络协议·node.js
90后的晨仔6 小时前
Vue3 状态管理完全指南:从响应式 API 到 Pinia
前端·vue.js
90后的晨仔6 小时前
Vue 内置组件全解析:提升开发效率的五大神器
前端·vue.js