1
2
3
4
5
6
| export const transformElement: NodeTransform = (node, context) => {
return function postTransformElement() {
// ...
// 主函数,返回一个闭包函数
}
}
|
resolveComponentType 🔗
动态组件 🔗
1
| const isProp = findProp(node, 'is')
|
并且这里的值是绑定的属性, 这里会调用createCallExpression
, 传入的是动态组件函数
1
2
3
4
5
6
7
8
| const exp =
isProp.type === NodeTypes.ATTRIBUTE
? isProp.value && createSimpleExpression(isProp.value.content, true)
: isProp.exp
if (exp) {
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [ exp ]
)
}
|
如果是属性值,并且以vue:
开头, 这里会把tag重置成剪裁后的字符串
1
2
3
4
5
6
7
8
9
10
| if (
isProp.type === NodeTypes.ATTRIBUTE &&
isProp.value!.content.startsWith('vue:')
) {
// <button is="vue:xxx">
// if not <component>, only is value that starts with "vue:" will be
// treated as component by the parse phase and reach here, unless it's
// compat mode where all is values are considered components
tag = isProp.value!.content.slice(4)
}
|
非component组件,存在is属性 🔗
1
| const isDir = !isExplicitDynamic && findDir(node, 'is')
|
调用动态组件,传入指令
1
2
3
4
5
| if (isDir && isDir.exp) {
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
isDir.exp
])
}
|
内置组件名 🔗
1
| const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag)
|
内置组件直接通过
1
2
3
4
5
6
| if (builtIn) {
// built-ins are simply fallthroughs / have special handling during ssr
// so we don't need to import their runtime equivalents
if (!ssr) context.helper(builtIn)
return builtIn
}
|
如果是从setup中引用的组件 🔗
这里会先去解析context中 setup函数内的引用
1
2
3
4
5
6
7
8
9
10
11
| const fromSetup = resolveSetupReference(tag, context)
if (fromSetup) {
return fromSetup
}
const dotIndex = tag.indexOf('.')
if (dotIndex > 0) {
const ns = resolveSetupReference(tag.slice(0, dotIndex), context)
if (ns) {
return ns + tag.slice(dotIndex)
}
}
|
如果是自引用组件 🔗
也就是在组件中自己引用自己
这里会增加一个__self
尾缀的tag
1
2
3
4
5
6
7
8
9
10
11
12
| if (
!__BROWSER__ &&
context.selfName &&
capitalize(camelize(tag)) === context.selfName
) {
context.helper(RESOLVE_COMPONENT)
// codegen.ts has special check for __self postfix when generating
// component imports, which will pass additional `maybeSelfReference` flag
// to `resolveComponent`.
context.components.add(tag + `__self`)
return toValidAssetId(tag, `component`)
}
|
用户组件 🔗
直接走用户组件引用逻辑
1
2
3
| context.helper(RESOLVE_COMPONENT)
context.components.add(tag)
return toValidAssetId(tag, `component`)
|
buildProps 🔗
构建props属性结果
第一步对props进行遍历
1
2
3
4
5
| for (let i = 0; i < props.length; i++) {
// static attribute
const prop = props[i]
// ...
}
|
如果prop的name为ref
1
2
3
| let isStatic = true
if (name === 'ref') {
hasRef = true
|
如果存在v-for
1
2
3
4
5
6
7
8
| if (context.scopes.vFor > 0) {
properties.push(
createObjectProperty(
createSimpleExpression('ref_for', true),
createSimpleExpression('true')
)
)
}
|
构建ref_for
语句
如果属性name是is或者是component 或者 是 vue:
这里会直接跳过
1
2
3
4
5
6
7
8
9
10
11
12
| if (
name === 'is' &&
(isComponentTag(tag) ||
(value && value.content.startsWith('vue:')) ||
(__COMPAT__ &&
isCompatEnabled(
CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT,
context
)))
) {
continue
}
|
创建属性语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| properties.push(
createObjectProperty(
createSimpleExpression(
name,
true,
getInnerRange(loc, 0, name.length)
),
createSimpleExpression(
value ? value.content : '',
isStatic,
value ? value.loc : loc
)
)
)
|
1
2
3
| const { name, arg, exp, loc } = prop
const isVBind = name === 'bind'
const isVOn = name === 'on'
|
slot会直接跳过
1
2
3
4
5
6
7
8
| if (name === 'slot') {
if (!isComponent) {
context.onError(
createCompilerError(ErrorCodes.X_V_SLOT_MISPLACED, loc)
)
}
continue
}
|
once/memo也会直接跳过
1
2
3
| if (name === 'once' || name === 'memo') {
continue
}
|
is属性也会跳过
1
2
3
4
5
6
7
8
9
10
11
12
13
| if (
name === 'is' ||
(isVBind &&
isStaticArgOf(arg, 'is') &&
(isComponentTag(tag) ||
(__COMPAT__ &&
isCompatEnabled(
CompilerDeprecationTypes.COMPILER_IS_ON_ELEMENT,
context
))))
) {
continue
}
|
如果是动态的key,这里强制使用block
1
2
3
4
5
6
7
8
9
| if (
// #938: elements with dynamic keys should be forced into blocks
(isVBind && isStaticArgOf(arg, 'key')) ||
// inline before-update hooks need to force block so that it is invoked
// before children
(isVOn && hasChildren && isStaticArgOf(arg, 'vue:before-update'))
) {
shouldUseBlock = true
}
|
如果有动态绑定的参数, 这里会推入到mergeArgs
中去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| hasDynamicKeys = true
if (exp) {
if (isVBind) {
// have to merge early for compat build check
pushMergeArg()
mergeArgs.push(exp)
} else {
// v-on="obj" -> toHandlers(obj)
pushMergeArg({
type: NodeTypes.JS_CALL_EXPRESSION,
loc,
callee: context.helper(TO_HANDLERS),
arguments: isComponent ? [exp] : [exp, `true`]
})
}
}
|
如果有对应的指令的transform , 这里还会进行指令的整合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| const directiveTransform = context.directiveTransforms[name]
if (directiveTransform) {
// has built-in directive transform.
const { props, needRuntime } = directiveTransform(prop, node, context)
!ssr && props.forEach(analyzePatchFlag)
if (isVOn && arg && !isStaticExp(arg)) {
pushMergeArg(createObjectExpression(props, elementLoc))
} else {
properties.push(...props)
}
if (needRuntime) {
runtimeDirectives.push(prop)
if (isSymbol(needRuntime)) {
directiveImportMap.set(prop, needRuntime)
}
}
} else if (!isBuiltInDirective(name)) {
// no built-in transform, this is a user custom directive.
runtimeDirectives.push(prop)
// custom dirs may use beforeUpdate so they need to force blocks
// to ensure before-update gets called before children update
if (hasChildren) {
shouldUseBlock = true
}
}
|
构建propsExpression
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // has v-bind="object" or v-on="object", wrap with mergeProps
if (mergeArgs.length) {
// close up any not-yet-merged props
pushMergeArg()
if (mergeArgs.length > 1) {
propsExpression = createCallExpression(
context.helper(MERGE_PROPS),
mergeArgs,
elementLoc
)
} else {
// single v-bind with nothing else - no need for a mergeProps call
propsExpression = mergeArgs[0]
}
} else if (properties.length) {
propsExpression = createObjectExpression(
dedupeProperties(properties),
elementLoc
)
}
|
重写PatchFlags
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| // patchFlag analysis
if (hasDynamicKeys) {
patchFlag |= PatchFlags.FULL_PROPS
} else {
if (hasClassBinding && !isComponent) {
patchFlag |= PatchFlags.CLASS
}
if (hasStyleBinding && !isComponent) {
patchFlag |= PatchFlags.STYLE
}
if (dynamicPropNames.length) {
patchFlag |= PatchFlags.PROPS
}
if (hasHydrationEventBinding) {
patchFlag |= PatchFlags.HYDRATE_EVENTS
}
}
if (
!shouldUseBlock &&
(patchFlag === 0 || patchFlag === PatchFlags.HYDRATE_EVENTS) &&
(hasRef || hasVnodeHook || runtimeDirectives.length > 0)
) {
patchFlag |= PatchFlags.NEED_PATCH
}
|
根据propsExpression 的type进行进一步处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
| switch (propsExpression.type) {
case NodeTypes.JS_OBJECT_EXPRESSION:
// means that there is no v-bind,
// but still need to deal with dynamic key binding
let classKeyIndex = -1
let styleKeyIndex = -1
let hasDynamicKey = false
for (let i = 0; i < propsExpression.properties.length; i++) {
const key = propsExpression.properties[i].key
if (isStaticExp(key)) {
if (key.content === 'class') {
classKeyIndex = i
} else if (key.content === 'style') {
styleKeyIndex = i
}
} else if (!key.isHandlerKey) {
hasDynamicKey = true
}
}
const classProp = propsExpression.properties[classKeyIndex]
const styleProp = propsExpression.properties[styleKeyIndex]
// no dynamic key
if (!hasDynamicKey) {
if (classProp && !isStaticExp(classProp.value)) {
classProp.value = createCallExpression(
context.helper(NORMALIZE_CLASS),
[classProp.value]
)
}
if (
styleProp &&
// the static style is compiled into an object,
// so use `hasStyleBinding` to ensure that it is a dynamic style binding
(hasStyleBinding ||
(styleProp.value.type === NodeTypes.SIMPLE_EXPRESSION &&
styleProp.value.content.trim()[0] === `[`) ||
// v-bind:style and style both exist,
// v-bind:style with static literal object
styleProp.value.type === NodeTypes.JS_ARRAY_EXPRESSION)
) {
styleProp.value = createCallExpression(
context.helper(NORMALIZE_STYLE),
[styleProp.value]
)
}
} else {
// dynamic key binding, wrap with `normalizeProps`
propsExpression = createCallExpression(
context.helper(NORMALIZE_PROPS),
[propsExpression]
)
}
break
case NodeTypes.JS_CALL_EXPRESSION:
// mergeProps call, do nothing
break
default:
// single v-bind
propsExpression = createCallExpression(
context.helper(NORMALIZE_PROPS),
[
createCallExpression(context.helper(GUARD_REACTIVE_PROPS), [
propsExpression
])
]
)
break
}
|