vue.js 更新数据时,出现数据更新,界面没有更新的情况【普通对象,不包含数组】

发布于:2025-05-19 ⋅ 阅读:(20) ⋅ 点赞:(0)

问题出现

代码如下:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>列表</title>
		<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
		<style>
			body {
				padding: 20px;
			}
		</style>
	</head>
	<body>
		<div id="app">
			<div>
					<button @click="updateData()">更新张伟信息</button>
			</div>
			<div style="margin-top: 20px;">
				<div v-for="(item,index) in persons" :key="item.id">
					<span>{{item.name}}</span>
					<span>-</span>
					<span>{{item.age}}</span>
					<span>-</span>
					<span>{{item.gender}}</span>
				</div>

			</div>
		</div>
	</body>
	<script>
		var vm = new Vue({
			el: '#app',
			data() {
				return {
					persons: [{
							id: 1,
							name: '张伟',
							age: 32,
							gender: '男'
						},
						{
							id: 2,
							name: '王芳',
							age: 28,
							gender: '女'
						},
						{
							id: 3,
							name: '李娜',
							age: 25,
							gender: '女'
						},
						{
							id: 4,
							name: '赵强',
							age: 40,
							gender: '男'
						},
						{
							id: 5,
							name: '刘洋',
							age: 30,
							gender: '男'
						},
						{
							id: 6,
							name: '陈静',
							age: 27,
							gender: '女'
						},
						{
							id: 7,
							name: '杨磊',
							age: 35,
							gender: '男'
						},
						{
							id: 8,
							name: '黄丽',
							age: 26,
							gender: '女'
						},
						{
							id: 9,
							name: '吴杰',
							age: 38,
							gender: '男'
						},
						{
							id: 10,
							name: '周敏',
							age: 29,
							gender: '女'
						}
					],
					
				}
			},
			mounted() {

			},
			methods:{
				updateData(){
					this.persons[0]={
							id: 1,
							name: '张伟111',
							age: 32,
							gender: '男'
						}
						
					console.log(this.persons)
				},
			},
			
		})
	</script>
</html>

按照希望的的情况,点击按钮的时候,第一行数据的name 应该从张伟 变成 张伟111 。但是实际情况?
在这里插入图片描述

数据更新的原理

Vue(尤其是 Vue 2 和 Vue 3)对数据变化的监测机制是其响应式系统的核心,理解这一机制有助于更高效地开发和调试 Vue 应用。下面将分别解释 Vue 2 和 Vue 3 的监测原理。

Vue 2 的数据监测原理(基于 Object.defineProperty)

核心原理:Object.defineProperty

Vue 2 通过递归地遍历对象的属性,并使用 Object.defineProperty 将其转化为“getter”和“setter”,从而拦截对数据的读取和修改。

Object.defineProperty(obj, 'key', {
  get() {
    // 收集依赖
    return value;
  },
  set(newVal) {
    // 触发更新
    value = newVal;
  }
});

Observer 模式

Vue 2 内部构建了一个 Observer 类,对每个对象进行监听。每当数据被读取时,收集当前组件(Watcher),当数据被修改时通知这些 Watcher 重新执行。

缺点与局限性

  • 新增属性无响应:必须使用 Vue.set 才能让新属性响应式。
  • 数组监听有限:只能监听数组的变异方法(如 push/pop/splice),无法拦截通过索引直接设置值(如 arr[0] = 1)。

Vue 3 的数据监测原理(基于 Proxy)

核心原理:Proxy

Vue 3 放弃了 Object.defineProperty,转而使用现代浏览器支持的 Proxy。它可以直接监听整个对象的操作,包括属性读取、设置、删除等,甚至能监听数组索引和新增属性。

const proxy = new Proxy(target, {
  get(target, key, receiver) {
    // 依赖收集
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    // 派发更新
    return Reflect.set(target, key, value, receiver);
  }
});

优势

  • 支持 数组索引 和 新增属性。
  • 不再需要递归地遍历所有属性(延迟代理)。
  • 可以更好地检测对象何时被访问或修改。

响应式系统的两个重要角色

Dep(依赖收集)

每个响应式属性都有一个依赖管理器(Dep),用来记录哪些 Watcher(组件或计算属性)使用了这个数据。

Watcher(依赖执行者)

当组件使用响应式数据时,会生成对应的 Watcher,并自动订阅这个数据的变化;当数据改变时,这些 Watcher 会被通知,从而触发组件更新或重新计算。

总结对比

特性 Vue 2 (defineProperty) Vue 3 (Proxy)
新增属性响应式 ❌ 需手动 Vue.set ✅ 自动监听
数组索引变更 ❌ 无法监听 ✅ 支持
性能 递归劣化性能 延迟代理,性能优
API 支持度 老浏览器兼容好 需现代浏览器

如果你想深入调试 Vue 的响应式系统,可以尝试查看源码中的以下文件:

  • Vue 2:src/core/observer/index.js
  • Vue 3:@vue/reactivity 包下的 reactive.ts

修改之后的代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>列表</title>

		<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
		<style>
			body {
				padding: 20px;
			}
		</style>
	</head>
	<body>
		<div id="app">
			
			<div>
					<button @click="updateData()">更新张伟信息</button>
			</div>
			

			<div style="margin-top: 20px;">
				<div v-for="(item,index) in persons" :key="item.id">
					<span>{{item.name}}</span>
					<span>-</span>
					<span>{{item.age}}</span>
					<span>-</span>
					<span>{{item.gender}}</span>
				</div>

			</div>
		</div>
	</body>
	<script>
		var vm = new Vue({
			el: '#app',
			data() {
				return {
					persons: [{
							id: 1,
							name: '张伟',
							age: 32,
							gender: '男'
						},
						{
							id: 2,
							name: '王芳',
							age: 28,
							gender: '女'
						},
						{
							id: 3,
							name: '李娜',
							age: 25,
							gender: '女'
						},
						{
							id: 4,
							name: '赵强',
							age: 40,
							gender: '男'
						},
						{
							id: 5,
							name: '刘洋',
							age: 30,
							gender: '男'
						}
					],
					
				}
			},
			mounted() {

			},
			methods:{
				updateData(){
					this.$set(this.persons, 0, {
					  id: 1,
					  name: '张伟111',
					  age: 32,
					  gender: '男'
					})
				},
				
				
			},
			
		})
	</script>
</html>

网站公告

今日签到

点亮在社区的每一天
去签到