function Mvvm(options = {}) {
this.$options = options
let data = this._data = this.$options.data
//数据劫持
observe(data)
//数据代理
//为了不用写如mvvm._data.a.b这种
//可以直接写mvvm.a.b
for(let key in data) {
Object.defineProperty(this, key, {
configurable: true,
get() {
return this._data[key]
},
set(newVal) {
this._data[key] = newVal
}
})
}
//初始化computed,将this指向实例
initComputed.call(this)
//数据编译
new Compile(options.el, this)
//所有事情处理好后执行mounted钩子函数
options.mounted.call(this) //这就实现了mounted钩子函数
}
function initComputed() {
let vm = this
let computed = this.$options.computed
Object.keys(computed).forEach(key=>{
Object.defineProperty(vm,key,{
// 这里判断是computed里的key是对象还是函数
// 如果是函数直接就会调get方法
// 如果是对象的话,手动调一下get方法即可
// 如: sum() {return this.a + this.b;},他们获取a和b的值就会调用get方法
get:typeof computed[key] === ‘function‘ ? computed[key] : computed[key].get,
set() {}
})
})
}
let options = {
data: {
a:1,
b:9
},
computed: {
sum() {
return this.a+this.b
},
noop() {
}
},
mounted() {
setTimeout(()=>{
console.log(‘所有事情都搞定了‘)
},1000)
}
}
function Observe(data) {
for(let key in data) {
let dep = new Dep()
let val = data[key]
observe(val) //递归劫持
Object.defineProperty(data, key, {
configurable: true,
get() {
//进来到这里的Dep.targe就是那个Watch的实例,这是通过get这个操作进来的,get这个操作在Watch的构造函数里面
//所以此时的Dep.targe还没有执行Dep.targe=null这个
Dep.target && dep.addSub(Dep.target)// 将watcher添加到订阅事件中 [watcher]
return val //闭包返回值
},
set(newVal) {
if (val === newVal) {
return
}
val = newVal; //如果以后再获取值(get)的时候,将刚才设置的值再返回去(其实就是利用了闭包的原理保存了val的状态)
observe(newVal) //mvvm._data.a = {b:‘ok‘}这种情况要对其进行递归
dep.notify() // 让所有watcher的update方法执行即可
}
})
}
}
function observe(data) {
//如果不是对象直接return防止递归溢出
if (!data || typeof data !== ‘object‘) return
return new Observe(data)
}
function Compile(el, vm) {
vm.$el = document.querySelector(el)
let fragment = document.createDocumentFragment()
while (child = vm.$el.firstChild) {
fragment.appendChild(child)
}
//对el里的内容进行替换
function replace(frag) {
Array.from(frag.childNodes).forEach(node => {
let txt = node.textContent
let reg = /\{\{(.*?)\}\}/g
// 即是文本节点又有大括号的情况{{}}
if (node.nodeType === 3 && reg.test(txt)) {
// console.log(RegExp.$1)
// let arr = RegExp.$1.split(‘.‘)
// let val = vm
// arr.forEach(key => {
// val = val[key] //如this.a.b
// })
// node.textContent = txt.replace(reg,val).trim()
// //监听变化
// //给Watcher再添加两个参数,用来取新的值(newVal)给回调函数传参
// new Watcher(vm, RegExp.$1, newval=>{
// node.textContent = txt.replace(reg,newval).trim()
// })
function replaceTxt() {
node.textContent = txt.replace(reg, (matched,placeholder)=>{
console.log(placeholder);
new Watcher(vm,placeholder,replaceTxt) //只是把replaceTxt作为参数传入没有递归调用。。。
return placeholder.split(‘.‘).reduce((val,key)=>{
return val[key]
},vm)
})
}
replaceTxt()
}
if (node.nodeType === 1) {
let nodeAttr = node.attributes// 获取dom上的所有属性,是个类数组
Array.from(nodeAttr).forEach(attr=>{
let name = attr.name //属性的名字 v-mode
let exp = attr.value //属性的值 c
if (name.includes(‘v-‘)) {
node.value = vm[exp] //this.c 为 2
}
//监听变化
new Watcher(vm, exp, function(newVal) {
node.value = newVal // 当watcher触发时会自动将内容放进输入框中
})
node.addEventListener(‘input‘,e => {
let newVal = e.target.value
// 相当于给this.c赋了一个新值
// 而值的改变会调用set,set中又会调用notify,notify中调用watcher的update方法实现了更新
vm[exp] = newVal
})
})
}
//如果还有子节点,继续递归replace
if (node.childNodes && node.childNodes.length) {
replace(node) //形成一个递归,DFS向下找。直到找到一个text节点
}
})
}
replace(fragment)
vm.$el.appendChild(fragment)
}
//发布订阅模式
function Dep() {
//一个数组(存放函数的事件池)
this.subs = []
}
Dep.prototype = {
addSub(sub) {
this.subs.push(sub)
},
notify() {
this.subs.forEach(sub=>sub.update())
}
}
//监听函数
//通过Watch这个类创建的实例都拥有update方法
function Watcher(vm, exp, fn) {
this.fn = fn
this.vm = vm
this.exp = exp
Dep.target = this //这个操作是为了可以使用Dep直接获取到new出来的Watch实例,
let arr = exp.split(‘.‘)
let val = vm
arr.forEach(key=>{
val = val[key] // 获取到this.a.b,默认就会调用get方法
})
Dep.target = null
}
Watcher.prototype.update = function () {
// notify的时候值已经更改了
// 再通过vm, exp来获取新的值
let arr = this.exp.split(‘.‘)
let val = this.vm
arr.forEach(key=> {
val = val[key]
})
this.fn(val)
}
// //test
// let watcher = new Watcher(() => console.log(111)); //
// let dep = new Dep();
// dep.addSub(watcher); // 将watcher放到数组中,watcher自带update方法, => [watcher]
// dep.addSub(watcher);
// dep.notify(); // 111, 111
原文:https://www.cnblogs.com/lbzli/p/12933778.html