本文为原创文章,未获授权禁止转载,侵权必究!
本篇是 Vue3 源码解析系列第 16 篇,关注专栏
前言
组件
的渲染除了之前提到的情况外, 在 Composition Api
中还可以通过 setup
方法来进行,那它是如何运行的呢?我们来逐一分析。
案例
首先引入 h
、 render
、reactive
函数,声明一个 component
包含 setup
函数的组件对象,通过 h
函数生成 组件 vnode
对象,最后通过 render
函数渲染该对象。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="../../../dist/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const { h, render, reactive } = Vue
const component = {
setup() {
const obj = reactive({
name: 'Jason Chen'
})
return () => h('div', obj.name)
}
}
const vnode = h(component)
render(vnode, document.querySelector('#app'))
</script>
</body>
</html>
render component setup
我们知道组件的挂载通过 mountComponent
方法,之后执行 setupComponent
触发 setupStatefulComponent
方法:
function setupStatefulComponent(
instance: ComponentInternalInstance,
isSSR: boolean
) {
const Component = instance.type as ComponentOptions
// 省略
const { setup } = Component
if (setup) {
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
setCurrentInstance(instance)
pauseTracking()
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
resetTracking()
unsetCurrentInstance()
if (isPromise(setupResult)) {
setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
if (isSSR) {
// return the promise so server-renderer can wait on it
return setupResult
.then((resolvedResult: unknown) => {
handleSetupResult(instance, resolvedResult, isSSR)
})
.catch(e => {
handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
})
} else if (__FEATURE_SUSPENSE__) {
// async setup returned Promise.
// bail here and wait for re-entry.
instance.asyncDep = setupResult
if (__DEV__ && !instance.suspense) {
const name = Component.name ?? 'Anonymous'
warn(
`Component <${name}>: setup function returned a promise, but no ` +
`<Suspense> boundary was found in the parent component tree. ` +
`A component with async setup() must be nested in a <Suspense> ` +
`in order to be rendered.`
)
}
} else if (__DEV__) {
warn(
`setup() returned a Promise, but the version of Vue you are using ` +
`does not support it yet.`
)
}
} else {
handleSetupResult(instance, setupResult, isSSR)
}
} else {
finishComponentSetup(instance, isSSR)
}
}
通过 组件对象
解构出 setup
函数,之后执行 createSetupContext
方法来创建上下文,我们知道 setup
函数第二个参数为 context
执行上下文。
接着执行 callWithErrorHandling
方法即传入的 setup
方法执行:
此时 setupResult
为 setup
函数返回值 () => h('div', obj.name)
,之后执行 handleSetupResult
方法:
export function handleSetupResult(
instance: ComponentInternalInstance,
setupResult: unknown,
isSSR: boolean
) {
if (isFunction(setupResult)) {
// setup returned an inline render function
if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
// when the function's name is `ssrRender` (compiled by SFC inline mode),
// set it as ssrRender instead.
instance.ssrRender = setupResult
} else {
instance.render = setupResult as InternalRenderFunction
}
} else if (isObject(setupResult)) {
// 省略
} else if (__DEV__ && setupResult !== undefined) {
// 省略
}
finishComponentSetup(instance, isSSR)
}
将 setupResult
赋值给 instance.render
即 instance.render = () => h('div', obj.name)
,handleSetupResult
方法执行完成就表示 组件实例
就存在 render
函数。
之后执行 setupRenderEffect
方法,触发 patch
进行组件的挂载,此时页面呈现:
总结
组件
本质上就是通过render
函数实现的。- 组件内部构建了一个
ReactiveEffect
实例,它是实现组件内部响应式渲染的核心。 Options Api
中存在this
,所以必须要改变this
指向来访问对应的数据,内部通过bind
或者call
来进行改变。Composition Api
不存在this
,也就不存在this
指向问题,逻辑更加简单。
Vue3 源码实现
Vue3 源码解析系列
- Vue3源码解析之 源码调试
- Vue3源码解析之 reactive
- Vue3源码解析之 ref
- Vue3源码解析之 computed
- Vue3源码解析之 watch
- Vue3源码解析之 runtime
- Vue3源码解析之 h
- Vue3源码解析之 render(一)
- Vue3源码解析之 render(二)
- Vue3源码解析之 render(三)
- Vue3源码解析之 render(四)
- Vue3源码解析之 render component(一)
- Vue3源码解析之 render component(二)
- Vue3源码解析之 render component(三)
- Vue3源码解析之 render component(四)
- Vue3源码解析之 render component(五)
- Vue3源码解析之 diff(一)
- Vue3源码解析之 diff(二)
- Vue3源码解析之 compiler(一)
- Vue3源码解析之 compiler(二)
- Vue3源码解析之 compiler(三)
- Vue3源码解析之 createApp