探究Vue源码:mustache模板引擎(8) 了解nestTokens 手写梳理模板字符串井号循环嵌套结构tokens

news2024/9/24 17:18:11

上文 探究Vue源码:mustache模板引擎(7) 手写模板字符串转换tokens数组过程中 我们操作出了一个较为简单的 tokens数组 并简单处理了 井号反斜杠的特殊符号语法

那么 我们现在需要将零散的tokens嵌套起来
主要就体现在 我们 井号 到 反斜杠 中间的内容 显然是属于循环语句中的子集
但我们现在是一个平级的关系
在这里插入图片描述
而 这个东西主要麻烦在 内部是可能存在多层嵌套的
例如 我们循环中 再循环

处理它 就会形成一个一层一层的数据结构 那么 很多人会想到 栈
那么 这又是一个 很多人听过 但没有深入了解到的知识点
我们先来讲 栈 的概念

首先 这是一个堆叠结果 生活中也比较常见 例如 我们的羽毛球桶
他只有一头是开口的
我们想放球进去 只能一个叠一个的放进去
在这里插入图片描述
而这个结构 你往出拿时 肯定是拿最后放的
你如 你放进去 顺序是 abcd 那么 他堆叠的结构 由上至下 是 dcba
那么 你往出拿 自然也是这个顺序
简单说 就叫 先进后出

那么 概念就是 遇见 井号 进栈 遇到反斜杠 则出栈

那么 有了思路 我们就打开项目
在src下创建一个 nestTokens.js
在这里插入图片描述
先将代码写成这样

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

};

我们这个文件只抛出一个函数 函数用来处理tokens
将 井号下的内容 到反斜杠之前的内容 整合成 下标为三的数组

然后 我们打开 formConversToken.js 引入并使用这个nestTokens
在这里插入图片描述
上面引入一下这个处理tokens的函数
然后返回时 顺带放到函数中让他处理一下这个数据
但 这样 原本有点 tokens 现在就成了 undefined
在这里插入图片描述
因为我们nestTokens还没写东西 他没有返回值 那我们这样套 自然是没有东西的

那么 这里 我们先改成 nestTokens 直接将接到的返回回去
在这里插入图片描述
我们的tokens 就又出来了
在这里插入图片描述
然后 接下来就要开始写算法了 当然 任何算法 只要滤清思路 都不会难
重要的是 理解 我们要做什么 然后 我们用什么样的方式去实现

我们先将nestTokens函数改成这样

/*
    整合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;
};

我们先进来定义一个nestedTokens 用来存储最后返回的结果
然后 定义sections数组 来处理我们入栈和出栈的逻辑
然后 循环遍历传进来的tokens数组
然后 定义一个 token 来存储当前循环遍历的下标内容
如果第 0 下标是 井号 说明 这是 我们要入栈
用push加内容加进来
如果是反斜杠 则 用pop 这个方法大家可以了解一下 删除数组最后一个下标 并将删除的内容返回回来
然后 我们接收返回值 输出一下

运行结果如下
在这里插入图片描述
匹配到了井号之后 将内容通过push加进数组
然后匹配到反斜杠 通过pop将数组最后一个下标干掉

因为我们nestedTokens没有做任何处理 返回值自然就是个空数组

然后 我们就可以进而将代码改成这样

/*
    整合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;
};

重点改动的是 default 当前遍历的下标 数组0下标 既不是 井号 又不是反斜杠 那么 我们就判断 sections中有没有内容 因为 sections 有内容 则表示 当前是循环在收集元素
如果sections是空的 说明 当前这块内容 没有被包裹在 井号和反斜杠内 直接可以加入到nestedTokens 成为第一级的数据
否则 直接将当前这个 push进sections最后一个下标的 数组的 2下标下 我们之前case ‘#’:中写到过 如果是井号 直接创建出一个 2下标 默认值是一个空数组 这里 则是将内容push进这个空数组

然后 另一个改的就是 出栈 case ‘/’:
在出栈时 将sections 通过 push 加入到nestedTokens 结果数组中
运行结果如下
在这里插入图片描述
可以看到 我们数组中内容 很成功的收集到了 井号和反斜杠中间的内容

但是 这样还有点问题
我们将 www下的 index.html 改成这样

<!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>

这里 我们改成了两层嵌套 students先循环 然后 在里面有写了 循环对应下标下的list数组
但是 我们运行结果如下
在这里插入图片描述
很明显 两个 井号变成了平级的结构 但是 显然 item.list 应该是 students的子集

我们可以看mustache.js官方的写法 在 mustache.js 中搜索nestTokens 就是如下的一个函数内容
在这里插入图片描述
首先 这句话 利用了 js中浅拷贝的语法 大家可以去了解一下深拷贝和浅拷贝概念 这里 我们就是利用了引用类型的 直接等于
这样就相当于 collector和nestedTokens 指向的都是同一个存储空间
也可以理解为 这两个变量都指向同一个数组
在这里插入图片描述
但是 我们会发现 其实这个方法还是不太一样的 但是 思路上 差的不是特别大
其实在用我们这个研究研究 也能解决这个问题
我只是带着大家自己写一下 那么 我们这个 循环语句直接重写 还是按官方的方案来

这里 直接将我们的 nestTokens 改成这样

/*
    整合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;
};

这个函数 很巧妙的利用了js浅拷贝 数组等引用类型可以直接利用 等于来改变指向的原理
很多人可能因为浅拷贝给代码带来BUG 但这里 就是对浅拷贝作用很好的一个利用

首先 collector最开始指向的 nestedTokens 就是我们结果数组
然后 每次collector.push 都会存入nestedTokens
当遇到井号之后呢 每次 就会 collector.push 进到 最近 一次入栈的 元素的 2下标中
然后 出栈时 这个判断比较精髓
如果sections通过 pop 干掉最后一个元素之后 还有元素
那么说明 他之前还匹配过其他 井号 说明 我们这个循环是在其他循环中编写的
就直接继续等于上一个入栈的 元素的 2下标
如果没了 表示 到最后了 就重新指回 nestedTokens
在这里插入图片描述
可以看到 这个结果就非常的完美 这个算法可以说非常的巧妙 尤其是对 浅拷贝的精准应用
真的希望 大家如果学一款框架 重点记API 如果学原理 例如这些算法 还是要理解 然后延伸出更多场景用法
我说实话这东西如果死记硬背 那真的不如不要浪费时间 真的没用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/822933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Postman如何做接口测试1:如何导入 swagger 接口文档

在使用 postman 做接口测试过程中&#xff0c;测试工程师会往界面中填入非常多的参数&#xff0c;包括 url 地址&#xff0c;请求方法&#xff0c;消息头和消息体等一系列数据&#xff0c;在请求参数比较多的情况下非常花时间。 我们可以使用 postman 的文档导入功能&#xff…

Chapter 11: Tuples | Python for Everybody 讲义笔记_En

文章目录 Python for Everybody课程简介TuplesTuples are immutableComparing tuplesTuple assignmentDictionaries and tuplesMultiple assignment with dictionariesThe most common wordsUsing tuples as keys in dictionariesSequences: strings, lists, and tuples - Oh M…

因果推断(二)倾向匹配得分(PSM)

因果推断&#xff08;二&#xff09;倾向匹配得分&#xff08;PSM&#xff09; 前文介绍了如何通过合成控制法构造相似的对照组&#xff0c;除此之外&#xff0c;也可以根据倾向匹配得分&#xff08;PSM&#xff09;进行构造&#xff0c;即为每一个试验组样本在对照组中找对与…

dialog => :before-close的属性应用

在element-ui里面关闭弹窗的时候before-close会触发。 也就是点击X的时候回触发before-close这个属性, 代码实例: <el-dialogtitle"新增用户":visible.sync"dialogVisible"width"50%":before-close"handleClose"> handleClose…

【动态规划part15】| 392.判断子序列、115.不同的子序列

&#x1f388;LeetCode392.判断子序列 链接&#xff1a;392.判断子序列 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;…

Linux端口与netstat使用

端口是设备与外界交流的通道&#xff0c;有物理端口和虚拟端口。 Linux有六万多端口&#xff0c;可以分为下面几类&#xff1a; 1.公认端口&#xff08;1~1023&#xff09;&#xff1a;用于系统内置与知名程序的预留使用 2.注册端口&#xff08;1024~49151&#xff09;&…

使用webdriver-manager解决浏览器与驱动不匹配所带来自动化无法执行的问题

1、前言 在我们使用 Selenium 进行 UI 自动化测试时&#xff0c;常常会因为浏览器驱动与浏览器版本不匹配&#xff0c;而导致自动化测试无法执行&#xff0c;需要手动去下载对应的驱动版本&#xff0c;并替换原有的驱动&#xff0c;可能还会遇到跨操作系统进行测试的时候&…

【autoresizing案例 Objective-C语言】

一、autoresizing案例 1.在介绍autoresizing之前,告诉大家,这个只是介绍,以后不要用这个东西,都用autolayout 还有一个非常重要的就是,使用autoresizing,就不能用autolayout,反之亦然 2.我们来看一个案例,看一个什么案例呢,看这么一个案例, 大家先看我这个的要求:…

如何安装、部署、启动Jenkins

一、测试环境 Linux系统 Centos 7 二、安装步骤&#xff1a; 1、安装jdk 我安装的是jdk8&#xff0c;此处就不多说了&#xff0c;自己百度哈&#xff0c;很简单 2、安装jenkins 首先依次执行如下三个命令&#xff1a; 2.1、导入镜像&#xff1a; [rootcentos7 ~]# sudo …

《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(18)-Fiddler如何接口测试,妈妈再也不担心我不会接口测试了

1.简介 Fiddler最大的优势在于抓包&#xff0c;我们大部分使用的功能也在抓包的功能上&#xff0c;fiddler做接口测试也是非常方便的。 领导或者开发给你安排接口测试的工作任务&#xff0c;但是没有给你接口文档&#xff08;由于开发周期没有时间出接口文档&#xff09;&…

小程序商品如何上传视频

小程序商品展示的方式在不断创新&#xff0c;除了传统的图片展示&#xff0c;视频成为了吸引用户注意力的重要方式之一。今天就讲解一下&#xff0c;商家怎么上传商品视频。 1. 商家需要准备好商品视频。商家可以自己拍摄商品的使用演示视频、产品介绍视频等&#xff0c;也可以…

Linux部署jar包,隐藏命令行参数

Linux部署jar包&#xff0c;隐藏命令行参数 一、背景需求二、查阅资料三、实现隐藏库3.1、测试test.c3.2、设置隐藏库3.3、验证 四、应用jar启动命令五、直接应用结果 最新项目安全检测&#xff0c;发现配置文件中数据库密码&#xff0c;redis密码仍处理明文状态 于是整理了一篇…

做软件测试,掌握哪些技术才能算作“测试大佬”?

一、过硬的基础能力 其实所有的测试大佬都是从底层基础开始的&#xff0c;随着时间&#xff0c;经验的积累慢慢变成大佬。要想稳扎稳打在测试行业深耕&#xff0c;成为测试大牛&#xff0c;首当其冲的肯定就是拥有过硬的基础&#xff0c;所有的基础都是根基&#xff0c;后期所…

【应用层】HTTPS协议详细介绍

文章目录 前言一、什么是"加密"二、常见的加密方式三、数据摘要&#xff08;数据指纹&#xff09;四、证书总结 前言 HTTPS也是一个应用层协议&#xff0c;是在HTTP协议的基础上引入了一个加密层&#xff0c;由于HTTP协议内容都是按照文本的方式明文传输的&#xff…

【外卖系统】修改菜品

需求分析 在菜品管理列表页面点击修改按钮&#xff0c;跳转到修改页面&#xff0c;在修改页面回显菜品相关信息并进行修改&#xff0c;在最后点击确定按钮完成修改操作 代码设计 页面发送ajax请求&#xff0c;请求服务端获取分类数据&#xff0c;用于菜品分类下拉框中数据显…

【Unity学习笔记】对象池

文章目录 设计思路总体设计从生命周期考虑 一些代码 对象池这个东西老生常谈了&#xff0c;使用它的好处在于&#xff1a;当我们需要重复创建或者销毁一些物体&#xff0c;例如限制子弹数量上限为10发&#xff0c;当射出第11发就需要使第10发消失&#xff0c;第11出现。销毁10号…

vue中人员导出功能实现

大纲&#xff1a; 1、导出定义的export.js文件 代码展示 import axios from axios //导出一 export const exportExcel (url, params, name, type post) > {// url url路径 params 查询参数 name 文件名 type 请求方式axios[type](url, params, {responseType: blob,}).t…

微信小程序多码融合

1、多码融合实现 如果需要实现扫码关注、跳转页面、扫码充电以及第三方融合扫码充电的需求&#xff0c;通过“扫普通链接二维码打开小程序” 的功能采用hlht协议的方式进行融合&#xff0c;使用代码生成新的二维码&#xff0c;二维码内容格式如下&#xff1a; hlht://9900000…

性能测试基础知识(三)性能指标

性能测试基础知识&#xff08;三&#xff09;性能指标 前言一、时间特性1、响应时间2、并发数3、吞吐量&#xff08;TPS&#xff09; 二、资源特性1、CPU利用率2、内存利用率3、I/O利用率4、网络带宽使用率5、网络传输速率&#xff08;MB/s&#xff09; 三、实例场景 前言 性能…

ES6系列之let、const、箭头函数使用的坑

变量提升块级作用域的重要性箭头函数this的指向rest参数和arguments 1.ECMAScript与Js的关系 2.Babel转码器 Babel是一个广泛使用的ES6转码器&#xff0c;可以将ES6代码转为ES5代码&#xff0c;从而在老版本的浏览器执行。这意味着&#xff0c;你可以用ES6的方式编写程序&…