美文网首页
ast语法树变成render字符串

ast语法树变成render字符串

作者: JX灬君 | 来源:发表于2021-08-19 21:50 被阅读0次

    ast语法树变成render字符串

    1. ast语法树变成字符串generate(ast)

      =>html元素:
      <div id='app' style="color:red">hello {{msg}}</div>

      =>转换成ast语法树:
      {"tag":"div","type":1,"children":[{"type":3,"text":"hello{{msg}}"}],"attrs":[{"name":"id","value":"app"},{"name":"style","value":"color:red"}],"parent":null}

      =>转换成render字符串:
      _c('div',{id:"app",style:{"color":"red"}},_v("hello"+_s(msg)))

      _c:处理标签 _v:处理文本 _s:处理{{}}

    let code = generate(ast)
    function generate(el) {
        // console.log(el)
        let children = genChildren(el)
        //方法 拼接字符串  源码也是这样操作 [{}]    ${el.attrs.length?`{style:{color:red}}`:'undefined'}
        let code = `_c('${el.tag}',${el.attrs.length ? `${genProps(el.attrs)}` : 'undefined'}${
            children ? `,${children}` : ''
            })`
    
        return code
    
    }
    
    1. 处理属性

      // 处理元素的属性
      function genProps(attrs) {
          //处理属性
          let str = ''
          for (let i = 0; i < attrs.length; i++) {
              let attr = attrs[i]
              //注意;   style:"color:red;font-size: 20px
              if (attr.name === 'style') {
                  let obj = {} //对样式进行特殊处理
                  attr.value.split(';').forEach(item => {
                      let [key, value] = item.split(':')
                      obj[key] = value
                  })
                  attr.value = obj //
              }
              //其他  'id:app',注意最后会多个属性化 逗号
              str += `${attr.name}:${JSON.stringify(attr.value)},`
          }
          return `{${str.slice(0, -1)}}`  // -1为最后一个字符串的位置  演示一下 
          // let reg =/a/g    reg.test('ad') false  
      }
      
    1. 处理子节点genChildren()方法(1)

      //判断是否有儿子
      function genChildren(el) {
          const children = el.children
          if (children) { //将所有
              return children.map(child => gen(child)).join(',')
          }
      
      
      }
      
      
    2. 处理子节点gen()方法(2)

      判断{{}}的正则const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g // {{xx}} 默认的 双大括号

      正则表达式通过exec返回匹配到的内容

    ex: let res = /a/g res.exec('abc') 结果: ["a", index: 0, input: "abc", groups: undefined]

    • 多次使用正则会在第二次使用时失效
    • 例如:let reg = /a/g reg.test('ab') //true
    • 接着re g.test('abc') // false
    • 处理方案 每次使用前设置 reg.lastIndex = 0
    // 类型:1:元素div 3:文本
    function gen(node) { //获取到的元素
        //注意 是什么类型    1.div 3.文本
        if (node.type === 1) {
            return generate(node) //生成元素节点的字符串
        } else {
            let text = node.text // 获取文本  注意  普通的文本  hello{{name}}?{{num}}
            if (!defaultTagRE.test(text)) {
                return `_v(${JSON.stringify(text)})`  // _v(html)  _v('hello'+_s(name))
            }
            let tokens = [] //存放每一段的代码
            let lastIndex = defaultTagRE.lastIndex = 0;//如果正则是全局模式 需要每次使用前变为0
            let match;// 每次匹配到的结果  exec 获取 {{name}}
            while (match = defaultTagRE.exec(text)) {
                // console.log(match) 获取到 又{{}}  元素
                //  console.log(match)
               let index = match.index;// 保存匹配到的索引
              // hello{{name}} ? {{num}}
                if (index > lastIndex) {
                   tokens.push(JSON.stringify(text.slice(lastIndex,index))) //添加的是文本
                }
                //{{name}} 添加{{}} aa
                tokens.push(`_s(${match[1].trim()})`)
                lastIndex = index+match[0].length //最后 {{}} 索引位置
            }
            // 判读最后{{}}的位置是否小于当前文本的位置,如果是,说明还有未解析的文本
            if(lastIndex<text.length){
               tokens.push(JSON.stringify(text.slice(lastIndex))) 
            }
            //最终返回出去
    
            return `_v(${tokens.join("+")})`
        }
    }
    

    trim()去除空格,但是trim是VB/VBScript的字符串处理函数,JS并没有同名或同功能的函数为了能更好的实现功能可以使用正则replace(/(^\s)|(\s$)/g

    相关文章

      网友评论

          本文标题:ast语法树变成render字符串

          本文链接:https://www.haomeiwen.com/subject/ivzabltx.html