一段示例代码,版本是Vue 2.6.10。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/javascript">
const vm = new Vue({
el: "#root",
data: {
name: "张三",
age: 18,
},
});
</script>
</body>
</html>
首先打印一下vm,观察一下age和name属性(…) ,弹出提示invoke property getter,意思是‘调用属性getter’,也就是说我们看到的name属性值,是被Object.defineProperty()中getter所获取的值。
获取name值的链路就是,访问vm.name会调用Object.defineProperty()中的getter,从我们定义的data对象中(代理中)获取(读)name值,又或是修改时,从data中修改(写)值。
现在可以来验证一下以上链路:
- 读:用户访问vm.name---->触发getter方法---->将data中name赋值给vm.name
- 写:用户修改vm.name---->触发setter方法---->将data中name修改后赋值给vm.name
getter是比较好验证的,只要打印出来的数据和当时定义的数据一致,就可以确定是数据代理。(??代理了data数据)
接下来验证setter
如何判断vm.data.name值有没有发生变化?直接打印data.name是获取不到值的,原因是data只是vm的一个options(配置项),不是全局都可以调用的变量,同理,通过vm.data也是获取不到值
我们再返回去打印一下vm,发现一个类似于data的_data数据,但是显示的是’{ob:Observer}'展开发现_data与data中定义的数据一致。
在这里可以先跳过{ob:Observer}(后面研究),来分析一下‘可能性’?这会不会是,vue把data中的数据,通过vm._data=options.data(可以把options堪称vue的配置项,比如options.el)定义到_data上?
再上一段代码,把data抽离出来,方便验证vm._data=options.data=data这三个等于。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<p>姓名:{{name}}</p>
<p>年龄:{{age}}</p>
</div>
<script type="text/javascript">
let data = {
name: "张三",
age: 18,
},
const vm = new Vue({
el: "#root",
data
});
</script>
</body>
</html>
打印如下,可以发现vm._data 是全等于data的
我们再打印vm._data.name和vm.name,结果如下,也就是说,其实访问vm.name(访问vm.name也就是从data中取name值,毕竟代码中只有一处定义了name)和访问vm._data.name效果是一样的,只不过更多的是选择vm.name这种方式获取data中的数据。
<body>
<div id="root">
<p>姓名:{{_data.name}}</p>
<p>年龄:{{_data.age}}</p>
</div>
<script type="text/javascript">
const vm = new Vue({
el: "#root",
data: {
name: "张三",
age: 18,
},
});
</script>
</body>
接着再去修改vm._data.name的数据,可以发现页面发生相应的变化。
这时候再通过vm获取name(实际上是data.name)值,会发现data中的数据被修改了
现在再来验证setter,通过vm修改name,观察data中的name是否会发生改变,
如下图,通过vm修改name值为王五,实际上是调用了setter方法,修改了data中的数据,被vue监测到,页面同步更新(数据代理)。
图示
最后说明一点,通过展开vm._data,会发现里面套了娃,实际上里面并不是数据代理,而是vue做了一层数据劫持,以方便实现数据监听,最终达到数据响应式的功能。