Exploring Vue source code: mustache template engine (8) Understanding nestTokens handwritten carding template string pound sign loop nested structure tokens

The above explores the Vue source code: mustache template engine (7) In the process of handwritten template string conversion tokens array, we have operated a relatively simple tokens array and simply processed the special symbol syntax of pound sign and backslash

So now we need to nest the scattered tokens,
which is mainly reflected in the fact that the content between the pound sign and the backslash obviously belongs to the subset of the loop statement,
but we are now in a horizontal relationship
insert image description here
, and the main trouble of this thing is internal. There may be multiple levels of nesting.
For example, we recycle in the loop

Processing it will form a layer-by-layer data structure, so many people will think of the stack.
This is another knowledge point that many people have heard but have not deeply understood.
Let’s talk about the concept of the stack first.

First of all, this is a stacking result, which is quite common in life. For example, our badminton bucket
has only one end open.
If we want to put balls in, we can only put them in one by one. When you take out this structure, you
insert image description here
must take the last one.
If the order you put in is abcd, then the stacking structure is dcba from top to bottom
, then the order you take out is naturally in this order.
Simply put, it is called first-in, last-out.

Then the concept is that if you encounter a pound sign, you will push it into the stack and if you encounter a backslash, you will pop it out of the stack

Then we have an idea, let's open the project
and create a nestTokens.js under src,
insert image description here
first write the code like this

/*
    整合tokens  将#与/ 之前的内容  整合为 下标为三的元素
*/
export default function nestTokens(tokens) {
    
    

};

Our file only throws out a function function to process tokens
and integrate the content under the pound sign to the content before the backslash into an array with a subscript of three

Then we open formConversToken.js, import and use the nestTokens,
insert image description here
introduce the tokens processing function above
, and then put it in the function to let him process the data when returning
, but the original tokens are now undefined
insert image description here,
because we haven’t written nestTokens yet. If there is no return value for something, then we will naturally get nothing if we do this.

So here we first change it to nestTokens and return the received ones directly.
insert image description here
Our tokens will come out again
insert image description here
, and then we will start to write the algorithm. Of course, any algorithm will not be difficult as long as
we filter out the ideas. How do we achieve

Let's first change the nestTokens function to this

/*
    整合tokens  将#与/ 之前的内容  整合为 下标为三的元素
*/
export default function nestTokens(tokens) {
    
    
    //先定义一个数组 存储放回结果
    let nestedTokens = [];
    //做一个数组  来存栈中数据
    let sections = [];
    //循环遍历外面传进来的tokens
    for(let i = 0;i < tokens.length;i++) {
    
    
        //定义一个叫token的变量存储  当前循环遍历的下标
        let token = tokens[i];
        //判断下一当前循环的下标数组 第一个下标是不是 井号 或者 反斜杠  如果都不是 做另外处理
        switch(token[0]) {
    
    
            case '#':
                //如果是井号
                sections.push(token);
                console.log(token[1],"入栈啦");
                break;
            case '/':
                //如果是反斜杠
                let pot = sections.pop();
                console.log(pot[1],"出栈啦");
                break;
            default:
                //既不是井号  又不是反斜杠
        }
    }

    return nestedTokens;
};

Let's first define a nestedTokens to store the last returned result
, then define the sections array to handle the logic of our stacking and popping,
then loop through the tokens array passed in,
and then define a token to store the subscript content of the current loop traversal
. 0 The subscript is a pound sign, which means that we want to push it
into the stack. Use push to add content.
If it is a backslash, use pop. You can learn about this method to delete the last subscript of the array and return the deleted content, and then
we receive and return output the value

The result of the operation is as follows.
insert image description here
After matching the pound sign, add the content to the array through push
, and then delete the last subscript of the array through pop when the backslash is matched.

Because our nestedTokens did not do any processing, the return value is naturally an empty array

Then we can further change the code to this

/*
    整合tokens  将#与/ 之前的内容  整合为 下标为三的元素
*/
export default function nestTokens(tokens) {
    
    
    //先定义一个数组 存储放回结果
    let nestedTokens = [];
    //做一个数组  来存栈中数据
    let sections = [];
    //循环遍历外面传进来的tokens
    for(let i = 0;i < tokens.length;i++) {
    
    
        //定义一个叫token的变量存储  当前循环遍历的下标
        let token = tokens[i];
        //判断下一当前循环的下标数组 第一个下标是不是 井号 或者 反斜杠  如果都不是 做另外处理
        switch(token[0]) {
    
    
            case '#':
                //将当前遍历的元素的第二个下标定义为一个空数组  以便收集井号内的子元素
                token[2] = [];
                //如果是井号
                sections.push(token);
                break;
            case '/':
                //如果是反斜杠
                let pot = sections.pop();
                //将被删除的栈内容 加入到nestedTokens中
                nestedTokens.push(pot);
                break;
            default:
                //既不是井号  又不是反斜杠

                //判断sections储存栈内容数组 是否是空的
                if(sections.length == 0) {
    
    
                    //直接将当前下标装入结果数组
                    nestedTokens.push(token);
                } else {
    
    
                    //走进else  说明  sections  中是有内容的 当前正在入栈

                    //将当前遍历的下标内容  push进sections最后一个元素的  2下标下
                    sections[sections.length - 1][2].push(token);

                }
        }
    }

    return nestedTokens;
};

The important thing to change is that the subscript array 0 currently traversed by default is neither a pound sign nor a backslash, then we judge whether there is content in the sections, because if the sections have content, it means that the current cycle is collecting elements. If the sections are
empty The current piece of content is not wrapped in pound signs and backslashes and can be directly added to nestedTokens to become the first-level data.
Otherwise, push the current content directly into the subscript 2 of the last subscripted array of sections. Our previous case '#' : It is written that if it is a pound sign, a 2 subscript is directly created. The default value is an empty array. Here, the content is pushed into this empty array.

Then another change is the stack case '/':
add the sections to the nestedTokens result array by push when popping the stack. The
running results are as follows,
insert image description here
you can see that the content in our array has been successfully collected in the middle of the pound sign and the backslash content

But there are still some problems with this.
We will change the index.html under www to this

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src = "/xuni/bundle.js"></script>
    <script>
        let templateStr = `
            <div>
                {
       
       {#students}}
                    <ul>
                        <li>{
       
       { item.name }}</li>
                        {
       
       {#item.list}}
                            <li>{
       
       { . }}</li>
                        {
       
       {/item.list}}
                    </ul>
                {
       
       {/students}}
            </div>
        `;
        let data = {
      
      
            name: "小猫猫",
            age: 2,
            students: [
                {
      
      
                    id: 0,
                    name: "小明",
                    list: [
                        "篮球",
                        "唱",
                        "跳"
                    ]
                },
                {
      
      
                    id: 1,
                    name: "小红",
                    list: [
                        "电子游戏",
                        "计算机编程"
                    ]
                }
            ]
        }
        GrManagData.render(templateStr,data);
    </script>
</body>
</html>

Here we changed the two-level nested students to loop first, and then wrote the list array under the subscript corresponding to the loop,
but the result of our operation is as follows.
insert image description here
It is obvious that the two pound signs have become a flat structure, but obviously item.list should be is a subset of students

We can see the official writing method of mustache.js. Searching for nestTokens in mustache.js is the following function content.
insert image description here
First of all, this sentence uses the syntax of shallow copy in js. You can learn about the concepts of deep copy and shallow copy. Here we are using it. The reference type is directly equal to
this, which means that collector and nestedTokens point to the same storage space.
It can also be understood that these two variables point to the same array,
insert image description here
but we will find that this method is actually not the same, but the idea is poor. It’s not particularly big
. In fact, we can use our research to solve this problem.
I’m just taking everyone to write it yourself. Then we can directly rewrite this loop statement or follow the official plan.

Here directly change our nestTokens to this

/*
    整合tokens  将#与/ 之前的内容  整合为 下标为三的元素
*/
export default function nestTokens(tokens) {
    
    
    //先定义一个数组 存储放回结果
    let nestedTokens = [];
    //做一个数组  来存栈中数据
    let sections = [];
    //创建 collector 收集器  直接指向 nestedTokens 结果数组
    let collector = nestedTokens;

    //循环遍历外面传进来的tokens
    for(let i = 0;i < tokens.length;i++) {
    
    
        //定义一个叫token的变量存储  当前循环遍历的下标
        let token = tokens[i];
        //判断下一当前循环的下标数组 第一个下标是不是 井号 或者 反斜杠  如果都不是 做另外处理
        switch(token[0]) {
    
    
            case '#':
                //如果是井号
                //将当前元素  存入collector收集器
                collector.push(token);
                //当前元素入栈
                sections.push(token);
                //直接在用等于 让收集器collector指向 当前元素的2下标  顺手token[2] = [];  就是给token定义 2下标 默认值一个空数组
                collector = token[2] = [];
                break;
            case '/':
                //如果是反斜杠 出栈
                let pot = sections.pop();
                //判断  如果sections是空的  则等于nestedTokens结果数组  如果不是  就等于 sections最后一个下标的二下标
                collector = sections.length > 0?sections[sections.length - 1][2]:nestedTokens;
                break;
            default:
                //直接将内容放入收起器中
                collector.push(token);
        }
    }

    return nestedTokens;
};

This function cleverly uses the principle that js shallow copy arrays and other reference types can directly use equals to change the pointing principle.
Many people may cause bugs in the code because of shallow copy, but here is a good use of shallow copy.

First of all, the nestedTokens pointed to by the collector at the beginning is our result array.
Then every time collector.push will store nestedTokens.
When encountering a pound sign, every time collector.push will enter the 2 subscript of the last element pushed into the stack
and then exit it . This judgment is more essential when stacking.
If sections still have elements after the last element is killed by pop,
it means that it has matched other well signs before, indicating that our loop is written in other loops, and it
will directly continue to be equal to the last element that was pushed onto the stack. 2 If the subscript
is gone, it means that it is at the end, then point back to nestedTokens.
insert image description here
You can see that the result is very perfect. This algorithm can be said to be very ingenious, especially for the precise application of shallow copy. I
really hope that if you learn a framework, you can remember the key points. If you learn the principles of API, such as these algorithms, you still need to understand and then extend the use of more scenarios. To be honest,
if you memorize this stuff by rote, it’s really useless not to waste time.

Guess you like

Origin blog.csdn.net/weixin_45966674/article/details/132039500