Fork me on GitHub

MVVM框架进阶与实现--解析版

Vue基本思路

  • 创建vue对象:构造函数或者class类
  • 声明data层数据(data,methods)
  • 创建dom对象(#app)(告诉vue解析那一部分的dom对象)(view层)
  • 创建compile:解析view层
    • 解析指令:v-text等
    • 解析文本
  • 创建observer对象:劫持data层;
  • 创建watcher层
    • 连接compile和observer作用

compile

创建文本碎片

  • 所有操作dom在文本碎片中进行
  • 处理结束之后放到body里面,再由浏览器进行解析;
  • 可以避免一定程度的回流和重绘

compile方法解析node节点

  • 注意区分元素节点和文本节点
  • 进行递归操作:解析下一层的node

元素节点

  • 主要解析指令
  • 是在元素节点处进行递归解析
  • 主要解析有”v-“开头指令
  • 需要尤其注意v-on:click绑定事件特殊处理

文本节点

  • 主要解析文本中包含此种形式的数据
正则分组

参考文件

解析完毕

解析完毕之后要将文本碎片放回body中

observer

  • 主要作用通过Object.defineProperty检测data中所有的属性
  • 此处依旧注意递归操作;

watcher

连接observer和compile

  • 如果需要使页面更新,需要在compile中的指令解析的时候;
  • 如果需要检测数据改变,需要在observer中的数据劫持的set中进行;

所以就需要

  • 在更改dom对象值的时候使用watcher检测:声明watcher对象;
1
2
3
4
5
6
7

if (type === 'text') {
node.textContent = this.getVmValue(expr)
new Watcher(this.$vm, expr, (newAalue, oldValue) => {
node.textContent = newAalue;
})
}
  • 如果需要更改调用回调:在watcher内部需要一个方法来调用callback;
1
2
3
4
5
6
7
8

update() {
let oldValue = this.oldValue;
let newValue = this.getVMValue(this.vm, this.expr)
if (oldValue !== newValue) {
this.cb(newValue, oldValue)
}
}
  • 需要知道在什么时候调用update:数据改变的时候-也就是observer中的set方法调用的时候

问题:

  • 我们是在compile中实例化的watcher,需要在observer中调用watcher中的updata方法
  • 而且每一个数据在compile过程中,可能不只有一个watcher对象

data.msg==>=>v-test=>v-html;

  • data中的每一个key,都需要一个对象挂载上他自己的watcher对象

实现步骤

需要在observer中劫持data中的每一个属性的时候,声明一个Dep(挂载此属性下watcher)对象

1
2
3
4
let dep = new Dep();
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,

需要每个watcher在实例化的时候把自己放到Dep中的一个属性里面:;

1
2
3
4
class Dep {
constructor() {
this.subs = [];//就是这个
}
  • 先把watcher挂载到一个对象上面,要求在observer中也能拿到:window可以,此处用的是dep本身;

    1
    2

    Dep.target = this;//此处的this就是watcher实例化之后的对象
  • 在wathcer中调用获取oldValue的时候就会触发get方法,此时把我们挂载到Dep中的watcher放到dep中的subs数组中

1
Dep.target && dep.addSub(Dep.target)

解析总结

整个完成之后data中的每一个属性都会有自己一个Dep,Dep中的subs包含着凡是用到过此属性的所有watcher;

在data发生改变之后

  • 首先触发的是observer中的set方法;
  • 调用dep中的发布函数:notify
  • 发布函数会通知subs中的所有watcher调用自身的update函数
  • watcher中的updata会触发回调,改变元素的值;