Vue源码探索--mustache模板引擎(把模板字符串编译为虚拟DOM并渲染出真实DOM)下篇

本文档详细介绍了如何将模板字符串转换为tokens数组,并结合数据生成真实的DOM。通过解析tokens,处理文本、对象属性以及数组类型的值,实现模板引擎的功能。主要涉及了lookup函数用于处理对象嵌套数据,parseArray函数处理数组类型的tokens,最终在index.js中调用这些函数完成渲染。
摘要由CSDN通过智能技术生成

目录

一、图示:

 二、代码演示:

1. index.js(入口文件):

2.数据渲染过程:

(1)解决数据是对象嵌套形式的问题函数(renderTemplate.js ):

(2)编写从对象中识别字符串函数(lookUp.js):

 (3)处理"#"类型函数(parseArray.js):

(4)最终实现:


注:在上篇中我们实现了将模板字符串转换为tokens数组,那么这一篇我们来聊聊将tokens数组结合数据并转换为真是DOM渲染出来的过程。

一、图示:

这一篇我们将来实现红框里面的过程。

 二、代码演示:

1. index.js(入口文件):

import parseTemplateToTokens from './parseTemplateToTokens.js'
import renderTemplate from './renderTemplate.js';
import lookup from "./lookup"
//全局提供templateEngine对象
window.templateEngine = {
    //渲染方法
    render(templateStr, data) {
        var tokens = parseTemplateToTokens(templateStr);
        console.log(tokens);
        // 调用renderTemplate函数,让tokens数组变为dom字符串
        var domStr = renderTemplate(tokens,data);
        return domStr;
    }

};

将渲染出来的tokens字符串数组结合数据传入到 renderTemplate方法中。

2.数据渲染过程:

renderTemplate.js 函数的功能:是让tokens数组变为dom字符串 ,#号标记的tokens,需要递归处理它的下标为2的小数组。

首先将传入进来的tokens遍历分为三种情况进行处理,分别是:"text"类型,"name"类型和"#"类型

  • "text"类型:直接追加文本内容。
  • "name"类型:这里需要进行数据处理。(因为JS不识别 ' . ' 的形式)
  • "#"类型:递归处理它下标为2的小数组。

(1)解决数据是对象嵌套形式的问题函数renderTemplate.js ):

renderTemplate.js 函数:

/*
   函数的功能是让tokens数组变为dom字符串
    #号标记的tokens,需要递归处理它的下标为2的小数组

*/

import lookup from "./lookup";
import parseArray from "./parseArray"

export default function renderTemplate(tokens, data){
    // console.log(data);
//    结果字符串
    var resultStr = "";
//    遍历tokens
    for(let i = 0;i<tokens.length;i++){
        let token = tokens[i];
    //    查看类型
        if(token[0] === "text"){
            resultStr += token[1];
        }else if(token[0] === "name"){
            //如果是name类型,那么就直接使用它的值,
            //使用lookup防止是对象取值的形式
            resultStr += data[token[1]];
        }else if(token[0] === "#"){
            //稍后处理
        }
    }
    return resultStr;
}

 index.html文件:

    <div class="container">

    </div>

    <script src="/xuni/bundle.js"></script>
    <script>

         var templateStr = `<h1>我考了{{n.a.b}}分,好{{mood}}啊</h1>`;

         var data = {
             n:{
                 a:{
                     b:100
                 }
             },
             mood:"激动"
         };

        //    调用
        var inx = templateEngine.render(templateStr, data);
        var container = document.getElementsByClassName("container")[0];
        container.innerHTML = inx;
        console.log(inx);
    </script>

 


 注:可以看到data里面以对象嵌套形式的数据并没有取出来,所以我们要先解决此类问题。

 

(2)编写从对象中识别字符串函数(lookUp.js):

lookUp.js函数功能:可以在dataObj对象中,寻找用连续点符号的keyName属性

比如:dataObj是
 {
  a:{
       b:{
         c:100
       }
   }
 }
那么lookup(dataObj,a.b.c)的结果就是100
/*
功能是:可以在dataObj对象中,寻找用连续点符号的keyName属性
比如:dataObj是
 {
  a:{
       b:{
         c:100
       }
   }
 }
那么lookup(dataObj,a.b.c)的结果就是100
* */
export default function lookup(dataObj,keyName){
    // console.log(dataObj,keyName);
//    看看keyName中有没有点符号
    if(keyName.indexOf(".") !== -1 && keyName!='.'){
        //将其装换为数组
        var keys = keyName.split('.');
        //temp存放这个嵌套对象
        var temp = dataObj;
        for(let i = 0;i<keys.length;i++){
            //从嵌套对象中一层一层查找
            temp = temp[keys[i]];
        }
        return temp;
    }
    return dataObj[keyName];
};

 修改renderTemplate.js 函数

/*
   函数的功能是让tokens数组变为dom字符串
    #号标记的tokens,需要递归处理它的下标为2的小数组

*/

import lookup from "./lookup";
import parseArray from "./parseArray"

export default function renderTemplate(tokens, data){
    // console.log(data);
//    结果字符串
    var resultStr = "";
//    遍历tokens
    for(let i = 0;i<tokens.length;i++){
        let token = tokens[i];
    //    查看类型
        if(token[0] === "text"){
            resultStr += token[1];
        }else if(token[0] === "name"){
            //如果是name类型,那么就直接使用它的值,
            //使用lookup防止是对象取值的形式
            resultStr += lookup(data,token[1]);
        }else if(token[0] === "#"){
           //处理递归
        }
    }
    return resultStr;
}

 (3)处理"#"类型函数(parseArray.js):

创建一个parseArray.js函数,处理数组。结合renderTemplate实现递归。此函数接收两个参数一个是token一个是data(token=tokens[i])

import lookup from "./lookup"
import renderTemplate from "./renderTemplate";
/*
* 处理数组,结合renderTemplate实现递归
* 注意:这个函数收的参数是token!而不是tokens
* token = tokens[i]
* 这个函数要递归调用renderTemplate函数,调用多少次?根据data数组的长度
* */
export default function parseArray(token,data){
    //从#类型中 根据token[1]的值取不同的数据
    //token[1]指的是 #类型嵌套数组中第二个值,根据此值利用lookUp函数向data中取值
    var v = lookup(data,token[1]);
    console.log("-------------");
    console.log(v);
    console.log("++++++++++");
    //结果字符串
    var resultStr = "";
    //便利V数组,v一定是数组
    //下面的循环是一个比较难思考的循环
    //它是遍历数据,而不是遍历tokens.数组中的数据有几条,就要遍历几条。
    //token[2]指的是 #类型 的数组中嵌套的数组

    console.log(token[2]);

    for(let i = 0;i<v.length;i++){
        resultStr += renderTemplate(token[2],{
            //这里要考虑到数据是简单类型的只有一个.的情况
            /**
             *
             var templateStr = `
                 <ul>
                     {{#arr}}
                        <li>{{.}}</li>
                     {{/arr}}
                 </ul>
             `;

             var data = {
                    arr:["A","B","C"]
                };
             * */
            //现在是这个数据的小对象,是v[i]的展开,就是v[i]本身
            ...v[i],
            //补充一个.属性
            '.':v[i]
        });
    }
    return resultStr;
}

 注意点:

  • var v = lookup(data,token[1]); token[1]是取嵌套数组中#后面的下一个值。

 

 根据此值和data数据传入到lookUp函数中我们得到 v 的值。v一定是数组。

 


  •  token[2]指的是 #类型 的数组中嵌套的数组。
  • for循环中遍历数组,根据数据的长度我们调用renderTemplate函数向模板字符串中添加数据。

 renderTemplate.js函数:

/*
   函数的功能是让tokens数组变为dom字符串
    #号标记的tokens,需要递归处理它的下标为2的小数组

*/

import lookup from "./lookup";
import parseArray from "./parseArray"

export default function renderTemplate(tokens, data){
    // console.log(data);
//    结果字符串
    var resultStr = "";
//    遍历tokens
    for(let i = 0;i<tokens.length;i++){
        let token = tokens[i];
    //    查看类型
        if(token[0] === "text"){
            resultStr += token[1];
        }else if(token[0] === "name"){
            //如果是name类型,那么就直接使用它的值,
            //使用lookup防止是对象取值的形式
            resultStr += lookup(data,token[1]);
        }else if(token[0] === "#"){
            resultStr += parseArray(token,data);
        }
    }
    return resultStr;
}

(4)最终实现:

index.js(入口文件):

import parseTemplateToTokens from './parseTemplateToTokens.js'
import renderTemplate from './renderTemplate.js';
import lookup from "./lookup"
//全局提供templateEngine对象
window.templateEngine = {
    //渲染方法
    render(templateStr, data) {
        var tokens = parseTemplateToTokens(templateStr);
        console.log(tokens);
        // 调用renderTemplate函数,让tokens数组变为dom字符串
        var domStr = renderTemplate(tokens,data);
        return domStr;
    }

};

 renderTemplate.js函数: 函数的功能是让tokens数组变为dom字符串,#号标记的tokens,需要递归处理它的下标为2的小数组。

/*
   函数的功能是让tokens数组变为dom字符串
    #号标记的tokens,需要递归处理它的下标为2的小数组

*/

import lookup from "./lookup";
import parseArray from "./parseArray"

export default function renderTemplate(tokens, data){
    // console.log(data);
//    结果字符串
    var resultStr = "";
//    遍历tokens
    for(let i = 0;i<tokens.length;i++){
        let token = tokens[i];
    //    查看类型
        if(token[0] === "text"){
            resultStr += token[1];
        }else if(token[0] === "name"){
            //如果是name类型,那么就直接使用它的值,
            //使用lookup防止是对象取值的形式
            resultStr += lookup(data,token[1]);
        }else if(token[0] === "#"){
            resultStr += parseArray(token,data);
        }
    }
    return resultStr;
}

 lookUp.js函数:功能是:可以在dataObj对象中,寻找用连续点符号的keyName属性(用来处理数据)

/*
功能是:可以在dataObj对象中,寻找用连续点符号的keyName属性
比如:dataObj是
 {
  a:{
       b:{
         c:100
       }
   }
 }
那么lookup(dataObj,a.b.c)的结果就是100
* */
export default function lookup(dataObj,keyName){
    // console.log(dataObj,keyName);
//    看看keyName中有没有点符号
    if(keyName.indexOf(".") !== -1 && keyName!='.'){
        //将其装换为数组
        var keys = keyName.split('.');
        //temp存放这个嵌套对象
        var temp = dataObj;
        for(let i = 0;i<keys.length;i++){
            //从嵌套对象中一层一层查找
            temp = temp[keys[i]];
        }
        return temp;
    }
    return dataObj[keyName];
};

 parseArray.js函数: 处理数组,结合renderTemplate实现递归

import lookup from "./lookup"
import renderTemplate from "./renderTemplate";
/*
* 处理数组,结合renderTemplate实现递归
* 注意:这个函数收的参数是token!而不是tokens
* token = tokens[i]
* 这个函数要递归调用renderTemplate函数,调用多少次?根据data数组的长度
* */
export default function parseArray(token,data){
    //从#类型中 根据token[1]的值取不同的数据
    //token[1]指的是 #类型嵌套数组中第二个值,根据此值利用lookUp函数向data中取值
    var v = lookup(data,token[1]);
    console.log("-------------");
    console.log(v);
    console.log("++++++++++");
    //结果字符串
    var resultStr = "";
    //便利V数组,v一定是数组
    //下面的循环是一个比较难思考的循环
    //它是遍历数据,而不是遍历tokens.数组中的数据有几条,就要遍历几条。
    //token[2]指的是 #类型 的数组中嵌套的数组

    console.log(token[2]);

    for(let i = 0;i<v.length;i++){
        resultStr += renderTemplate(token[2],{
            //这里要考虑到数据是简单类型的只有一个.的情况
            /**
             *
             var templateStr = `
                 <ul>
                     {{#arr}}
                        <li>{{.}}</li>
                     {{/arr}}
                 </ul>
             `;

             var data = {
                    arr:["A","B","C"]
                };
             * */
            //现在是这个数据的小对象,是v[i]的展开,就是v[i]本身
            ...v[i],
            //补充一个.属性
            '.':v[i]
        });
    }
    return resultStr;
}

 

 

注:代码地址

Mustache完结撒花。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值