当前位置: 首页 > news >正文

手写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);

相关文章:

  • Dinky MySQLCDC 整库同步到 MySQL jar包冲突问题解决
  • 用esp prog烧录ESP32-C3板踩坑
  • pytorch-卷积神经网络
  • 【计算机视觉】数字图像处理基础知识(模拟和数字图像、采样量化、像素的基本关系、灰度直方图、图像的分类)
  • PS Mac Photoshop 2024 for Mac[破]图像处理软件[解]PS 2024安装教程[版]
  • 怎么一键备份还原Win10系统?
  • FTP原理
  • JsonCpp源码跨平台编译
  • 01Linux以及操作系统概述
  • 【wiki知识库】03.前后端的初步交互(展现所有的电子书)
  • 【深度学习】Transformer梳理
  • nginx 安全配置
  • docker安装ubtuntu
  • MongoDB~存储引擎了解
  • C/C++|回调函数的正确打开方式
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 0基础学习移动端适配
  • conda常用的命令
  • cookie和session
  • CSS盒模型深入
  • extjs4学习之配置
  • js递归,无限分级树形折叠菜单
  • js正则,这点儿就够用了
  • Nacos系列:Nacos的Java SDK使用
  • Spark RDD学习: aggregate函数
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • 动态魔术使用DBMS_SQL
  • 仿天猫超市收藏抛物线动画工具库
  • 技术胖1-4季视频复习— (看视频笔记)
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 如何实现 font-size 的响应式
  • 事件委托的小应用
  • 我的面试准备过程--容器(更新中)
  • 一些关于Rust在2019年的思考
  • 赢得Docker挑战最佳实践
  • 自定义函数
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (实战篇)如何缓存数据
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • ***详解账号泄露:全球约1亿用户已泄露
  • .chm格式文件如何阅读
  • .NET 4.0中的泛型协变和反变
  • .NET Core 2.1路线图
  • .NET Micro Framework 4.2 beta 源码探析
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .net反编译工具
  • .net反混淆脱壳工具de4dot的使用
  • ;号自动换行
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?