数据劫持部分:在vue2中,采取的是用object.defineProperty()方法来对数据进行劫持,在该方法中,需要去遍历data中的每一个数据,并且在监听数据的时候需要new一个Dep对象来存放需要当前数据的订阅者,当data被访问时,将对应的watcher订阅者加入到Dep
中, 当data发生改变时,调用存放在Dep中所有watcher的notify方法来更新视图。
编译部分:首先对传进来的el元素进行逐个进行判断,判断其是属于元素节点还是文本节点,不通的节点采用不同的方法进行编译,还要判断该节点是否含有子节点,如果还有子节点,则递归该操作,直到所有节点编译完成。
具体编译过程:如果是元素节点,则将其属性逐个遍历,获取每个的指令和属性值,根据指令的不同,所对应的方法也不同,像click则执行回调函数,若为普通指令,则直接将文本替换成data即可。
如果是文本节点,则直接将文本替换成data。
在对不同的执行进行处理的时候,我们需要对每一个指令都new一个Watcher,当前watcher是用来监听之后的渲染中如果data中的数据发生了变化,会调用watcher中的notify方法来更新视图。
watcher部分:笔者认为watcher才是发布订阅者模式的核心。因为当数据劫持和模版编译部分都已经完成,要如何将这两个东西连接起来了,就算连接起来了,要怎么才能实现数据发生变化的时候,页面发生更新呢?
要解决上述两个疑问,我们需要watcher明白watcher是用来干什么的。我们知道watcher时充当一个“中间人”的作用,第一次编译的时候,watcher需要从data中获取数据到页面上进行渲染。在第二次及以后的渲染的时候,我们需要监听data中的变化来更新视图。总而言之,就是在页面渲染的时候,watcher必不可少,不然整个流程我们不知道哪部份data放在哪里渲染,哪部分data改变了之后又应该渲染在什么地方。有了这个思路之后,我门可以沿着这个思路来探讨这个watcher应该放在什么地方。在第一次编译的时候,可以创建一个watcher,当我们需要访问data中的数据,data的就将watcher加入到该data中,数据被劫持时,访问该数据就会调用该数据的get()方法,可以调用get方法的时候将编译时候的watcher添加进一个为Dep的数组,此时watcher就当作这个中间人,将编译和数据劫持部分进行连接。在第二次及以后访问的时候,每次数据发生变化,都会调用set方法,可以在set方法中定义一个方法,当该数据发生改变的时候,将该数据下的所有watcher都执行notify方法,从而达到更新视图的目的。