vue-mustache模板引擎

什么是模板引擎?

模板引擎是将数据变为视图的最优雅方案

一:mustache的基本使用

mustache官方git: https://github.com/janl/mustache.js

mustache是“胡子“的意思,因为它的嵌入标记{{ }}非常像胡子

{{ }}的语法也被Vue沿用。

1:基本渲染数据

v-text,v-html

//渲染模板
        var templateStr=`<h1>这是{{str1}}的{{str2}}</h1>`
        //数据
        var Obj = {
            str1:'一个',
            str2:'模板'
        }
//把数据渲染到模板上
        var htmlStr=Mustache.render(templateStr,Obj)
        console.log(htmlStr);

2:数组循环渲染数据

v-for

// {{#arr}}{{/arr}}要循环的数组
        var temstr =`
            <ul>
                {{#arr}}
                    <li>我叫{{name}},今年{{age}}岁</li>
                {{/arr}}
            </ul>
        `
        var obj={
            arr:[{name:'小张',age:'20'},
                {name:'小红',age:'21'},
                {name:'小明',age:'22'}]
        }
        var htmlStr= Mustache.render(temstr,obj)
        console.log(htmlStr);

3:嵌套数组循环渲染数据

// {{#arr}}{{/arr}}要循环的数组
        var temstr =`
            <ul>
                {{#arr}}
                    <li>我叫{{name}},今年{{age}}岁,爱好
                        {{#hobbies}}
                        <span>{{.}}</span>
                        {{/hobbies}}
                    </li>
                {{/arr}}
            </ul>
        `
        var obj={
            arr:[{name:'小张',age:'20',hobbies:['看书','学习']},
                {name:'小红',age:'21',hobbies:['化妆','逛街']},
                {name:'小明',age:'22',hobbies:['跑步','篮球']}]
        }
        var htmlStr= Mustache.render(temstr,obj)
        console.log(htmlStr);

4:布尔值

v-if

var temstr =`
            <ul>
                {{#m}}
                <li>A</li>
                {{/m}}
                {{#w}}
                <li>B</li>
                {{/w}}
            </ul>
        `
        var obj = {
            m:true,
            w:false
        }
        var htmlStr= Mustache.render(temstr,obj);

5:模板引擎实例

<div id="test"></div>
    <script src="./node_modules/mustache/mustache.js"></script>
    <script type="text/template" id="templateStr">
        <ul>
            {{#arr}}
            <li>
                <h1>{{name}}</h1>
                <div>{{age}}</div>
                {{#hobby}}
                <span>{{.}}</span>
                {{/hobby}}
            </li>
            {{/arr}}
        </ul>
    </script>
    <script>
        var tStr=document.getElementById('templateStr').innerHTML;
        var obj={
            arr:[
                {name:'小王',age:'19',hobby:['学习','睡觉']},
                {name:'小红',age:'20',hobby:['逛街','睡觉']},
                {name:'小张',age:'21',hobby:['跑步','篮球']},
            ]
        }
        document.getElementById('test').innerHTML = Mustache.render(tStr,obj);
    </script>

二:mustache的底层核心机理

1:正则表达式思路

.replace( RegExp 对象 , fun )

RegExp 对象:正则表达式

用正则表达式找到相应的字符:列:找到 {{}}  正则表达式/\{\{(\w+)\}\}/g

var htmlstr="<h1>我买了一个{{thing}},是{{mood}}</h1>"
        var obj={
            thing:'手机',
            mood:'华为'
        }
        var newhtmlstr = htmlstr.replace(/\{\{(\w+)\}\}/g,function(findStr,$1){
            // 替换目标:findStr {{thing}}
            //表达式内匹配的文本:$1 thing
            return obj[$1]
        })

封装一个简单的render

 var htmlstr="<h1>我买了一个{{thing}},是{{mood}}</h1>"
        var obj={
            thing:'手机s',
            mood:'华为'
        }
        function render(templateStr,data){
            return templateStr.replace(/\{\{(\w+)\}\}/g,function(findStr,$1){
                return data[$1]
            })
        }

但以上只能对简单的数据进行处理

要对复杂的数据进行处理就要用到tokens

mustache底层重点要做两件事

1);将模板字符串编译为token形式

2):将tokens结合数据,解析为dom字符串

三:手写mustache库

1:配置webpack

1)初始化包管理 npm init

2)下载webpack相关包

webpack   webpack-cli   webpack-dev-server

3)新建并配置 webpack.config.js 文件

const path = require('path')

module.exports = {
    //模式:开发
    mode:'development',
    //入口
    entry:'./src/index.js',
    //打包到什么文件
    output:{
        filename:'bundle.js'
    },
    //配置wabpack-dev-server
    devServer:{
        //静态文件根目录
        contentBase:path.join(__dirname,'www'),
        //不压缩
        compress:false,
        //端口号
        port:8080,
        //虚拟打包路径,bundle.js文件没有真正生成
        publicPath:'/xuni/'
    }
}

4)创建入口文件可和静态文件

 5)在package.json文件中添加运行命令

"dev": "webpack-dev-server"

2:scanner类

这个类的主要目的就是以“{{”,“}}”为截取对象,截取字符串并分段返回出来。

export default class Scanner{
    constructor(templateStr){
        this.templateStr=templateStr;
        //指针
        this.pos=0;
        //尾巴
        this.tail=templateStr;
    }
    //功能弱,就是走过指定内容,没有返回值
    scan(stopTag){
        if(this.tail.indexOf(stopTag)==0){
            this.pos+=stopTag.length;
            this.tail=this.templateStr.substr(this.pos)
        }
    }
    //让指针进行扫描,直到遇见指定内容结束,并且能够返回结束之前路过的文字
    scanUtile(stopTag){
        const pos_backup=this.pos;
        //当尾巴的开头不是stoptap的时候,说明还没有扫描到stoptag
        // 指针长度小于模板长度说明改模板还没查找完
        while(this.tail.indexOf(stopTag)!=0&& !this.eos()){
            this.pos++;
            //改变尾巴从当前指针的这个字符开始。到最后全部
            this.tail=this.templateStr.substr(this.pos)
        }
        return this.templateStr.substring(pos_backup,this.pos)
    }

    // 判断指针是否到头
    eos(){
        return this.pos>=templateStr.length;
    }
}

3:获取Token数组

生成token数组

import Scanner from "./Scanner.js"
import nestTokens from "./nestTokens.js"
    
export default function parseTemplateTotoken(templateStr){
    var tokens=[];
    var scanner = new Scanner(templateStr);
    let words=null;
    while(!scanner.eos()){
        // 将模板除{{}}外的所有字符封开
        words=scanner.scanUtile('{{');
        if(words!=''){
            tokens.push(['text',words])
        }
        scanner.scan('{{');

        // 对{{}}内的字符串做数据类型判断
        words=scanner.scanUtile('}}');
        if(words!=''){
            if(words[0]=='#'){
                tokens.push(['#',words.substring(1)]);
            }else if(words[0]=='/'){
                tokens.push(['/',words.substring(1)]);
            }else{
                tokens.push(['name',words])
            }
        }
        scanner.scan('}}');
    }

    return nestTokens(tokens);
}

结果

 

 

此时的token字符串数组还只是一个一维数组,并不能直接拿来渲染

还需要做二次处理,上面中循环时我们对每个字符串做了判断分类:如text,name,#,/。为为二次处理做标识符

实现数组嵌套将一维数组转成可渲染的二维数组

实现思想:栈数据结构,现进后i出

export default function nestTokens(tokens){
    var nestedToken=[];
    // 收集器
    var collector=nestedToken;
    // 栈
    var sections=[];

    for(let i=0;i<tokens.length;i++){
        let token=tokens[i];
        switch (token[0]) {
            case '#':
                // 收集器中放入这个token
                collector.push(token);
                // 入栈
                sections.push(token);
                // 收集器要换人。给token添加为2的下标,并让收集器指向它
                collector=token[2]=[];
                break;
            case '/':
                // 出栈
                let section_pop=sections.pop();
                //改变收集器为栈结构队尾(队尾是栈顶),那项的下标为2的数组
                collector=sections.length>0?sections[sections.length-1][2]:nestedToken;
                break;    
            default:
                // 不管当前的collector是谁,可能是结果nestedTokens,也可能是某个token的下标为21的数组
                collector.push(token)
                break;
        }
    }

    return nestedToken;
}

5:lookup函数

识别 . 点符号

export default function lookup(dataObj,keyName){

    if(keyName.indexOf('.')!=-1){
        var keys = keyName.split('.')
        var temr = dataObj;
        for(let i=0;i<keys.length;i++){
            temr=temr[keys[i]]
        }
        return temr;
    }
    return dataObj[keyName];
}

4:将Token数组变为DOM

renderTemplate.js

import lookup from './lookup.js'
import parseArray from './parseArray.js'

export default function renderTemplate(tokens,data){
    var resUltStr = '';
    for(let i=0;i<tokens.length;i++){
        let token=tokens[i];
        if(token[0]=='text'){
            resUltStr+=token[1]
        }else if(token[0]=='name'){
            resUltStr+=lookup(data,token[1])
        }else if(token[0]=='#'){
            resUltStr+=parseArray(token,data)
        }
    }

    return resUltStr;
} 

parseArray.js

import lookup from './lookup.js'
import renderTemplate from './renderTemplate.js'

export default function parseArray(token,data){
    var v=lookup(data,token[1]);
    var resultStr = '';
    for(let i=0;i<v.length;i++){
        resultStr+=renderTemplate(token[2],{
            ...v[i],
            '.':v[i],
        })
    }
    return resultStr;
}

5:结果

 <div id="asd"></div>
    <script src="/xuni/bundle.js"></script>
    <script>
        var templateStr=`
        <div>
            <ul>
                {{#students}}
                <li>
                    学生是{{name}}的爱好是
                    <ul>
                        {{#hobbies}}
                        <li>{{.}}</li>
                        {{/hobbies}}
                    </ul>
                </li>
                {{/students}}
            </ul>
        </div>`
        var data={
            students:[
                {name:'小米',hobbies:['游泳','健身']},
                {name:'小王',hobbies:['学习','睡觉','洗澡']},
                {name:'小马',hobbies:['吃饭','逛街']}
            ]
        }
        var htmlstr=SSg_TemplateEngine.render(templateStr,data);
        document.getElementById('asd').innerHTML=htmlstr
import parseTemplateTotoken from './parseTemplateTotoken.js'
import renderTemplate from './renderTemplate.js'

window.SSg_TemplateEngine={
   render(templateStr,data){
       var token = parseTemplateTotoken(templateStr);
       return renderTemplate(token,data)
   }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值