This note is from the Silicon Valley – Vue source code parsing series of courses.

Mustache

Mustache in Vue is a template engine in Vue, and the V-for command is also an application of a template engine.

A template engine is the most elegant solution for turning data into views.

Data:

[{"name":"Xiao Ming"."age":12."sex":"Male"},
   {"name":"Little red"."age":11."sex":"Female"},
   {"name":"Small strong"."age":13."sex":"Male"}]Copy the code

View:

<ul>
   <li>
   	<div class="hd">Basic information about Xiao Ming</div>
      <div class="bd">
      	<p>Name: Xiao Ming</p>
         <p>Age: 12</p>
         <p>Gender: male</p>
      </div>
   </li>
   <li>.</li>
</ul>
Copy the code

The historical scheme

  • Pure DOM methods: Very clumsy and have no real value

  • Array Joins: Used to be very popular and a prerequisite for the front end

  • ES6 backquotes: the new ‘${a}’ syntactic sugar in ES6 (backquotes fail, single quotes are used instead) is very useful

  • Template engine: The most elegant solution for turning data into views

Pure DOM method

<body>
 <ul id="list"></ul>
 <script>
    let data = [
       {"name":"Xiao Ming"."age":12."sex":"Male"},
       {"name":"Little red"."age":11."sex":"Female"},
       {"name":"Small strong"."age":13."sex":"Male"}]let list  = document.querySelector('#list')
    // The DOM is generated once for each loop. This is just a simple generation. If you implement the view above, you need to create more DOM
    for(let obj of data){
       for(let item in obj){
          let tmp = document.createElement('li')
          tmp.innerText=obj[item]
          list.appendChild(tmp)
       }
       // Split line for aesthetics
       list.appendChild(document.createElement('hr'))}</script>
</body>
Copy the code

An array of the join method

Javascript DOM creation methods can also use HTML strings, but traditional strings in JS are not newline, as follows:

let str = 'asdasd
asdasd
asdasd'
Copy the code

As a result, the hierarchy is not clearly visible in the code and the readability is poor.

Backquoted template strings are not yet available in ES6, but elements in data can be wrapped in code, so array Join is used to achieve a similar effect:

let arr = ['A'.'B'.'C'.'D']
let str = arr.join(' ')
consloe.log(str)//ABCD
Copy the code

implementation

<body>
  <ul id="list"></ul>
  <script>
    let data = [
      {"name":"Xiao Ming"."age":12."sex":"Male"},
      {"name":"Little red"."age":11."sex":"Female"},
      {"name":"Small strong"."age":13."sex":"Male"}]let list  = document.querySelector('#list')
    //// concatenates data with strings
    for(let obj of data){
      // The code hierarchy is very clear. Such code can be edited in HTML and then used for multi-line editing in the editor
      list.innerHTML+=[
      '<li>'.' 
       
'
+obj.name+''.'
'
.'

Name:'

+obj.name+'</p>'.'

Age:'

+obj.age+'</p>'.'

Gender:'

+obj.sex+'</p>'.' '.'</li>', ].join(' ') // Split line for aesthetics list.appendChild(document.createElement('hr'))}
</script> </body> Copy the code

ES6 Reverse quotation marks

<body>
  <ul id="list"></ul>
  <script>
    let data = [
      {"name":"Xiao Ming"."age":12."sex":"Male"},
      {"name":"Little red"."age":11."sex":"Female"},
      {"name":"Small strong"."age":13."sex":"Male"}]let list  = document.querySelector('#list')
    
    for(let obj of data){
      // Template strings in ES6 syntax can be wrapped directly and can be concatenated with ${}
      list.innerHTML+=`
        <li>
          <div class="hd">${obj.name}</div> <div class="bd">${obj.name}</p> <p>${obj.age}</p> <p>${obj.sex}</p>
            </div>
        </li>`
      // Split line for aesthetics
      list.appendChild(document.createElement('hr'))}</script>
</body>
Copy the code

The effect is the same as above.

The basic use

  • Mustache official git: github.com/janl/mustac…
  • Mustache, because of its embedded tag{{}}Very much like a beard
  • {{}}Syntax is also used by Vue
  • Mustache is one of the earliest template engine libraries, much older than Vue, and its underlying implementation mechanism was very creative and sensational at the time, providing a new way to develop template engines

NPM and Node are not used for installation and use, directly use reference compiled libraries or CDN links.

Mustache4.0.1 is used here

<script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.0.1/mustache.js"></script>
Copy the code

Once referenced, the library provides a global variable named Mustache.

The most important method Mustache. Render (templateStr,data) takes the first argument to the template string and the second argument to the render data.

  1. Ordinary objects

    <body>
      <script src=". / mustache - 4.0.1. Js. ""></script>
      <div id="box"></div>
      <script>
        let data={
          OS:'HongMeng'.mood:'good'
        }
        let html = 'Today I installed the {{OS}} system on my phone, and I became really {{mood}}'
        let box  = document.querySelector('#box')
        box.innerHTML=Mustache.render(html,data)
      </script>
    </body>
    Copy the code
  2. Circular array

      <script src=". / mustache - 4.0.1. Js. ""></script>
      <ul id="list"></ul>
      <script>Let data = {arr: [{" name ":" xiao Ming ", "age" : 12, "sex", "male", "hobbies" : [" basketball ", "programming"]}, {" name ":" little red ", "age" : 11, "Sex", "female", "hobbies" : "sing"}, {" name ":" small strong ", "age" : 13, "Sex", "male", "hobbies" : []}}] let list = document. QuerySelector (' # list ') / /{{#arr}}{{/arr}}The content of the template engine is generated by the template engine loop, arR object properties can be used directly // the traditional array directly{{...}}// loop can be nested let HTML = '{{#arr}}
              <li>
                <div class="hd">{{name}}Basic information about</div>
                  <div class="bd">
                    <p>Name:{{name}}</p>
                    <p>Age:{{age}}</p>
                    <p>Gender:{{sex}}</p>
                    <p>Gender: Hobbies:</p>
                    <ol> 
                      {{#hobbies}}
                      <p><li>{{...}}</li></p>
                      {{/hobbies}}
                    </ol>
                  </div>
              </li>
            {{/arr}}'// Data must be an arR array object, not the arR array object itself list.innerhtml =Mustache. Render (HTML,data)</script>
    </body>
    Copy the code

  1. Boolean value

    <body>
      <script src=". / mustache - 4.0.1. Js. ""></script>
      <div id="box"></div>
      <script>
        let data={
          show : true
        }
        // Use a Boolean value to indicate whether to display the contained element. False does not display the contained element, and vice versa.
        let html= '{{#show}} 

    Guess if I show up?

    {{/show}} `
    let box = document.querySelector('#box') // Data must be an object containing an ARR array, not the ARR array object itself box.innerHTML=Mustache.render(html,data)
    </script> </body> Copy the code
  2. Script tag content as template

    <body>
      <script src=". / mustache - 4.0.1. Js. ""></script>
      <! -- Type is not recognized by the browser, so that you can see the element hierarchy and continue to use the editor's Emmet feature for quick editing --> 
      <script type="text/template" id="myTemplate">
       {{#arr}}
         <li>
           <div class="hd">{{name}}Basic information about</div>
             <div class="bd">
               <p>Name:{{name}}</p>
               <p>Age:{{age}}</p>
               <p>Gender:{{sex}}</p>
               <p>Gender: Hobbies:</p>
               <ol> 
                 {{#hobbies}}
                 <p><li>{{...}}</li></p>
                 {{/hobbies}}
               </ol>
             </div>
         </li>
       {{/arr}}
      </script>
      <ul id="list"></ul>
      <script>
        let data = {
          arr:[
            {"name":"Xiao Ming"."age":12."sex":"Male"."hobbies": ["Basketball"."Programming"] {},"name":"Little red"."age":11."sex":"Female"."hobbies": ["Sing"] {},"name":"Small strong"."age":13."sex":"Male"."hobbies"] : []}}let list  = document.querySelector('#list')
        let html = document.querySelector('#myTemplate').innerHTML
        list.innerHTML=Mustache.render(html,data)
      </script>
    </body>
    Copy the code

Mustache does not support any operations or expressions such as {{1+1}}

Underlying core mechanism

One diagram sums up Mustache’s mechanism

Mustache only does two things

  1. Compile the template string totokensIn the form of
  2. willtokensThe data is parsed into a DOM string

Simple regular expressions

Mustache libraries are not implemented using regular expressions, but simple Mustache functions can be implemented using regular expressions.

<body>
  <div id="box"></div>
  <script>
    let html  = 'TODAY I learned the basic principle of {{knowledge}}, although {{level}}, but very {{mood}}'.
    let data = {
      knowledge: 'Mustache'.level: 'hard'.mood: 'happy'
    }
    function render(templateStr,data){
      //str.replace(regular expression, replacement character, can be function return value)
      // The parentheses for (\w+) are used to capture content
      // /g global mode, otherwise only the first matching will be replaced
      // findStr: matched strings such as {{knowledge}}, $1: captured strings such as knowledge, $2 and $3(not used here) are matched subscripts and the entire templateStr string itself, respectively
      return templateStr.replace(/\{\{(\w+)\}\}/g.function(findStr,$1){
        return data[$1]})}document.querySelector('#box').innerText = render(html,data)// I learned the basics of Mustache today, which is a little difficult but fun
  </script>
</body>
Copy the code

Token thought

Simple regular expressions are like hors d ‘oentos. What do you see in these tokens?

Tokens are an array of JS representations of template strings. They are the first generation of abstract Syntax trees (AST), virtual DOM, and more.

In Mustache, the template string is abstracted as follows:

Tokens are a two-dimensional array. You can see that the template strings are divided into different parts with {{and}} boundaries. The string outside {{}} is named text, and the string inside {{}} is named name. Their values are placed in the second place of the array. And each of these will be an array object. That array object is called token. The array that contains these array objects is called tokens.

When there is a loop in the template string, the tokens will be converted into deeper tokens:

Tokens tokens tokens tokens tokens tokens tokens tokens tokens tokens tokens tokens

Double loop template strings with tokens to make them deeper nested:

Where null is the start index to the end index of the matching position, no verification, no use.

Expose source tokens

Because Mustache library just compiles tokens and combines them with data, it doesn’t show tokens directly. So we need to change the source code to see what tokens really look like.

This is the version used here: cdn.bootcdn.net/ajax/libs/m… , the browser can view it directly, but the browser viewing readability is poor. You are advised to save as or copy the file to a local PC and view it in the editor.

The source code that needs to be modified is at the end of the parseTemplate method.

  function parseTemplate (template, tags) {...return nestTokens(squashTokens(tokens));
  }
Copy the code

To:

function parseTemplate (template, tags) {
    var tokens = nestTokens(squashTokens(tokens));
    console.log(tokens);
    return tokens;
}
Copy the code

This automatically outputs Tokens on the console whenever you use Mustache.

Run the previous basic examples 1 and 4 again and look at the console:

The numbers are the start index to the end index of the matching position, and are not used for verification.