美文网首页web前端
vue源码分析(三):模板解析

vue源码分析(三):模板解析

作者: 姜治宇 | 来源:发表于2020-09-01 11:00 被阅读0次

什么是模板解析?

如下面一段代码,{{name}}是如何工作的呢?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="app">
        {{name}}
    </div>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>
<script>
    const vm = new Vue({
        el:'#app',
        data:{
            name: 'jack'
        }
    })
</script>

思路很简单:
1、获取#app元素下的所有节点,放入文档碎片处理
2、用正则表达式将文本节点的{{name}}匹配出来
3、用vm.data里面的name替换掉大括号信息
4、将替换完成的文档碎片塞回去

function Vue(options){
    this.$options = options
    this._data = this.$options.data
    this.$compile = new Compile(options.el,this) //模板解析
}

function Compile(el,vm){
    this.$vm = vm
    this.$el = document.querySelector(el)
    console.log(this.$el)
    if(this.$el){
        this.$fragment = this.node2Fragment(this.$el) //将节点转到fragment处理

        this.init() //开始处理

        this.$el.appendChild(this.$fragment) //塞回原位
    }
}
Compile.prototype = {
    //将#app里面的节点都转到文档碎片中
    node2Fragment(el){
        var fragment = document.createDocumentFragment()
        var child = null
        while(child = el.firstChild) {
            fragment.appendChild(child)
        }
        return fragment
    },
    //处理碎片中的信息
    init(){
        this.compileElement(this.$fragment)
    },
    //正则匹配
    compileElement(el){
        var childNodes = el.childNodes;
        [].slice.call(childNodes).forEach(node=>{
            var text = node.textContent // 获取文本信息
            var reg = /\{\{(.*)\}\}/
            if(node.nodeType === 3 && reg.test(text)) {
                
                this.compileText(node,RegExp.$1)
            }
        })
    },
//用vm.data信息,替换大括号的name
    compileText(node,exp){

        node.textContent = this.$vm._data[exp]
    }
}

嵌套对象如何处理?

这个例子比较简单,那如果是嵌套的对象呢?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="app">
        {{a.b.c}}
    </div>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>
<script>
    const vm = new Vue({
        el:'#app',
        data:{
            a: {
                b:{
                    c:'mary'
                }
            }
        }
    })
</script>

其实也很容易处理:

function Vue(options){
    this.$options = options
    this._data = this.$options.data
    this.$compile = new Compile(options.el,this) //模板解析
}

function Compile(el,vm){
    this.$vm = vm
    this.$el = document.querySelector(el)
    console.log(this.$el)
    if(this.$el){
        this.$fragment = this.node2Fragment(this.$el) //将节点转到fragment处理

        this.init() //开始处理

        this.$el.appendChild(this.$fragment) //塞回原位
    }
}
Compile.prototype = {
    //将#app里面的节点都转到文档碎片中
    node2Fragment(el){
        var fragment = document.createDocumentFragment()
        var child = null
        while(child = el.firstChild) {
            fragment.appendChild(child)
        }
        return fragment
    },
    //处理碎片中的信息
    init(){
        this.compileElement(this.$fragment)
    },
    //正则匹配
    compileElement(el){
        var childNodes = el.childNodes;
        [].slice.call(childNodes).forEach(node=>{
            var text = node.textContent // 获取文本信息
            var reg = /\{\{(.*)\}\}/
            if(node.nodeType === 3 && reg.test(text)) {

                this.compileText(node,RegExp.$1)
            }
        })
    },
//用vm.data信息,替换大括号的name
    compileText(node,exp){

        node.textContent = this.getVMVal(exp)
    },
//处理层级问题
    getVMVal(exp){ // a.b.c
        var val = this.$vm._data
        var arr = exp.split('.') //["a", "b", "c"]
        console.log(exp)
        arr.forEach(k=>{
            //debugger
            val = val[k] // 层级递进
        })
        return val
    }
}

子节点如何处理?

再延伸一下,如果是有子节点的呢?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div id="app">
       <p>名字:<b>{{a.b.c}}</b></p>
    </div>
</body>
</html>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>

<script>
    const vm = new Vue({
        el:'#app',
        data:{
            a: {
                b:{
                    c:'tom'
                }
            }
        }
    })
</script>

我们可以用递归来处理:

function Vue(options){
    this.$options = options
    this._data = this.$options.data
    this.$compile = new Compile(options.el,this) //模板解析
}

function Compile(el,vm){
    this.$vm = vm
    this.$el = document.querySelector(el)
    console.log(this.$el)
    if(this.$el){
        this.$fragment = this.node2Fragment(this.$el) //将节点转到fragment处理

        this.init() //开始处理

        this.$el.appendChild(this.$fragment) //塞回原位
    }
}
Compile.prototype = {
    //将#app里面的节点都转到文档碎片中
    node2Fragment(el){
        var fragment = document.createDocumentFragment()
        var child = null
        while(child = el.firstChild) {
            fragment.appendChild(child)
        }
        return fragment
    },
    //处理碎片中的信息
    init(){
        this.compileElement(this.$fragment)
    },
    //正则匹配
    compileElement(el){
        var childNodes = el.childNodes;
        [].slice.call(childNodes).forEach(node=>{
            var text = node.textContent // 获取文本信息
            var reg = /\{\{(.*)\}\}/
            if(node.nodeType === 3 && reg.test(text)) {

                this.compileText(node,RegExp.$1)
            } else if(node.childNodes && node.childNodes.length) {
                //递归
                this.compileElement(node)
            }
        })
    },
//用vm.data信息,替换大括号的name
    compileText(node,exp){

        node.textContent = this.getVMVal(exp)
    },
//处理层级问题
    getVMVal(exp){ // a.b.c
        var val = this.$vm._data
        var arr = exp.split('.') //["a", "b", "c"]
        console.log(exp)
        arr.forEach(k=>{
            //debugger
            val = val[k] // 层级递进
        })
        return val
    }
}

相关文章

网友评论

    本文标题:vue源码分析(三):模板解析

    本文链接:https://www.haomeiwen.com/subject/lwzusktx.html