导航
$data 中$data 还能访问属性const App = Vue.createApp({
data() {
return {
title: 'This is my TITLE',
}
},
template: `
<h1>{{ title }}</h1>
`
});
const vm = App.mount('#app');
console.log(vm);
console.log(vm.$data.title);
console.log(vm.title);
vm.$data.title = 'This is your TITLE';
console.log(vm.title);

后续在 vm 上添加 author ,不会出现在 vm.$data 上:
vm.author = 'Lance';

在 $data 上添加属性,不会出现在 vm 上:
vm.$data.author = 'Lance';

两种添加方式都不能渲染到页面上:
const App = Vue.createApp({
data() {
return {
title: 'This is my TITLE',
}
},
template: `
<div>
<h1>{{ title }}</h1>
<h1>{{ author }}</h1>
</div>
`
});
const vm = App.mount('#app');
// vm.author = 'Lance';
vm.$data.author = 'Lance';

var vm = new Vue({
data() {
return {
a: 1,
b: 2
}
}
});
console.log(vm.a);
vm.b = 233;
console.log(vm.b);
function Vue(options) {
this.$data = options.data();
var _this = this;
for (var key in this.$data) {
(function(k) {
Object.defineProperty(_this, k, {
get: function() {
return _this.$data[k];
},
set: function(newVal) {
_this.$data[k] = newVal;
}
});
})(key);
}
}
Object.defineProperty 在 IE8 下只支持DOM,可以使用 defineGetter、defineSetter 替换方案:
var vm = new Vue({
data() {
return {
a: 1,
b: 2
}
}
});
console.log(vm.a);
vm.b = 233;
console.log(vm.b);
function Vue(options) {
this.$data = options.data();
var _this = this;
for (var key in this.$data) {
(function(k) {
_this.__defineGetter__(k, function() {
return _this.$data[k];
});
_this.__defineSetter__(k, function(newVal) {
_this.$data[k] = newVal;
});
})(key);
}
}
var data = {
a: 1,
b: 2
}
var vm = new Vue({
data: data
});
var vm2 = new Vue({
data: data
});
vm.a = 233;
console.log(vm, vm2);
function Vue(options) {
this.$data = options.data;
var _this = this;
for (var key in this.$data) {
(function(k) {
Object.defineProperty(_this, k, {
get: function() {
return _this.$data[k];
},
set: function(newVal) {
_this.$data[k] = newVal;
}
});
})(key);
}
}

当然分别给两个新对象也没问题,但 Vue 为了避免出现上边情况,所以强制你得是个 function,返回一个新对象,这样在 Vue 内部执行它时,每次就是新对象了:
var vm = new Vue({
data: {
a: 1,
b: 2
}
});
var vm2 = new Vue({
data: {
a: 1,
b: 2
}
});
vm.a = 233;
console.log(vm, vm2);
function Vue(options) {
this.$data = options.data;
var _this = this;
for (var key in this.$data) {
(function(k) {
Object.defineProperty(_this, k, {
get: function() {
return _this.$data[k];
},
set: function(newVal) {
_this.$data[k] = newVal;
}
});
})(key);
}
}

/**
* 数据变更检测
*/
const vm = {
data: {
a: 1,
b: 2,
list: [1, 2, 3, 4, 5]
}
};
for (var key in vm.data) {
(function(key) {
Object.defineProperty(vm, key, {
get() {
console.log('数据获取');
return vm.data[key];
},
set(newValue) {
console.log('数据设置');
vm.data[key] = newValue;
// 视图更新操作
}
});
})(key);
}
console.log(vm);
vm.a = 233;
console.log(vm);
// 重新赋值能够触发 set
vm.list = vm.list.map(item => item * 2);
console.log(vm);
// 新增一个数组之前没监听的成员,数组倒是push了999,但没能触发set,间接导致没法更新视图
/**
* 下列方法,都不返回新数组(7种)
* Object.defineProperty 没办法监听下列方法对数组的操作变更
*/
// vm.list.unshift(1);
// vm.list.push(999);
// vm.list.pop();
// vm.list.shift();
// vm.list.splice(2, 1);
// vm.list.sort((a, b) => b - a);
// vm.list.reverse();
console.log(vm.list);
// Vue对下列方法进行了包裹封装
// function push() {
// vm.list.push(233);
// // 视图更新
// }
const vm = new Vue({
el: '#app',
template: `<div></div>`,
data() {
return {
list: [1, 2, 3, 4, 5]
}
}
});
console.log(vm.list);

Vue 类似做了这个操作:
_data 去接收 options.data() 函数返回的对象,保存到 vm 实例中
vm.title 的形式操作数据(跳过 vm._data.title 开发更方便),我们遍历 _data,通过 Object.defineProperty 在 vm 实例上挂载了所有 _data 下的属性_data 后,我们就需要对 _data 本身的改变进行拦截观察了(为了更新值时做页面更新等操作)
new Observer(data)_data 下的属性也有可能是个对象,属性的属性也有可能是个对象...Observer 观察者构造函数
是[] - 给数组的原型链上加中间桥梁
[].__proto__ = arrMethods、arrMethods.__proto__ = Array.prototypearrMethods 下挂载了 7 个属性,分别对应能原地改变数组的 7 个方法push、unshift、splice 三个方法有可能新增数组成员,而新增的成员也有可能是数组或对象,所以还得用一个 newArr 保存下新数组成员,然后对它们进行遍历观察是{} - 遍历对象属性并用 Object.defineProperty 进行数据劫持
new Observer(value) 观察它function Vue(options)
_init(options) 初始化Vue
initState(this) 初始化状态(data、computed ...)initState(vm) 方法,用来初始化各种状态
initData(vm) 初始化 data