This article focuses on how the underlying mechanism of Mustache template engine is implemented in VUE, which is divided into four parts: Get to know it firstWhat is a template engine, masterMustache basic useAnalysis,Mustache's underlying core mechanismAnd finally write by handRealize the mustache libraryBottom code, refuse to talk about it. The whole process is a step-by-step process, and this article is full of dry stuff to make sure you really understandVue template engineHow the bottom layer works. Finally, I hope you guys pointpraise!!!!! So without further ado, mustache Template engine!!

Knowledge reserves

  • I can write some J’savaScriptCommon algorithms, for examplerecursive,Two dimensional array traversalAnd so on;
  • Be familiar withES6Common features, such aslet,Arrow function,Deconstruction assignmentAnd so on;
  • To understandwebpackwebpack-dev-server

If you are not familiar with Webpack, you can go to the updated Webpack column Webpack To master column for detailed reading.

What is a template engine

A template engine is the most elegant solution for turning data into views. Turn the data into a DOM, and then render the DOM structure into the page.

/ / data
const data = {
            array: [{name: 'Alex'.sex: 'male'.age: 18},
                {name: 'Jack'.sex: 'male'.age: 20},
                {name: 'the qingfeng-xiangguang fracture'.sex: 'male'.age: 19]}},Copy the code
<! -- DOM structure view -->
<ul>
        <li>
            <div class="hd">Basic information about Alex</div>
            <div class="bd">
                <p>Name: Alex</p>
                <p>Gender: male</p>
                <p>Age: 18</p>
            </div>
        </li>
        <li>
            <div class="hd">Jack's basic information</div>
            <div class="bd">
                <p>Name: Jack</p>
                <p>Gender: male</p>
                <p>Age: 20</p>
            </div>
        </li>
        <li>
            <div class="hd">Basic information about Alex</div>
            <div class="bd">
                <p>Name: Qingfeng</p>
                <p>Gender: male</p>
                <p>Age: 19</p>
            </div>
        </li>
    </ul>
Copy the code

In the example above, in Vue, it is easy to iterate through it with a V-for and render it directly to the page.

<li -v-for = "(item, index) in data.array" :key="index">
Copy the code

Historically, data has been transformed into views

Pure DOM methods

Create dOM tags by iterating through the data using pure dOM methods and manually climbing the tree.

const data = {
    array: [{name: 'Alex'.sex: 'male'.age: 18},
        {name: 'Jack'.sex: 'male'.age: 20},
        {name: 'the qingfeng-xiangguang fracture'.sex: 'male'.age: 19]}},let ul = document.querySelector('.list')
for(let i = 0; i < data.array.length; i++) {
    let oli = document.createElement('li')
    oli.innerText = data.array[i].name + 'Basic Information'
    let divhd = document.createElement('div')
    divhd.className = "hd"
    let divbd = document.createElement('div')
    divbd.className = "bd"
    let p1 = document.createElement('p')
    let p2 = document.createElement('p')
    let p3 = document.createElement('p')
    p1.innerText = 'Name:' + data.array[i].name 
    p2.innerText = 'Gender:' + data.array[i].sex 
    p3.innerText = 'Age:' + data.array[i].age 
    divbd.appendChild(p1)
    divbd.appendChild(p2)
    divbd.appendChild(p3)
    oli.appendChild(divhd)
    oli.appendChild(divbd)
    ul.appendChild(oli)
}
Copy the code

I’m sure you’re all freaking out when you see how to create a pure DOM above. This isn’t a very complicated example, but if you nested a loop inside a loop, you might want to switch industries. This method is very primitive and has no practical value.

Array Join () method

Because of the complexity of the pure DOM approach above, people came up with a way to create DOM tags using strings instead of structured HTML.

const list = document.querySelector('.list')     
const data = {
    array: [{name: 'Alex'.sex: 'male'.age: 18},
        {name: 'Jack'.sex: 'male'.age: 20},
        {name: 'the qingfeng-xiangguang fracture'.sex: 'male'.age: 19]}},// Iterate over the data to add the HTNL string to the list from a string perspective
for(let i = 0; i < data.array.length; i++) {
    list.innerHTML += [
    '<li>'.' 
      
'
+ data.array[i].name +'Basic info '.'
'
.'

Name:'

+ data.array[i].name + '</p>'.'

Gender:'

+ data.array[i].sex + '</p>'.'

Age:'

+ data.array[i].age + '</p>'.' '.'</li>' ].join(' ')}Copy the code

Join () methodIsn’t it better than the topPure DOM methodsIt’s a lot simpler, and the code looks a lot cleaner, the way we program.

ES6 backquotes

We can also optimize our join method using the backquoted template string of ES6 syntax.

const list = document.querySelector('.list')
const data = {
    array: [{name: 'Alex'.sex: 'male'.age: 18},
        {name: 'Jack'.sex: 'male'.age: 20},
        {name: 'the qingfeng-xiangguang fracture'.sex: 'male'.age: 19]}},for(let i = 0; i < data.array.length; i++) {
    list.innerHTML += `
        <li>
            <div class="hd">${data.array[i].name}</div> <div class="bd">${data.array[i].name}</p> <p>${data.array[i].sex}</p> <p>${data.array[i].age}</p>
            </div>
        </li>
    ` 
}
Copy the code

Of the three methods above, the one we use more often in actual development is the third way to create tags in backquotes and render the DOM onto the view layer. So let’s go to mustache, the most elegant way to turn data into views.

The basic use of Mustache

  • The introduction ofMustache library, either through NPM or through CDN using script.
  • useMustache. Render ()Merge templates with data, with the first parameter representing the template and the second parameter representing the data.

Array of loop objects

In Mustache you must have {{#}} to start and {{/}} to end.

<div class="container"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.0.1/mustache.js"></script>
    <script>Const container = document.querySelector('. Container ') const data = {array: [{name: 'Alex', sex: 'male ', age: 18}, {name: 'Jack', sex: 'male ', age: 20},]} var templateStr ='<ul>
                {{#array}}
                    <li>
                        <div class="hd">{{name}}Basic information about</div>
                        <div class="bd">
                            <p>Name:{{name}}</p>
                            <p>Gender:{{sex}}</p>
                            <p>Age:{{age}}</p>
                        </div>
                    </li>
                {{/array}}
            </ul>'let dom = Mustache. Render (templateStr,data) // Write to innerHtml in container. InnerHtml = dom</script>
Copy the code

Loop through nested arrays

 <div class="container"></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.0.1/mustache.js"></script>
    <script>const container = document.querySelector('.container') const data = { array: [ { name: 'Alex, hobies: [' basketball', 'badminton]}, {name: "Jack", hobies: [' swimming', 'sing']}, {name: 'the qingfeng-xiangguang fracture, hobies: [' game', 'play football]}, var templateStr = `]}<ul>
                {{#array}}
                    <li>
                        {{name}}hobby<ol>
                            {{#hobies}}
                                <li>{{...}}</li>
                            {{/hobies}}    
                        </ol>
                    </li>
                {{/array}}
            </ul>'let dom = Mustache. Render (templateStr,data) // Write to innerHtml in container. InnerHtml = dom</script>
Copy the code

Loop simple array

<div class="container"></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.0.1/mustache.js"></script>
    <script>
        const container = document.querySelector('.container')
        const data = {
            array: ['the qingfeng-xiangguang fracture'.'Alex'.'Jack']}var templateStr = ` {{#array}} 
        
  • {{.}}
{{/array}} `
let dom = Mustache.render(templateStr,data) // Write to innerHtml container.innerHTML = dom
</script>
Copy the code

Don’t cycle

<div class="container"></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.0.1/mustache.js"></script>
    <script>
        const container = document.querySelector('.container')
        const data = {
            name: 'the qingfeng-xiangguang fracture'.age: 18
        }
        var templateStr = '< H1 > Name: {{name}}, age: {{age}}
        ' 
        let dom = Mustache.render(templateStr,data)
        // Write to innerHtml
        container.innerHTML = dom
    </script>
Copy the code

Boolean value

Mustache also uses conditional rendering, which is very similar to the V-if we use with Vue. One caveat though: You can’t write expressions, which proves that Mustache is a weakly-typed library.

<div class="container"></div>
    <script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.0.1/mustache.js"></script>
    <script>
        const container = document.querySelector('.container')
        const data = {
            isShow: true
        }
        var templateStr = '{{#isShow}} 

Qingfeng

{{/isShow}}'
let dom = Mustache.render(templateStr,data) // Write to innerHtml container.innerHTML = dom
</script>
Copy the code

So much for the basic use of mustache, we’ve found that with the exception of no loop, you need to use {{#}} to start and {{/}} to end.

The underlying mechanism of Mustache

When we started mustache, a lot of people probably thought that mustache library was just a regular expression idea. Of course, this is a good idea, and it is possible to implement some simple examples, but there are some complex cases where the idea of regular expressions fails, such as loop nesting. Of course, let’s see if we can do this with regular expressions.

Replace () method

Before implementing it, we need to look at how the replace () method in regular expressions is used to give us a better understanding of what the code we are writing means.

Use the replace () method to match regular expressions:

  • The first parameter:The re to match.
  • The second parameter:It can be a replacement string, or it can be a function.
  • The second argument is in the form of a function: the first argument representsThe portion of the matched re, the second parameter indicatesMatched characterThe third one meansThe position of the matched characterAnd the fourth one isString to match. Normally the second parameter is the data we want.

<div class="container"></div>
    <script>
        const container = document.querySelector('.container')
        const data = {
            name: 'the qingfeng-xiangguang fracture'.age: 18
        }
        var templateStr = '< H3 > Name: {{name}}, age: {{age}} 
        // Inside the re () means to capture the letter data inside
        let dom = templateStr.replace(/\{\{(\w+)\}\}/g.(. args) = > data[args[1]])
        console.log(dom);
    </script>
Copy the code

A simple template engine implementation mechanism is written, using regular expressionsReplace () method.Replace () methodThe second argument to can be a function that provides an argument to the thing captured, combined with the data object, for intelligent substitution.

Bottom tokens

We know that Mustache isn’t implemented with regular expressions, so how does it work underneath? In fact, the mechanism of this is the picture below.Let’s interpret the basic flow of the picture above:

willTemplate charactersList firstCompiled into tokensTokens as intermediate forms and thenThen combine data with tokens, andParse to a DOM string.

So the question is, right? What are tokens? Tokens are nested arrays of JS, which are, in essence, JS representations of template strings. They are the origin of AST and virtual nodes. It may seem abstract, but let’s use code to understand what tokens are.

Simple tokens

Template string:

<h1>Name: {{name}} age: {{age}}</h1>
Copy the code

Tokens:

[["text"."

Name:

], ["name"."name"], ["text".", age:], ["name"."age"], ["tetx"."</h1>"]]Copy the code

Circularly nested tokens

Template string:

var templateStr = ` < ul > < li > {{# array}} {{name}} hobby < ol > < li > {{# hobbies}} {{...}} < / li > {{/ hobbies}} < / ol > < / li > {{/ array}} < / ul > ` 
Copy the code

Tokens:

[["text"."<ul>"],
    ["#"."array"The [["text"."<li>"],
        ["name"."name"],
        ["text".< OL >], ["#","hobbies", [["text","<li>"[the],"name","."[the],"text","</li>"[the]]],"text","</ol></li>"[the]]],"text","</ul>"]]Copy the code

By printing tokens, we can see that there are three tokens on the outer layer. The tokens of the # type in the middle also contain nested tokens. After careful observation, we know that whenWith {{}}Is wrapped into an array, the first of which represents the type (The text typeIncluding tags, text,Type the nameRepresents a match to {{}},# typeThe second is text content, and this array is one of themtoken. All the tokens combine to form onetokens.

Write a mustache

Handwriting implementation Scanner class

As we’ve seen before, we use mustache’s basic usage by combining the template with the data using the mustache library’s mustache.render () method to generate a DOM structure. How does the bottom layer compile our template strings into tokens? Use a Scanner Scanner to intercept and transition the {{and}} of the template string. Let’s look at the two main methods of the Scanner class:

  • The scan functionAction: Skips the specified content without returning a value.
  • ScanUtil functionAllows a pointer to scan a template string until it encounters the specified content and returns the text before the specified content.

So how do you collect strings? We need to provide an identified variable tail (the tail changes with the pointer and contains the pointer) to collect strings

Now that you know the two main ways that Scanner works, let’s look at its flow chart:

The basic code implementation process is as follows:

export default class Scanner {
    constructor(templateStr) {
        this.templateStr = templateStr
        / / pointer
        this.pos = 0
        // The tail is the original length of the template string
        this.tail = templateStr
    }
    // Skip the specified content without returning a value
    scan(jump) {
        // indexOf the string returns 0 to indicate that the first item is the specified one
        if (this.tail.indexOf(jump) == 0) {
            // Change the pointer to skip the specified content directly
            this.pos += jump.length
            // Update the tail
            this.tail = this.templateStr.substring(this.pos)
        }
    }
    // Let the pointer scan the template string until it encounters the specified content and returns the text before the specified content
    scanUtil(stopTag) {
        // Record the position of the pos pointer to which this method is executed
        const pos_pack = this.pos
        // If the tail does not start with the specified content, the scanner is not looking for the content
        // The length of the pointer must be smaller than the length of the template string
        while (!this.eos() && this.tail.indexOf(stopTag) ! = =0 ) {
            // Move the pointer down if the specified content is not found
            this.pos++
            // The tail follows the pointer
            this.tail = this.templateStr.substring(this.pos)
        }
        // Return the preceding text if found
        return this.templateStr.substring(pos_pack, this.pos)
    }
    // Whether the pointer has reached the end
    eos () {
        return this.pos >= this.templateStr.length
    }
}
Copy the code

ParseTemplelateTokens () method

The main effect of this method is to turn HTML into tokens.

import Scanner from './Scanner'
import nestTokens from './nestTokens'
export default function parseTemplateTokens(templateStr) {
    // Instantiate a scanner construct that provides an argument specifically to serve the template string
    // Handle template strings to generate tokens service
    const scanner = new Scanner(templateStr)
    // Store tokens in an array to form tokens
    let tokens = []
    // Collect text content as it passes by
    let words
    while(! scanner.eos()) {// Collect text content as it passes by
        words = scanner.scanUtil('{{')
        if(words ! =' ') {
            // Flag bit cannot remove Spaces from class names
            let isClass = false
            // remove whitespace
            / / stitching
            var _words = ' '
            for (let i = 0; i < words.length; i++) {
                // Check whether it is in the tag
                if (words[i] == '<') {
                    isClass = true
                } else if (words[i] == '>') {
                    isClass = false
                }
                // The current item is not on space concatenation
                if (!/\s/.test(words[i])) {
                    _words += words[i] 
                } else {
                    // Yes Spaces are concatenated only within tags
                    if(isClass) {
                        _words += ' ' 
                    }
                }
            }
            tokens.push(["text",_words])
        }
         // Skip the specified content
        scanner.scan('{{')
        words = scanner.scanUtil('}} ')
        if(words ! =' ') {
            if (words[0= = =The '#') {
                tokens.push(["#", words.substring(1)])}else if (words[0= = ='/') {
                tokens.push(["/", words.substring(1)])}else {
                tokens.push(["name", words])
            }
            
        }
        scanner.scan('}} ')}return nestTokens(tokens)
}
Copy the code

Ok, so we’ve wrapped a template string into a token, just a simple template string into a token, we haven’t done the loop bridge yet, so let’s go ahead and fold the token,

NestTokens () method

The nestTokens () method folds tokens, combining tokens between # and/as its subscript 2 to form a token. When writing nestTokens, we need to know the concept of stack in data structure. Stack first in first out, queue first in first out. When folding tokens, we need to use the idea of stack in data structure to fold tokens.

Given the idea of the stack, we can think of when we run through loopsThe # symbolWill advance the stack (stack) when encountered/ symbolIt will be out of the stack. The relevant codes are as follows:

export default function nestTokens(tokens) {
    // The assembled result array
    const nestTokens = []
    // stack is used to store #
    let section = []
    console.log(tokens);
    for (let i = 0; i < tokens.length; i++) {
        // iterate over each term
        let token = tokens[i]
        // Determine whether the first item of the array is of type # or/or text
        switch (token[0]) {
            case "#":
                // store tokens after # into the item with subscript 2 at the end of the stack array.
                // as a child of the current array
                token[2] = []
                / / into the stack
                section.push(token)
                // Pushing is also storing an array in the result array
                nestTokens.push(token)
                break
            case "/":
                / / out of the stack
                let section_pop = section.pop()
                // Place the stack array in the result array
                nestTokens.push(section_pop)
                break
            default:
                // If the stack is empty, place it in the result array
                if (section.length == 0) {
                    nestTokens.push(token)
                } else {
                    // Not null indicates that the stack has data fetched from the top of the stack and the token array after # is placed in the top item of the stack with the current subscript 2
                    section[section.length - 1] [2].push(token)
                }
        }
        
    }
    console.log(nestTokens);
}
Copy the code

Here’s a rundown of the code: First, we declare a section array as the stack and return result array, iterate over each token, and determine whether the first item in the current array is # or /, # pushed. To push the current array, / is removed, and the current array is popped. That is to say the current array first is neither # is not/can be directly deposited into the array, when the judge has an array is the first item #, will pressure into the stack, and can store the results in an array, we can know by what is said above token, will encounter # in the current array subscript 2 create an array used to store data, The # above is pushed, indicating that the section stack has data, and all subsequent array data will be stored in the array with subscript 2 at the top of the stack until the next # is reached, and then the process is repeated. When the first item of the array is /, it indicates that the unstacked array needs to be stored in the result array.

The above is the general process of nestTokens, combined with the following diagram, I believe you can better understand.

Test results:When we thought we were really going to write the nestTokens method, the printed results were not what we expected. And if you look at the code that we’ve written, you can see that we’re actually on the right track, but every time we do a stack we create an array for the top of the stack, and then the top of the stack doesn’t become a subarray for the bottom of the stack. So, we need to find a collector that can go up layer by layer. We know what the problem is, so we’re going to change the code, right

export default function nestTokens(tokens) {
    // The assembled result array
    const nestTokens = []
    // stack is used to store #
    let section = []
    // The collector starts by pointing to the result array as a reference type
    When # is encountered, the collector points to a new array with the token subscript 2
    var collector = nestTokens
    for (let i = 0; i < tokens.length; i++) {
        // iterate over each term
        let token = tokens[i]
        // Determine whether the first item of the array is of type # or/or text
        switch (token[0]) {
            case "#":
                // The token is placed in the collector
                collector.push(token)
                / / into the stack
                section.push(token)
                // Change the collector
                // store tokens after # into the item with subscript 2 at the end of the stack array.
                // Add an item with subscript 2 to the token and have the collector point to it
                collector = token[2] = []
                break
            case "/":
                / / out of the stack
                section.pop()
                // Change the collector to an array of subscripts 2 for the item at the bottom of the stack stack
                collector = section.length > 0 ? section[section.length - 1] [2] : nestTokens
                break
            default:
                collector.push(token)
        }
        
    }
    console.log(nestTokens);
    return nestTokens
}

Copy the code

Running results:

Once we do that, we have exactly the nested array of subarrays we want, and we have perfectly folded tokens. Now that you have compiled tokens from template strings, all that remains is to merge tokens with data and parse to generate DOM.

RenderTemplate () method

The renderTemplate () method combines tokens with data to generate DOM strings. Let’s start with a simple tokens and data.

<script src="/xuni/bundle.js"></script>
    <script>Const data = {name: ' ', age: 18} var templateStr =<h3>Name:{{name}}Age:{{age}}</h3>` 
        templateEngine.render(templateStr, data)
    </script>
Copy the code
/* Merge tokens with data to generate DOM strings */ 
export default function renderTemplate (tokens, data) {
    console.log(tokens);
    console.log(data);
    // Result string
    let str = ' '
    for(let i = 0; i < tokens.length; i++) {
        let token = tokens[i]
        // Determine whether the first item is text or name and #
        if(token[0] = ="text") {
            // Concatenate data directly into the result string
            str +=  token[1]}else if (token[0] = ="name") {
            // Find data from data
            str += data[token[1]]}else if (token[0] = ="#") {
            // loop recursively}}console.log(str);
}
Copy the code

We can merge a simple template string with data to generate a DOM string, but it is important to note that data[token[1]] cannot be used to retrieve data from data when the data is deeply nested. For example: data[‘ A.B.C ‘], change the ‘A.B.C’ in [] to a string, and then search for the data corresponding to the string. So we need to process the form data[‘ A.B.C ‘].

Lookup () method

When the template string has a form such as item.name, we need to process the string and retrieve the corresponding data.

<script>
        const data = {
            person: {
                name: 'the qingfeng-xiangguang fracture'.age: 18}}var templateStr = '

Name: {{person.name}}, age: {{person.age}}

'
templateEngine.render(templateStr, data) </script> Copy the code
/** * process nested objects to get the data of the lowest object *@param {*} Obj The object passed in the original object *@param {*} KeyName The string passed in, such as Person.name *@returns Returns to get data in a nested object */
export default function lookup(obj, keyName) {
    // Handle the normal array of data
   if(keyName ! ='. ') {// Use the array redecu method for traversal
        return keyName.split('. ').reduce((pre, next) = > {
            return pre[next]
        }, obj)
   }
    return data[keyName]
}
Copy the code
/* Merge tokens with data to generate DOM strings */ 
import lookup from "./lookup";
export default function renderTemplate (tokens, data) {
    console.log(tokens);
    console.log(data);
    // Result string
    let str = ' '
    for(let i = 0; i < tokens.length; i++) {
        let token = tokens[i]
        // Determine whether the first item is text or name and #
        if(token[0] = ="text") {
            // Concatenate data directly into the result string
            str +=  token[1]}else if (token[0] = ="name") {
            // Find data from data to concatenate into the result string
            str += lookup(data, token[1])}else if (token[0] = ="#") {
            // loop recursively}}console.log(str);
}
Copy the code

Once you’ve dealt with the nesting of objects in data, it’s just a matter of determining the recursion of #.

ParseArray () method

The parseArray () method is mainly used to recursively traverse the array inside #. It should be noted that when data is a simple array, we directly render in the form of {{.}}, before this, we did not process the ‘. ‘attribute, so when we recurse, we need to add a’. ‘attribute, and the value is itself, so as to correctly render the data.

import lookup from "./lookup";
import renderTemplate from "./renderTemplate";

/** * Handle array structure renderTemplate to implement recursion *@param {*} Token An item of data that contains # ['#','hobbies',[]] *@param {*} The number of calls is determined by the length of the data */
export default function parseArray (token, data) {
    // Get the data needed in this array
   
    var v = lookup(data, token[1]) 
    // Result string
    var str = ' '
     // Iterate over data instead of tokens Data as many times as you have in the array of Tokens Data
    for(let i = 0; i < v.length; i++) {
        str += renderTemplate(token[2] and {// When the data is a simple array rather than an array of objects
            // We need to add a '. 'attribute for the current v[I] data itself. v[i],'. ': v[i]
        })
    }
    return str
}
Copy the code

TemplateEngine () method

import parseTemplateTokens from './parseTemplateTokens'
import renderTemplate from './renderTemplate'
window.templateEngine = {
    render(templateStr, data) {
        // Make template strings into tokens
        const tokens = parseTemplateTokens(templateStr)
        // Combine tokens with data to generate DOM strings
        let domStr =  renderTemplate(tokens, data)
        // Returns the DOM string
        return domStr
    }
}
Copy the code
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <div class="container"></div>
    <script src="/xuni/bundle.js"></script>
    <script>
        const templateStr = `
            <ul>
                {{#array}}
                    <li class='qf'>
                        {{name}}hobby<ol>
                            {{#hobbies}}
                                <li>{{...}}</li>
                            {{/hobbies}}    
                        </ol>
                    </li>
                {{/array}}
            </ul>` const data = {array: [{name: 'Alex, hobbies: [' basketball', 'badminton]}, {name: "Jack", hobbies: [' swimming', 'sing']}, {name: 'the qingfeng-xiangguang fracture, hobbies: [' game', 'play football]}, / / var templateStr = `]}<h3>Name:{{person.name}}Age:{{person.age}}</h3>` 
        const domStr =  templateEngine.render(templateStr, data)
        const container = document.querySelector('.container')
        container.innerHTML = domStr
    </script>
</body>
</html>
Copy the code

Run the code output:

This is the main part of the Mustache source code for Vue source parsing. Some of the details are not addressed in this article, but the general function is basically implemented. After reading this article, you may find that the best part of this article is the idea of using collectors and stacks in the nestTokens method. This idea is worth learning! This is also read the charm of the source code, you will find that the algorithm is really wonderful!! The relevant source code in the article has been sent to Gitee, you can get the source code stamp me!!

Finally, after reading this article, I hope it will help you to understand how Vue performs V-for rendering. I hope you can give a thumbs-up!!