import Vue from "vue/dist/vue.js";
import App from "./App.vue";
Vue.config.productionTip = false;
const myData = {
n: 0
}
// console.log(myData) // {n : 0}
new Vue({
data: myData,
template: `
<div>{{n}}</div>
`
}).$mount("#app");
setTimeout(()=>{
myData.n += 10
// console.log(myData) // n {...}
},3000)
问题一:首先需要弄明白,data传入new Vue后,data产生了变化,Vue对data做了什么?
要解决这个问题,需要两个前置知识
- getter/setter
- Object.defineProperty
1、getter和setter的用法?
getter读取属性,setter写入属性。
getter/setter就是对虚拟属性进行读/写。
let obj2 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
};
console.log("需求二:" + obj2.姓名);
// 总结:getter 就是这样用的。不加括号的函数,仅此而已。
let obj3 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx){
this.姓 = xxx[0]
this.名 = xxx.substring(1)
},
};
obj3.姓名 = '高媛媛'
console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)
// 总结:setter 就是这样用的。用 = xxx 触发 set 函数
2、Object.defineProperty的用法?
Object.defineProperty用来在定义完一个对象后,继续在该对象上加入新的属性。
Object.defineProperty(
obj,//mydata
"n",//我要对哪个属性进行代理和监听
{value/get,set}
let obj ={
属性1:xxx
属性2:yyy
get(){},
set(){},
};
var _zzz = 0//声明一个属性zzz,_zzz是用来存放属性"zzz"的值。
Object.defineProperty(obj , 添加属性"zzz" , {
get(){
return _zzz
},
set(value){
_zzz = value
}//写入值value,把值value存入 _zzz
})
- 给对象添加属性value
- 给对象添加getter/setter
- getter/setter用于对属性的读/写进行监控。
3、代理和监听
需要的前置知识
- 解构赋值 developer.mozilla.org/zh-CN/docs/…
- Proxy--ES6中的代理模式 developer.mozilla.org/zh-CN/docs/…
语法
const p = new Proxy(target, handler)
//target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
//handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
Proxy对象可以让你去对JavaScript中的一切合法对象的基本操作进行自定义,然后用你自定义的操作去覆盖其对象的基本操作。也就是当一个对象去执行一个基本操作时,其执行的过程和结果是你自定义的,而不是对象的.(引用于 juejin.cn/post/684490… )
- function proxy(){}是什么含义?
Proxy的作用:用代理包住对象,形成匿名对象,防止用户通过对象篡改属性。
如下是没有使用代理的对象,有名字的对象。对象名"data2".
let data2 = {};
data2._n = 0; // _n 用来偷偷存储 n 的值
其他人可以通过篡改data2._n来篡改对象data2的属性
如下是使用代理的对象,匿名对象{ n: 0 },代理为obj
let data3 = proxy({ data: { n: 0 } }); // 括号里是匿名对象,无法访问
function proxy({ data } /* 解构赋值*/) {
const obj = {};//代理
// 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
Object.defineProperty(obj, "n", {
get() {
return data.n;
},
set(value) {
if (value < 0) return;
data.n = value;
}
});
return obj; // obj 就是代理
}
代理obj与匿名对象{ n: 0 }通过proxy(){}进行关联。
读取obj的n时,通过proxy(){}返回匿名对象{ n: 0 }的n;
设置obj的n时,通过proxy(){}设置匿名对象{ n: 0 }的n。
由于只暴露代理对象obj,不暴露匿名对象{ n: 0 },因此其他人无法修改{ n: 0 }的值。
监听data,防止在初始化的时候篡改n的值
let myData5 = { n: 0 };
let data5 = proxy2({ data: myData5 }); // 括号里是匿名对象,无法访问
function proxy2({ data } /* 解构赋值*/) {
let value = data.n;//把n的原始值记录下来
Object.defineProperty(data, "n", {//安装一个新的n
get() {
return value;
},
set(newValue) {
if (newValue < 0) return;
value = newValue;
}
});
// 上面这几句话会监听 data
const obj = {};
Object.defineProperty(obj, "n", {
get() {
return data.n;
},
set(value) {
if (value < 0) return; //这句话多余了
data.n = value;
}
});
return obj; // obj 就是代理
}
// data3 就是 obj
console.log(`需求五:${data5.n}`);
myData5.n = -1;
console.log(`需求五:${data5.n},设置为 -1 失败了`);
myData5.n = 1;
console.log(`需求五:${data5.n},设置为 1 成功了`);
代理防止篡改左边的data5,监听防止篡改右边的myData5
let data5 = proxy2({ data: myData5 });//与下面"const vm = new Vue({data: {n:0})"这段代码是相似的
const vm = new Vue({data: {n:0})//Vue内部的源代码
- 对myData对象的属性读写,全权由另一个对象vm负责
- vm就是myData的代理,代理是一种设计模式,很多地方都会用到代理。
- 不直接使用myData.n,用vm.n来操作myData.n,或者使用this.n来操作myData.n。
回到最开头的问题一:data传入new Vue后,data产生了什么变化,vue对data做了什么?
vm = new Vue({data: myData})
1、会让vm成为myData的代理
2、会对myData的所有属性进行监听。
3、为什么要监控?
监控的目的是在你设置data的时候,去更新UI。 防止myData的属性变了,vm不知道。属性变了调用render(data) , UI=render(data)
如果data有多个属性n\m\k,那么就用循环和闭包,实现上述的代理和监听。
const vm = new Vue({
data1: {n:0}
data2: {m:1}
data3: {k:2}
)
第一步:对mydata加监听
while(){}或for(){}循环语句//每次循环产生一个单独的value
闭包//读取对应的value,有几个属性就对应几个value,他们互相之间没有干扰。
let value = 0
{
get n (){return value}
set n (v){value = v}
get m (){return value}
set m (v){value = v}
get k (){return value}
set k (v){value = v}
}
//监听代码全程只对原始对象的属性进行修改,没有删除该对象,修改后得到一个被修改的原始对象。
//在被修改的对象的基础上,生成一个新的代理,如下
第二步:生成新的代理
{
get n (){
return data1.n
}
set n(v){
data1.n = v
}
get m (){...}
set m (){...}
...
...
}
第三步:新的代理对象返回给vm
新的代理对象会来代理被修改过(被监听)的原始对象。
问题二:什么是Vue数据响应式?
let myData = { n: 0 };
const vm = new Vue({data: mydata})
我如果修改vm.n或者修改mydata.n,那么UI中的n就会出现相应的变化来响应,Vue2通过Object.defineProperty来实现数据响应式。
要深入理解options.data
主要内容可以在如下链接找到 cn.vuejs.org/v2/guide/re… --深入响应式原理