今儿,小计一则开源模板 template_js 读后笔记

之前在阅读《现代JavaScript库开发》一书时,作者之一的自我介绍中有提及template.js库,抱着好奇心于是去看了下,由于笔者入行时间较晚,但是一眼还是看出,这貌似就是当时php那种模板的语法,想着来都来了,那就看看咋写的。

1、使用示例子

<ul>
    <%for(var i = 0; i < list.length; i++) {%>
        <li><%:=list[i].name%></li>
    <%}%>
</ul>

const data = {
    list: [
        {name: "yan"},
        {name: "haijing"}
    ]
};

template(tpl,data)

懂事的已经能够猜到,这玩意儿就是传入模板+数据,返回渲染好的html,然后挂载到指定页面容器中(想了想,现在vue、react前端框架貌似也是这种思路,解析模板+数据组装,最后返回html)

2、查看源码

之前以为会类似vue类框架一样,依赖某些解析模板的库,根据模板的规则,解析出来那样,但仔细一看,只有区区三百多行,并没有依赖什么额外的库,更加激起了欲望

// 借鉴一下template
;(function(root,factory) {
    var template = factory(root)
    // 进行一下模块化检测
    if(typeof define === 'function' && define.amd) {
        // AMD
        define('template',function() {
            return template
        })
    }else if(typeof exports === 'object') {
        // Node.js
        module.exports = template
    }else{
        var _template = root.template
        // 添加一个防止重复安装的函数
        root.noConflict = function () {
            if(root.template === template) {
                root.template = _template
            }
            return template
        }
        root.template = template
    }
})
(window,function(root) {
	...// 核心处理逻辑
});

首先是一个很常规的通用js模块写法(对几种环境的检测,核心处理函数)

3、核心处理

通常我们会从主入口下手,好比vue,则是从new Vue()这一步开始,看看它到底干了什么,这里引入插件后,会暴露一个template变量,使用方法是直接调用它,那么我们只需要找到这个函数即可

function template(tpl, data) {
        if (typeof tpl !== 'string') {
            return '';
        }
        var fn = compile(tpl);
        if (!isObject(data)) {
            return fn;
        }
        return fn(data);
    }

可以看到,这个主函数很简单,大体分为两步:
1、编译模板,并且返回一个函数
2、给返回的函数传入渲染数据
由上可以看出,该库核心点就是模板语法处理+数据渲染,最后返回渲染好的html

4、查看compile和compiler

function compile(tpl, opt) {
   		...
        try {
        	// 看样子用compiler又包了一层
            var Render = compiler(tpl, opt);
        } catch(e) {
         ...
        }
		// 返回的渲染函数
        function render(data) {
           ...
        }
        render.toString = function () {
            return Render.toString();
        };
        return render;
    }

继续查看compiler,由于篇幅过程,只保留核心点

function compiler(tpl, opt) {
        var mainCode = parse(tpl, opt);

        var headerCode = '\n' + 
        '    var html = (function (__data__, __modifierMap__) {\n' + 
        ...

        var footerCode = '\n' + 
      	...
        '    return html;\n';

        var code = headerCode + mainCode + footerCode;
        code = code.replace(/[\r]/g, ' '); // ie 7 8 会报错,不知道为什么
        try {
            var Render = new Function('__data__', '__modifierMap__', code); 
           ...
            return Render;
        } catch(e) {
           ...
        }  
    }

看到这里一下子有点懵逼(额,和预想的不太一样呢?),仔细一看,有几个代码的字符串以及new Function这种,看到new Function 大致就能猜到,应该是动态生成了一个函数执行的思路,但是还是没搞懂为啥是这样
还好在README.md中看到相关链接中有一则只有20行Javascript代码!手把手教你写一个页面模板引擎,于是就去看了下才恍然大悟,看来对于开源库,良好的README.md 还是非常重要的

链接在那儿了,就不啰嗦了,简单介绍下思路

5、页面模板引擎思路

假设,现在你有如下模板和数据

let template = 'Hello, my name is <%name%>. I\'m <%age%> years old.';
let data = { name: '这小子',age: '25' }

常规思路:根据特定标记,把变量给分割出来,然后把数据放进去,完成渲染,这一步大部分分也能完成,但是对于复杂的就不太理想了(再加点 if else 啥的),于是有了如下思路

1、把字符串分割为纯字符串、代码
2、构造一个函数,去执行上面生成的代码
3、最后返回 纯字符串+函数执行后生成的字符串

// 模板字符串
let template = 'Hello, my name is <%this.name%>. I\'m <%this.age%> years old.';
// 构造一个渲染函数
let TemplateEngine = function (tpl,data) {
            let re = /<%([^%>]+)?%>/g,
                match,
                code = 'var r=[];\n',
                cursor = 0;

            let add = function(line,js) {
                js? code += 'r.push(' + line + ');\n' :
                    code += 'r.push("' + line.replace(/"/g, '\\"') + '");\n';
            }

            while(match = re.exec(tpl)) {
                add(tpl.slice(cursor,match.index))
                add(match[1],true)
                cursor = match.index + match[0].length
            }

            add(tpl.substr(cursor, tpl.length - cursor))
            code += 'return r.join("");'
         
            return new Function(code.replace(/[\r\t\n]/g, '')).apply(data)
        }

1、利用正则把特定的 纯字符串+带变量的找出来
2、用一个数组来存放,最后转成字符串
3、构造一个函数执行,并且指定执行环境

上述模板解析后的code为如下

var r=[];
r.push("Hello, my name is ");
r.push("this.name");
r.push(". I'm ");
r.push("this.profile.age");
return r.join("");
// 也就是把这些传入 new Function 中并且执行,就会得到我们想要的东西了

到这里你可能会问,那复杂的模板怎么搞呢,什么 for if 啥的,其实不用担心,既然用了new Function,就是用来解决这些问题的,也就是会把js代码转化到函数内部去执行,其他的字符串自己拼接就可以了,其他的细节可以去看原文就不多说了

6、总结

再回到template.js中,作者处理更为细致些,同时也额外提供一些api,可以自定义注入一些配置项,总之学习一些优秀的库,可以学到更规范更合理的开发规则和思路

你可能感兴趣的:(笔记,javascript,开源,前端)