Vue源码之mustache模板引擎 01

317 阅读2分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。

什么是模板引擎

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

[
  {"name":"小明","age":12,"sex":"m"},
  {"name":"小红","age":11,"sex":"f"}
]

视图:

<ul>
  <li>
  	<div>小明的基本信息</div>
  	<div>
  	  <p>姓名:小明</p>
  	  <p>年龄:12</p>
  	  <p>性别:男</p>
  	</div>
  </li>
</ul>

<li v-for="item in arr"></li>

就是一种模板引擎!!

历史上曾经出现的数据变为视图的方法

纯DOM法

我自己写完之后才发现,纯DOM方法是真的很复杂(扶额)

<body>
    <ul id="list"></ul>
    <script>
        let list = document.querySelector("#list");

        for (let index = 0; index < arr.length; index++) {
            let li = document.createElement("li");
            let infoDiv = document.createElement("div");
            infoDiv.className = "info";
            list.appendChild(li);
            li.appendChild(infoDiv);

            let p1 = document.createElement("p");
            p1.innerHTML = "姓名:" + arr[index].name;

            let p2 = document.createElement("p");
            p2.innerHTML = "年龄:" + arr[index].age;

            let p3 = document.createElement("p");
            p3.innerHTML = "性别:" + arr[index].sex;

            infoDiv.appendChild(p1);
            infoDiv.appendChild(p2);
            infoDiv.appendChild(p3);
        }
    </script>
</body>

数组join法

其实就是数组转换成字符串。浏览器将字符串转换成DOM结构。

<body>
    <ul id="list"></ul>
    <script>
        let list = document.querySelector('#list')

        for (let index = 0; index < arr.length; index++) {
            list.innerHTML += [
                '<li>',
                '  <div class="info">',
                '    <p>姓名:' + arr[index].name + '</p>',
                '    <p>年龄:' + arr[index].age + '</p>',
                '    <p>性别:' + arr[index].sex + '</p>',
                '  </div>',
                '</li>'
            ].join("");
        }
    </script>
</body>

ES6的反引号法

本质也是拼接,但是比数组来得更美观。 同时,利用${}就可以向元素添加内容。不再需要拼接字符串。

<body>
    <ul id="list"></ul>
    <script>
       let list = document.getElementById("list");
        for (let index = 0; index < arr.length; index++) {
            list.innerHTML += `
                <li>
                    <div>
                        <p>姓名:${arr[index].name}</p>
                        <p>年龄:${arr[index].age}</p>
                        <p>性别:${arr[index].sex}</p>
                    >/div>
                </li>
            `
        }
    </script>
</body>

mustache库基本使用

  • 必须要引入mustache库,可以在bootcdn.com找到打包好的;
  • 利用Mustache的render方法将模板和数据合并,生成能打印在控制台的dom结构。

循环对象数组

<body>
    <ul id="list"></ul>
    <script src="jslib/mustache.js"></script>
    <script>
        // 循环数组
        var templateStr = `
            <ul>
                {{#arr}}
                <li>
                    <div>
                        <p>姓名:{{name}}</p>
                        <p>年龄:{{age}}</p>
                        <p>性别:{{sex}}</p>
                    </div>
                </li>
                {{/arr}}
            </ul>
        `;

        var domStr = Mustache.render(templateStr, data);
        console.log(domStr);

        let list = document.getElementById("list");
        list.innerHTML = domStr;
    </script>
</body>

image.png

循环简单数组

<body>
    <div id="list"></div>
    <script>
        var templateStr = `
            <ul>
                {{#arr}}
                <li>
                    {{.}}
                </li>
                {{/arr}}
            </ul>
        `;

        var data = {
            arr: ["a", "b", "c"],
        }

        var domStr = Mustache.render(templateStr, data);
        console.log(domStr);

        let div = document.querySelector("div");
        div.innerHTML = domStr;
    </script>
</body>

image.png

不循环

<body>
    <div></div>
    <script>
        var templateStr = `
            <h1>今天是星期{{day}},天气{{weath}}</h1>
       `;
        var data = {
            day: "天",
            weath: "小雪"
        };
        var domStr = Mustache.render(templateStr, data);
        console.log(domStr);

        let div = document.querySelector("div");
        div.innerHTML = domStr;
    </script>
</body>

image.png

循环嵌套数组

<body>
    <div id="list"></div>
    <script>
        var templateStr = `
            <ul>
                {{#arr}}
                <li>
                    <p>姓名:{{name}}</p>
                    <p>年龄:{{age}}</p>
                    <p>性别:{{sex}}</p>
                    <ol>
                        {{#hobbies}}
                            <li>{{.}}</li>
                        {{/hobbies}}
                    </ol>
                </li>
                {{/arr}}
            </ul>
        `;

        var data = {
            arr: [{
                    name: '小明',
                    age: 12,
                    sex: 'f',
                    hobbies: ["第一个", "第二个"]
                },
                {
                    name: '小红',
                    age: 11,
                    sex: 'm',
                    hobbies: ["第三个", "第四个"]
                },
                {
                    name: '小强',
                    age: 13,
                    sex: 'f',
                    hobbies: ["第五个", "第六个"]
                },
            ]
        }

        var domStr = Mustache.render(templateStr, data);
        console.log(domStr);

        let div = document.querySelector("div");
        div.innerHTML = domStr;
    </script>
</body>

image.png

布尔值

<body>
    <div id="list"></div>
    <script>
        var templateStr = `
            {{#m}}
                <h6>这个会出现</h6>
            {{/m}}
        `;

        var data = {
            m: true,
        }

        var domStr = Mustache.render(templateStr, data);
        console.log(domStr);

        let div = document.querySelector("div");
        div.innerHTML = domStr;
    </script>
</body>

image.png

mustache的底层核心机理

最简单的模板引擎实现机理,正是利用正则表达式中的replace()方法。(替代) replace()的第二个参数可以是一个函数,这个函数提供捕获的内容的参数,即$1
正则表达式思路: 通过调用replace方法,设计{{}}为目标查找对象,即查找以{{开头,}}结尾,并且中间是字符的一段内容,精确捕获{{}}中的内容,替换成data中对应属性名的属性值。(这里用到了ES6新增的在对象字面量中直接动态命名属性

<body>
    <div class="box"></div>
    <script>
        var templateStr = "<h3>我觉得{{name}}好{{appear}}</h3>";

        var data = {
            name: "道枝骏佑",
            appear: "帅",
        }


        function render(templateStr, data) {
            return templateStr.replace(/\{\{(\w+)\}\}/g, function (findStr, $1) {
                return data[$1]; // 相当于 data.属性名
            })
        };

        let box = document.querySelector(".box");
        var result = render(templateStr, data);
        console.log(result);

        box.innerHTML = result;
    </script>
</body>

但是!当模板字符串较为复杂时,正咋表达式就心有余而力不足了,这个时候,就可以用正则的思想改进一下了!

底层tokens思想

什么是tokens? tokens是一个JS的嵌套数组(即模板字符串的JS表示)
image.png

mustache库的机理 image.png

image.png

循环状态下的tokens:当模板字符串中有循环存在时,它将被变异成嵌套更深的tokens。

mustache底层库重点完成

  • 将模板字符串变异成tokens形式;
  • 将tokens结合数据,解析为dom字符串。

下面,我将用另外的篇幅来专门记录手写mustache库。