# 自定义组件的处理
先看一下这一大段代码
const processComponent = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
optimized: boolean
) => {
if (n1 == null) {
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
(parentComponent!.ctx as KeepAliveContext).activate(
n2,
container,
anchor,
isSVG,
optimized
);
} else {
mountComponent(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
);
}
} else {
updateComponent(n1, n2, optimized);
}
};
const mountComponent: MountComponentFn = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
const instance: ComponentInternalInstance = (initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
));
// inject renderer internals for keepAlive
if (isKeepAlive(initialVNode)) {
(instance.ctx as KeepAliveContext).renderer = internals;
}
setupComponent(instance);
// setup() is async. This component relies on async logic to be resolved
// before proceeding
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
if (!parentSuspense) {
if (__DEV__) warn("async setup() is used without a suspense boundary!");
return;
}
parentSuspense.registerDep(instance, setupRenderEffect);
// Give it a placeholder if this is not hydration
if (!initialVNode.el) {
const placeholder = (instance.subTree = createVNode(Comment));
processCommentNode(null, placeholder, container!, anchor);
}
return;
}
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
);
};
说实话这个函数命名方式还有调用方式。。。是真的很像 react。
我们直接看mountComponent
,update
和mount
大致上类似,当然会有区别,我们后续再讲。
第一步创建实例:
const instance: ComponentInternalInstance = (initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
));
关于实例请参考附录:组件实例
创建完实例之后,则调用setupComponent(instance)
,这里就是根 vue3 中才加入的setup
方法有关了,很重要哦。
export function setupComponent(
instance: ComponentInternalInstance,
isSSR = false
) {
isInSSRComponentSetup = isSSR;
const { props, children, shapeFlag } = instance.vnode;
const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT;
initProps(instance, props, isStateful, isSSR);
initSlots(instance, children);
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined;
isInSSRComponentSetup = false;
return setupResult;
}
# 处理 props
首先调用了initProps
,那么这里 init 做了啥呢?
- 根据你组件的
props
声明来把属性放到 props 或者 attrs 里面(让我觉得很没必要的设计,意义不明) - 根据你声明的 props 来进行校验
函数调用链:initProps -> setFullProps -> normalizePropsOptions <-> normalizePropsOptions
这一系列的方法调用就留给各位自己去看了,这真的就是细节了,没啥能讲的,简单概括一下做了哪些事情
setFullProps 里面根据最终得到的[props, attrs]
然后判断是否是函数组件,如果是函数组件并且没有声明props
那么把所有 attrs 作为 props:
if (isStateful) {
// stateful
instance.props = isSSR ? props : shallowReactive(props);
} else {
if (!instance.type.props) {
// functional w/ optional props, props === attrs
instance.props = attrs;
} else {
// functional w/ declared props
instance.props = props;
}
}
normalizePropsOptions 里面要把extends
、mixins
里面可能存在的所有 props 进行合并:
let hasExtends = false;
if (__FEATURE_OPTIONS__ && !isFunction(comp)) {
const extendProps = (raw: ComponentOptions) => {
const [props, keys] = normalizePropsOptions(raw);
extend(normalized, props);
if (keys) needCastKeys.push(...keys);
};
if (comp.extends) {
hasExtends = true;
extendProps(comp.extends);
}
if (comp.mixins) {
hasExtends = true;
comp.mixins.forEach(extendProps);
}
}
这里还有一个needCastKeys
的概念,啥意思呢
if (prop) {
const booleanIndex = getTypeIndex(Boolean, prop.type);
const stringIndex = getTypeIndex(String, prop.type);
prop[BooleanFlags.shouldCast] = booleanIndex > -1;
prop[BooleanFlags.shouldCastTrue] =
stringIndex < 0 || booleanIndex < stringIndex;
// if the prop needs boolean casting or default value
if (booleanIndex > -1 || hasOwn(prop, "default")) {
needCastKeys.push(normalizedKey);
}
}
不太好解释,就是对于一些情况需要构造 props
联系 setFullProps 里面的操作来看可能更好理解:
if (needCastKeys) {
const rawCurrentProps = toRaw(props);
for (let i = 0; i < needCastKeys.length; i++) {
const key = needCastKeys[i];
props[key] = resolvePropValue(
options!,
rawCurrentProps,
key,
rawCurrentProps[key]
);
}
}
对于我们的声明的 props,我们可以增加像default
这样的关键字,在我们没有传递的时候使用默认值,或者像boolean
类型的 props,如果没有传递那么undefined
类型不匹配,就默认给 false, resolvePropValue
方法的内容也证明了我这个观点
setFullProps 会根据props
声明来区分 props 和 attrs
for (const key in rawProps) {
const value = rawProps[key];
if (isReservedProp(key)) {
continue;
}
let camelKey;
if (options && hasOwn(options, (camelKey = camelize(key)))) {
props[camelKey] = value;
} else if (!isEmitListener(instance.type, key)) {
attrs[key] = value;
}
}
props 先讲到这里,组件的处理内容较多,篇幅原因就把剩下放到下一章讲解