| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- /* @flow */
- import { genHandlers } from './events'
- import { baseWarn, pluckModuleFunction } from '../helpers'
- import baseDirectives from '../directives/index'
- // 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)
- }
- }
- // v-if with v-once shuold generate code like (a)?_m(0):_m(1)
- function genIf (el: any): string {
- const exp = el.if
- el.ifProcessed = true // avoid recursion
- return `(${exp})?${el.once ? genOnce(el) : genElement(el)}:${genElse(el)}`
- }
- function genElse (el: ASTElement): string {
- return el.elseBlock
- ? genElement(el.elseBlock)
- : '_e()'
- }
- 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}",`
- }
- // slot target
- if (el.slotTarget) {
- data += `slot:${el.slotTarget},`
- }
- // 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)},`
- }
- // inline-template
- if (el.inlineTemplate) {
- data += `${genInlineTemplate(el)},`
- }
- // scoped slots
- if (el.scopedSlots) {
- data += `${genScopedSlots(el.scopedSlots)},`
- }
- 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) {
- 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(',')
- }]}`
- } else {
- return ''
- }
- }
- 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()
- : JSON.stringify(text.text)
- }
- function genSlot (el: ASTElement): string {
- const slotName = el.slotName || '"default"'
- const children = genChildren(el)
- return `_t(${slotName}${
- children ? `,${children}` : ''
- })`
- }
- // 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}":${prop.value},`
- }
- return res.slice(0, -1)
- }
|