手写HTML字符串解析成对应的 AST语法树

先看效果 展示如下:

  • HTML模版
    在这里插入图片描述
  • 转成ast语法树后
    在这里插入图片描述

在学习之前,我们需要了解这么一个问题,为什么要将HTML字符串解析成对应的 AST语法树。

为什么?

  • 语法分析:HTML字符串是一种标记语言,其中包含了大量的标签、属性、文本等内容。通过解析HTML字符串,可以将其转换为更易于操作和理解的数据结构,方便对HTML进行进一步处理。

  • 数据驱动:现代前端开发中,数据驱动视图渲染是一种常见的模式。在许多框架中,开发者可以使用类似JSX或模板语言的方式编写组件的结构,然后通过解析和转换成AST树来实现动态渲染。

  • 模板编译:许多前端框架(如Vue、React等)都会将模板编译成渲染函数,以实现高效的视图更新。在这个过程中,将模板解析成AST树是必不可少的步骤。

  • 性能优化:通过将HTML字符串转换成AST树,可以更方便地进行性能优化,比如进行静态节点的标记、事件绑定等操作,以提高页面渲染的效率。

  • 安全性:通过AST树可以更容易地进行XSS(跨站脚本攻击)防护,对用户输入的HTML进行合理的过滤和转义,避免恶意脚本的注入。

优势:

  • 更高效的处理和操作:AST提供了对HTML结构的抽象表示,使得可以更轻松地对HTML进行遍历、操作和分析。这使得在前端开发中,可以更方便地实现诸如模板编译、组件化等功能。

  • 提高性能:在前端框架中,将HTML模板转换为AST可以帮助进行模板编译,将模板转换为可执行的渲染函数。这样可以避免在每次渲染时重新解析模板,从而提高渲染性能。

  • 支持差异化更新:通过比较两个AST树,可以更高效地实现差异化更新,即只更新发生变化的部分,而不需要重新渲染整个页面。这对于提高页面渲染性能和用户体验至关重要。

  • 便于优化和分析:AST树可以用于静态分析,有助于发现潜在的性能问题或安全问题,以进行优化和安全防护。

  • 支持扩展和定制:通过AST树,可以轻松实现对HTML的自定义扩展,如自定义指令、自定义组件等功能,从而提供更灵活的开发和定制能力。

案例:可以动手敲一敲

直接新建一个html文件夹,将一下的代码放进script先运行一下,体验一下,然后再去理解,如果能手写的话,手写一遍。

const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; // 标签名 
const qnameCapture = `((?:${ncname}\\:)?${ncname})`; //  用来获取的标签名的 match 后的索引为1的
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 匹配开始标签的 
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配闭合标签的
// 匹配属性的正则表达式
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
const startTagClose = /^\s*(\/?)>/; // 匹配开始标签的闭合部分
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; // 匹配双花括号内容的正则表达式,用于处理文本节点中的插值表达式

// 创建 AST 元素
function createAstElement(tagName, attrs) {
    return {
        tag: tagName,
        type: 1, // 代表元素节点
        children: [],
        parent: null,
        attrs
    }
}

let root = null;
let stack = [];

// 处理开始标签
function start(tagName, attributes) {
    let parent = stack[stack.length - 1];
    let element = createAstElement(tagName, attributes);
    if (!root) {
        root = element;
    }
    if (parent) {
        element.parent = parent;
        parent.children.push(element);
    }
    stack.push(element);
}

// 处理结束标签
function end(tagName) {
    let last = stack.pop();
    if (last.tag !== tagName) {
        throw new Error('标签有误');
    }
}

// 处理文本节点
function chars(text) {
    text = text.replace(/\s/g, ""); // 移除空格
    let parent = stack[stack.length - 1];
    if (text) {
        parent.children.push({
            type: 3, // 代表文本节点
            text
        })
    }
}

// 解析 HTML 字符串,生成对应的 AST
function parserHTML(html) {
    function advance(len) {
        html = html.substring(len);
    }

    function parseStartTag() {
        const start = html.match(startTagOpen);
        if (start) {
            const match = {
                tagName: start[1],
                attrs: []
            }
            advance(start[0].length);
            let end;
            let attr;
            while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
                match.attrs.push({ name: attr[1], value: attr[3] || attr[4] || attr[5] });
                advance(attr[0].length);
            }
            if (end) {
                advance(end[0].length);
            }
            return match;
        }
        return false; // 不是开始标签
    }

    // 逐步解析HTML字符串
    while (html) {
        let textEnd = html.indexOf('<'); // 当前解析的开头  
        if (textEnd == 0) {
            const startTagMatch = parseStartTag(html); // 解析开始标签
            if (startTagMatch) {
                start(startTagMatch.tagName, startTagMatch.attrs);
                continue;
            }
            const endTagMatch = html.match(endTag);
            if (endTagMatch) {
                end(endTagMatch[1]);
                advance(endTagMatch[0].length);
                continue;
            }
        }
        let text;
        if (textEnd > 0) {
            text = html.substring(0, textEnd)
        }
        if (text) {
            chars(text);
            advance(text.length);
        }
    }

    return root;
}

let htmls = '<div>111</div>';
let str = parserHTML(htmls);
console.log("str", str);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电动汽车电子系统架构

电动汽车的普及正在稳步发展&#xff0c;供应链的各个环节也在发生变化。它涵盖了制造电动汽车零件的原材料、化学品、电池和各种组件。与此同时&#xff0c;汽车充电基础设施也参与其中&#xff0c;它们正经历一个历史性的阶段&#xff0c;经过彻底的重新设计。它们的电气化以…

Echarts实现半圆形饼图,Echarts实现扇形图

效果预览,此处的双半圆扇形图是使用v-for循环的出来的 dom部分 <template><div><div class="mainDiv"><div class="headTit">全校平台最近作业</div><div class="loopSubject"><div id="app"…

反射获取成员变量

目录 利用反射获取成员变量 ​编辑 代码实现 获取class对象 获取成员变量 获取单个成员变量 获取成员变量的名字 获取权限修饰符 获取成员变量的数据类型 获取成员变量记录的值 修改对象里面记录的值 利用反射获取成员变量 代码实现 Student类&#xff1a; 获取clas…

JVM学习-类加载过程(二)

Initialization初始化阶段 为类的静态变量赋予正确的初始值 具体描述 类的初始化是类装载的最后一个阶段&#xff0c;如果前面的步骤没有问题&#xff0c;那么表示类可以顺利装载到系统中&#xff0c;此时&#xff0c;类才会开始执行Java字节码(即&#xff0c;到了初始化阶段…

使用YOLOv10训练自己的数据集

1. yolov10源码下载 THU-MIG/yolov10: YOLOv10: Real-Time End-to-End Object Detection (github.com)https://github.com/THU-MIG/yolov10?tabreadme-ov-file 2. 环境配置 预先安装好ANACONDA、PyCharm或者VSCode等基本软件。参考以下博客&#xff1a; 史上最全最详细的An…

六一礼物怎么选?来用python采集几套试卷送给小朋友们吧

马上要六一了&#xff0c;想一想我小时候的儿童节老师大概率都会布置一些试卷&#xff0c;所以也算是渡过了一个很"快乐"的童年呢。 所以今天这篇文章来采集一下试卷网中的试卷&#xff0c;快来学习一下&#xff0c;然后采集几套试卷送给你身边还在上学的小朋友们吧…

《面试笔记》——MySQL终结篇30

三大范式&#xff1f; 第一范式&#xff1a;字段具有原子性&#xff0c;不可再分&#xff08;字段单一职责&#xff09; 第二范式&#xff1a;满足第一范式&#xff0c;每行应该被唯一区分&#xff0c;加一列存放每行的唯一标识符&#xff0c;称为主键&#xff08;都要依赖主…

系统架构设计师【第10章】: 软件架构的演化和维护 (核心总结)

文章目录 10.1 软件架构演化和定义的关系10.1.1 演化的重要性10.1.2 演化和定义的关系 10.2 面向对象软件架构演化过程10.2.1 对象演化10.2.2 消息演化10.2.3 复合片段演化10.2.4 约束演化 10.3 软件架构演化方式的分类10.3.1 软件架构演化时期10.3.2 软件架构静态演…

第二十五章新增H5基础(以及视频~兼容)

1.HTML5中新增布局标签 HTML5新增了页眉&#xff0c;页脚&#xff0c;内容块等文档结构相关标签&#xff0c;可以使文档结构更加清晰明了。 1.新增的结构标签 1、<header>标签 定义文档或者文档中内容块的页眉。通常可以包含整个页面或一个内容区域的标题&#xff0c…

【Java】刚刚!突然!紧急通知!垃圾回收!

【Java】刚刚&#xff01;突然&#xff01;紧急通知&#xff01;垃圾回收&#xff01; 文章目录 【Java】刚刚&#xff01;突然&#xff01;紧急通知&#xff01;垃圾回收&#xff01;从C语言的内存管理引入&#xff1a;手动回收Java的垃圾回收机制引用计数器循环引用问题 可达…

支付宝支付-Java基于沙箱环境实现支付宝支付

一、支付宝沙箱环境介绍 沙箱环境是支付宝开放平台为开发者提供的安全低门槛的测试环境&#xff0c;开发者在沙箱环境中调用接口无需具备所需的商业资质&#xff0c;无需绑定和开通产品&#xff0c;同时不会对生产环境中的数据造成任何影响。合理使用沙箱环境&#xff0c;可以…

7-Django项目--账号管理

目录 templates/admin_role/admin_list.html ​编辑 templates/admin_role/add_modify.html views/admin_role.py 账号管理----> 账号添加 views.py 身份修改 views.py 重置密码 views.py templates/admin_role/admin_list.html {% extends "index/index.ht…

[有监督学习] 7.详细图解随机森林

随机森林 随机森林&#xff08;random forest&#xff09;是将多个模型综合起来创建更高性能模型的方法&#xff0c;既可用于回归&#xff0c;也可用于分类。同样的算法有梯度提升&#xff08;gradient boosting&#xff09;等在机器学习竞赛中很受欢迎的算法。 通过学习随机森…

SpringBoot整合jasypt加密配置文件敏感信息

SpringBoot整合jasypt加密配置文件敏感信息 在项目中我们需要对配置文件的一些敏感信息进行加密处理&#xff0c;比如数据库账户密码&#xff0c;避免直接暴露出来&#xff0c;这种场景常常用于生产环境&#xff0c;我们不想让开发人员知道生产库的密码&#xff0c;有运维人员…

Ubuntu 安装好虚拟环境后,找不到workon 命令

1、安装虚拟环境 pip3 install virtualenv pip3 install virtualenvwrapper 2、安装完成后 workon 命令。 找不到workon 命令 执行&#xff0c;source virtualenvwrapper.sh 执行后&#xff0c;在使用workon命令&#xff0c;即可完成。

1.1 Mediapipe随手简记(一)

为了后续项目展开&#xff0c;需要Python、C、Linux、OpenCV、Mediapipe、ROS知识。 最后面有手势识别&#xff08;数字&#xff09;精准案例&#xff0c;项目会用到。 Mediapipe学习篇1 Mediapipe 是一个开源的跨平台框架&#xff0c;它提供了大量的解决方案&#xff0c;用…

MySQL十部曲之九:MySQL优化理论

文章目录 前言概述查询优化查询执行计划EXPLAIN获取表结构信息获取执行计划信息 EXPLAIN 输出格式如何使用EXPLAIN进行优化 范围访问优化单列索引的范围访问多列索引的范围访问 索引合并优化索引合并交叉访问算法索引合并联合访问算法索引合并排序联合访问算法 索引下推优化连接…

随身wifi网络卡顿怎么解决?随身WiFi哪个牌子的最好用?排名第一名的随身WiFi!

对于随身wifi靠不靠谱这个问题&#xff0c;网上一直存在争议。很多人的随身wifi网速不稳定&#xff0c;信号看着满格就是上不了网。关于随身wifi卡顿到底该怎么解决呢&#xff1f; 1.如果是设备网络在一个地方上网速度很快&#xff0c;换一个地方网络就不行了&#xff0c;很可能…

爬虫学习2

中国国家地理网 单张图片爬取 import requests url http://img0.dili360.com/ga/M00/02/AB/wKgBzFQ26i2AWujSAA_-xvEYLbU441.jpg!rw9 headers {"User-Agent": Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0…

CC工具箱使用指南:【山西省村规结构调整表(亦求长生亦求你)】

一、简介 群友定制工具。 工具根据输入的用地图层&#xff0c;生成山西村规的结构调整表。 和一般的用地表有些不一样的地方是&#xff0c;现状和规划字段都在同一个图层里。 并且还有一个【村庄名称】的字段&#xff0c;可以将多个村庄放在一个图层中&#xff0c;一次性生…