手写一个mustache模板引擎

index.js文件

调用parseTemplateToTokens函数,将传进来的模板字符串转为tokens数组
调用renderTemplate函数,把tokens数组变成DOM结构的字符串

import parseTemplateToTokens from './parseTemplateToTokens'
import renderTemplate from './renderTemplate'
window.MY_templateEngine = {
    render(renderStr, data){
        // parseTemplateToTokens函数 将传进来的模板字符串转为tokens数组
        let tokens = parseTemplateToTokens(renderStr)

        let domstr = renderTemplate(tokens, data)
        return domstr
    }
}
parseTemplateToTokens函数

功能是 将传进来的模板字符串转为tokens数组

import Scanner from "./Scanner"
import nextTokens from "./nextTokens"

export default function parseTemplateToTokens(templateStr) {
    let tokens = []
    let scanner = new Scanner(templateStr)
    let words
    while (!scanner.eos()) {
        // 这个words是{{}}外面的内容 取消前后空格
        words = scanner.scanUtil("{{")
        if (!!words) {
            tokens.push(['text', words.trim()])
        }
        scanner.scan("{{")
        // 这个words是{{}}中间的内容
        words = scanner.scanUtil("}}")
        if (!!words) {
            // {{#arr}}开头
            if (words[0] === '#') {
                tokens.push(['#', words.substring(1)])
            // {{/arr}}结尾
            } else if(words[0] === '/'){
                tokens.push(['/', words.substring(1)])
            } else{
                tokens.push(['name', words])
            }
        }
        scanner.scan("}}")
    }
    return nextTokens(tokens) 
}
renderTemplateToDom函数

// 调用renderTemplateToDom函数,把tokens数组变成DOM结构的字符串
// 函数的功能是tokens数组结合data数据 变成dom结构

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

export default function renderTemplateToDom(tokens, data) {
    let resultStr = ''
    for (let i = 0; i < tokens.length; i++) {
        const token = tokens[i];
        if (token[0] === 'text') {
            resultStr += token[1]
        } else if(token[0] === 'name') {
            // 调用lookup函数是为了得到这里是a.b.c的形式
            resultStr += lookup(data, token[1])
        } 
        else if(token[0] === '#') { 
            // 标记的tokens 需要递归处理token[2]的嵌套数组
            // 处理数组 结合template实现递归
            // resultStr += data[token[1]]            
            resultStr += parseArray(token, data)   
        } 
    }
    return resultStr
}
lookup函数

功能是 在dataObj对象中,寻找连续点符号的keyName属性

export default function lookup(dataObj, keyName){
    // 存在a.b.c的情况 并且不能是.本身
    if (keyName !== '.' &&  keyName.indexOf('.' !== -1)) {
        // 方法一
        let keys = keyName.split('.')
        let temp = dataObj
        for (let i = 0; i < keys.length; i++) {
             temp = temp[keys[i]]
        }
        return temp
    }
    // 如果没有点符号 直接返回
    return dataObj[keyName]
}
nextTokens函数

功能是 折叠tokens 处理嵌套的数组
1、就是说,如果是遇到#,就进栈,遇到/就出栈,而/刚好是结束标签,
2、而出栈的是#那一项,因为现在没有将出栈的收集起来,所以default做的是将text的收集

export default function nextTokens(tokens){
    let nextTokens = []
    let sections = []
    let collector = nextTokens
    for (let i = 0; i < tokens.length; i++) {
        const token = tokens[i];
        switch (token[0]) {
            // #开始 入栈
            case '#':
                collector.push(token)
                sections.push(token)
                collector  = token[2] = []
                break;
            // /结束 出栈
            case '/':
                sections.pop()
                collector = sections.length>0?sections[sections.length - 1][2] : nextTokens
                break;
            default:
                collector.push(token)
        }
    }
    return nextTokens
}
parseArray函数

功能是 处理数组 结合renderTtemplate实现递归

import lookup from "./lookup";
import renderTemplateToDom from "./renderTemplate";
export default function  parseArray (token, data) {
    let result = ''
    let v = lookup(data, token[1])
    for (let i = 0; i < v.length; i++) {
        // 这里要考虑{{.}} 以及 a.b.c的点属性
        // 实现v['.'] = v[i]
        result += renderTemplateToDom(token[2], {
            ...v[i],
            '.': v[i]
        })
    } 
    return result
}
scanner类

创建scanner类,功能是处理扫描字符串,进行截断和返回

export default class Scanner  {
    constructor(templateStr){
        // 指针
        this.pot = 0
        // 尾巴
        this.tail = templateStr
        // 输入的字符串
        this.templateStr = templateStr  
    }
    //scan 功能弱 走过指定内容 没有返回值
    scan(tag) {
       if (this.tail.indexOf(tag) == 0) {
           this.pot += tag.length
           this.tail = this.templateStr.substring(this.pot)
       } 
    }
    //scanUtil 让指针进行扫描,直到遇见指定内容结束,并且能够遇见返回结束之前的文字
    scanUtil(stopTag){
        // 记录pot的值
        const cur_pot = this.pot
        while (!this.eos() && this.tail.indexOf(stopTag) !== 0 ) {
            this.pot++
            // 改变尾巴从当前这个指针开始,到最后的字符串
            this.tail = this.templateStr.substring(this.pot)
        }
        return this.templateStr.substring(cur_pot, this.pot)
    }
    // 判断指针是否到头
    eos(){
        return this.pot >= this.templateStr.length
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容