Home > Web Front-end > JS Tutorial > body text

Simple js template engine writing method

一个新手
Release: 2017-10-20 10:30:03
Original
2413 people have browsed it

Front

There are many, many js template engines. I used to often use art-template, and sometimes vue was used as a template engine.

Until...

At the beginning of the year, I was still in the previous project team. At that time, the code specification wasYou cannot use [external code] without permission,Oops.

If there is a need, then write it, but later it is not used for some reasons. Later, we divided the production line and built a set of builds by myself. After using it for a few months, I felt very comfortable. I rewrote this small piece of code according to more popular standards and shared it with everyone.

https://github.com/shalldie/mini-tpl

Grammar

The first is to choose template syntax, ejs syntax is the first choice, because it is popular, there is no need to learn it Things like directive template engines.

If you have written jsp or asp/asp.net, you can get started directly.

How to use it?

I want to use it this way

<body>
        <p id="root"></p>
        <script id="tplContent" type="text/html">
        <ul>
            <% for(var i=0; i<data.length; i++){
                var item = data[i];
                if(item.age < 30){%>
                    <li>我的名字是<%=item.name%>,我的年龄是<%=item.age%></li>
                <%}else{%>
                    <li>my name is <%=item.name%>,my age is a sercet.</li>
                <%}%>
            <% } %>
        </ul>
        </script>
        <script src="../build/mini-tpl.min.js"></script>
        <script>
            var data = [{ name: &#39;tom&#39;, age: 12 }, { name: &#39;lily&#39;, age: 24 }, { name: &#39;lucy&#39;, age: 55 }];
            var content = document.getElementById(&#39;tplContent&#39;).innerHTML;
            var result = miniTpl(content, data);
            document.getElementById(&#39;root&#39;).innerHTML = result;
        </script>
    </body>
Copy after login

If you want to use it this way, then let’s analyze how to achieve it.

new Function

    1 const content = &#39;console.log("hello world");&#39;;    
    2 
    3 let func = new Function(content);    
    4 
    5 func(); // hello world
Copy after login
new Function ([arg1[, arg2[, ...argN]],] functionBody)
Copy after login

functionBody A string <strong></strong> containing a JavaScript statement that includes the function definition.

Functions generated using the Function constructor do not create closures in the context in which they are created; they are generally created in the global scope.
When these functions are run, they can only access their own local variables and global variables, and cannot access the scope of the context generated by the Function constructor being called. (MDN)

That is to say:

  1. You can use new Function to dynamically create a function to execute a dynamically generated function definition js statement.

  2. The function generated through new Function has a global scope.

  3. There are three ways to pass parameters: Put the variable globally (nonsense), Function parameter passing,Use call/ apply passes the value to this of the function.

At first I used call to pass the value, but now that I thought about it it was not very elegant, so I changed it to passing it by parameters. That's it:

const content = &#39;console.log(data);&#39;;
    
    let func = new Function(&#39;data&#39;, content);
    
    func(&#39;hello world&#39;); // hello world
Copy after login

So far, the prototype is here. Let’s break it down below.

Template split

Look at the template first:

<% for(var i=0; i<data.length; i++){        
var item = data[i];        
if(item.age < 30){%>
            <li>我的名字是<%=item.name%>,我的年龄是<%=item.age%></li>
        <%}else{%>
            <li>my name is <%=item.name%>,my age is a sercet.</li>
        <%}%>
    <% } %>
Copy after login

js logical part, wrapped by <%%> , The placeholder for the js variable is wrapped by <%= %>, and the rest is the ordinary html string part to be spliced.

In other words, there are three types of parts that need to be found using regular expressions:

  1. <%%> The js content of the logical part

  2. ##<%=%> The js content of the placeholder part

  3. Others

    plain text Content

The second item, the js placeholder part, is also a spliced ​​text. So they can be put together, that is,

js part and splicing part.

Regular extraction

Of course, choose regular expressions!

Here we will first expand on the content of pseudo arrays and how the browser console treats pseudo arrays. Array:

Don’t go too far, let’s just say the conclusion:

As long as there is a length attribute

of int type, there is a function splice attribute of type. Then the browser will think it is an array.

If other properties inside are sorted by index, they can even be displayed on the console like items in an array.

This method of judgment is called

duck typing. If something looks like a duck and quacks like a duck, then it is a duck 0_o

Back to text , this requires extracting the js logical part and text from the template multiple times.

For each extraction, the extracted content must be obtained. This time, the last index item is matched (used to lift the text content). So I chose RegExp.prototype.exec .

For example, RegExp.prototype.exec returns a collection (pseudo array), and its type is like this:

Property/IndexDescription##[0][1],...[n]indexinput##

通过这样,就可以拿到匹配到的 js 逻辑部分,并通过 index 和本次匹配到的内容,来获取每个js逻辑部分之间的文本内容项。

要注意,在全局匹配模式下,正则表达式会接着上次匹配的结果继续匹配新的字符串。

    /**
     * 从原始模板中提取 文本/js 部分
     * 
     * @param {string} content 
     * @returns {Array<{type:number,txt:string}>} 
     */
    function transform(content) {
        var arr = [];                 //返回的数组,用于保存匹配结果
        var reg = /<%(?!=)([\s\S]*?)%>/g;  //用于匹配js代码的正则
        var match;   				  //当前匹配到的match
        var nowIndex = 0;			  //当前匹配到的索引        

        while (match = reg.exec(content)) {
            // 保存当前匹配项之前的普通文本/占位
            appendTxt(arr, content.substring(nowIndex, match.index));
            //保存当前匹配项
            arr.push({
                type: 1,  //js代码
                txt: match[1]  //匹配到的内容
            });
            //更新当前匹配索引
            nowIndex = match.index + match[0].length;
        }
        //保存文本尾部
        appendTxt(arr, content.substr(nowIndex));
        return arr;
    }

    /**
     * 普通文本添加到数组,对换行部分进行转义
     * 
     * @param {Array<{type:number,txt:string}>} list 
     * @param {string} content 
     */
    function appendTxt(list, content) {
        content = content.replace(/\r?\n/g, "\\n");
        list.push({ txt: content });
    }
Copy after login

得到了js逻辑项 和 文本内容 ,就可以把他们拼在一起,来动态生成一个function。要注意的是,文本内容中,包含 js占位项,这个地方要转换一下。

    /**
     * 模板 + 数据 =》 渲染后的字符串
     * 
     * @param {string} content 模板
     * @param {any} data 数据
     * @returns 渲染后的字符串
     */
    function render(content, data) {
        data = data || {};
        var list = [&#39;var tpl = "";&#39;];
        var codeArr = transform(content);  // 代码分割项数组

        for (var i = 0, len = codeArr.length; i < len; i++) {
            var item = codeArr[i]; // 当前分割项

            // 如果是文本类型,或者js占位项
            if (!item.type) {
                var txt = &#39;tpl+="&#39; +
                    item.txt.replace(/<%=(.*?)%>/g, function (g0, g1) {
                        return &#39;"+&#39; + g1 + &#39;+"&#39;;
                    }) + &#39;"&#39;;
                list.push(txt);
            }
            else {  // 如果是js代码
                list.push(item.txt);
            }
        }
        list.push(&#39;return tpl;&#39;);

        return new Function(&#39;data&#39;, list.join(&#39;\n&#39;))(data);
    }
Copy after login

这样就完成了简易的模板引擎,不要觉得拼字符串慢。

在现代浏览器(IE8开始)中,特地对字符串的操作做了大量的优化,用 += 拼字符串,要比用数组 push 再 join 的方式快很多很多,即使放到IE7(IE6不清楚)中,我这里测试也是拼字符串快。。。

The above is the detailed content of Simple js template engine writing method. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
Match All strings
Group capture in brackets
The matched character is at the 0-based index of the original string
Original string