(一)vue 模板编译器 compiler

vue的整个架构的流程图

image.png

compiler目的实现解析html里面的vue指令,例如:v-test,v-html,v-model @click等等

//compile工具库
const compileUtil = {
    getValue(expr,vm)
    {
        //根据 字符串 test.name.key 来获取实际对象的 test[name][key] = 123
        return expr.split(".").reduce((data,currentVal)=>{
            //不断递归循环直到 expr.split(".")的数组完毕
            return data[currentVal];
        },vm.$data)
        //vm.$data 是reduce的初始值
    },
    text(node,expr,vm)
    {
        let value;
        console.log(expr)
        if(expr.indexOf('{{')!==-1)
        {
            //replace args[0] = "{{xxx}}" args[1] = xxx
            value = expr.replace(/\{\{(.+?)\}\}/g,(...args)=>{
                console.log(args[1])
                return this.getValue(args[1],vm)
            })
        }
        else
        {
            this.getValue(expr,vm)
        }
        this.updater.textUpdater(node,value)

    },
    html(node,expr,vm)
    {
        const value = this.getValue(expr,vm)
        this.updater.htmlUpdater(node,value)
    },
    model(node,expr,vm)
    {
        const value = this.getValue(expr,vm)
        this.updater.modelUpdater(node,value)

    },
    on(node,expr,vm,detailStr)
    {
        let fn = vm.$options.methods && vm.$options.methods[expr]
        //把功能绑定到 vm执行
        node.addEventListener(detailStr,fn.bind(vm),false)
    },
    bind(node,expr,vm,detailStr)
    {
      node.setAttribute(detailStr,expr)
    },
    //视图更新函数
    updater:{
        textUpdater(node,value)
        {
            node.textContent = value
        },
        htmlUpdater(node,value)
        {
            node.innerHTML = value;
        },
        modelUpdater(node,value)
        {
            //input 里面的value值
            node.value = value;
        }
    }
}

class Compiler
{
    constructor(el,vm)
    {
        this.el = this.isElementNode(el) ? el:document.querySelector(el);
        this.vm = vm;
        // 1. 将预编译的元素节点放入文档碎片对象中,避免DOM频繁的回流与重绘,提高渲染性能
        const fragments = this.node2fragments(this.el)
        //2.编译模板
        this.compile(fragments)
        //3. 追加子元素到根元素
        this.el.appendChild(fragments);


    }

    compile(fragments)
    {
        // 1.获取子节点
        let childNodes = fragments.childNodes
        //2.递归循环编译
        for(var i = 0;i<childNodes.length;i++)
        {
            let child = childNodes[i]

            if(this.isElementNode(child))
            {
                this.compileElement(child)
            }
            else
            {
                this.compileText(child)
            }
            if(child.childNodes && child.childNodes.length)
            {
                this.compile(child)
            }
        }

    }

    compileText(node)
    {
        //解析温饱中的 {{ xxx}}
        const content = node.textContent;
        //(.+)默认是贪婪匹配
        //(.+?)为惰性匹配
        if(/\{\{(.+?)\}\}/.test(content)){
            console.log("testtt")
            console.log(content)
            compileUtil['text'](node,content,this.vm);
        }
    }

    compileElement(node)
    {
        //v-html v-test v-mode v-bind v-on:click
        let attributes = node.attributes
        for(var i =0;i<attributes.length;i++)
        {
            //object
            let attr = attributes[i]
            //-text="msg"  v-html=htmlStr  type="text"  v-model="msg"
            let {name,value} = attr

            if(this.isDirector(name))
            {
                let [,directive] = name.split("-")
                let [compileKey,detailStr] = directive.split(":");
                //根据 属性来处理对应的指令
                compileUtil[compileKey](node,value,this.vm,detailStr);
                // 删除有指令的标签属性 v-text v-html等,普通的value等原生html标签不必删除
                node.removeAttribute('v-' + directive);
            }
            else if(this.isEventName(name))
            {
                let [,detailStr] = name.split("@")
                compileUtil['on'](node,value,this.vm,detailStr)
                node.removeAttribute('@'+detailStr)
            }
            else;
        }

    }

    isEventName(attrName)
    {
      //判断是否@开头 事件绑定
      return attrName.startsWith('@')
    }

    isDirector(attrName)
    {
        //判断是否vue特性标签
        return attrName.startsWith('v-')
    }

    isElementNode(node)
    {
        // nodeType 属性返回节点类型。
        // 如果节点是一个元素节点,nodeType 属性返回 1。
        // 如果节点是属性节点, nodeType 属性返回 2。
        // 如果节点是一个文本节点,nodeType 属性返回 3。
        // 如果节点是一个注释节点,nodeType 属性返回 8。
        //元素节点的nodeType属性等于 1
        return node.nodeType === 1
    }

    node2fragments(el)
    {
        const f = document.createDocumentFragment()
        let firstChild
        while(firstChild = el.firstChild)
        {
            //这里的虚拟节点 append之后 就会把被添加过的节点从el中删除掉
            f.appendChild(firstChild)
        }
        return f;
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 33、JS中的本地存储 把一些信息存储在当前浏览器指定域下的某一个地方(存储到物理硬盘中)1、不能跨浏览器传输:在...
    萌妹撒阅读 6,452评论 0 2
  • Vue是一个前端js框架,由尤雨溪开发,是个人项目 Vue近几年来特别的受关注,三年前的时候angularJS霸占...
    6e5e50574d74阅读 3,561评论 0 0
  • active-class是哪个组件的属性?嵌套路由怎么定义? vue-router模块的router-link组件...
    慢慢慢热型阅读 3,741评论 0 2
  • 一、了解Vue.js 1.1.1 Vue.js是什么? 简单小巧、渐进式、功能强大的技术栈 1.1.2 为什么学习...
    蔡华鹏阅读 8,696评论 0 3
  • Vue官方文档以下内容作为本人日常学习使用,不作为参考 一、Vue环境搭建以及vue-cli的使用 Vue多页面应...
    好一只帅卤蛋阅读 4,206评论 0 1

友情链接更多精彩内容