Vue 源码之 mustache 模板引擎(一)

简介: Vue 源码之 mustache 模板引擎(一)
前言:如果这篇文章 对你有帮助,请不要吝啬你的赞。😃

个人练习结果仓库(持续更新):Vue 源码解析

模板引擎是什么

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

image-20220312180129318

其中,Vue 中的列表渲染指令 v-for就是一种模板引擎。而插值表达式{{}}便是本次要研究的 mustache模板引擎的语法

将数据变为视图的方法

纯 DOM 法

很笨拙。需要频繁创建节点,添加数据,添加节点。

const arr = [
  {
    name: "clz",
    age: 21,
    sex: "男",
  },
  {
    name: "cc",
    age: 21,
    sex: "女",
  },
  {
    name: "赤蓝紫",
    age: 21,
    sex: "男",
  },
];

const list = document.getElementById("list");

for (let i = 0; i < arr.length; i++) {
  const li = document.createElement("li"); // 新建li元素

  const bd = document.createElement("div");
  bd.className = "bd";
  bd.innerText = arr[i].name + "的基本信息";
  li.appendChild(bd);

  const hd = document.createElement("div");
  hd.className = "hd";

  for (const item in arr[i]) {
    const p = document.createElement("p");
    p.innerText = item + ": " + arr[i][item];
    hd.appendChild(p);
  }

  li.appendChild(hd);
  list.appendChild(li);
}

image-20220312182355046

数组 join 法

本质上就是字符串拼接,只是用过数组 join 法,可以让结构变得更清晰

const arr = [
  {
    name: "clz",
    age: 21,
    sex: "男",
  },
  {
    name: "cc",
    age: 21,
    sex: "女",
  },
  {
    name: "赤蓝紫",
    age: 21,
    sex: "男",
  },
];

for (let i = 0; i < arr.length; i++) {
  list.innerHTML += [
    "<li>",
    '  <div class="hd">' + arr[i].name + "的基本信息</div>",
    '  <div class="bd">',
    "    <p>name: " + arr[i].name + "</p>",
    "    <p>age: " + arr[i].age + "</p>",
    "    <p>sex" + arr[i].sex + "</p>",
    "  </div>",
    "</li>",
  ].join("");
}

ES6 的模板字符串法

  • 反引号中,文本可以直接换行
  • 反引号中的${expression}占位符中 expression 可以为任意的 JavaScript 表达式,甚至为模板字符串
const arr = [
  {
    name: "clz",
    age: 21,
    sex: "男",
  },
  {
    name: "cc",
    age: 21,
    sex: "女",
  },
  {
    name: "赤蓝紫",
    age: 21,
    sex: "男",
  },
];

for (let i = 0; i < arr.length; i++) {
  list.innerHTML += `
      <li>
          <div class="hd">${arr[i].name} 的基本信息</div>
          <div class="bd">
            <p>name: ${arr[i].name} </p>
            <p>age: ${arr[i].age} </p>
            <p>sex: ${arr[i].sex} </p>
          </div>
        </li>
      `;
}


模板引擎 mustache

mustache 仓库

mustache 是最早的模板引擎库


<script src="./lib/mustache.js"></script>
<script>
  // console.log(Mustache)

  const templateStr = `
    <ul>
      {{ #arr }}
        <li>
          <div class="hd">{{ name }}的基本信息</div>
          <div class="bd">
            <p>name: {{ name }} </p>
            <p>age: {{ age }} </p>
            <p>sex: {{ sex }} </p>
          </div>
        </li>
      {{ /arr }}
    </ul>
  `;
  const data = {
    arr: [
      {
        name: "clz",
        age: 21,
        sex: "男",
      },
      {
        name: "cc",
        age: 21,
        sex: "女",
      },
      {
        name: "赤蓝紫",
        age: 21,
        sex: "男",
      },
    ],
  };

  const domStr = Mustache.render(templateStr, data);

  document.getElementsByClassName("container")[0].innerHTML = domStr;
</script>

引入 mustache后,就会后一个 Mustache对象,其中有一个方法 render就可以用来实现将数据变为视图。

  • render 的第一个参数是模板字符串,第二个参数是数据
  • 如果需要使用数据,直接通过 {{ }}使用即可
  • 要实现循环的话,则需要用 {{ #arr }}, {{ /arr }}包住要循环的内容

mustache 的基本使用

mustache.js

简单使用

const templateStr = `
  <h2>我是{{name}}, 年龄为{{age}}岁</h2>
`;
const data = {
  name: "clz",
  age: 21,
};

const domStr = Mustache.render(templateStr, data);

document.getElementsByClassName("container")[0].innerHTML = domStr;

image-20220312211938118

循环简单数组

循环的不是对象数组,而是简单数组时,使用 .即可

const templateStr = `
  {{#arr}}
    <h2 style="color: {{.}}">{{.}}</h2>
  {{/arr}}
`;
const data = {
  arr: ["red", "blue", "purple"],
};

const domStr = Mustache.render(templateStr, data);

document.getElementsByClassName("container")[0].innerHTML = domStr;

image-20220312212348808

数组嵌套

就是上面两部分的结合版本。

const templateStr = `
  <ul>
    {{#arr}}
      <li>
        {{name}}喜欢的颜色是:
        <ol>
          {{#colors}}
            <li>{{.}}</li>
          {{/colors}}
        </ol>
      </li>
    {{/arr}}
  </ul>
`;
const data = {
  arr: [
    {
      name: "clz",
      colors: ["red", "blue", "purple"],
    },
    {
      name: "cc",
      colors: ["white", "red", "black"],
    },
  ],
};

const domStr = Mustache.render(templateStr, data);

document.getElementsByClassName("container")[0].innerHTML = domStr;

image-20220312214124863

布尔值

和循环类似,通过使用 {{#布尔值属性}},{{/布尔值属性}},包住要条件渲染的内容即可

const templateStr = `
  {{#arr}}
    {{#show}}
      <h2>{{name}}</h2>
    {{/show}}
  {{/arr}}
`;
const data = {
  arr: [
    {
      name: "clz",
      show: true,
    },
    {
      name: "czh",
      show: false,
    },
  ],
};

const domStr = Mustache.render(templateStr, data);

document.getElementsByClassName("container")[0].innerHTML = domStr;

image-20220312214938151

通过查看 DOM 树,可以发现和 Vue 中的 v-if指令类似,是压根就没有上 DOM 树。另外,Vue 中的 v-show指令则是动态为元素添加或移除 display: none;来控制元素的显示与隐藏。

es6 之前使用 mustache

众所周知,es6 之前是没有模板字符串(反引号)的。那么方便的使用 mustache 呢?

当然,可以使用上面的数组 join 法,不过,还有一个更方便的方法。

通过使用 script标签,只要添加type text/template,然后在里面填模板字符串即可(实际上,只要不被浏览器识别即可)

<script type="text/template" id="templateStr">
  {{#arr}}
    {{#show}}
      <h2>{{name}}</h2>
    {{/show}}
  {{/arr}}
</script>

<script src="./lib/mustache.js"></script>
<script>
  const templateStr = document.getElementById('templateStr').innerHTML

  const data = {
    arr: [
      {
        name: 'clz',
        show: true
      },
      {
        name: 'czh',
        show: false
      }
    ]
  }

  const domStr = Mustache.render(templateStr, data)

  document.getElementsByClassName('container')[0].innerHTML = domStr
</script>

只能说想到这个方法的人太优秀了

mustache 底层原理

正则表达式实现最简单的 mustache

String.prototype.replace()

在开始之前,首先需要了解一下字符串的 replace方法

语法

str.replace(regexp|substr, newSubStr|function)

参数

  • regexp (pattern):一个RegExp 对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。
  • substr (pattern):一个将被 newSubStr 替换的 字符串。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。
  • newSubStr (replacement):用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名。参考使用字符串作为参数
  • function (replacement):一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考指定一个函数作为参数

返回值

一个部分或全部匹配由替代模式所取代的新的字符串。

const templateStr = `
  <h2>我是{{name}}, 年龄为{{age}}岁</h2>
`;
const data = {
  name: "clz",
  age: 21,
};

console.log(templateStr.replace(/\{\{\w+\}\}/g, "123"));

image-20220313091349829


可以发现,上面的做法还无法实现,所以研究一下,第二个参数为函数的情况

变量名 代表的值
match 匹配的子串。(对应于上述的$&。)
p1,p2, ... 假如 replace()方法的第一个参数是一个RegExp 对象,则代表第 n 个括号匹配的字符串。(对应于上述的$1,$2 等。)例如,如果是用 /(\a+)(\b+)/ 这个来匹配,p1 就是匹配的 \a+p2 就是匹配的 \b+
offset 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将会是 1)
string 被匹配的原字符串。
NamedCaptureGroup 命名捕获组匹配的对象
const templateStr = `
  <h2>我是{{name}}, 年龄为{{age}}岁</h2>
`;
const data = {
  name: "clz",
  age: 21,
};

templateStr.replace(/\{\{(\w+)\}\}/g, (match, p1, offset, string) => {
  console.log(match);
  console.log(p1);
  console.log(offset);
  console.log(string);
});

image-20220313092139759

可以发现,只需要在正则表达式中使用 ()把要捕获的内容包起来,然后通过 replace方法的函数参数中的 p1 参数获取捕获内容,既然如此,那就可以开始使用正则表达式实现简单的 mustache 了。

实现简单的 mustache

const templateStr = `
  <h2>我是{{name}}, 年龄为{{age}}岁</h2>
`;
const data = {
  name: "clz",
  age: 21,
};

const render = (templateStr, data) => {
  return templateStr.replace(
    /\{\{(\w+)\}\}/g,
    function (match, p1, offset, string) {
      return data[p1]; // 把正则所匹配的内容替换成return的内容
    }
  );
};

const domStr = render(templateStr, data);
document.querySelector(".container").innerHTML = domStr;


mustache 底层 tokens 原理

image-20220313094603475

mustache 底层主要干两件事

  • 将模板字符串编译为 tokens 形式
  • tokens 结合数据,解析为 dom 字符串

tokens 是什么

  • tokens 是一个嵌套数组,也可以说是模板字符串的 JS 表示
  • tokens抽象语法树(AST)、虚拟节点的开山鼻祖

看下下面的例子,就能明白了


简单 tokens

模板字符串

<h2>我是{{name}}, 年龄为{{age}}岁</h2>


tokens

[
  ["text", "<h2>我是"],
  ["name", "name"],
  ["text", ", 年龄为"],
  ["name", "age"],
  ["text", "岁</h2>"],
];

简单数组情况下的 tokens

模板字符串

{{#arr}}
<h2 style="color: {{.}}">{{.}}</h2>
{{/arr}}


tokens

[
    ["#", "arr", [
        ["text": "<h2 style='color: "],
        ["name": "."],
        ["text": "'>"],
        ["name", "."],
        ["text", "</h2>"]
    ]]
]

嵌套数组情况下的 tokens

模板字符串

<ul>
  {{#arr}}
  <li>
    {{name}}喜欢的颜色是:
    <ol>
      {{#colors}}
      <li>{{.}}</li>
      {{/colors}}
    </ol>
  </li>
  {{/arr}}
</ul>


tokens

[
    ["text", "<ul>"],
    ["#", "arr", [
        ["text", "li"],
        ["name", "name"],
        ["text": "喜欢的颜色是:<ol>"],
        ["#", "colors", [
            ["text", "<li>"],
            ["name", "."],
            ["text", "</li>"]
        ]],
        ["text", "</ol></li>"]
    ]],
    ["text", "</ul>"]
]


查看 mustache 的 tokens

进入之前下载的源码文件中, ctrl+f,搜索 parseTemplate,到该方法最后把返回值存好并打印

image-20220313122831330


重新去跑mustache 的基本使用的代码,就可以在控制台中看到 tokens

如循环简单数组

image-20220313123423702

学习链接:【尚硅谷】Vue 源码解析之 mustache 模板引擎

目录
相关文章
|
2月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
281 2
|
26天前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
256 137
|
5月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
720 0
|
5月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
4月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
384 1
|
4月前
|
JavaScript 安全
在 Vue 中,如何在回调函数中正确使用 this?
在 Vue 中,如何在回调函数中正确使用 this?
223 0
|
5月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
264 1
|
7月前
|
JavaScript
vue实现任务周期cron表达式选择组件
vue实现任务周期cron表达式选择组件
964 4
|
6月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
769 77
|
7月前
|
缓存 JavaScript 前端开发
Vue 基础语法介绍
Vue 基础语法介绍