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

vue

/* @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')

}

以上是 vue.js 源代码学习笔记 ----- codegen.js 的全部内容, 来源链接: utcz.com/z/379257.html

回到顶部