您的位置:首页 > Web前端 > Vue.js

Vue 双向绑定原理分析

2020-08-06 14:41 100 查看

Vue 双向绑定原理分析

首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会,接着为input会添加监听事件,修改值就会为该属性赋值,触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。

一、数据变页面变

1.页面一定会变吗?什么情况下不会变?怎么解决?

首先,数据变页面不一定会变。以下情况不会变:

  • 情况一、Vue只兼容IE8以上的版本,view层改变是基于Vue底层的Object.difineProtery 和getter ,setter,而IE8及以下浏览器没有这些属性,所以根本无法改变view层。
  • 情况二、当给对象 动态添加一个属性(data中没有该属性),所以添加的属性没有getter和setter,
  • 情况三、数组 动态修改数组的length相关,数据的修改也不会引起页面的变化

综上,页面没有实现双向绑定最主要的原因是没有setter和getter。在生命周期的创建阶段,vue会用object.definePropty 将data 里的数据进行处理,处理完成就拥有了setter和getter

二、用js实现简单的双向绑定

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" onkeydown="inputChange(this)">
<span></span>
</body>
<script>
/*
input 框里的数据发生改变后页面自动变

on:
1.处理数据有setter和getter
2.数据改变 触发set
3.set 通知watcher监听
4.watcher监听更新页面
*/

let data={
inputValue:''
}

let middle=''

Object.defineProperty(data,'inputValue',{
get(){
return middle
},
set(param){
middle=param
watcher()
}
})

function inputChange(e){
// console.log(e)
data.inputValue=e.value
}

function watcher(){
// 根据修改后的数据控制页面刷新
let value=data.inputValue
document.getElementsByTagName('span')[0].innerHTML=value
}
</script>
</html>

三、Vue如何实现

原理图:

  • 3.1 observer用来实现对每个vue中的data中定义的属性循环用Object.defineProperty()实现数据劫持,以便利用其中的setter和getter,然后通知订阅者,订阅者会触发它的update方法,对视图进行更新。

  • 3.2 我们介绍为什么要订阅者,在vue中v-model,v-name,{{}}等都可以对数据进行显示,也就是说假如一个属性都通过这三个指令了,那么每当这个属性改变的时候,相应的这个三个指令的html视图也必须改变,于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是更新自己的指令对应的数据,也就是v-model='name’和{{name}}有两个对应的订阅者,各自管理自己的地方。每当属性的set方法触发,就循环更新Dep中的订阅者。

四、如何解决

  • 法一、对于为创建的实例:将property 写在 data 对象中
  • 法二、对于已经创建的实例:可以使用 Vue.set(object, propertyName, value) 或 vm.$set 实例方法方法向嵌套对象添加响应式 property

eg:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
{{info.age}}
<hr>
{{info.name}}
<button @click='add' >add</button>
<button @click='change'>change</button>
</div>
</body>
<script src="./Week3_VueS/day02/myapp/node_modules/vue/dist/vue.js"></script>
<script>
const vm=new Vue({
el:"#app",
data:{
info:{
age:1
}
},
methods:{
add(){
this.info.age++
console.log(this)
},
change(){
//  this.info.name='hehe'
this.$set(this.info,'name','hehe')
//  console.log(this)
}
}
})
</script>
</html>

五、总结

首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会,接着为input会添加监听事件,修改值就会为该属性赋值,触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: