经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Vue.js » 查看文章
Vue源码学习(三):<templete>渲染第二步,创建ast语法树
来源:cnblogs  作者:养肥胖虎  时间:2023/9/9 10:38:27  对本文有异议

好家伙,书接上回

 

在上一篇Vue源码学习(二):<templete>渲染第一步,模板解析中,我们完成了模板解析

现在我们继续,将模板解析的转换为ast语法树

 

1.前情提要

代码已开源https://github.com/Fattiger4399/analytic-vue.git手动调试一遍,

胜过我解释给你听一万遍

  1. function start(tag, attrs) { //开始标签
  2. console.log(tag, attrs, '开始的标签')
  3. }
  4. function charts(text) { //获取文本
  5. console.log(text, '文本')
  6. }
  7. function end(tag) { //结束的标签
  8. console.log(tag, '结束标签')
  9. }

在这里,我们知道start,charts,end分别可以拿到

我们的`开始标签`,`文本`,`结束标签`

效果如下:(仔细看,这也是我们实验要用到的例子)

 

随后我们开始改造这几个方法

 

2.代码详解

2.1.ast树节点的结构

确定我们ast树节点的结构:

  1. let root; //根元素
  2. let createParent //当前元素的父亲
  3. let stack = []
  4. function createASTElement(tag, attrs) {
  5. return {
  6. tag,
  7. attrs,
  8. children: [],
  9. type: 1,
  10. parent: null
  11. }
  12. }

节点元素分别为

  • tag:标签名
  • attrs:标签属性
  • children:子元素(数组)
  • type:类型(后面会用到,目前"1"代表标签"3"代表文本)
  • parent:父元素

 

2.2.start()方法

  1. function start(tag, attrs) { //开始标签
  2. let element = createASTElement(tag, attrs) //生成一个开始标签元素
  3. //查看root根元素是否为空
  4. //若是,将该元素作为根
  5. //非原则
  6. if (!root) {
  7. root = element
  8. }
  9. createParent = element
  10. stack.push(element)
  11. console.log(tag, attrs, '开始的标签')
  12. }

此处,生成一个开始标签元素,判断root是否为空,若为空,则将该元素作为根元素

随后将该元素作为父元素.

 

2.3.charts()方法

  1. function charts(text) { //获取文本
  2. console.log(text, '文本')
  3. // text = text.replace(/a/g,'')
  4. if(text){
  5. createParent.children.push({
  6. type:3,
  7. text
  8. })
  9. }
  10. // console.log(stack,'stack')
  11. }

这个好理解,将"文本内容"作为父元素的孩子

 

2.4.end()方法

  1. function end(tag) { //结束的标签
  2. let element = stack.pop()
  3. createParent = stack[stack.length - 1]
  4. if (createParent) { //元素闭合
  5. element.parent = createParent.tag
  6. createParent.children.push(element)
  7. }
  8. console.log(tag, '结束标签')
  9. }

此处,我们先将栈stack最新的元素弹出栈(作为当前元素,我们要对他进行操作),

随后获取栈的前一个元素作为父元素,

当前元素的父元素属性指向父元素的标签属性

随后将该元素推入父元素的children中,

emmmm,我还是说人话吧

 

假设现在stack=['div','h1']

然后pop了,createParent = 'h1'

'h1'.parent =>'div'

'div'.children =>'h1'

(多看几遍就理解了,其实非常简单)

 

来看看最终实现的ast语法树长什么样子

 (父子关系和谐)

 

搞定啦!

 

3.完整代码

  1. const attribute =
  2. /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
  3. //属性 例如: {id=app}
  4. const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; //标签名称
  5. const qnameCapture = `((?:${ncname}\\:)?${ncname})` //<span:xx>
  6. const startTagOpen = new RegExp(`^<${qnameCapture}`) //标签开头
  7. const startTagClose = /^\s*(\/?)>/ //匹配结束标签 的 >
  8. const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`) //结束标签 例如</div>
  9. const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g
  10. let root; //根元素
  11. let createParent //当前元素的父亲
  12. let stack = []
  13. function createASTElement(tag, attrs) {
  14. return {
  15. tag,
  16. attrs,
  17. children: [],
  18. type: 1,
  19. parent: null
  20. }
  21. }
  22. function start(tag, attrs) { //开始标签
  23. let element = createASTElement(tag, attrs) //生成一个开始标签元素
  24. //查看root根元素是否为空
  25. //若是,将该元素作为根
  26. //非原则
  27. if (!root) {
  28. root = element
  29. }
  30. createParent = element
  31. stack.push(element)
  32. console.log(tag, attrs, '开始的标签')
  33. }
  34. function charts(text) { //获取文本
  35. console.log(text, '文本')
  36. // text = text.replace(/a/g,'')
  37. if(text){
  38. createParent.children.push({
  39. type:3,
  40. text
  41. })
  42. }
  43. // console.log(stack,'stack')
  44. }
  45. function end(tag) { //结束的标签
  46. let element = stack.pop()
  47. createParent = stack[stack.length - 1]
  48. if (createParent) { //元素闭合
  49. element.parent = createParent.tag
  50. createParent.children.push(element)
  51. }
  52. console.log(tag, '结束标签')
  53. }
  54. export function parseHTML(html) {
  55. while (html) { //html 为空时,结束
  56. //判断标签 <>
  57. let textEnd = html.indexOf('<') //0
  58. // console.log(html,textEnd,'this is textEnd')
  59. if (textEnd === 0) { //标签
  60. // (1) 开始标签
  61. const startTagMatch = parseStartTag() //开始标签的内容{}
  62. if (startTagMatch) {
  63. start(startTagMatch.tagName, startTagMatch.attrs);
  64. continue;
  65. }
  66. // console.log(endTagMatch, '结束标签')
  67. //结束标签
  68. let endTagMatch = html.match(endTag)
  69. if (endTagMatch) {
  70. advance(endTagMatch[0].length)
  71. end(endTagMatch[1])
  72. continue;
  73. }
  74. }
  75. let text
  76. //文本
  77. if (textEnd > 0) {
  78. // console.log(textEnd)
  79. //获取文本内容
  80. text = html.substring(0, textEnd)
  81. // console.log(text)
  82. }
  83. if (text) {
  84. advance(text.length)
  85. charts(text)
  86. // console.log(html)
  87. }
  88. }
  89. function parseStartTag() {
  90. //
  91. const start = html.match(startTagOpen) // 1结果 2false
  92. // console.log(start,'this is start')
  93. // match() 方法检索字符串与正则表达式进行匹配的结果
  94. // console.log(start)
  95. //创建ast 语法树
  96. if (start) {
  97. let match = {
  98. tagName: start[1],
  99. attrs: []
  100. }
  101. // console.log(match,'match match')
  102. //删除 开始标签
  103. advance(start[0].length)
  104. //属性
  105. //注意 多个 遍历
  106. //注意>
  107. let attr //属性
  108. let end //结束标签
  109. //attr=html.match(attribute)用于匹配
  110. //非结束位'>',且有属性存在
  111. while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
  112. // console.log(attr,'attr attr'); //{}
  113. // console.log(end,'end end')
  114. match.attrs.push({
  115. name: attr[1],
  116. value: attr[3] || attr[4] || attr[5]
  117. })
  118. advance(attr[0].length)
  119. //匹配完后,就进行删除操作
  120. }
  121. //end里面有东西了(只能是有">"),那么将其删除
  122. if (end) {
  123. // console.log(end)
  124. advance(end[0].length)
  125. return match
  126. }
  127. }
  128. }
  129. function advance(n) {
  130. // console.log(html)
  131. // console.log(n)
  132. html = html.substring(n)
  133. // substring() 方法返回一个字符串在开始索引到结束索引之间的一个子集,
  134. // 或从开始索引直到字符串的末尾的一个子集。
  135. // console.log(html)
  136. }
  137. // console.log(root)
  138. return root
  139. }

 

原文链接:https://www.cnblogs.com/FatTiger4399/p/17683332.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号