。也很简单,使用html.match(regularExp)即可,我们可以从Vue.js源码中的html-parser.js中找到对应的正则表达式startTagOpen。源码的正则化非常复杂,因为我们需要兼容生产环境的各种标签,暂时不考虑,简化版为:/^<([a-zA-Z_]*)/.在线调试启动标签正则化。利用这个规律调用html.match()得到数组['
{让开始=html.match(this.startTagOpen);//['
。找到结束符号后,同样删除对应遍历的部分。constend=html.match(this.startTagClose)debuggerif(end){advance(end[0].length)}returnmatch两部分结合后,可以完整遍历模板字符串中的
开始标签,解析了。3、解析文本内容{{msg}}接下来就是提取模板字符串中的文本内容{{msg}},依旧是遍历字符串&&正则匹配继续补充while循环:while(html){debugger//顺序影响逻辑,startTag必须在text之前,endTag必须在startTag之前textEnd>=0){text=html.substring(0,textEnd)}if(text){advance(text.length)}}因为解析的部分被删除了,每个部分都有解析顺序,我们只需要检测next标签的位置可以得到html中文本内容的结束下标:html.indexOf('<')。然后就可以得到完整的文本内容{{msg}}:text=html.substring(0,textEnd)。最后别忘了删除已经遍历的文本内容:advance(text.length)4.解析结束标签
此时html字符串只剩下'
',我们继续使用Traversing&&正则解析:while(html){debugger//顺序影响逻辑,startTag必须在text之前,endTag必须在startTag之前letendTagMatch=html.match(this.endTag)if(endTagMatch){advance(endTagMatch[0].length)continue}}我们暂时不需要从结束标签中提取信息,只需要遍历、匹配、删除即可。解析完成总结至此,我们基本实现了classHTMLParser{},使用正则解析提取模板字符串中的3部分信息:开始标签:'
'textcontent:'{完整代码{msg}}'结束标签:'
',可以访问DEMO-《8分钟学会 Vue.js 原理》:1.模板字符串编译成抽象语法树AST-在JSBin中查看。但是为了得到AST,我们还需要根据这些信息做一些简单的拼接。5.初始化抽象语法树AST的根节点。我们继续参考Vue.js源码中拼接AST的实现,改进VueCompiler类,添加HTMLParser=newHTMLParser()实例,parse(template)方法。classVueCompiler{HTMLParser=newHTMLParser()constructor(){}parse(template){}}什么是AST?不需要先理解晦涩的概念。在Vue.js的实现中,AST是一个普通的JS对象,记录了标签名、父元素、子元素等属性:createASTElement(tag,parent){return{type:1,tag,parent,children:[]}}我们还将createASTElement方法添加到类VueCompiler中。并在parse方法中增加this.HTMLParser.parseHTML()的调用parse(template){const_this=thisletrootletcurrentParentthis.HTMLParser.parseHTML(template,{start(tag){},chars(text){},})debuggerreturnroot}start(tag){}是我们提取AST节点对应的开始标签的回调,接受一个参数tag,调用_this.createASTElement(tag,currentParent)生成AST节点.start(tag){letelement=_this.createASTElement(tag,currentParent)if(!root){root=element}currentParent=element},调用start(tag)的位置是类中的parseHTML(html,options)方法HTMLParser:consthandleStartTag=(match)=>{if(options.start){options.start(match.tagName)}}while(html){conststartTag=parseStartTag()if(startTag){handleStartTag(startTag)继续}当我们通过parseStartTag()得到{tagName:'div'}时,我们将它传给options.start(match.tagName)来生成AST的根节点://root'{"type":1,"tag":"div","children":[]}'我们将根节点保存到root变量中,用于最终返回整个AST的引用。6、给AST添加子节点除了根节点,我们还需要继续给AST树添加子节点:文本内容节点还是以回调的形式(options.char(text))来提取textcontent节点需要的信息,改进VueCompiler.parse()中的chars(text)方法chars(text){debuggerconstres=parseText(text)constchild={type:2,expression:res.expression,tokens:res.tokens,text}if(currentParent){currentParent.children.push(child)}},在parseHTML(html,options)的循环中添加options.chars(text)调用:while(html){//...省略其他标签的解析lettextlettextEnd=html.indexOf('<')//...if(options.chars&&text){options.chars(text)}}mustache标签语法??options.chars(text)to解析接收到的文本内容文本值是字符串'{{msg}}',我们还需要从中去掉{{}}得到msg字符串。还是用熟悉的正则匹配:constdefaultTagRE=/\{\{((?:.|\r?\n)+?)\}\}/functionparseText(text){lettokens=[]letrawTokens=[]constmatch=defaultTagRE.exec(text)constexp=match[1]tokens.push(("_s("+exp+")"))rawTokens.push({'@binding':exp})return{表达式:tokens.join('+'),tokens:rawTokens}}结果会是:{expression:"_s(msg)",tokens:{@binding:"msg"}}不需要理解表达式,tokens和它们的内容对于现在的具体含义,我们会在后面的runtime阶段详细介绍。7、完成模板字符串的遍历,返回完整的AST示例:DEMO-《8分钟学会 Vue.js 原理》:1.模板字符串编译成抽象语法树AST-JSBin经过以上步骤,我们解析模板字符串得到这样的一个对象://root==={"type":1,"tag":"div","children":[{"type":2,"expression":"_s(msg)","tokens":[{"@binding":"msg"}],"text":"{{msg}}"}]}这是Vue.js的AST,实现就是这么简单。例子中的代码直接来自于Vue.js的源码(编译器部分)后面我们会根据AST生成render()函数,最终渲染出真正的DOM。
《8分钟学会 Vue.js 原理》系列,共5部分:1.模板字符串编译成抽象语法树AST2.AST编译render()实现原理3.执行渲染函数render()生成虚拟节点vnode4.虚拟节点vnode生成真实DOM5.数据驱动DOM更新-WatcherObserver和Dep正在火热更新中,欢迎交流~欢迎更新~