首页 > 编程语言 >vue2源码-五、将模板编译解析成AST语法树1

vue2源码-五、将模板编译解析成AST语法树1

时间:2023-04-14 21:57:07浏览次数:39  
标签:匹配 AST 标签 html 源码 let attrs vue2 属性

将模板编译成ast语法树

  1. complileToFunction方法

    vue数据渲染:template模板->ast语法树->render函数,模板编译的最终结果结果就是render函数。

    complileToFunction方法中,生成render函数,需要以下两个核心步骤:

    • 通过parserHTML方法:将模板(templatehtml)内容编译成ast语法树
    • 通过codegen方法:根据ast语法树生成为render函数
    export function complileToFunction(template) {
      // 1.就是将template转化为ast语法树
      let ast = parseHTML(template)
      // 2.生成render方法(render方法执行后的返回结果就是虚拟DOM),使用ast生成render函数
      // 模板引擎的实现原理就是with + new Function
      let code = codegen(ast)
    
      code = `with(this){return ${code}}`
      let render = new Function(code)
      return render
    }
    
  2. parserHTML方法

    parserHTML方法:将模板(templatehtml)编译成为ast语法树

    注意:parserHTML方法的入参template,指的是<template>标签内部的内容并不包括<template>标签本身。

    Vue初始化时:

    • 如果options选项选项中设置了template,将优先使用template内容作为模板
    • 如果options选项没有设置template,将采用元素内容作为html模板:<div id="app"></div>

    主要使用的正则匹配。七个正则匹配。

    • 匹配标签号
    • 匹配命名空间标签名
    • 匹配开始标签-开始部分
    • 匹配结束标签
    • 匹配属性
    • 匹配开始标签-闭合部分
    • 匹配插值表达式
    export function parseHTML(html) {
      const ELEMENT_TYPE = 1
      const TEXT_TYPE = 3
      const stack = [] // 用于存放元素的
      let currenrParent // 指向栈的最后一个
      let root
    
      // 最终转化为一颗抽象语法树
      function createASTElement(tag, attrs) {
        return {
          tag,
          type: ELEMENT_TYPE,
          children: [],
          attrs,
          parent: null,
        }
      }
    
      // 利用栈构造一棵树
      function start(tag, attrs) {
        let node = createASTElement(tag, attrs) // 创造一个ast节点
        // 看一下是否为空树
        if (!root) {
          root = node // 如果为空则当前是树的根节点
        }
        if (currenrParent) {
          node.parent = currenrParent // 只赋予了parent属性
          currenrParent.children.push(node) // 还需要让父亲记住自己
        }
    
        stack.push(node)
        currenrParent = node // currenrParent为栈中的最后一个
      }
        
      // 文本 
      function chars(text) {
        text = text.replace(/\s/g, '')
        // 文本直接放到当前指向的节点中
        text &&
          currenrParent.children.push({
            type: TEXT_TYPE,
            text,
            parent: currenrParent,
          })
      }
      
      // 结束
      function end(tag) {
        stack.pop() // 弹出最后一个,校验标签是否合法
        currenrParent = stack[stack.length - 1]
      }
    
      function advance(n) {
        html = html.substring(n)
      }
      function parseStartTag() {
        // 1. 匹配开始标签
        const start = html.match(startTagOpen)
        if (start) {
          // 2. 构造怕匹配结果对象:包含标签名和属性
          const match = {
            tagName: start[1], // 标签名
            attrs: [], // 属性
          }
          // 3. 截取匹配到的结果
          advance(start[0].length)
          
          // 如果不是开始标签的结束,就一直匹配下去
          // 4. 开始解析标签的属性 id="app" a=1 b=2>
          let attr   // 是否匹配开始标签的结束符号 > 或 />
          let end    // 存储属性匹配的结果
          // 匹配并获取属性,放入 match.attrs 数组
          while (
            !(end = html.match(startTagClose)) &&
            (attr = html.match(attribute))
          ) {
            // 4.1 截取掉已匹配的结果
            advance(attr[0].length)
            // 4.2 将匹配到的属性记录到数组 match.attrs(属性对象包含属性名和属性值)
            match.attrs.push({
              name: attr[1],
              value: attr[3] || attr[4] || attr[5],
            })
          }
          // 4.3 将匹配到的属性记录到数组 match.attrs(属性对象包含属性名和属性值)
          if (end) {
            // <div id="app" 处理完成,需要连同关闭符号 > 一起截取掉,截取掉
            advance(end[0].length)
          }
          //  4.4 开始标签处理完成后,返回匹配结果:tagName 标签名 + attrs 属性对象
          return match
        }
        return false // 不是开始标签
      }
      while (html) {
        // 如果textEnd为0,说明是一个开始标签或者结束标签
        // 如果textEnd>0说明就是文本的结束位置
        // 解析标签or文本,判断html的第一个字符,是否为 < 尖角号
        let textEnd = html.indexOf('<') // 如果索引是0则说明是一个标签
        if (textEnd === 0) {
          // 解析开始标签,返回匹配结果,即标签名。
          const startTagMatch = parseStartTag()
          if (startTagMatch) {
            // 解析到的开始标签,传递标签名和属性
            start(startTagMatch.tagName, startTagMatch.attrs)
            continue
          }
          // 如果开始标签没有匹配到,有可能是结束标签 </div>
          let endTageMatch = html.match(endTag)
          if (endTageMatch) {
            // 删除已匹配完成的部分
            advance(endTageMatch[0].length)
            // 匹配到开始标签,向外传递标签名和属性
            end(endTageMatch[1])
            continue
          }
        }
    	// 如果是文本:将文本内容取出来并发射出去,并从html片段中截取掉
        if (textEnd > 0) {
          let text = html.substring(0, textEnd) // 文本内容
          if (text) {
            // 向外传递文本
            chars(text)
            advance(text.length) // 解析到的文本
          }
        }
      }
      return root
    }
    

标签:匹配,AST,标签,html,源码,let,attrs,vue2,属性
From: https://www.cnblogs.com/dgqp/p/17320057.html

相关文章

  • 【fastadmin】hasone和blongeto 怎么选?
    hasOne:有一个,加上主谓语应该是,A有一个BhasMany:有很多,A有很多BbelongsTo:属于,A属于B外键是主表中和关联表有关的字段,主键一般是关联表自己的id或者是和外键有关的字段hasOne和belongsTo这两种方法都可以应用在一对一关联上,但是两者也是有区别的:最主要的区别就在于:谁是......
  • elk学习笔记-elasticsearh-head插件以及elasticsearch-dump
     测试使用的elasitcsearch版本是6.3nodejs版本10.9linux版本为centos7.9elasicsearh-head插件Head插件是Elasticsearch的图形化界面工具,通过此插件可以很方便的对数据进行增删改查等数据交互操作。在Elasticsearch5.x版本以后,head插件已经是一个独立的WebApp了,所以不需要和Elasti......
  • 未来源码|什么是数据集成?超全的SeaTunnel 集成工具介绍
    以下文章来源于大数据与云原生技术分享,作者liugp推荐语:随着互联网流量爆发式增长,越来越多的公司业务需要支撑海量数据存储,对高并发、高可用、高可扩展性等特性提出了更高的要求。这也促使各种类型的数据库快速发展,至今常见数据库已经达到200多个。与之相伴的便是,各种数据库之间......
  • Spring很常用的@Conditional注解的使用场景和源码解析
    你好,我是刘牌!介绍今天要分享的是Spring的注解@Conditional,@Conditional是一个条件注解,它的作用是判断Bean是否满足条件,如果满足条件,则将Bean注册进IOC中,如果不满足条件,则不进行注册,这个注解在SpringBoot中衍生出很多注解,比如@ConditionalOnProperty,@ConditionalOnBean,@Conditi......
  • centos 升级内核版本(源码)
    查看内核版本号:username-r 或者username-a  1、安装依赖yuminstall-ygccmakegitctagsncurses-developenssl-develyuminstall-ybisonflexelfutils-libelf-develbc如果担心依赖问题不够新,可以在执行:yum-yupgrade,更新操作系统上的所有依赖 2......
  • fastjson 1.2.24 反序列化漏洞(审计分析)
    环境JDK8u181Fastjson1.2.24POC跟进parse方法跟进到底层deserialze方法Poc中传入的dataSourceName:ldap://192.168.3.229:8084/vnSYPYwMs值这里实际对应setDataSourceName方法,调用此方法并传入ldap跟进setDataSourceName方法,这里只是简单赋值 步出......
  • elasticsearch索引如何删除字段
    有两种方法可以删除Elasticsearch索引中的字段。1.通过mapping更新您可以使用MappingAPI更新索引映射并删除字段。以下是删除字段的步骤:1.通过 GET/your-index/_mapping 获取当前的索引映射。2.编辑映射,删除要删除的字段。3.将更新的映射传回Elasticsearch,使用以......
  • Vue2总结
    笔记脚手架文件结构├──node_modules├──public│├──favicon.ico:页签图标│└──index.html:主页面├──src│├──assets:存放静态资源││└──logo.png││──component:存放组件││└──HelloWorld.vue│......
  • c# 计算器2.0源码
      主要时间浪费在 //执行计算objectresult=newDataTable().Compute(s,"");这条语句上。如果不加处理,特别大的整数相乘,会提示值太大。只好将整数加个0变为小数;usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;......
  • 互联网医院源码|互联网医院软件体现智慧医疗的优势
    现在大家看病一般都会直接在互联网医院平台上去就诊,每次大家需要看病时,可以在手机上直接去预约指定的医生,同城周边的所有医院都是可以去直接选择的,这样也可以去帮助大家节省很多的看病时间,在互联网医院软件中所具备的功能一般都是比较齐全的,那么互联网医院源码开发功能特色你了解吗......