把computed里面的数据,挂载在vm上即可实现
function Vue(options = {}) {
this.$options = options;//将所有属性挂载在options
var data = this._data = this.$options.data;
observe(data)
//观察完之后,this代理了this._data 数据代理
for (let key in data) {
Object.defineProperty(this, key, {
enumerable: true,
get() {
return this._data[key];//获取到新值
},
set(newVal) {
this._data[key] = newVal//这里触发观察者【再次执行Observe(data)中的set方法】
}
})
}
initComputed.call(this);
new Compile(options.el, this)
}
//把computed里面的数据,挂载在vm上即可实现
function initComputed(){//具有缓存功能
let vm = this
let computed = vm.$options.computed; //Object.keys
console.log(Object.keys(computed)) //['hello']
Object.keys(computed).forEach((key)=>{
Object.defineProperty(vm, key, {//computed[key]
enumerable: true,
get:typeof computed[key] === "function"?computed[key]:computed[key].get,
set() {
}
})
})
}
完整代码如下
function Vue(options = {}) {
this.$options = options;//将所有属性挂载在options
var data = this._data = this.$options.data;
observe(data)
//观察完之后,this代理了this._data 数据代理
for (let key in data) {
Object.defineProperty(this, key, {
enumerable: true,
get() {
return this._data[key];//获取到新值
},
set(newVal) {
this._data[key] = newVal//这里触发观察者【再次执行Observe(data)中的set方法】
}
})
}
initComputed.call(this);
new Compile(options.el, this)
}
//把computed里面的数据,挂载在vm上即可实现
function initComputed(){//具有缓存功能
let vm = this
let computed = vm.$options.computed; //Object.keys
console.log(Object.keys(computed)) //['hello']
Object.keys(computed).forEach((key)=>{
Object.defineProperty(vm, key, {//computed[key]
enumerable: true,
get:typeof computed[key] === "function"?computed[key]:computed[key].get,
set() {
}
})
})
}
//模板编译器
function Compile(el, vm) {
//el代表替换的范围
vm.$el = document.querySelector(el);
/*获取到元素以后,把里面的子元素都拿到,需要移动到内存中操作,所以需要创建文档碎片*/
//创建一个虚拟的节点对象,或者说,是用来创建文档碎片节点。它可以包含各种类型的节点,在创建之初是空的。
let fragment = document.createDocumentFragment();
//把app里面的内容都放到内存中,此时页面上为空白
while (child = vm.$el.firstChild) {
fragment.appendChild(child)
}
//递归 替换虚拟节点中的 {{}}
replace(fragment)
function replace(fragment) {
Array.from(fragment.childNodes).forEach((node) => {
//循环每一层
let text = node.textContent;
let reg = /\{\{(.*)\}\}/
//nodeType为3时,是文本内容
if (node.nodeType === 3 && reg.test(text)) {
//console.log(RegExp.$1) // a.a vm.b
let arr = RegExp.$1.split('.') //[a,a]
let val = vm;
arr.forEach((k) => {
val = val[k]
})
new Watcher(vm, RegExp.$1, function(newVal){//函数里需要接收一个新的值
node.textContent = text.replace(/\{\{(.*)\}\}/, newVal)
})
//替换逻辑
node.textContent = text.replace(/\{\{(.*)\}\}/, val)
}
//nodeType为1时,是元素节点
if(node.nodeType === 1){
let nodeAttrs = node.attributes //类数组对象
Array.from(nodeAttrs).forEach((attr)=>{
console.log(attr.name)
let name = attr.name
let exp = attr.value //v-model = 'b'
if(name.indexOf('v-') == 0){//v-model
node.value = vm[exp]
}
new Watcher(vm, RegExp.$1, function(newVal){//函数里需要接收一个新的值
node.value = vm[exp] //当watcher触发时描绘自动将内容放到输入框里
})
node.addEventListener('input',function(e){
let newVal = e.target.value;
vm[exp] = newVal // this.c = 新值
})
})
}
if (node.childNodes) {
replace(node)
}
})
}
//把内存中的文档碎片放回到页面上,此时页面上的东西显示回来
vm.$el.appendChild(fragment)
}
//vm.$options
function Observe(data) {//这里写我们的主要逻辑
let dep = new Dep()
for (let key in data) {//把data属性通过object.defineProperty的方式定义
let val = data[key]
observe(val)//递归 使data的中的每个属性都被Object.defineProperty处理
Object.defineProperty(data, key, {
enumerable: true,
get() {
//把watcher 放入到 订阅者中
Dep.target && dep.addSub(Dep.target) //[watcher]
return val;
},
set(newVal) {//更改值得时候
if (newVal === val) {//设置的值和以前是一样的东西
return;
}
val = newVal;//如果以后在获取值得时候,将刚才设置的值丢回去
observe(newVal)
//发布
dep.notify() // 让所有的watcher的update方法执行
}
})
}
}
function observe(data) {
if (typeof data !== 'object') {
return
}
return new Observe(data)
}
//发布订阅模式 先有订阅再有发布
//有一个方法,可以订阅一些事件 这些事件会放在数组里 [fn1,fn2,fn3]
function Dep() {
this.subs = [];
}
//订阅的实现
Dep.prototype.addSub = function (sub) {
this.subs.push(sub)
}
//发布的实现 绑定的方法,如[fn1,fn2,fn3] 都有一个update属性
Dep.prototype.notify = function () {
this.subs.forEach(sub => sub.update())
}
//Watcher
function Watcher(vm, exp, fn) {
this.fn = fn
this.vm = vm
this.exp = exp
//添加到订阅中
Dep.target = this //获取当前的 watcher实例 添加到订阅中
let val = vm
let arr = exp.split('.')
arr.forEach((k) => { // this.a.a 回调用 get方法
val = val[k]
})
Dep.target = null
}
Watcher.prototype.update = function () {
let val = this.vm
let arr = this.exp.split('.')
arr.forEach((k) => { // this.a.a 回调用 get方法
val = val[k]
})
this.fn(val) //
}