简单概括
vue 的数据绑定基于 ES5 的 Object.defineProperty() 这个方法对数据做劫持。Object.defineProperty的 setter 与 getter用户看不到 ,但是在内部它们让 Vue追踪依赖,在属性被访问和修改时通知变化。然后结合发布订阅模式在数据发生数据,通知页面进行更新。(由于 ES5 的 Object.defineProperty() 这个方法不兼容 IE8 。所以 vue 的兼容性也是不兼容 IE8 既以下版本)
简单实现一个 Vue 源代码
<div id="app">
<p>{{ sex }}</p>
<p>{{ name }}</p>
<p>{{ age }}</p>
</div>
<script>
// 提供一个 Event 类 发布订阅
class Event {
constructor() {
this.dep = {}
}
$on (eventName, callback) {
if (!this.dep[eventName]) {
this.dep[eventName] = []
}
this.dep[eventName].push(callback)
}
$emit (eventName, payload) {
if (this.dep[eventName]) {
this.dep[eventName].forEach(cb => {
cb(payload)
})
}
}
}
// 提供一个 Vue 的构造函数,或者 Vue 类
class Vue {
constructor(options) {
// 设置一些 实例属性
this.$el = document.querySelector(options.el)
this.$data = options.data;
// 实现 代理属性
// this.name === this.$data.name
this.observer(this.$data)
// 生成一个 Event 的实例
this._ev = new Event()
// 解析
new Compile(this.$el, this)
}
/**
* 将传递过来的对象中的属性直接绑定到实例对象上
* observer(data)
*/
observer(data) {
// data {name: '张三', age: 18}
// 遍历 data 这个对象
//console.log(Object.keys(data)) // ['name', 'age']
// console.log(Object.values(data)) // ['张三', 18]
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
get () {
return data[key]
},
set (value) {
data[key] = value
// 数据更新了, 触发事件
this._ev.$emit(key)
}
})
})
}
}
// 提供一个 Compile 类。来做页面解析的工作
class Compile {
/**
* {DocumentFragment} el DOM 对象
* {Vue} vm Vue 实例对象
*/
constructor(el, vm) {
// 将 vm 绑定到 当前 Compile 的实例对象上
this.vm = vm
this.compile(el)
}
compile (el) {
// 遍历 el 这个 DOM 对象 子节点
// console.log(el.childNodes)
el.childNodes.forEach(node => {
// 得到所有的文本节点,节点的文本内容类似 {{ xxx }} 这种格式的
if (node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent)) {
console.log(node.textContent)
// 直接将当前的 node.textContent 给修改了
// {{ name }} => 张三
// {{ age }} => 18
// 1. 得到 {{ }} 里面的表达式
let exp = RegExp.$1.trim()
// 2. 将对应 exp 的数据设置到 当前节点的 textContent 上。
node.textContent = this.vm[exp]
// 3. 监听 this.vm === new Vue 的实例
this.vm._ev.$on(exp, () => {
node.textContent = this.vm[exp]
})
}
// 递归遍历子节点
if (node.childNodes) {
this.compile(node)
}
})
}
}
const vm = new Vue({
el: '#app',
data: {
name: '豪哥',
age: 18,
sex: '男'
}
})
</script>
使用控制台修改数据