前言
vue3发布至今将近一年时间,一直没有使用的机会,公司使用的技术栈是vue2,因此做一下记录,希望能对大家有帮助,有不对的地方请指正,共同进步。
示例代码
还是先来看下vue3对于options api是如何处理的,以下代码是从vue3源码里粘出来的demo
<script type="text/x-template" id="template">
<table v-if="filteredData.length">
<thead>
<tr>
<td v-for="key in cloumns" click="sortBy(key)">
{{capitalize(key)}}
</td>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredData">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
<script>
const DemoGride = {
template: '#template',
props: {
data: Array,
columns: Array,
filterKey: String
},
data() {
return {
sortKey: '',
sortOrders: this.columns.reduce((o, key) => (o[key] = 1, o), {})
}
},
computed: {
filteredData() {
const sortKey = this.sortKey
const filterKey = this.filterKey && this.filterKey.toLowerCase()
const order = this.sortOrders[sortKey] || 1
let data = this.data
if (filterKey) {
data = data.filter(row => {
return Object.keys(row).some(key => {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortKey) {
data = data.slice().sort((a, b) => {
a = a[sortKey]
b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
}
},
methods: {
sortBy(key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
},
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
}
}
</script>
<div id="demo">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid
:data="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</demo-grid>
</div>
<script>
Vue.createApp({
components: {
DemoGrid
},
data: () => ({
searchQuery: '',
gridColumns: ['name', 'power'],
gridData: [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
]
})
}).mount('#demo')
</script>
接下来我们来看看createApp都做了哪些工作
createApp
ensureRenderer最终会定位baseCreateRenderer方法上
// packages/runtime-dom/src/index.ts
export const createApp = ((...args)) => {
// ensureRenderer()执行后会得到{render,hydrate,createApp: createAppAPI(render,hydrate)}
const app = ensureRenderer().createApp(...args)
const { mount } = app
app.mount = (containerOrSelector) => {
// 获取dom节点
const container = normalizeContainer(containerOrSelector)
const component = app._component
component.template = container.innerHTML
container.innerHTML = ''
const proxy = mount(container, false, container instanceof SVGElement)
return proxy
}
return app
}
baseCreateRenderer
方法内会定义好诸多渲染相关的方法,看名字就能猜到是干嘛的
// packages/runtime-core/src/renderer.ts
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?:typeof createHydrationFunctions
){
const patch:patchFn = (...args) => {}
const processText: processTextOrCommentFn = (...args) => {}
const processCommentNode: processTextOrCommentFn = (...args) => {}
const mountStaticNode = (...args) => {}
const patchStaticNode = (...args) => {}
const moveStaticNode = (...args) => {}
const removeStaticNode = (...args) => {}
const processElement = (...args) => {}
const mountElement = (...args) => {}
const setScopeId = (...args) => {}
const mountChildren: mountChildrenFn = (...args) => {}
const patchBlockChildren:patchBlockChildrenFn = (...args) => {}
const patchProps = (...args) => {}
const processFragment = (...args) => {}
const processComponent = (...args) => {}
const updateComponent = (...args) => {}
const setupRenderEffect: setupRenderEffectFn = (...args) => {}
const updateComponentPreRender = (...args) => {}
const patchChildren:patchChildrenFn = (...args) => {}
const patchUnkeyedChildren = (...args) => {}
const patchKeyedChildren = (...args) => {}
const move = (...args) => {}
const unmount = (...args) => {}
const remove = (...args) => {}
const removeFragment = (...args) => {}
const unmountComponent = (...args) => {}
const unmountChildren = (...args) => {}
const render = (...args) => {}
const internals = (...args) => {}
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
}
}
接下来看下createAppAPI里做了什么
createAppAPI
// packages/runtime-core/src/apiCreateApp.ts
export function createAppAPI(
render:RootRenderFunction,
hydrate?:RootHydrateFunction
){
return function createApp(rootComponent, rootProps = null){
// 返回一个应用上下文
const context = createAppContext()
// 创建app实例
const app: App = (context.app = {
_uid: uid++,
_component: rootComponent as ConcreteComponent,
_props: rootProps,
_container: null,
_context: context,
_instance: null,
version,
get config(){},
set config(){},
// 安装插件,use(store),use(vueRouter)
use(plugin:Plugin,...options:any[]){},
// 混入相关
mixin(mixin: ComponentOptions){},
// 组件
component(name:string,component?:Component){},
// 指令
directive(name:string, directive:Directive){},
// 挂载
mount(rootContainer:HostElement,isHydrate?:boolean,isSVG?:boolean){
},
// 卸载
unmount(){},
// provide
provide(key,value){}
})
}
}
createAppContext()创建应用上下文
function createAppContext(){
return {
// 应用实例
app: null as any,
// 配置项
config: {
isNativeTag: NO,
performance: false,
globalProperties: {},
optionMergeStrategies: {},
errorHandler: undefined,
warnHandler: undefined,
compilerOptions: {}
},
// 混入相关
mixins: [],
// 组件
components: {},
// 指令
directives: {},
provides: Object.create(null),
optionsCache: new WeakMap(),
propsCache: new WeakMap(),
emitsCache: new WeakMap()
}
}
createApp的任务就是创建一个app实例,并定义好use、mixins、components、directives等全局方法以及mount方法,接下来我们看下mount做了哪些事情。
mount
mount方法是定义在app实例上的
// packages/runtime-core/src/apiCreateApp.ts
export function createAppAPI(...){
return function createApp(rootComponent,rootProps=null){
const context = createAppContext()
const app: App = (context.app = {
...
mount(rootContainer: HostElement,isHydrate?: boolean,isSVG?: boolean){
if(!isMounted){
// rootComponent 就是options config
// 这里的vnode是组件的vnode,不是dom的vnode
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
// render方法是在baseCreateRenderer函数里定义好的
render(vnode, rootContainer, isSVG)
}
}
}
...
})
}
}
mount 函数做的事情
1、创建组件的vnode
2、执行render函数
createVNode
// packages/runtime-core/src/vnode.ts
function _createVNode(
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
patchFlag: number = 0,
dynamicProps: string[] | null = null,
isBlockNode = false
){
//如果type为空会得到一个symbol类型的对象 const Comment= Symbol(__DEV__?'comment':undeined)
if (!type || type === NULL_DYNAMIC_COMPONENT) {
type = Comment
}
if(isVNode(type)){
// 如果type是vnode,在克隆过程中合并引用,而不是覆盖它
// <component :is="vnode">
const cloned = cloneVNode(type, props, true /* mergeRef: true */)
if (children) {
normalizeChildren(cloned, children)
}
return cloned
}
// 如果是类组件,__vccOpts为true
if (isClassComponent(type)) {
type = type.__vccOpts
}
// 2.x async/functional component compat
if (__COMPAT__) {
type = convertLegacyComponent(type, currentRenderingInstance)
}
// class & style normalization.
// class 和 style 的标准化,处理在vue中 class=['a'] class={} 等多种情况
if (props) {
// for reactive or proxy objects, we need to clone it to enable mutation.
if (isProxy(props) || InternalObjectKey in props) {
props = extend({}, props)
}
let { class: klass, style } = props
if (klass && !isString(klass)) {
props.class = normalizeClass(klass)
}
if (isObject(style)) {
// reactive state objects need to be cloned since they are likely to be
// mutated
if (isProxy(style) && !isArray(style)) {
style = extend({}, style)
}
props.style = normalizeStyle(style)
}
}
// 标识当前组件vnode的类型
const shapeFlag = isString(type)
? ShapeFlags.ELEMENT
: __FEATURE_SUSPENSE__ && isSuspense(type)
? ShapeFlags.SUSPENSE
: isTeleport(type)
? ShapeFlags.TELEPORT
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
? ShapeFlags.FUNCTIONAL_COMPONENT
: 0
// 定义vnode
const vnode: VNode = {
__v_isVNode: true,
__v_skip: true,
type,
props,
key: props && normalizeKey(props),
ref: props && normalizeRef(props),
scopeId: currentScopeId,
slotScopeIds: null,
children: null,
component: null,
suspense: null,
ssContent: null,
ssFallback: null,
dirs: null,
transition: null,
el: null,
anchor: null,
target: null,
targetAnchor: null,
shapeFlag,
patchFlag,
dynamicProps,
dynamicChildren: null,
appContext: null
}
// 处理children
normalizeChildren(vnode, children)
// 在3.0里有Block Tree的概念,为了解决2.0diff低效的问题,在每个Block下都有dynamicChildren,在vnode/Block创建阶段时会将当前block区域内的动态内容收集并填充到dynamicChildren,render执行完后,每个block下的动态内容都会被收集到各自的block中,在diff时不再需要对比整棵树,只需要对比同级block下的dynamicChildren即可。
if (
isBlockTreeEnabled > 0 &&
!isBlockNode &&
// has current parent block
currentBlock &&
(patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
patchFlag !== PatchFlags.HYDRATE_EVENTS
) {
currentBlock.push(vnode)
}
if (__COMPAT__) {
convertLegacyVModelProps(vnode)
convertLegacyRefInFor(vnode)
defineLegacyVNodeProperties(vnode)
}
return vnode
}
createVNode函数做的事情
1、处理class、style以及children
2、创建vnode,并打上shapeFlag标识当前组件的类型
3、blockTree的收集工作
render
// packages/runtime-core/src/renderer.ts
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?:typeof createHydrationFunctions
){
...
const render(vnode, container, isSVG){
if(vnode == null){
//vnode为空执行卸载的工作
if(container._vnode){
unmount(container._vnode, null, null, true)
}
}else {
// 进行打补丁操作
patch(container._vnode||null, vnode, container, null, null, null, isSVG)
}
// 执行后置的回调任务队列
flushPostFlushCbs()
}
...
}
render函数做的事情
1、执行patch操作
2、执行后置的回调任务
patch
接下来看patch内是如何进行打补丁的工作,渲染的相关方法是定义在baseCreateRenderer方法内patch也在其中
// packages/runtime-core/src/renderer.ts
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?:typeof createHydrationFunctions
){
...
const render = (vnode,container,isSVG) => {...}
const patch = (
n1,
n2,
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = false
) => {
// 特殊标签会跳出优化
if (n2.patchFlag === PatchFlags.BAIL) {
optimized = false
n2.dynamicChildren = null
}
// type是我们传递的options 配置,shapeFlag是创建组件vnode时候打上的标识
const { type, ref, shapeFlag } = n2
//例子里传递的是options,打上的shapeFlag是4,因此会执行processComponent方法
switch (type) {
case Text:
processText(n1, n2, container, anchor)
break
case Comment:
processCommentNode(n1, n2, container, anchor)
break
case Static:
if (n1 == null) {
mountStaticNode(n2, container, anchor, isSVG)
} else if (__DEV__) {
patchStaticNode(n1, n2, container, isSVG)
}
break
case Fragment:
processFragment(...)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(...)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.TELEPORT) {
;(type as typeof TeleportImpl).process(...)
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).process(...)
} else if (__DEV__) {
warn('Invalid VNode type:', type, `(${typeof type})`)
}
}
}
...
}
patch方法做的事情
1、根据type和shapeFlag来选择对应的处理方法
processComponent
渲染的相关方法是定义在baseCreateRenderer方法内processComponent也在其中
// packages/runtime-core/src/renderer.ts
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?:typeof createHydrationFunctions
){
...
const render = (vnode,container,isSVG) => {...}
const patch = (...) => {...}
const processComponent = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
if (n1 == null) {
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
// keep-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)
}
}
processComponent方法做的事情
1、判断oldVnode,有值就更新组件,没值就挂载组件
mountComponent
渲染的相关方法是定义在baseCreateRenderer方法内processComponent也在其中
// packages/runtime-core/src/renderer.ts
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?:typeof createHydrationFunctions
){
...
const render = (vnode,container,isSVG) => {...}
const patch = (...) => {...}
const processComponent = (...) => {...}
const mountComponent = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
//获取组件实例
const instance = compatMountInstance
|| (initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
// inject renderer internals for keepAlive
// 为keep-alive注入渲染器
if (isKeepAlive(initialVNode)) {
;(instance.ctx as KeepAliveContext).renderer = internals
}
// resolve props and slots for setup context
if (!(__COMPAT__ && compatMountInstance)) {
// 渲染之前用做的事情都会在这里进行,比如props、slots处理,状态响应化处理、编译template生成render函数、注册hooks等等
setupComponent(instance)
}
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
}
}
instance长啥样
let uid = 0
export function createComponentInstance(
vnode: VNode,
parent: ComponentInternalInstance | null,
suspense: SuspenseBoundary | null
) {
const type = vnode.type as ConcreteComponent
// inherit parent app context - or - if root, adopt from root vnode
// 还记得createAppContext创建的上下文吗?里面有全局的mixins、components、directive
const appContext =
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
const instance: ComponentInternalInstance = {
uid: uid++,
vnode,
type,
parent,
appContext,
root: null!, // to be immediately set
next: null,
subTree: null!, // will be set synchronously right after creation
update: null!, // will be set synchronously right after creation
render: null,
proxy: null,
exposed: null,
exposeProxy: null,
withProxy: null,
effects: null,
provides: parent ? parent.provides : Object.create(appContext.provides),
accessCache: null!,
renderCache: [],
// local resovled assets
components: null,
directives: null,
// resolved props and emits options
propsOptions: normalizePropsOptions(type, appContext),
emitsOptions: normalizeEmitsOptions(type, appContext),
// emit
emit: null as any, // to be set immediately
emitted: null,
// props default value
propsDefaults: EMPTY_OBJ,
// inheritAttrs
inheritAttrs: type.inheritAttrs,
// state
ctx: EMPTY_OBJ,
data: EMPTY_OBJ,
props: EMPTY_OBJ,
attrs: EMPTY_OBJ,
slots: EMPTY_OBJ,
refs: EMPTY_OBJ,
setupState: EMPTY_OBJ,
setupContext: null,
// suspense related
suspense,
suspenseId: suspense ? suspense.pendingId : 0,
asyncDep: null,
asyncResolved: false,
// lifecycle hooks
// not using enums here because it results in computed properties
isMounted: false,
isUnmounted: false,
isDeactivated: false,
bc: null,
c: null,
bm: null,
m: null,
bu: null,
u: null,
um: null,
bum: null,
da: null,
a: null,
rtg: null,
rtc: null,
ec: null,
sp: null
}
if (__DEV__) {
instance.ctx = createRenderContext(instance)
} else {
instance.ctx = { _: instance }
}
instance.root = parent ? parent.root : instance
instance.emit = emit.bind(null, instance)
return instance
}
setupComponent
// packages/runtime-core/src/components.ts
export function setupComponent(instance, isSSR){
isInSSRComponentSetup = isSSR
const { props, children } = instance.vnode
const isStateful = isStatefulComponent(instance)
// props
initProps(instance, props, isStateful, isSSR)
// slots处理
initSlots(instance, children)
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined
isInSSRComponentSetup = false
return setupResult
}
setupComponent方法做的事情
1、props、slots处理
2、处理有状态的组件
setupStatefulComponent
// packages/runtime-core/src/components.ts
function setupStatefulComponent(
instance: ComponentInternalInstance,
isSSR: boolean
) {
const Component = instance.type as ComponentOptions
// 0. create render proxy property access cache
instance.accessCache = Object.create(null)
// 1. create public instance / render proxy
// also mark it raw so it's never observed
// 为对象增加___v_skip表示当前对象不会被包装成proxy,遇到__v_skip会跳过处理
instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
// 2. call setup()
const { setup } = Component
if(setup){
// 这里是对composition api的处理
}else{
finishComponentSetup(instance, isSSR)
}
}
setupStatefulComponent方法做的事情
1、composition api的处理
2、调用finishComponentSetup继续处理options api
finishComponentSetup
// packages/runtime-core/src/components.ts
export function finishComponentSetup(
instance: ComponentInternalInstance,
isSSR: boolean,
skipOptions?: boolean
){
const Component = instance.type as ComponentOptions
// template / render function normalization
if(__NODE_JS__ && isSRR){
// ssr
}else{
// 生成渲染vdom用的render函数
if(compile && !Component.render){
const template = (_COMPAT_ && instance.vnode.props && instance.vnode.props['inline-template']) || Component.template
if(template){
const finalCompilerOptions = extend(
extend({isCustomElement, delimiters},compilerOptions),
componentCompilerOptions
)
Component.render = compile(template, finalCompilerOptions)
}
}
}
instance.render = (Component.render || NOOP) as InternalRenderFunction
// support for 2.x options
// 处理2.0版的options api
if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
currentInstance = instance
pauseTracking()
// 处理生命周期钩子,以及其他方法filter、components、directive
applyOptions(instance)
resetTracking()
currentInstance = null
}
}
finishComponentSetup方法的作用
1、生成render函数
2、兼容2.0的options api
applyOptions
// packages/runtime-core/src/componentOptions.ts
export function applyOptions(instance: ComponentInternalInstance) {
const options = resolveMergedOptions(instance)
const publicThis = instance.proxy! as any
const ctx = instance.ctx
const {
data: dataOptions,
computed,
methods,
watch: watchOptions,
provide:provideOptions,
inject: injectOptions,
...
components,
directives,
filters
} = options
if (injectOptions) {
resolveInjections(injectOptions, ctx, checkDuplicateProperties)
}
if(methods){
// 将method绑到当前实例的上下文上,并将this指向为当前的
for(const key in methods){
const methodHandler = methods[key]
if(isFunction(methodHandler)){
ctx[key] = mthodHandler.bind(publicThis)
}
}
}
if(dataOptions){
const data = (dataOptions as any).call(publicThis, publicThis)
// data的响应化处理
instance.data = reactive(data)
}
if(computedOptions){
// 计算属性会生成一个effect,会push到当前实例下的instance.effects,不是存到响应化时get收集时存放的地方
// 这里是循环computedOptions,因此一个计算属性就是一个effect
...
}
if(watchOptions){
// 对于watch处理也是最终会生一个effect,会push到当前实例下的instance.effects,
for (const key in watchOptions) {
createWatcher(watchOptions[key], ctx, publicThis, key)
}
}
....
//注册生命周期钩子
registerLifecycleHook(onBeforeMount, beforeMount)
registerLifecycleHook(onMounted, mounted)
registerLifecycleHook(onBeforeUpdate, beforeUpdate)
registerLifecycleHook(onUpdated, updated)
registerLifecycleHook(onActivated, activated)
registerLifecycleHook(onDeactivated, deactivated)
registerLifecycleHook(onErrorCaptured, errorCaptured)
registerLifecycleHook(onRenderTracked, renderTracked)
registerLifecycleHook(onRenderTriggered, renderTriggered)
registerLifecycleHook(onBeforeUnmount, beforeUnmount)
registerLifecycleHook(onUnmounted, unmounted)
registerLifecycleHook(onServerPrefetch, serverPrefetch)
...
}
applyOptions方法做的事情
1、处理options api
2、注册hook
setupRenderEffect
// packages/runtime-core/src/renderer.ts
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?:typeof createHydrationFunctions
){
...
const render = (vnode,container,isSVG) => {...}
const patch = (...) => {...}
const processComponent = (...) => {...}
const mountComponent = (...) => {...}
const setupRenderEffect: SetupRenderEffectFn = (
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
) => {
instance.update = effect(function componentEffect(){..})
}
setupRenderEffect方法做的事情
1、往instance挂载update方法
effect
// packages/reactivity/src/effect.ts
export function effect<T = any>(
fn: () => T,
options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect<T> {
// 判断是否是effect函数,如果是取出原始值(就是传递进来的fn)
if (isEffect(fn)) {
fn = fn.raw
}
// 创建新的effect,这个effect就是传进来的fn的执行结果
const effect = createReactiveEffect(fn, options)
// computed惰性原因在这里,
if (!options.lazy) {
effect()
}
return effect
}
effect方法做的事情
1、创建一个响应化的effect副作用
2、判断options.lazy是否执行effect
createReactiveEffect
function createReactiveEffect<T = any>(
fn: () => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
const effect = function reactiveEffect(): unknown {
// 如果不是激活状态,执行fn
if (!effect.active) {
return fn()
}
if (!effectStack.includes(effect)) {
// 持有当前effect的deps将会删除当前的effect
cleanup(effect)
try {
// 收集依赖,将当前的effect的入栈
enableTracking()
// effect入栈
effectStack.push(effect)
activeEffect = effect
return fn()
} finally {
effectStack.pop()
resetTracking()
activeEffect = effectStack[effectStack.length - 1]
}
}
} as ReactiveEffect
effect.id = uid++
effect.allowRecurse = !!options.allowRecurse
effect._isEffect = true
effect.active = true
effect.raw = fn // 创建update方法时传进来的fn
effect.deps = [] // 持有当前effect的dep数组
effect.options = options
return effect
}
执行effect
执行的就是上面createReactiveEffect返回的effect,除了收集effect外,还执行了fn(instance.update(fn)执行的是这个fn),fn的内容请看componentEffect
componentEffect
if(!instance.isMounted){
const { el, props } = initialVNode
const { bm, m, parent } = instance
if (el && hydrateNode){
...
}else{
// 生成子树结构,执行render函数(手写或者template生成的),生成渲染vnode,经过处理后,此时的instance.vnode的type类型会变为Fragment/Comment/Text/Static,在执行patch时就会进入到相应的方法内开始渲染工作,
const subTree = (instance.subTree = renderComponentRoot(instance))
// 又跑到了patch的方法内,
patch(
null,
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
}
}