经常在面试中被问到,vue的双向绑定是如何实现的?今天自己来实现一个简易的vue,来深入理解下vue的实现思路。
数据显示到页面
首先 把vue中传入的数据,显示到html页面
<div id="vue">
<p>名称:{{name.name}}</p>
<p>年龄{{age}}</p>
</div>
<script>
new MVue({
el: '#vue',
data: {
name:{
name:'张三'
} ,
age: '12'
}
})
</script>
class MVue {
constructor(options) {
this.$options = options
const node = document.querySelector(this.$options.el)
this.complie(node)
}
complie(node) {
Array.from(node.childNodes).forEach(element => {
const reg = /\{\{(.*?)\}\}/g
if (element.nodeType === 3 && reg.test(element.textContent)) {
const fieldNameArray = RegExp.$1.split('.') //当出现a.b的情况处理
let val=this.$options.data
fieldNameArray.forEach(fieldName=>{
val=val[fieldName]
})
element.textContent = element.textContent.replace(reg, val).trim();
}
// 如果还有子节点,继续递归replace
if (element.childNodes && element.childNodes.length) {
this.complie(element);
}
})
}
}
数据的响应式
直接上代码
class MVue {
constructor(options) {
this.$options = options
const node = document.querySelector(this.$options.el)
this.observe(options.data)
this.complie(node)
Object.keys(this.$options.computed).forEach(key => {
})
}
observe(data) {
Object.keys(data).forEach(key => {
const subs = new Observer()
data['_' + key] = data[key]
Object.defineProperty(data, key, {
get() { // 获取值的时候 将对应的观察者对象
Observer.target && subs.addSubs(Observer.target)
return data['_' + key]
},
set(newValue) { // 通知对应的观察者对象 修改页面中的值
subs.notifySub(newValue)
}
})
})
}
complie(node) {
Array.from(node.childNodes).forEach(element => {
const reg = /\{\{(.*?)\}\}/g
if (element.nodeType === 3 && reg.test(element.textContent)) {
const fieldName = RegExp.$1
/* let val = this.$options.data
fieldNameArray.forEach(fieldName => {
val = val[fieldName]
})*/
Observer.target = element
this.$options.data[fieldName]
Observer.target = null
element.textContent = element.textContent.replace(reg, this.$options.data["_" + fieldName]).trim();
}
// 如果还有子节点,继续递归replace
if (element.childNodes && element.childNodes.length) {
this.complie(element);
}
})
}
}
// 观察者对象
class Observer {
constructor() {
this.subNodes = []
}
addSubs(target) {
this.subNodes.push(target)
}
notifySub(val) {
this.subNodes.forEach(node => {
node.textContent = val
})
}
}
方法observe:
1、先对data对象进行for循环,使用object.definePrototype的get方法来设置观察者对象(每个变量对应的模板Node节点),当值改变是,使用set方法来通知观察对象,修改对应的值
2。每次for循环形成闭包,观察者对象一直保存在闭包中
方法complie: 主要用于查找{{}}形式的节点,并且将对应的字段改成字段的值。同时调用observe中的get方法,对属性设置观察则对象
数据代理
我们在使用vue的时候,在获取data中某个属性的值时,不是使用this.$options.data.a.b,经常使用this.a.d的简写形式。observe函数的优化方式如下
observe(data) {
Object.keys(data).forEach(key => {
const subs = new Observer()
data['_' + key] = data[key]
Object.defineProperty(data, key, {
get() { // 获取值的时候 将对应的观察者对象
Observer.target && subs.addSubs(Observer.target)
return data['_' + key]
},
set(newValue) { // 通知对应的观察者对象 修改页面中的值
subs.notifySub(newValue)
}
})
// 数据代理 将data值 直接代理到实例中
Object.defineProperty(this, key,{
get() {
return data['_' + key]
},
set(newValue) {
data['_' + key]=newValue
}
})
})
}