cct-html-ast html语法树操作与渲染
- 基于hyntax开源项目的html语法树操作
- 支持html及vue文件的ast语法树操作,添加了常用的语法树操作方法与语法树递归钩子
- 修复了hyntax自关闭标签只支持内置标签的问题,支持自定义的自关闭标签
- 添加了ast转html代码的能力,添加了ast转html时递归生成html的钩子
安装
使用npm或cnpm安装
// npm安装 npm install --save cct-html-ast // cnpm安装 cnpm install --save cct-html-ast // yarn安装 yarn add cct-html-ast
使用
esm模块化引用
;
cjs模块化引用
const HTMLCompilerHTMLRenderdefault:HTMLAST = ;
HTMLAST类
- 该类只是提供了一些快捷方法,实际功能是由HTMLCompiler与HTMLRender类实现
- 为了方便扩展自定义的需求预留了astWalk与renderWalk回调函数,在ast递归时与ast渲染时执行
- 建议尽可能的通过ast语法树的节点操作来改变渲染的结果,不建议通过renderWalk来改变html的渲染结果,除非html渲染存在bug或者无法通过ast的修改实现自己的需求
- 默认在递归ast的时候将html中的src与href属性节点保存在了srcList与hrefList对象中以便操作
- ast的节点都继承于Node类,该类提供了一些常用的节点操作的api
;;;const tokenize constructTree = hyntax; { const compiler = content astWalk ; const render = renderWalk ; render; this_compiler = compiler; this_render = render; } { return this_compilerast; } { return this_renderhtml; } { return this_compilersrcList; } { return this_compilerhrefList; } { this_render; };
Node节点类
{ for const key in attrs thiskey = attrskey; } { const children = thisparent; const length = children; for let i = 0; i < length; i++ if childreni === this return i; return -1; } { /** * 如果参数是Node节点对象则直接返回该节点引用,并没有对节点进行复制 * 或许你本就希望将同一个节点复用并插入在不同节点或位置,引用的好处就是任何对节点的操作都会同步发生变化 * 也有可能你希望对已有的节点进行复制避免引用造成的问题 * 还有可能你希望节点的操作像DOM中的节点操作一样,将一个节点插入另一个节点会先将这个节点从原有的父级删除 * 所以我不太确定每个人的需求是什么样的,因此默认是引用方式没有做其它的处理 * 其实无论是哪种需求都很简单,所以用的时候有自己的需求就自己来写吧 */ if typeof node === 'string' return content: node astchildren; else if node instanceof Node return node; else throw '无效的node'; } { const children = this; children; } { const children = this; children; } { if thisparent const children = thisparent; const index = thisindex + 1; children; else throw 'this.parent is not defined!'; } { if thisparent const children = thisparent; const index = thisindex; children; else throw 'this.parent is not defined!'; } { thischildren; } { const children = thisparent; const length = children; for let i = 0; i < length; i++ if childreni === this children; break; }
HTMLRender类
{ thishtml = ''; thisast = ast; thisrenderWalk = typeof renderWalk === 'function' ? renderWalk : false; thisrenderNodeType = 10: 'renderDocument' 9: 'renderDoctype' 8: 'renderComment' 1: 'renderTag' 3: 'renderText' ; } { if ast thisast = ast; } { return ''; } { return `<!Doctype />` ; } { return `<!---->`; } { return `< ` selfClosing ? '/>' : `</>` ; } { return ``; } { return Object; } { const ast renderNodeType renderWalk = this; const walk = { const type children = text } = node; const renderMethod = renderNodeTypetype; if thisrenderMethod const openTag closeTag = '' = thisrenderMethodnode; const content = type === 1 && text ? text : children; const open close = renderWalk ? || : ; return open === undefined && close === undefined ? ` ` : ``; else throw `无法解析的节点类型【 】`; }; thishtml = ; }
示例
;const code = `<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>cct-html-ast</title></head><body> <div id="app"></div> <!--{{dll}}--> <a href="a.html">sdf</a> <a href="/a.html">sdf</a> <a href="https://nick.com/a.html">sdf</a> <img src="a/b/c.png"/> <img src="https://nick.com/a/b/c.png"/></body> </html> `;const obj = content: code// 要操作的html源码或vue源码 // ast语法树递归的钩子函数,接收一个node节点参数,该node节点为引用类型,// 直接操作该节点将可以改变ast语法树,如果回调函数返回false则该节点将被忽略// 以下示例是通过node.type过滤掉了所有的text文本节点// 如果只是想修改node节点的属性可以不返回任何值 比如可以node.tag='div'可以修改节点tag名 nodetype !== 3 // ast渲染成html代码的钩子函数,接收一个node节点参数,返回的数据必须是数组格式[openTag,closeTag] // 标签中的内容可以在保存在第一个数组中,如果没有结束标签数组第二元素可以为空或undefined { if nodetag === 'a' //此处通过钩子函数修改了a标签的渲染结果 return '<a>test' '</a>'; };// 通过ast节点的before方法插入了新的节点objastchildren1;// 执行ast的渲染obj;// HTMLAST 对象的结构参见下图console;
HTMLAST对象数据结构