接上篇template
模版编译,上篇主要解析了template
模版编译的入口以及解析模版字符串的parseChildren
方法的内部实现。在生成ast
对象之后,继续调用transform
方法转化ast对象,主要是codegenNode
属性的赋值,后续生成render
方法主要依赖codegenNode
属性。
transform方法调用位置
上篇解析完ast
对象之后,回归到baseCompile
方法中,继续执行transform
方法
// baseCompile函数后续操作
const ast = isString(template) ? baseParse(template, options) : template
// ... nodeTransforms directiveTransforms 数组的组成
transform(
ast,
extend({}, options, {
prefixIdentifiers,
nodeTransforms: [
...nodeTransforms,
...(options.nodeTransforms || []) // user transforms
],
directiveTransforms: extend(
{},
directiveTransforms,
options.directiveTransforms || {} // user transforms
)
})
)
调用transform
方法时传递两个参数,第一个参数ast
对象就是baseParse
进行模版解析的初步结果对象,第二个参数是options
对象(里面包含解析指令以及方法的函数,后续解析到具体调用时再详细分析)。接下来看下transform
方法内部实现。
function transform(root, options) {
const context = createTransformContext(root, options);
traverseNode(root, context);
// ...
}
traverseNode循环解析ast对象
transform
方法内部首先是调用createTransformContext
方法生成一个context
对象。然后调用traverseNode
方法(这个方法内部就是使用options
参数中的方法进一步解析指令、方法)。
export function traverseNode(
node: RootNode | TemplateChildNode,
context: TransformContext
) {
context.currentNode = node
// apply transform plugins
const { nodeTransforms } = context
const exitFns = []
for (let i = 0; i < nodeTransforms.length; i++) {
const onExit = nodeTransforms[i](node, context)
if (onExit) {
if (isArray(onExit)) {
exitFns.push(...onExit)
} else {
exitFns.push(onExit)
}
}
if (!context.currentNode) {
// node was removed
return
} else {
// node may have been replaced
node = context.currentNo
}
}
// ...
}
nodeTransforms处理指令的函数数组
首先是for
循环nodeTransforms
数组,依次调用数组中的每个方法,参数是node
对象(初始化是type
为0
的ast对象) 和 context
对象(调用createTransformContext
方法生成的)。接下来看下nodeTransforms
数组中的方法(大致看下每个函数是解析哪个指令或者方法的,然后以本例为模版详细解析调用的函数,后续再解析其它命中的函数)。
// 回归到baseCompile方法中 - nodeTransforms数组的由来
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset(prefixIdentifiers)
// getBaseTransformPreset方法内部实现
export function getBaseTransformPreset(
prefixIdentifiers?: boolean
): TransformPreset {
return [
[
transformOnce,
transformIf,
transformFor,
...(!__BROWSER__ && prefixIdentifiers
? [
// order is important
trackVForSlotScopes,
transformExpression
]
: __BROWSER__ && __DEV__
? [transformExpression]
: []),
transformSlotOutlet,
transformElement,
trackSlotScopes,
transformText
],
// ... directiveTransforms对象
]
}
解析root根节点对象(type=0)
初始化ast
是type
为0
的根节点对象,循环调用nodeTransforms
数组中的方法,最终会命中transformText
方法,返回一个自定的函数() => {}
赋值给onExit
对象,push
到exitFns
数组中。接下来看下traverseNode
方法for
循环之后的内部实现
// traverseNode方法后续 switch-case解析ast的子节点对象
switch (node.type) {
case NodeTypes.COMMENT: // type = 3
if (!context.ssr) {
context.helper(CREATE_COMMENT)
}
break
case NodeTypes.INTERPOLATION: // type = 5
if (!context.ssr) {
context.helper(TO_DISPLAY_STRING)
}
break
case NodeTypes.IF: // type = 9 v-if
for (let i = 0; i < node.branches.length; i++) {
traverseNode(node.branches[i], context)
}
break
case NodeTypes.IF_BRANCH: // type = 0 | 1
case NodeTypes.FOR:
case NodeTypes.ELEMENT:
case NodeTypes.ROOT:
traverseChildren(node, context)
break
}
解析动态数据节点(type=5)
当解析完type=0
的root
对象时,根据switch-case
的选项调用traverseChildren
方法解析children
数组中的子节点对象。
export function traverseChildren(
parent: ParentNode,
context: TransformContext
) {
let i = 0
const nodeRemoved = () => {
i--
}
for (; i < parent.children.length; i++) {
const child = parent.children[i]
if (isString(child)) continue
context.parent = parent
context.childIndex = i
context.onNodeRemoved = nodeRemoved
traverseNode(child, context)
}
}
可以看到traverseChildren
函数中大致的实现是循环parent
的children
数组中的元素,再次调用traverseNode
方法进行解析,如果child
是字符串则跳过。
命中transformExpression函数
以本例为模版,根节点的第一个子对象是type
为5
的动态数据message
的结果对象,所以在nodeTransforms
数组中会调用transformExpression
方法
// transformExpression 方法
export const transformExpression: NodeTransform = (node, context) => {
if (node.type === NodeTypes.INTERPOLATION/* 5 */) {
node.content = processExpression(
node.content as SimpleExpressionNode,
context
)
} else if () {} //...
}
// processExpression 方法
export function processExpression(
node: SimpleExpressionNode,
context: TransformContext,
asParams = false,
asRawStatements = false
): ExpressionNode {
if (__BROWSER__) {
if (__DEV__) {
// simple in-browser validation (same logic in 2.x)
validateBrowserExpression(node, context, asParams, asRawStatements)
}
return node
}
// ...
}
这个方法调用processExpression
方法处理表达式,内部实现大致是校验content
属性(就是message
)是否为空、校验下new Function()
在当前运行环境下执行会不会报错等等,最后返回传入的node.content
。在nodeTransforms
数组循环完之后进入switch-case
// traverseNode方法中type为5时执行的switch-case分支
context.helper(TO_DISPLAY_STRING)
// TO_DISPLAY_STRING 是一个 Symbol对象
export const TO_DISPLAY_STRING = Symbol(__DEV__ ? `toDisplayString` : ``)
// methods
helper(name) {
context.helpers.add(name)
return name
}
// helpers: new Set() helpers是一个set对象
在context
的helpers
属性(new Set()
对象)中添加key
(Symbol('toDisplayString')
)、 value
(1
)值。switch-case
结束之后会调用while
循环,依次执行exitFns
数组中的函数(就是nodeTransforms
循环执行的结果存放在这个数组中)
// traverseNode 后续循环执行exitFns数组中的方法
context.currentNode = node
let i = exitFns.length
while (i--) {
exitFns[i]()
}
type
为5
的对象循环时并没有返回数组,所以没有方法可以执行,本例后续会解析type
为2
的文本对象,因为type
为2
是空节点没有命中任何方法,这里直接跳过了。
解析元素节点对象(type=1)
最后执行type为1的元素节点对象
命中transformExpression函数
在nodeTransforms
数组循环中首先会命中transformExpression
中node.type === NodeTypes.ELEMENT
的分支判断
// transformExpression方法node.type=1的分支后续
else if (node.type === NodeTypes.ELEMENT) {
for (let i = 0; i < node.props.length; i++) {
const dir = node.props[i]
// do not process for v-on & v-for since they are special handled
if (dir.type === NodeTypes.DIRECTIVE/* 7 */ && dir.name !== 'for') {
const exp = dir.exp
const arg = dir.arg
if (exp && exp.type === NodeTypes.SIMPLE_EXPRESSION &&
!(dir.name === 'on' && arg)
) {/* ... */}
if (arg && arg.type === NodeTypes.SIMPLE_EXPRESSION && !arg.isStatic) {/* ... */}
}
}
}
当node.type===1
时会循环props
数组,判断props
对象的type
属性是否为7并且不是v-for
指令,再判断exp
(属性值)的type
是否为4
并且属性名不是on
(本例中属性为on
事件监听),再判断arg
(属性名)的type
是否为4
并且不是静态的(本例中isStatic
值为true
),所以条件判断都不成立(若条件成立,后续还是执行processExpression
方法进行一些校验)。
命中transformElement函数
nodeTransforms
数组循环再次命中transformElement
方法
export const transformElement: NodeTransform = (node, context) => {
if (
!(
node.type === NodeTypes.ELEMENT/* 1 */ &&
(node.tagType === ElementTypes.ELEMENT/* 0 */ ||
node.tagType === ElementTypes.COMPONENT)
)
) {
return
}
return function postTransformElement() {}
}
满足node.type===1
并且node.tagType === 0
,所以返回postTransformElement
方法。
命中transformText函数
因为node.type === 1
,执行transformText
方法,所以返回() => {}
自定义函数
export const transformText: NodeTransform = (node, context) => {
if (
node.type === NodeTypes.ROOT ||
node.type === NodeTypes.ELEMENT ||
node.type === NodeTypes.FOR ||
node.type === NodeTypes.IF_BRANCH
) {
return () => { /* ... */ }
}
}
循环结束之后执行switch-case
命中case NodeTypes.ELEMENT
分支,执行traverseChildren
方法解析子节点(继续调用traverseNode
方法解析子节点),因为本例中button
元素的内部子节点是一个type
为2
的文本节点,所以没有命中nodeTransforms
中的方法(exitFns
数组为空),switch-case
也没有命中,因此while
循环也没有执行任何方法。type
为1
的children
数组解析完了,再次回到type=1
的元素对象解析过程中,开始while
循环执行exitFns
数组中的方法。
traverseNode中执行回调函数(type=1)
执行postTransformElement方法
第一个是postTransformElement
方法
function postTransformElement() {
const { tag, props } = node
const isComponent = node.tagType === ElementTypes.COMPONENT
const vnodeTag = isComponent
? resolveComponentType(node as ComponentNode, context)
: `"${tag}"`
const isDynamicComponent =
isObject(vnodeTag) && vnodeTag.callee === RESOLVE_DYNAMIC_COMPONENT
let vnodeProps: VNodeCall['props']
let vnodeChildren: VNodeCall['children']
let vnodePatchFlag: VNodeCall['patchFlag']
let patchFlag: number = 0
let vnodeDynamicProps: VNodeCall['dynamicProps']
let dynamicPropNames: string[] | undefined
let vnodeDirectives: VNodeCall['directives']
// ... shouldUseBlock = false
// props
if (props.length > 0) {
const propsBuildResult = buildProps(node, context)
// 将返回的属性解析对象进行赋值
vnodeProps = propsBuildResult.props // 属性对象
patchFlag = propsBuildResult.patchFlag // 8
dynamicPropNames = propsBuildResult.dynamicPropNames // [name]
const directives = propsBuildResult.directives
vnodeDirectives =
directives && directives.length
? (createArrayExpression(
directives.map(dir => buildDirectiveArgs(dir, context))
) as DirectiveArguments)
: undefined // undefined
}
}
该方法中判断是否是组件node.tagType === 1
(本例tagType
为0
);vnodeTag
赋值,本例中为"button"
;是否是动态组件isDynamicComponent
(本例因为vnodeTag
是字符串,所以是false
)
buildProps解析props属性
然后调用buildProps
方法解析props
属性
export function buildProps(
node: ElementNode,
context: TransformContext,
props: ElementNode['props'] = node.props,
ssr = false
): {
props: PropsExpression | undefined
directives: DirectiveNode[]
patchFlag: number
dynamicPropNames: string[]
} {
const { tag, loc: elementLoc } = node
const isComponent = node.tagType === ElementTypes.COMPONENT
let properties: ObjectExpression['properties'] = []
const mergeArgs: PropsExpression[] = []
const runtimeDirectives: DirectiveNode[] = []
let patchFlag = 0
let hasRef = false
let hasClassBinding = false
let hasStyleBinding = false
let hasHydrationEventBinding = false
let hasDynamicKeys = false
let hasVnodeHook = false
const dynamicPropNames: string[] = []
const analyzePatchFlag = ({ key, value }: Property) => {}
for (let i = 0; i < props.length; i++) {
const prop = props[i]
if (prop.type === NodeTypes.ATTRIBUTE /* 6 */) {}
else {
// directives
const { name, arg, exp, loc } = prop
const isVBind = name === 'bind' // false
const isVOn = name === 'on' // true
// ...
const directiveTransform = context.directiveTransforms[name]
if (directiveTransform) {
// has built-in directive transform.
const { props, needRuntime } = directiveTransform(prop, node, context)
// ...
} else {
// ...
}
}
}
}
在buildProps
方法中会循环props
中的每个属性,调用context.directiveTransforms[name]
对应的方法
命中transformOn函数
本例的属性名为on
,所以调用transformOn
方法(在getBaseTransformPreset
方法返回数组的第二个元素中定义)
// createSimpleExpression方法
export function createSimpleExpression(
content: SimpleExpressionNode['content'],
isStatic: SimpleExpressionNode['isStatic'] = false,
loc: SourceLocation = locStub,
constType: ConstantTypes = ConstantTypes.NOT_CONSTANT
): SimpleExpressionNode {
return {
type: NodeTypes.SIMPLE_EXPRESSION,
loc,
content,
isStatic,
constType: isStatic ? ConstantTypes.CAN_STRINGIFY : constType
}
}
// transformOn方法
export const transformOn: DirectiveTransform = (
dir,
node,
context,
augmentor
) => {
const { loc, modifiers, arg } = dir as VOnDirectiveNode
if (!dir.exp && !modifiers.length) {}
let eventName: ExpressionNode
if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
if (arg.isStatic) {
const rawName = arg.content
eventName = createSimpleExpression(
toHandlerKey(camelize(rawName)),
true,
arg.loc
)
}
else {}
} else {}
// ...
let shouldCache: boolean = context.cacheHandlers && !exp
if (exp) {
// ...
}
let ret: DirectiveTransformResult = {
props: [
createObjectProperty(
eventName,
exp || createSimpleExpression(`() => {}`, false, loc)
)
]
}
// ...
return ret
}
transformOn
方法的内部实现,首先属性名(arg
对象)的type
为4
(简单表达式)并且isStatic
为true
,则调用createSimpleExpression
方法创建简单表达式对象{ type: 4, content: onClick, constType: 3, ... }
。这里对arg
的name
(事件名称)做了处理(首先是中划线转驼峰,再是因为是on
事件监听,所以在name
开头加上on
并且name
的首字母大写,本例中name
由click
-> onClick
)。最后调用createObjectProperty
方法。
export function createObjectProperty(
key: Property['key'] | string,
value: Property['value']
): Property {
return {
type: NodeTypes.JS_PROPERTY, // type=16
loc: locStub,
key: isString(key) ? createSimpleExpression(key, true) : key,
value
}
}
createObjectProperty
方法最终返回一个type
为16
的对象,其中key
为type=4
的arg
对象,value
为type=4
的exp
对象,所以transformOn
方法返回{ props: [{type: 16, key, value, ...}] }
。回归到buildProps
方法中,调用完directiveTransform
(就是transformOn
函数)方法的后续实现
// buildProps函数中调用完directiveTransform对应的方法之后的内部实现
const { props, needRuntime/* undefined */ } = directiveTransform(prop, node, context)
!ssr && props.forEach(analyzePatchFlag)
properties.push(...props)
if (needRuntime) {}
props
数组中的每个元素依次调用analyzePatchFlag
方法,然后将props
的每个元素push
到properties
数组中。看下analyzePatchFlag
函数的内部实现
// buildProps方法内部定义的analyzePatchFlag函数
const analyzePatchFlag = ({ key, value }: Property) => {
// isStaticExp判断key的type === 4 并且 key的isStatic === true
if (isStaticExp(key)) {
const name = key.content
const isEventHandler = isOn(name) // 是否是on开头的事件监听
// ...
if (name === 'ref') {
hasRef = true
} else if (name === 'class') {
hasClassBinding = true
} else if (name === 'style') {
hasStyleBinding = true
} else if (name !== 'key' && !dynamicPropNames.includes(name)) {
dynamicPropNames.push(name)
}
// ...
}
else {}
}
以本例分析,analyzePatchFlag
方法的作用是将key.content
放到dynamicPropNames
数组中(收集属性名称)。后续回归到buildProps
方法中
// buildProps方法后续
if (mergeArgs.length) {/* ... */}
else if (properties.length) {
propsExpression = createObjectExpression(
dedupeProperties(properties), // 属性对象组成的数组[prop]
elementLoc
)
}
// createObjectExpression方法
export function createObjectExpression(
properties: ObjectExpression['properties'],
loc: SourceLocation = locStub
): ObjectExpression {
return {
type: NodeTypes.JS_OBJECT_EXPRESSION, // type = 15
loc,
properties
}
}
后续调用createObjectExpression
方法返回一个type
为15
的对象,properties
属性为prop
构成的数组。
// buildProps方法最后部分
if (hasDynamicKeys) {}
else {
if (dynamicPropNames.length) {
patchFlag |= PatchFlags.PROPS // 0|8 = 8
}
}
// ...
return {
props: propsExpression,
directives: runtimeDirectives,
patchFlag,
dynamicPropNames
}
最终buildProps
方法返回了一个对象,表示属性的进一步解析结果{ props: {...}, patchFlag: 8, ... }
,之后回归到transformElement
方法中,解析完属性之后开始处理children
数组
// transformElement方法 处理 children 后续
if (node.children.length > 0) {
// ...
const shouldBuildAsSlots = isComponent && vnodeTag !== TELEPORT && vnodeTag !== KEEP_ALIVE
if (shouldBuildAsSlots) {}
else if (node.children.length === 1 && vnodeTag !== TELEPORT) {
const child = node.children[0]
const type = child.type
const hasDynamicTextChild /* false */ =
type === NodeTypes.INTERPOLATION ||
type === NodeTypes.COMPOUND_EXPRESSION
// ...
if (hasDynamicTextChild || type === NodeTypes.TEXT) {
vnodeChildren = child as TemplateTextChildNode
} else {
vnodeChildren = node.children
}
} else {}
}
if (patchFlag !== 0) {
if (__DEV__) {
if (patchFlag < 0) {/* patchFlag = 8 */}
else {
const flagNames = Object.keys(PatchFlagNames)
.map(Number)
.filter(n => n > 0 && patchFlag & n)
.map(n => PatchFlagNames[n])
.join(`, `)
// PatchFlagNames对象中 { 8: 'PROPS' }
vnodePatchFlag = patchFlag + ` /* ${flagNames} */`
}
} else {
vnodePatchFlag = String(patchFlag)
}
if (dynamicPropNames && dynamicPropNames.length) {
vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames)
// 在name首尾添加[] -> "["onClick"]"
}
}
node.codegenNode = createVNodeCall(
context,
vnodeTag,
vnodeProps,
vnodeChildren,
vnodePatchFlag,
vnodeDynamicProps,
vnodeDirectives,
!!shouldUseBlock,
false /* disableTracking */,
node.loc
)
当children
长度为1
时(本例只有一个子节点),首先是给vnodeChildren
赋值child=node.children[0]
,vnodePatchFlag="8 /* PROPS */"
,vnodeDynamicProps=["onClick"]
,最后调用createVNodeCall
方法创建codegenNode
属性对象
export function createVNodeCall(
context: TransformContext | null,
tag: VNodeCall['tag'],
props?: VNodeCall['props'],
children?: VNodeCall['children'],
patchFlag?: VNodeCall['patchFlag'],
dynamicProps?: VNodeCall['dynamicProps'],
directives?: VNodeCall['directives'],
isBlock: VNodeCall['isBlock'] = false,
disableTracking: VNodeCall['disableTracking'] = false,
loc = locStub
): VNodeCall {
if (context) {
if (isBlock) {
context.helper(OPEN_BLOCK)
context.helper(CREATE_BLOCK)
} else {
context.helper(CREATE_VNODE)
}
if (directives) {
context.helper(WITH_DIRECTIVES)
}
}
return {
type: NodeTypes.VNODE_CALL, // type = 13
tag,
props,
children,
patchFlag,
dynamicProps,
directives,
isBlock,
disableTracking,
loc
}
}
// CREATE_VNODE定义
export const CREATE_VNODE = Symbol(__DEV__ ? `createVNode` : ``)
createVNodeCall
方法内部判断isBlock
是否为true
,本例为false
,所以在contetx
的helpers
对象中添加Symbol('createVNode')
,最终返回type
为13
的对象
执行transformText函数中返回的自定义方法
执行完postTransformElement
函数之后,继续执行自定义() => {}
方法,看下这个方法的内部实现
() => {
const children = node.children
let currentContainer: CompoundExpressionNode | undefined = undefined
let hasText = false
for (let i = 0; i < children.length; i++) {
const child = children[i]
// isText: node.type === 5 || 2
if (isText(child)) {
hasText = true
for (let j = i + 1; j < children.length; j++) {
const next = children[j]
if (isText(next)) {
if (!currentContainer) {
currentContainer = children[i] = {
type: NodeTypes.COMPOUND_EXPRESSION,
loc: child.loc,
children: [child]
}
}
// merge adjacent text node into current
currentContainer.children.push(` + `, next)
children.splice(j, 1)
j--
} else {
currentContainer = undefined
break
}
}
}
}
}
首选是for
循环,循环node
的children
数组,判断child
是否为文本节点(node.type
等于5
或者2
),然后再判断child
的下一个元素是否也是文本节点,本来中type
为1
的children
长度是1(只有一个子节点),所以跳出for
循环,执行后续代码
() => {
// 自定义函数for循环之后的 内部实现
if (!hasText ||
(children.length === 1 && (node.type === NodeTypes.ROOT || (node.type === NodeTypes.ELEMENT &&
node.tagType === ElementTypes.ELEMENT)))
) {
return
}
}
以本例为模版来说,type
为1
的元素满足children
数组长度是1并且node.tagType === 0
,所以return
返回了。至此,type
为1
节点的exitFns
数组中的回调函数已经全部执行完成了,主要是生成codegenNode
属性值(type
为13
的对象),其中对props
属性和children
子节点都做了进一步的分析。
traverseNode中执行回调函数(type=0)
最后执行type
为0
的根节点的回调函数,依然是上面的自定义函数() => {...}
。首先是循环children
数组(type
为0
的根节点的children
数组是type
为5,2,1
三个对象)。for
循环的内部实现大致是,如果child
和child
的下一个元素都是文本节点,则将child
节点替换为type
为8
的新对象,新对象的children
属性为[child, '+', next(child的下一个元素)]
(包含三个元素的数组),所以现在type
为0
的对象的children
属性的值变成了[{type:8, children:[{type:5,...}, '+', {type:2}], ...}, {type:1, ...}]
的新对象(原先type
为5
和2
的两个相邻文本对象替换为type
为8
的对象)。最后执行自定义函数的结尾部分。
// createCallExpression方法定义
export function createCallExpression<T extends CallExpression['callee']>(
callee: T,
args: CallExpression['arguments'] = [],
loc: SourceLocation = locStub
): InferCodegenNodeType<T> {
return {
type: NodeTypes.JS_CALL_EXPRESSION, // type = 14
loc,
callee,
arguments: args
} as any
}
// type为0的根节点对象 执行自定义函数的后续部分实现
() => {
// ...
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (isText(child) || child.type === NodeTypes.COMPOUND_EXPRESSION) {
const callArgs: CallExpression['arguments'] = []
// createTextVNode defaults to single whitespace, so if it is a
// single space the code could be an empty call to save bytes.
if (child.type !== NodeTypes.TEXT || child.content !== ' ') {
callArgs.push(child)
}
// mark dynamic text with flag so it gets patched inside a block
if (
!context.ssr &&
getConstantType(child, context) === ConstantTypes.NOT_CONSTANT
) {
callArgs.push(
PatchFlags.TEXT +
(__DEV__ ? ` /* ${PatchFlagNames[PatchFlags.TEXT]} */` : ``)
)
}
children[i] = {
type: NodeTypes.TEXT_CALL, // type = 12
content: child,
loc: child.loc,
codegenNode: createCallExpression(
context.helper(CREATE_TEXT),
// export const CREATE_TEXT = Symbol(__DEV__ ? `createTextVNode` : ``)
callArgs
)
}
}
}
}
最后的部分仍然是循环children
数组(本例为type
是8
和1
两个对象),进一步转化type
为8
的对象。以本例分析是将child
对象和"1 /* TEXT */"
字符串存放在callArgs
数组中,然后child
重新赋值为type=12
的新对象,新对象的codegenNode
属性赋值为createCallExpression
方法的返回值{ type: 14, callee: Symbol('createTextVNode'), arguments: callArgs数组, ... }
。那么type
为0
的回调函数也执行完成了(主要是对文本节点对象做了一个合并)。回归到transform
方法中,当traverseNode
函数执行完成之后,看下后续的内部实现。
if (options.hoistStatic/* true */) {
hoistStatic(root, context)
}
if (!options.ssr) {
createRootCodegen(root, context)
}
// finalize meta information
root.helpers = [...context.helpers]
root.components = [...context.components]
root.directives = [...context.directives]
root.imports = context.imports
root.hoists = context.hoists
root.temps = context.temps
root.cached = context.cached
// hoistStatic方法定义
export function hoistStatic(root: RootNode, context: TransformContext) {
walk(
root,
context,
// Root node is unfortunately non-hoistable due to potential parent
// fallthrough attributes.
isSingleElementRoot(root, root.children[0]) // false(children.length !== 1)
)
}
首先会调用walk
方法(依本例解析,walk
方法中的条件判断不会命中,这里不详述了)。
createRootCodegen创建根codegenNode属性
之后调用createRootCodegen
方法
function createRootCodegen(root: RootNode, context: TransformContext) {
const { helper } = context
const { children } = root
if (children.length === 1) {
} else if (children.length > 1) {
let patchFlag = PatchFlags.STABLE_FRAGMENT // 64
let patchFlagText = PatchFlagNames[PatchFlags.STABLE_FRAGMENT] // 'STABLE_FRAGMENT'
root.codegenNode = createVNodeCall(
context,
helper(FRAGMENT), // export const FRAGMENT = Symbol(__DEV__ ? `Fragment` : ``)
undefined,
root.children,
patchFlag + (__DEV__ ? ` /* ${patchFlagText} */` : ``),
undefined,
undefined,
true // isBlock
)
}
}
此方法内部调用createVNodeCall
函数,这个函数我们上文提到过,最终返回一个type
为13
的对象,因为isBlock
为true
,所以在context
对象的helpers
(Set
对象)中新增Symbol('openBlock')
和Symbol('createBlock')
,patchFlag
为64
,isBlock
为true
,children
为root.children
(type
为0
的children
数组),tag
是Symbol('Fragment')
。最后将context
上的一些对象赋值到了root
对象上。
总结
transform
方法将baseParse
函数解析的ast
对象作为源,进一步转化。主要是通过traverseNode
函数(当遇到根对象或者元素对象的时候会循环调用,解析子节点对象),这个方法内部首先是依次观察指令函数是否命中,收集回调函数进行循环执行,执行过程中会对元素节点的属性进行解析(以本例为模版,@click
会命中transformOn
处理事件监听对象,存储方法和事件回调函数);会对子节点进行解析(将两个相邻的文本节点进行合并,生成新的对象)等等。在根节点上生成codegenNode
属性,属性中囊括了children
子节点,属性props
、patchFlag
处理标志位等等。最后将context
上的部分属性转移到root
根对象上,为generate
方法铺垫(例如helpers
对象,其中存放的就是Vue
暴露的创建节点的方法:创建动态数据的toDisplayString
函数、创建节点的createVNode
函数、创建文本节点的createTextVNode
函数)。后文将解析generate
函数,是如何利用codegenNode
属性组装render函数字符串的。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!