🎉 Vue2 数组变化监测原理与实战指南
This commit is contained in:
parent
aa00c07eb0
commit
24786f25f4
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script src="js/vue@2.7.16.js"></script>
|
||||
<title>Vue监测数据的原理_数组</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1>列表过滤</h1>
|
||||
<button @click="changeList">修改</button>
|
||||
|
||||
<ul>
|
||||
<li v-for="(item,index) in list" :key="index">
|
||||
{{item.name}}---{{item.age}}---{{item.sex}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
const vm = new Vue({
|
||||
el: "#app",
|
||||
data: {
|
||||
list: [
|
||||
{ id: "001", name: "Bunny-001", age: 19, sex: "男" },
|
||||
{ id: "002", name: "Bunny-002", age: 16, sex: "男" },
|
||||
{ id: "003", name: "Bunny-003", age: 20, sex: "女" },
|
||||
{ id: "004", name: "Bunny-004", age: 18, sex: "男" },
|
||||
{ id: "005", name: "Bunny-005", age: 19, sex: "女" },
|
||||
],
|
||||
},
|
||||
methods: {
|
||||
changeList() {
|
||||
// 无法修改数组,显示不出来修改
|
||||
// this.list[0] = { id: "002", name: "Bunny-002", age: 16, sex: "男" };
|
||||
|
||||
// 可以这样修改
|
||||
// 详细参考 https://v2.cn.vuejs.org/v2/guide/list.html#%E5%8F%98%E6%9B%B4%E6%96%B9%E6%B3%95
|
||||
// this.list.splice(0, 1, {
|
||||
// id: "0021",
|
||||
// name: "Bunny-0021",
|
||||
// age: 16,
|
||||
// sex: "男",
|
||||
// });
|
||||
|
||||
// 或者这样修改
|
||||
// Vue.$set(this.list, 0, {
|
||||
this.$set(this.list, 0, {
|
||||
id: "0021",
|
||||
name: "Bunny-0021",
|
||||
age: 16,
|
||||
sex: "男",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</html>
|
|
@ -0,0 +1,175 @@
|
|||
# Vue2 数组变化监测原理与实战指南
|
||||
|
||||
## 一、核心原理
|
||||
|
||||
Vue2 通过重写数组的变异方法实现对数组变化的监测,这是其响应式系统的关键部分。当直接通过索引修改数组元素时(如 `arr[0] = newValue`),Vue 无法自动检测到这种变化。
|
||||
|
||||
## 二、代码示例解析
|
||||
|
||||
```html
|
||||
<div id="app">
|
||||
<h1>列表更新演示</h1>
|
||||
<button @click="changeList">修改数组</button>
|
||||
<ul>
|
||||
<li v-for="(item,index) in list" :key="item.id">
|
||||
{{item.name}}---{{item.age}}---{{item.sex}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const vm = new Vue({
|
||||
el: "#app",
|
||||
data: {
|
||||
list: [
|
||||
{ id: "001", name: "Bunny-001", age: 19, sex: "男" },
|
||||
// ...其他初始数据
|
||||
],
|
||||
},
|
||||
methods: {
|
||||
changeList() {
|
||||
// ❌ 无效的修改方式
|
||||
// this.list[0] = { id: "002", name: "Bunny-002", age: 16, sex: "男" };
|
||||
|
||||
// ✅ 方式1:使用变异方法
|
||||
this.list.splice(0, 1, {
|
||||
id: "0021",
|
||||
name: "Bunny-0021",
|
||||
age: 16,
|
||||
sex: "男",
|
||||
});
|
||||
|
||||
// ✅ 方式2:使用Vue.set/$set
|
||||
// Vue.$set(this.list, 0, {
|
||||
this.$set(this.list, 0, {
|
||||
id: "0021",
|
||||
name: "Bunny-0021",
|
||||
age: 16,
|
||||
sex: "男",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
## 三、响应式数组操作方法
|
||||
|
||||
### 1. Vue 包装的变异方法
|
||||
|
||||
Vue 重写了以下数组方法,使其能触发视图更新:
|
||||
|
||||
- `push()` / `pop()`
|
||||
- `shift()` / `unshift()`
|
||||
- `splice()`
|
||||
- `sort()` / `reverse()`
|
||||
|
||||
### 2. 特殊场景处理
|
||||
|
||||
| 场景 | 正确方法 | 错误方法 |
|
||||
| -------- | ------------------------------- | ----------------------- |
|
||||
| 修改元素 | `Vue.set(arr, index, newValue)` | `arr[index] = newValue` |
|
||||
| 添加元素 | `arr.splice(index, 0, newItem)` | `arr[index] = newItem` |
|
||||
| 删除元素 | `arr.splice(index, 1)` | `delete arr[index]` |
|
||||
|
||||
### 3. 注意事项
|
||||
|
||||
- 使用 `filter()`、`concat()` 等非变异方法时,需要用返回的新数组替换原数组
|
||||
- 嵌套数组需要深度观测
|
||||
|
||||
## 四、原理深入
|
||||
|
||||
### 1. 实现机制
|
||||
|
||||
Vue 通过以下步骤实现数组响应式:
|
||||
|
||||
1. 拦截数组原型方法
|
||||
2. 在方法执行后通知依赖更新
|
||||
3. 对新增元素进行响应式处理
|
||||
|
||||
### 2. 源码关键部分
|
||||
|
||||
```javascript
|
||||
// 简化版的数组响应式实现
|
||||
const arrayProto = Array.prototype;
|
||||
const arrayMethods = Object.create(arrayProto);
|
||||
|
||||
const methodsToPatch = [
|
||||
"push",
|
||||
"pop",
|
||||
"shift",
|
||||
"unshift",
|
||||
"splice",
|
||||
"sort",
|
||||
"reverse",
|
||||
];
|
||||
|
||||
methodsToPatch.forEach(function (method) {
|
||||
const original = arrayProto[method];
|
||||
def(arrayMethods, method, function mutator(...args) {
|
||||
const result = original.apply(this, args);
|
||||
const ob = this.__ob__;
|
||||
let inserted;
|
||||
switch (method) {
|
||||
case "push":
|
||||
case "unshift":
|
||||
inserted = args;
|
||||
break;
|
||||
case "splice":
|
||||
inserted = args.slice(2);
|
||||
break;
|
||||
}
|
||||
if (inserted) ob.observeArray(inserted);
|
||||
ob.dep.notify(); // 通知更新
|
||||
return result;
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 五、最佳实践
|
||||
|
||||
### 1. 性能优化
|
||||
|
||||
- 大数据量操作时使用 `Vue.nextTick` 批量更新
|
||||
- 避免在模板中直接操作复杂数组计算
|
||||
|
||||
### 2. 代码规范
|
||||
|
||||
- 统一使用 `Vue.set` 或 `splice` 修改数组
|
||||
- 为 `v-for` 设置合适的 `key`
|
||||
|
||||
### 3. 扩展方法
|
||||
|
||||
```javascript
|
||||
// 安全的数组修改方法
|
||||
function safeArrayUpdate(arr, index, newValue) {
|
||||
if (Array.isArray(arr)) {
|
||||
return Vue.set(arr, index, newValue);
|
||||
}
|
||||
throw new Error("Target is not an array");
|
||||
}
|
||||
```
|
||||
|
||||
## 六、常见问题解决方案
|
||||
|
||||
1. **为什么我的数组修改不生效?**
|
||||
|
||||
- 检查是否使用了 Vue 能检测的变异方法
|
||||
- 确认没有直接修改数组长度(如 `arr.length = 0`)
|
||||
|
||||
2. **如何强制更新数组?**
|
||||
|
||||
```javascript
|
||||
// 方法1:使用空splice触发更新
|
||||
this.list.splice();
|
||||
|
||||
// 方法2:使用Vue.set
|
||||
Vue.set(this.list, 0, this.list[0]);
|
||||
```
|
||||
|
||||
3. **对象数组中的对象属性修改**
|
||||
- 直接修改对象属性可以触发更新(因为对象是响应式的)
|
||||
```javascript
|
||||
// 这是有效的
|
||||
this.list[0].name = "new name";
|
||||
```
|
Loading…
Reference in New Issue