147 lines
3.6 KiB
Markdown
147 lines
3.6 KiB
Markdown
# Vue2 数据代理机制详解
|
||
|
||
## 一、核心概念
|
||
|
||
Vue2 中的数据代理是指 Vue 实例(vm)代理其 `data` 对象中属性的访问和修改操作。通过这种机制,我们可以直接通过 `vm.xxx` 访问 `data` 中的属性,而不需要写 `vm._data.xxx`。
|
||
|
||
## 二、代码示例解析
|
||
|
||
```html
|
||
<div id="app">
|
||
Vue中的数据代理: <span>{{name}}</span>
|
||
</div>
|
||
|
||
<script>
|
||
const vm = new Vue({
|
||
el: "#app",
|
||
data: {
|
||
name: "代理..."
|
||
}
|
||
});
|
||
</script>
|
||
```
|
||
|
||
## 三、实现原理
|
||
|
||
### 1. 数据存储结构
|
||
- 原始 `data` 对象被存储在 `vm._data` 属性中
|
||
- 通过 `Object.defineProperty` 将 `data` 的属性代理到 Vue 实例上
|
||
|
||
### 2. 验证方法
|
||
```javascript
|
||
// 验证代理关系
|
||
const externalData = { name: "代理..." };
|
||
const vm = new Vue({
|
||
el: "#app",
|
||
data: externalData
|
||
});
|
||
|
||
console.log(vm._data === externalData); // true
|
||
console.log(vm.name === vm._data.name); // true
|
||
```
|
||
|
||
### 3. 代理实现机制
|
||
Vue 内部大致执行了以下操作:
|
||
```javascript
|
||
// 伪代码展示代理原理
|
||
Object.keys(data).forEach(key => {
|
||
Object.defineProperty(vm, key, {
|
||
get() {
|
||
return vm._data[key];
|
||
},
|
||
set(value) {
|
||
vm._data[key] = value;
|
||
}
|
||
});
|
||
});
|
||
```
|
||
|
||
## 四、数据流图示
|
||
|
||
```
|
||
模板访问
|
||
↓
|
||
vm.name (代理访问)
|
||
↓
|
||
vm._data.name (实际数据存储)
|
||
↓
|
||
数据劫持 (响应式系统)
|
||
```
|
||
|
||
## 五、关键特性
|
||
|
||
| 特性 | 说明 | 示例 |
|
||
| ---------- | ---------------------- | ------------------------------ |
|
||
| 直接访问 | 省略 `_data` 前缀 | `vm.name` 代替 `vm._data.name` |
|
||
| 响应式绑定 | 代理属性也是响应式的 | 修改 `vm.name` 会触发视图更新 |
|
||
| 数据隔离 | `_data` 保存原始数据 | 防止直接修改破坏响应式 |
|
||
| 统一入口 | 提供一致的数据访问方式 | 简化模板和方法的编写 |
|
||
|
||
## 六、与数据劫持的关系
|
||
|
||
1. **数据代理**:
|
||
- 解决的是访问便捷性问题(`vm.xxx` vs `vm._data.xxx`)
|
||
- 通过 `Object.defineProperty` 实现属性转发
|
||
|
||
2. **数据劫持**:
|
||
- 解决的是响应式问题(数据变化触发视图更新)
|
||
- 在 `vm._data` 上实现,通过重写 getter/setter
|
||
|
||
3. **协作流程**:
|
||
- 模板访问 `{{name}}` → 触发代理 getter
|
||
- 代理 getter 访问 `_data.name` → 触发劫持 getter
|
||
- 建立依赖收集关系
|
||
|
||
## 七、注意事项
|
||
|
||
1. **新增属性**:
|
||
- 直接 `vm.newProp = value` 不会成为响应式
|
||
- 必须使用 `Vue.set` 或预先声明
|
||
|
||
2. **性能影响**:
|
||
- 每个代理属性都会产生一定的内存开销
|
||
- 大型项目应考虑分模块管理数据
|
||
|
||
3. **调试技巧**:
|
||
- 通过 `vm._data` 查看原始数据
|
||
- 使用 Vue Devtools 观察数据变化
|
||
|
||
4. **与 Vue3 区别**:
|
||
- Vue3 使用 Proxy 实现更完善的代理
|
||
- 可以检测属性添加/删除操作
|
||
|
||
## 八、扩展应用
|
||
|
||
### 1. 自定义代理逻辑
|
||
```javascript
|
||
// 在Vue实例上添加自定义代理
|
||
created() {
|
||
Object.defineProperty(this, 'fullName', {
|
||
get() {
|
||
return `${this.firstName} ${this.lastName}`;
|
||
},
|
||
set(value) {
|
||
const parts = value.split(' ');
|
||
this.firstName = parts[0];
|
||
this.lastName = parts[1] || '';
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
### 2. 代理模式应用
|
||
```javascript
|
||
// 实现组件间的数据代理
|
||
const proxyMixin = {
|
||
methods: {
|
||
proxy(prop) {
|
||
return {
|
||
get: () => this[prop],
|
||
set: (val) => this[prop] = val
|
||
};
|
||
}
|
||
}
|
||
};
|
||
```
|
||
|