VUE原理(三):Compile模板解析指令和vue原理概括

VUE原理(一):observe和Object.defineProperty()数据劫持_Zoie_ting的博客-CSDN博客

VUE原理(二):依赖收集:Dep属性订阅器、Watcher订阅者 

        之前介绍了vue的数据劫持和依赖收集,深入了解了vue数据双向绑定的原理的前半部分:

  • 对数据对象进行递归遍历,都加上getter和setter,这样一旦发生改变,就可以触发setter,从而监听到数据变化;
  • watcher是订阅者,在自身实例化的时候往属性订阅器dep中添加自己,属性变化时触发setter,调用dep.notice通知,调用自身update方法更新。

本篇主要记录一下具体是如何更新的。

一、理论知识 

  • 什么是compile:compile是模板解析指令,将变量替换为数据,初始化时为节点绑定更新函数,添加监听数据的订阅者,每当数据变化时,触发函数更新视图。
  • compile原理:将template转化成一个js函数,让浏览器执行这个函数,从而渲染出html元素,让视图跑起来。
  • 三个阶段:解析、优化、生成
  • 解析:创建虚拟节点,通过大量正则表达式对template字符串进行解析,包括标签、指令、属性等,转化为抽象语法树AST。
  • 优化:遍历节点,标记静态节点,目的是在重新渲染页面时diff可以直接跳过。
  • 生成:将最终的AST转化为render函数字符串

二、浅浅实践

        首先使用document.createDocumentFragment();创建虚拟节点,然后将document.querySelector(el)的节点添加到虚拟节点中,对虚拟节点进行加工后再挂到el上。

class Compile{
    constructor(el,vm){
        this.$el = document.querySelector(el);
        this.$vm = vm;

        if(this.$el){
            this.$fragment = this.getFragment(this.$el);
            this.compile(this.$fragment);
            this.$el.append(this.$fragment)
        }
    }
    getFragment(el){
        const fragment = document.createDocumentFragment();
        let node;
        while(node = el.firstChild){
            fragment.appendChild(node)
        }
        return fragment;
    }
}

        以{{}}为例进行compile,首先这是个插值文本,nodeTypes为3;其次,通过正则表达式匹配花括号中间的属性名:

node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);

        初始化update,且添加监听数据的订阅者, 然后Watcher类中应该接收到实例、属性名和一个回调函数,用来更新视图:

    update(node,vm,exp){
        const updaterFn = this.handleText;
        updaterFn && updaterFn(node,vm[exp]);
        new Watcher(vm,exp,function (value) {
            updaterFn && updaterFn(node,value)
        })
    }
    handleText(node,value){
        node.textContent = value;
    }

Wacther类:

class Watcher {
    constructor(vm,key,cb){
        this.vm = vm;
        this.key = key;
        this.cb = cb;
        Dep.target = this;
    }
    update(){
        this.cb.call(this.vm,this.vm[this.key]);
    }
}

        另外,还要触发一下取值,在自身实例化时往属性订阅器dep中添加自己:this.vm[this.key];这样就实现了插值文本的双向绑定。

        其他的也是如此,例如v-model,它是v-bind和@input的语法糖,以'v-'开头,我们且把它当做一个普通的指令处理:还是 初始化update且添加watcher,但是在这之外再添加一个对input事件的监听即可:

    model(node,vm,exp){ //节点、实例、属性
        this.update(node,vm,exp);
        node.addEventListener('input',(e)=>{
            vm[exp] = e.target.value; //输入内容改变,设置属性值,从而触发setter
        })
    }
    update(node,vm,exp){
        const updaterFn = this.handelModel;
        updaterFn && updaterFn(node,vm[exp]);
        new Watcher(vm,exp,function (value) {
            updaterFn && updaterFn(node,value)
        })
    }
    handelModel(node,value){
        node.value = value;
    }

       读一下代码:

        假设v-model绑定了name,初始化update,将name的初始值填在input输入框的value,并添加了一个Watcher,每次在输入框输入内容,属性值随着输入内容变化,属性值变化触发setter,setter触发dep.notice(),notice触发watcher的update,执行在compile中的回调函数handelModel,修改输入框的value,达到双向绑定的效果。

总结

        本篇记录了什么是compile、compile的过程、每个过程的功能以及实现了一个简单的compile,讲解了v-model的双向绑定原理。由此可见,Watcher是observe和compile之间的桥梁:observe递归属性,数据更新,触发Watcher,compile解析模板,绑定Watcher,数据变化,watcher触发update,执行回调函数,通知更新视图,达到  数据变化——>视图更新,视图交互变化——>数据更新  的效果。

        共勉!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值