1. 如何将一个字符传转换成一个AST树结构。

直接上代码:

 

const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
// 匹配 <div
const startTagOpen = new RegExp(`^<${qnameCapture}`)
// 匹配 > />
const startTagClose = /^\s*(\/?)>/
// 匹配 </div>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
const doctype = /^<!DOCTYPE [^>]+>/i

//注意树形的html 只能有一个根节点
function parseHtmlToAst(html){

	let text,root,currentParent,stack=[];
	
	while(html){
		let textEnd=html.indexOf("<");
		if(textEnd===0){
			//查找开始tag
			const startTagMatch=parseStartTag();
			if(startTagMatch){
				//生成AST树
				start(startTagMatch.tagName,startTagMatch.attrs);
				continue;
			}
			//查找结束标签
			const endTagMatch=html.match(endTag);
			if(endTagMatch){
				advance(endTagMatch[0].length);
				//构造ast树
				end(endTagMatch[1]);
				continue;
			}
		
		}
		//文本节点
		if(textEnd>0){
			text=html.substring(0,textEnd);
		}
		if(text){
			//截取字符串
			advance(text.length);
			chars(text);
		}
		
		 
	}
	//截些开始标记
	function parseStartTag(){
		const start=html.match(startTagOpen);
		
		let end,attr;
		//找到开始标记
		if(start){
			const match={
				tagName:start[1],
				attrs:[]
			}

			advance(start[0].length)
			//配置属性
			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;
			}
		}
		
	}
	
	//截取字符串
	function advance (n) {
    		html = html.substring(n)
  }
	//构造AST树形
	function start(tagName,attrs){
		const element=createAstElement(tagName,attrs);
		
		if(!root){
			root=element;
		}
		currentParent=element;
		stack.push(element);
	}
	
	//结束钩爪树形
	function end(tagName){
		const element=stack.pop();
		currentParent=stack[stack.length-1];
		if(currentParent){
			element.parent=currentParent;
			currentParent.children.push(element);
		}

	}
	//处理文本节点
	function chars(text){
		text=text.trim();
		if(text.length>0){
			currentParent.children.push({
				type:3,
				text
			})
		}
		 
	}
	
	
	function createAstElement(tagName,attrs){
		return {
			tag: tagName,
			type: 1,
			children:[],
			attrs,
			parent
		
		}
	}
	return root;

}

let html=`<div id="app" style="color:red;width:200px">
	你好:{{name}}
	<span class="text" style="color:green">{{age}}
	</span>
	</div>
`;


let root=parseHtmlToAst(html);
console.info(root)



代码的逻辑是:
对字符串的处理,从头开始处理,先找 < 开头的字符,如果找到则用 正则表达式查找 <div 的标签,找到后,截取 后面的字符串,然后循环查找 属性,直到 找到 > 或 />字符为止,找到让后截取后面的字符串。

中间用到了堆栈作为树节点作为串联。

2.构造结果

image

posted on 2022-07-17 15:35  自由港  阅读(393)  评论(0编辑  收藏  举报