Vue_2 -- 底层原理
1. MVVM模型
1. M: 模型(Model)
对应 Vue data中的数据
2. V: 视图(View)
模版
3. VM: 视图模型(ViewModel)
Vue 实例对象
2. 数据绑定之数据代理技术
通过一个对象代理另一个对象中属性的读写操作就是数据代理
2.1 Object.defineProperty
1. 设置属性
这样设置的属性age,不可被枚举(循环)
<script>
let person = {
name: "张三",
sex: "男",
}
// 给对象设置属性,如果想要这个属性可被枚举,必须设置 enumerable: true,
Object.defineProperty(person, 'age', {
value: 18,
})
console.log(Object.keys(person))
</script>
2. 设置属性可迭代
如果想要这个属性可被枚举,必须设置 enumerable: true
<script>
let person = {
name: "张三",
sex: "男",
}
Object.defineProperty(person, 'age', {
value: 18,
enumerable: true, // 设置属性可迭代
})
console.log(Object.keys(person))
</script>
3. 设置属性可修改
<script>
let person = {
name: "张三",
sex: "男",
}
Object.defineProperty(person, 'age', {
value: 18,
writable: true, // 设置属性可修改
})
console.log(Object.keys(person))
</script>
3. 设置属性可删除
<script>
let person = {
name: "张三",
sex: "男",
}
Object.defineProperty(person, 'age', {
value: 18,
configurable: true, // 设置属性可删除
})
console.log(Object.keys(person))
</script>
4. get()
<script>
let person = {
name: "张三",
sex: "男",
}
let number = 18
// 为保证person的age值跟随者number的改变而同时改变,需要用到以下函数
Object.defineProperty(person, 'age', {
get(){ // 当读取person的age属性时,get函数就会被调用,此函数的返回值为age 的值
return number
}
})
console.log(person)
number = 19
console.log(person)
</script>
5. set()
<script>
let person = {
name: "张三",
sex: "男",
}
let number = 18
Object.defineProperty(person, 'age', {
set(value){ // 当修改person的age属性时,set函数就会被调用,此函数的返回值为age 的值
number = value
}
})
console.log(number)
person.age = 19
console.log(number)
</script>
6. 示例
<script>
let obj = {x:100}
let obj2 = {x:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
2. Vue中对数据代理的应用
Vue将data对象中的每个属性进行代理,并保存到在自身的_data属性中
Vue的数据代理
总结
1. Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
2. Vue中数据代理的好处: 更加方便的操作data中的数据
3. 基本原理:
a. 通过Object.defineProperty()把data对象中所有属性添加到vm上
b. 为每一个添加到vm上的属性,都指定一个getter/setter
c. 在getter/setter内部取操作(读/写) data中对应的属性
3. 数据绑定之_data的数据劫持技术
4. 数据绑定之数据监视原理
1. 往数组中的首位更新数据时会出现的问题
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}
</li>
</ul>
<button @click="updateMdm2">更新马冬梅的信息</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
updateMdm() { // 通过属性修改时Vue是能同时修改页面显示的
this.persons[0].name = "马老师"
this.persons[0].age = 50
},
updateMdm2() { // 直接修改数组中的元素是无法被Vue监测到的,所以代码层面和数据已经改了,但是页面显示并未发生变化
this.persons[0] = {id: 1, name: "马老师", age: 50}
}
}
})
</script>
2. Vue是如何监测对象中的数据变化的
1. 模仿Vue做数据代理
<script>
let data = {
name: "小明"
}
Object.defineProperty(data, "name", { // 如果有人访问data中的name,就会执行get()
get() {
return data.name // 这里同样是在访问data中的name,同样需要执行get(),会造成递归,set()同理
}
set(val) {
data.name = val
}
})
</script>
2. Vue如何解决上述问题的
Vue中写的是对data的递归查找,会找到data中对象中的对象,所有层,这里的示例代码并没有考虑到对象中有对象
<script>
let data = {
name: "小明"
}
// 创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
function Observer(obj) {
// 汇总对象中的所有属性形成一个数组
const keys = Object.keys(obj)
// 遍历
keys.forEach((k) => {
// this 是Observer的实例对象
Object.defineProperty(this,k,{
get(){
return obj[k]
},
set(val){
console.log(`${k} 被改了,接下来,解析模板,生成虚拟DOM......`)
obj[k] = val
}
})
})
}
let vm = {}
// 这里相当于将obs同时复制给Vue中定义的data,和_data
vm._data = data = obs
console.log(vm)
</script>
3. 对于初始化时并未被Vue管理的对象属性,后面通过代码添加的属性并不会被Vue管理和响应式
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
addAttr() {
this.persons[0].sex = "男"
}
}
})
</script>
如何解决这个问题
1.一开始就定义好sex为空字符串,就会被Vue给管理,从而做响应式
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
// 一开始就定义好sex为空字符串,就会被Vue给管理,从而做响应式
persons: [
{id: 1, name: "马冬梅", age: 18, sex: ""},
{id: 2, name: "周冬雨", age: 19, sex: ""},
{id: 3, name: "周杰伦", age: 40, sex: ""},
{id: 4, name: "王兆伦", age: 25, sex: ""},
],
},
methods: {
addAttr() {
this.persons[0].sex = "男"
}
}
})
</script>
2. 通过Vue.set()来添加属性
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
addAttr() {
// 通过Vue.set() 来设置属性
Vue.set(this.persons[0],"sex","男")
}
}
})
</script>
3. vm.$set()
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
addAttr() {
// 通过 this.$set() 来设置属性
this.$set(this.persons[0],"sex","男")
}
}
})
</script>
4. Vue是如何监测数组的
Vue对于data中的数组元素,并未创建对应的getter()和setter(),这就表示,当使用数组索引 (arr[0]) 来查询和修改数组中的元素时并不会被Vue监测到
Vue中对于数组的检测,并不是通过getter()和setter()来检测的,而是用数组的一些方法,如push(),pop(),shift(),unshift(),splice(),sort(),reverse()方法才会被Vue检测到
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
addAttr() {
// 使用索引修改元素,并不会被Vue监测到,从而刷新页面显示
this.persons[0] = {id: 1, name: "马冬春", age: 22}
}
}
})
</script>
需要通过上述数组方法来修改
<div id="app">
<h1>更新时的问题</h1>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
<button @click="addAttr">添加性别到马冬梅</button>
</div>
<script>
new Vue({
el: "#app",
data: {
keyWord: "",
sortType: 0,
persons: [
{id: 1, name: "马冬梅", age: 18},
{id: 2, name: "周冬雨", age: 19},
{id: 3, name: "周杰伦", age: 40},
{id: 4, name: "王兆伦", age: 25},
],
},
methods: {
addAttr() {
this.persons.splice(0,1,{id: 1, name: "马冬春", age: 22})
}
}
})
</script>
那么Vue是如何检测到你执行了这些方法呢?
Vue对数组的push(),pop(),shift(),unshift(),splice(),sort(),reverse()方法,做了一层封装,并非是原数组的底层操作方法
python防脱发技巧