• vue.js 源代码学习笔记 ----- codegen.js


    /* @flow */
    
    import { genHandlers } from './events'
    import { baseWarn, pluckModuleFunction } from '../helpers'
    import baseDirectives from '../directives/index'
    import { camelize } from 'shared/util'
    
    // configurable state
    let warn
    let transforms
    let dataGenFns
    let platformDirectives
    let staticRenderFns
    let onceCount
    let currentOptions
    
    export function generate (
      ast: ASTElement | void,
      options: CompilerOptions
    ): {
      render: string,
      staticRenderFns: Array<string>
    } {
      // save previous staticRenderFns so generate calls can be nested
      const prevStaticRenderFns: Array<string> = staticRenderFns
      const currentStaticRenderFns: Array<string> = staticRenderFns = []
      const prevOnceCount = onceCount
      onceCount = 0
      currentOptions = options
      warn = options.warn || baseWarn
      transforms = pluckModuleFunction(options.modules, 'transformCode')
      dataGenFns = pluckModuleFunction(options.modules, 'genData')
      platformDirectives = options.directives || {}
      const code = ast ? genElement(ast) : '_h("div")'
      staticRenderFns = prevStaticRenderFns
      onceCount = prevOnceCount
      return {
        render: `with(this){return ${code}}`,
        staticRenderFns: currentStaticRenderFns
      }
    }
    
    function genElement (el: ASTElement): string {
      if (el.staticRoot && !el.staticProcessed) {
        return genStatic(el)
      } else if (el.once && !el.onceProcessed) {
        return genOnce(el)
      } else if (el.for && !el.forProcessed) {
        return genFor(el)
      } else if (el.if && !el.ifProcessed) {
        return genIf(el)
      } else if (el.tag === 'template' && !el.slotTarget) {
        return genChildren(el) || 'void 0'
      } else if (el.tag === 'slot') {
        return genSlot(el)
      } else {
        // component or element
        let code
        if (el.component) {
          code = genComponent(el.component, el)
        } else {
          const data = el.plain ? undefined : genData(el)
    
          const children = el.inlineTemplate ? null : genChildren(el)
          code = `_h('${el.tag}'${
            data ? `,${data}` : '' // data
          }${
            children ? `,${children}` : '' // children
          })`
        }
        // module transforms
        for (let i = 0; i < transforms.length; i++) {
          code = transforms[i](el, code)
        }
        return code
      }
    }
    
    // hoist static sub-trees out
    function genStatic (el: ASTElement): string {
      el.staticProcessed = true
      staticRenderFns.push(`with(this){return ${genElement(el)}}`)
      return `_m(${staticRenderFns.length - 1}${el.staticInFor ? ',true' : ''})`
    }
    
    // v-once
    function genOnce (el: ASTElement): string {
      el.onceProcessed = true
      if (el.if && !el.ifProcessed) {
        return genIf(el)
      } else if (el.staticInFor) {
        let key = ''
        let parent = el.parent
        while (parent) {
          if (parent.for) {
            key = parent.key
            break
          }
          parent = parent.parent
        }
        if (!key) {
          process.env.NODE_ENV !== 'production' && warn(
            `v-once can only be used inside v-for that is keyed. `
          )
          return genElement(el)
        }
        return `_o(${genElement(el)},${onceCount++}${key ? `,${key}` : ``})`
      } else {
        return genStatic(el)
      }
    }
    
    function genIf (el: any): string {
      el.ifProcessed = true // avoid recursion
      return genIfConditions(el.conditions)
    }
    
    function genIfConditions (conditions: ASTIfConditions): string {
      if (!conditions.length) {
        return '_e()'
      }
    
      var condition = conditions.shift()
      if (condition.exp) {
        return `(${condition.exp})?${genTernaryExp(condition.block)}:${genIfConditions(conditions)}`
      } else {
        return `${genTernaryExp(condition.block)}`
      }
    
      // v-if with v-once shuold generate code like (a)?_m(0):_m(1)
      function genTernaryExp (el) {
        return el.once ? genOnce(el) : genElement(el)
      }
    }
    
    function genFor (el: any): string {
      const exp = el.for
      const alias = el.alias
      const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
      const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
      el.forProcessed = true // avoid recursion
      return `_l((${exp}),` +
        `function(${alias}${iterator1}${iterator2}){` +
          `return ${genElement(el)}` +
        '})'
    }
    
    function genData (el: ASTElement): string {
      let data = '{'
    
      // directives first.
      // directives may mutate the el's other properties before they are generated.
      const dirs = genDirectives(el)
      if (dirs) data += dirs + ','
    
      // key
      if (el.key) {
        data += `key:${el.key},`
      }
      // ref
      if (el.ref) {
        data += `ref:${el.ref},`
      }
      if (el.refInFor) {
        data += `refInFor:true,`
      }
      // record original tag name for components using "is" attribute
      if (el.component) {
        data += `tag:"${el.tag}",`
      }
      // module data generation functions
      for (let i = 0; i < dataGenFns.length; i++) {
        data += dataGenFns[i](el)
      }
      // attributes
      if (el.attrs) {
        data += `attrs:{${genProps(el.attrs)}},`
      }
      // DOM props
      if (el.props) {
        data += `domProps:{${genProps(el.props)}},`
      }
      // event handlers
      if (el.events) {
        data += `${genHandlers(el.events)},`
      }
      if (el.nativeEvents) {
        data += `${genHandlers(el.nativeEvents, true)},`
      }
      // slot target
      if (el.slotTarget) {
        data += `slot:${el.slotTarget},`
      }
      // scoped slots
      if (el.scopedSlots) {
        data += `${genScopedSlots(el.scopedSlots)},`
      }
      // inline-template
      if (el.inlineTemplate) {
        const inlineTemplate = genInlineTemplate(el)
        if (inlineTemplate) {
          data += `${inlineTemplate},`
        }
      }
      data = data.replace(/,$/, '') + '}'
      // v-bind data wrap
      if (el.wrapData) {
        data = el.wrapData(data)
      }
      return data
    }
    
    function genDirectives (el: ASTElement): string | void {
      const dirs = el.directives
      if (!dirs) return
      let res = 'directives:['
      let hasRuntime = false
      let i, l, dir, needRuntime
      for (i = 0, l = dirs.length; i < l; i++) {
        dir = dirs[i]
        needRuntime = true
        const gen = platformDirectives[dir.name] || baseDirectives[dir.name]
        if (gen) {
          // compile-time directive that manipulates AST.
          // returns true if it also needs a runtime counterpart.
          needRuntime = !!gen(el, dir, warn)
        }
        if (needRuntime) {
          hasRuntime = true
          res += `{name:"${dir.name}",rawName:"${dir.rawName}"${
            dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : ''
          }${
            dir.arg ? `,arg:"${dir.arg}"` : ''
          }${
            dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : ''
          }},`
        }
      }
      if (hasRuntime) {
        return res.slice(0, -1) + ']'
      }
    }
    
    function genInlineTemplate (el: ASTElement): ?string {
      const ast = el.children[0]
      if (process.env.NODE_ENV !== 'production' && (
        el.children.length > 1 || ast.type !== 1
      )) {
        warn('Inline-template components must have exactly one child element.')
      }
      if (ast.type === 1) {
        const inlineRenderFns = generate(ast, currentOptions)
        return `inlineTemplate:{render:function(){${
          inlineRenderFns.render
        }},staticRenderFns:[${
          inlineRenderFns.staticRenderFns.map(code => `function(){${code}}`).join(',')
        }]}`
      }
    }
    
    function genScopedSlots (slots) {
      return `scopedSlots:{${
        Object.keys(slots).map(key => genScopedSlot(key, slots[key])).join(',')
      }}`
    }
    
    function genScopedSlot (key: string, el: ASTElement) {
      return `${key}:function(${String(el.attrsMap.scope)}){` +
        `return ${el.tag === 'template'
          ? genChildren(el) || 'void 0'
          : genElement(el)
      }}`
    }
    
    function genChildren (el: ASTElement): string | void {
      if (el.children.length) {
        return '[' + el.children.map(genNode).join(',') + ']'
      }
    }
    
    function genNode (node: ASTNode) {
      if (node.type === 1) {
        return genElement(node)
      } else {
        return genText(node)
      }
    }
    
    function genText (text: ASTText | ASTExpression): string {
      return text.type === 2
        ? text.expression // no need for () because already wrapped in _s()
        : transformSpecialNewlines(JSON.stringify(text.text))
    }
    
    function genSlot (el: ASTElement): string {
      const slotName = el.slotName || '"default"'
      const children = genChildren(el)
      return `_t(${slotName}${
        children ? `,${children}` : ''
      }${
        el.attrs ? `${children ? '' : ',null'},{${
          el.attrs.map(a => `${camelize(a.name)}:${a.value}`).join(',')
        }}` : ''
      })`
    }
    
    // componentName is el.component, take it as argument to shun flow's pessimistic refinement
    function genComponent (componentName, el): string {
      const children = el.inlineTemplate ? null : genChildren(el)
      return `_h(${componentName},${genData(el)}${
        children ? `,${children}` : ''
      })`
    }
    
    function genProps (props: Array<{ name: string, value: string }>): string {
      let res = ''
      for (let i = 0; i < props.length; i++) {
        const prop = props[i]
        res += `"${prop.name}":${transformSpecialNewlines(prop.value)},`
      }
      return res.slice(0, -1)
    }
    
    // #3895, #4268
    function transformSpecialNewlines (text: string): string {
      return text
        .replace(/u2028/g, '\u2028')
        .replace(/u2029/g, '\u2029')
    }
  • 相关阅读:
    nginx安装
    Mybatis使用generator自动生成映射配置文件信息
    Mysql报错Fatal error: Can't open and lock privilege tables: Table 'mysql.host' doesn't exist
    JS获取浏览器窗口大小 获取屏幕,浏览器,网页高度宽度
    js获取select标签选中的值
    linux下使用ffmpeg将amr转成mp3
    String与InputStream互转的几种方法
    javascript,检测对象中是否存在某个属性
    SQL语句在查询分析器中可以执行,代码中不能执行
    shell实现SSH自动登陆
  • 原文地址:https://www.cnblogs.com/dhsz/p/7245879.html
Copyright © 2020-2023  润新知