zoukankan      html  css  js  c++  java
  • 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')
    }
  • 相关阅读:
    服务部署 RPC vs RESTful
    模拟浏览器之从 Selenium 到splinter
    windows程序设计 vs2012 新建win32项目
    ubuntu python 安装numpy,scipy.pandas.....
    vmvare 将主机的文件复制到虚拟机系统中 安装WMware tools
    ubuntu 修改root密码
    python 定义类 简单使用
    python 定义函数 两个文件调用函数
    python 定义函数 调用函数
    python windows 安装gensim
  • 原文地址:https://www.cnblogs.com/dhsz/p/7245879.html
Copyright © 2011-2022 走看看