<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue双向绑定原理分析</title>
</head>
<style>
body{
margin: 0;
padding: 0;
}
</style>
<body>
<div id="app">
{{name}}
</div>
</body>
<script>
//vue类
class Vue {
constructor(options={}) {
this.data = options.data
this.el = options.el
// 数据劫持
observe(this.data)
// 模版编译
compile(this.el || document.body,this)
}
}
class Ovserver {
constructor(data) {
this.data = data
this.walk(this.data)
}
walk(data) {
Object.keys(data).forEach(key=>objectDefinedDate(data,key,data[key]))
}
}
observe=(data)=>{
if (!data || typeof data !=='object') {
return
}
return new Ovserver(data)
//Object.keys(data).forEach(key=>objectDefinedDate(data,key,data[key]))
}
objectDefinedDate = (data,key,val)=> {
let dep = new Dep()
Object.defineProperty(data,key,{
enumberable:true,
configurable:true,
get:()=>{
// 添加订阅者
if (Dep.target) {
dep.addSub(Dep.target) // Dep.target为一个全局的对象,是Watcher的实例,拥有Watcher的一切属性和方法
}
return val
},
set:(newVal)=>{
if (newVal === val) {
return
}
val = newVal
// 通知更新
dep.notify()
}
})
}
class Dep {
constructor() {
this.subs = [] // 存放订阅者 订阅者就是观察者,即观察者实例
}
addSub(sub){
this.subs.push(sub)
}
notify() {
this.subs.forEach(sub=>sub.update()) // 调用Watcher的实例的更新方法
}
}
// 模版编译
compile =(node,vm)=> {
let reg = /\{\{(.*)\}\}/g
// 节点类型为元素
if (node.nodeType === 1) {
if (reg.test(node.innerHTML)){
let name = RegExp.$1
node.textContent = vm.data[name]
// 添加观察者
new Watcher(vm,name,function (newVal) {
node.textContent = newVal
})
}
}
}
class Watcher {
constructor(vm,node,fn) {
this.vm = vm // vue实例
this.node = node // 绑定的节点
this.fn = fn // 更新视图的回调函数
this.value = this.get() // 触发 Object.defineProperty的get方法,然后触发添加订阅者方法
}
update(){
let value = this.vm.data[this.node]
let oldVal = this.value
if (value !== oldVal) {
this.value = value
this.fn.call(this.vm,value,oldVal)
}
}
get(){
Dep.target = this // this表示当前的Watcher的实例
let value = this.vm.data[this.node] // 触发 Object.defineProperty的get方法
Dep.target = null
return value
}
}
let el = document.getElementById('app')
const vue = new Vue({
data:{
name:'vue'
},
el:el
})
setTimeout(()=>{
vue.data.name='vue change'
},2000)
</script>
</html>
参考https://juejin.im/post/5acd0c8a6fb9a028da7cdfaf
来源:oschina
链接:https://my.oschina.net/u/4336129/blog/3981422