demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>demo</title>
</head>
<style></style>
<body></body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
var data = {
foo: "000"
};
var vm = new Vue({
data: data
});
console.warn(data.foo); //输出"000"
console.warn(vm.foo); //输出"000"
console.warn(vm._data.foo); //输出"000"
console.warn(vm.$data.foo); //输出"000"
vm.foo = "111";
console.warn(data.foo); //输出"111"
console.warn(vm.foo); //输出"111"
data.foo = "222";
console.warn(data.foo); //输出"222"
console.warn(vm.foo); //输出"222"
</script>
</html>
上面例子中 不管是修改了data.foo还是vm.foo,另一个值都会跟着改变。两个直接锁死?
var data = {
foo:"000"
};
Object.freeze(data);
var vm = new Vue({
data:data
});
data.foo = "111";
console.log(vm.foo);//输出"000"
但是一旦Object.freeze(data),vm.foo和data.foo却就此陌路了,直接互不相干。
从源码说起
function Vue(options){
this._init(options);
}
initMixin(Vue);
function initMixin(Vue){
Vue.prototype._init = function(options){
var vm = this;
vm.$options = mergeOptions(vm.constructor,options || {});
initState(vm);
return vm;
}
}
function initState(vm){
var opts = vm.$options;
if(opts.data){
initData(vm);
}else{
observe(vm.data={},true);
}
}
function initData(vm){
var data = vm.$options.data;
//data对象和vm._data指向了同一个引用,所以data和vm._data会互相影响
//本例中data只有一个属性'foo'
//data.foo变化时,vm._data.foo也会变化
//vm._data.foo变化时,data.foo也会发生变化。
vm._data = data;
//data对象中的所有属性都添加到vm实例中
//本例中,data只有一个foo属性
//将foo属性添加到vm实例中,所以有vm.foo
//访问vm.foo时,实际是返回vm._data.foo的值
//修改vm.foo时,其实是修改vm._data.foo的值
var keys = Object.keys(data);
var i = keys.length;
while(i--){
var key = keys[i];
proxy(vm,"_data",key);
}
}
function noop(a,b,c){}
var sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
};
//target 是 vm实例
//sourceKey 是 '_data'
//key 是 data对象中的属性,'foo'
//所以target[sourceKey] 就是 vm._data
function proxy(target,sourceKey,key){
sharedPropertyDefinition.get = function proxyGetter(){
return this[sourceKey][key];
}
sharedPropertyDefinition.set = function proxySetter(val){
this[sourceKey][key] = val;
}
Object.defineProperty(target,key,sharedPropertyDefinition)
}
function mergeOptions(parent,child){
var options = {};
var key,parentVal,childVal;
for(key in parent){
parentVal = parent[key];
var res = Object.create(parentVal||null);
options[key] = res;
}
for(key in child){
parentVal = parent[key];
childVal = child[key];
options[key] = childVal===undefined?parentVal:childVal;
}
return options;
}
function extend(to,_from){
for(var key in _from){
to[key] = _from[key];
}
return to;
}
先来看下第28行的vm._data =data。
赋值操作=让data对象和vm._data指向了同一个引用。
因此,data对象和vm._data二者会互相影响。
再到第39行proxy(vm,‘_data’,key)看看。
它的作用是将data对象中的所有属性都添加到vm实例上。
本例中,就是将foo属性添加到vm实例上,所以有vm.foo。并且,
访问vm.foo,实际是返回vm._data.foo;
修改vm.foo,其实就是修改vm._data.foo。
综上来说,data对象上的所有属性 与 vm实例上的数据 之间的关系是通过vm._data这座桥梁建立起来的。
_或者$开头的属性
<script>
var data = {
foo: "000",
_test: '测试'
};
var vm = new Vue({
data: data
});
console.warn(data.foo); //输出"000"
console.log(vm._test); // undefined
</script>
以 _ 或 $ 开头的 property 不会被 Vue 实例代理,因为它们可能和 Vue 内置的 property、API 方法冲突。你可以使用例如 vm.$data._property 的方式访问这些 property。
console.log(vm.$data._test) // 测试
实例创建之后,可以通过 vm.$data 访问原始数据对象。Vue 实例也代理了 data 对象上所有的 property,因此访问 vm.a 等价于访问 vm. $data.a。
版权声明:本文为weixin_43867717原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。