Vue之mustache模板引擎

参考

  • 尚硅谷邵山欢(考拉老师)Vue之mustache模板引擎
  • 为什么要学Vue源码
    • Vue组件由哪三部分组成?
    • Vue父子组件如何通信?
    • Vuex的作用是什么?
    • Vue是怎么实现双向绑定的?
    • Vue的最小化更新过程是怎样的?
    • Vue如何实现指令系统的?
  • 中高级前端、leader职位必会底层知识
    • 为企业“造轮子”,开发通用组件
    • 解决编程中遇见的问题
    • 解决效率问题
  • vue源码非常庞大,各种机理很多:模板技术、数据劫持、虚拟节点、最小量更新、抽象语法树……
  • 手写底层源码,拒绝纸上谈兵,让同学能实打实的提升编程
  • 核心机理是共通的、永恒的。

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

  • 纯DOM法:非常笨拙,没有实战价值

  • 数组join法: 曾几何时非常流行,是曾经的前端必会知识

  • ES6的反引号法:ES6中新增的${a}语法糖,很好用

  • 模板引擎:解决数据变为视图的最优雅的方法

  • 纯DOM法

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>mustache</title>
</head>
<body>
  <ul id="list"></ul>
  <script>
    var arr = [
      {"name": "小明", "age":12,"sex":"男"},
      {"name": "小红", "age":13,"sex":"女"},
      {"name": "小强", "age":14,"sex":"男"}
    ]
    var list = document.getElementById('list')
    for (var i=0,len = arr.length;i<len;++i){
      // 遍历每一项,都需要的DOM的方法创建li标签
      var oli = document.createElement("li")
      var hdDiv = document.createElement("div")
      hdDiv.className = 'hd'
      hdDiv.innerText = arr[i].name + '的基本信息'
      var bdDiv = document.createElement('div')
      bdDiv.className = 'bd'
      var p1 = document.createElement('p')
      p1.innerHTML = '姓名: ' + arr[i].name
      bdDiv.appendChild(p1)
      var p2 = document.createElement('p')
      p2.innerHTML = '年龄: ' + arr[i].age
      bdDiv.appendChild(p2)
      var p3 = document.createElement('p')
      p3.innerHTML = '性别: ' + arr[i].sex
      bdDiv.appendChild(p3)
      // 创建的节点是孤儿节点,所以必须要上DOM树才能被用户看见
      oli.appendChild(hdDiv)
      oli.appendChild(bdDiv)
      list.appendChild(oli)
    }
  </script>
</body>
</html>
  • 数组join方法 , html那块的文本有格式化的感觉
    for (var i=0,len = arr.length;i<len;++i){
      list.innerHTML += [
        '<li>',
        '  <div class="hd">' +arr[i].name + '的基本信息</div>',
        '  <div class="bd">',
        '    <p>姓名:'+arr[i].name+'</p>',
        '    <p>年龄:'+arr[i].age+'</p>',
        '    <p>性别:'+arr[i].sex+'</p>',
        '  </div>',
        '</li>'
      ].join('')
    }
  • ES6 反引号
    for (var i=0,len = arr.length;i<len;++i){
      list.innerHTML += `
        <li> 
          <div class="hd">${arr[i].name}的基本信息</div>  
          <div class="bd">    
            <p>姓名:${arr[i].name}</p>    
            <p>年龄:${arr[i].age}</p>    
            <p>性别:${arr[i].sex}</p>  
          </div>
        </li>
      `
    }

mustache

  • mustache官方git
  • mustache是“胡子”的意思,因为它的嵌入标记{{ }}非常像胡子,也被Vue沿用
  • mustache是最早的模板引擎库,比Vue诞生的早多了,它的底层实现机理在当时是非常有创造性的、轰动性的,为后续模板引擎的发展提供了崭新的思路
  • bootcdn加速
<body>
  <ul id="list">  </ul>
  <script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.1.0/mustache.js"></script>
  <script type ="data" id="tmpstr">
    {{#arr}}
      <li> 
        <div class="hd">{{name}}的基本信息</div>  
        <div class="bd">    
          <p>姓名:{{name}}</p>    
          <p>年龄:{{age}}</p>    
          <p>性别:{{sex}}</p>  
        </div>
      </li>
    {{/arr}}
  </script>
  <script>
    var data = {
      arr: [
        {"name": "小明", "age":12,"sex":"男"},
        {"name": "小红", "age":13,"sex":"女"},
        {"name": "小强", "age":14,"sex":"男"}
      ]
    }
    var list = document.getElementById('list')
    var tmpstr = document.getElementById('tmpstr').innerText ;// 这样把模板放到一个script (type 不能是能执行的脚本类型就行)
    document.getElementById('list').innerHTML = Mustache.render(tmpstr,data)
  </script>
</body>

正则表达式

  • 在较为简单的示例情况下,可以用正则表达式实现
  • 最简单的模板引擎的实现机理,利用的是正则表达式中的replace方法
function render(templateStr, data) {
 	return templateStr.replace(/\{\{(\w+)\}\}/g,function(findStr, $1){
   		return data[$1]
 })

自定义

  • mustache库机理
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 模块化打包工具有webpack (webpack-dev-server)、rollup、Parcel等
  • mustache官方库使用rollup进行模块化打包, 使用webpack(webpack-dev-server)进行模块化打包,能让我们更方便地在浏览器中(而不是nodejs环境中)实时调试程序,相比nodejs控制台,浏览器控制台更好用,比如能够点击展开数组的每项。
  • 生成库是UMD的,这意味着它可以同时在nodejs环境中使用,也可以在浏览器环境中使用。实现UMD不难,只需要一个“通用头”即可。
function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global = global || self, global.Mustache = factory());
  }(this, function () {})
  • mustache.ts 可以模块化开发
class Scanner {
  pos: number // 指针
  tail: string // 尾巴,一开始就是模板字符串原文
  templateStr: string //  模板字符串
  constructor(templateStr: string){
    this.pos = 0
    this.tail = templateStr
    this.templateStr = templateStr
  }
  scan(stopTag: string):void {
    if (this.tail.indexOf(stopTag) === 0) {
      this.pos += stopTag.length
      this.tail = this.templateStr.substring(this.pos);
    }
  } // 走过指定的内容,没有返回值
  // 让指针进行扫描,直到遇见指定的内容结束,并且能够返回结束之前路过的文字
  scanUtil(stopTag: string): string {
    const pos_backup = this.pos 
    // 当尾巴的开头不是stopTag 的时候,就说明还没有扫描到stopTag
    while(this.tail.indexOf(stopTag) !==0 && !this.eos()){
      this.pos++;
      // 改变尾巴从当前指针的这个字符开始到最后
      this.tail = this.templateStr.substr(this.pos)
    }
    return this.templateStr.substring(pos_backup, this.pos)
  }
  // 判断指针是否走到了头
  eos(): boolean{
    return this.pos >= this.templateStr.length
  }
}

function nestTokens(tokens) {
  let nestedTokens = [] // 结果数组
  let sections = [] // 一个栈结构
  let collector = nestedTokens // 收集器
  for (let i=0,len = tokens.length ;i<len ;++i) {
    let token = tokens[i]
    switch (token[0]) {
      case '#':
        collector.push(token)
        sections.push(token)
        collector = token[2] = [] // 给token 添加下标为2的项 并且让收集器指向它
        break
      case '/':
        sections.pop()
        collector = sections.length >0 ? sections[sections.length - 1][2]:nestedTokens
        break
      default:
        collector.push(token)
    }
  }
  return nestedTokens
}

function parseTemplateToTokens(templateStr: string) {
  let tokens = []
  let scanner = new Scanner(templateStr)
  let words:string
  while(!scanner.eos()){
    words = scanner.scanUtil('{{')
    if (words !== '' ){
      tokens.push(['text',words.replace(/\s+/g,' ')]) // 将多个空格合并成一个
    }
    scanner.scan('{{')
    words = scanner.scanUtil('}}')
    if (words!==''){
      if (words[0]==='#'){
        tokens.push(['#', words.substring(1)])
      } else if (words[0] === '/') {
        tokens.push(['/', words.substring(1)])
      } else {
        tokens.push(['name', words])
      }
    }
    scanner.scan('}}')
  }
  return nestTokens(tokens)
} // 将模板字符串转成tokens数组

function lookup (dataObj, keyName:string) {
  // 判断的时候不能是. 本身
  if (keyName.indexOf('.') !== -1 && keyName !== '.')  {
    let keys = keyName.split('.')
    let temp = dataObj
    for (let i=0, len = keys.length; i<len ; ++i) {
      temp = temp[keys[i]]
    }
    return temp
  }
  return dataObj[keyName] // 没有'.'
} // 处理级联属性 'a.b'


function parseArray(token, data) {
  let v = lookup(data, token[1]) as []
  let resultStr = ''
  for (let i=0, len = v.length ;i<len ;++i) {
    resultStr += renderTemplate(token[2], {
      ...(v[i] as {}),
      '.': v[i]
    })
  }
  return resultStr
}// 处理数组渲染


function renderTemplate (tokens:[], data:{} | []) {
  let resultStr = ''
  for (let i=0,len = tokens.length ; i<len ; ++i ){
    let token = tokens[i]
    if (token[0] === 'text') {
      resultStr += token[1]
    } else if (token[0] === 'name') {
      resultStr += lookup (data, token[1] as string)
    } else if (token[0] === '#') {
      resultStr += parseArray(token, data)
    }
  }
  return resultStr
} // 将tokens数组转成dom字符串



function render (templateStr, data) {
  let tokens = parseTemplateToTokens(templateStr)
  console.log(tokens)
  let result = renderTemplate(tokens,data)
  console.log (result)
  return result
}
  • index.html
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>mustache</title>
</head>
<body>
  <div id ="container">
  </div>
  <script src="TS/mustache.js"></script>
  <script>
    var templateStr = `
      <ol>
        {{#students}}
        <li> 
          学生{{name}} 好朋友是{{friend.name}}  自己的爱好是
          <ol>
            {{#hobbies}}
            <li>{{.}}</li>
            {{/hobbies}}
          </ol>
        </li>
        {{/students}}
      </ol>
    `
    var data = {
      students: [
        {name: "小明", hobbies: ['编程', '打游戏'], friend: {name: '小七'}},
        {name: "小红", hobbies: ['追剧'], friend: {name: '小紫'}}
      ]
    }
   document.getElementById("container").innerHTML = render(templateStr,data)
  </script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值