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

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

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


抽空把之前学的东西写成笔记。

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

模板引擎是什么

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

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标签,只要添加typetext/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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值