前言
- 将调用的生命周期函数, 用户传入的函数
- push到组件实例上的对应生命周期属性中
- 在特定的时期 去执行对应生命周期
- 如下面的示例
- 执行
setup让组件实例收集用户传入的函数
- 要考虑组件实例this变化(
父setup->子setup), 采用闭包的形式 将组件实例在调用时存储起来
- 在组件第一次渲染时执行
onBeforeMount, onMounted
- 在组件更新比对时执行
onBeforeUpdate, onUpdated
示例
<script src="../node_modules/@vue/runtime-dom/dist/runtime-dom.global.js"></script>
<div id="app"></div>
<script>
const {
createApp,
h,
reactive,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
getCurrentInstance
} = VueRuntimeDOM
let App = {
setup(props, context) {
let state = reactive({ flag: true })
let instance = getCurrentInstance()
console.log('获取当前实例', instance)
setTimeout(() => { state.flag = false }, 2000)
onBeforeMount(() => { console.log('onBeforeMount1') })
onBeforeMount(() => { console.log('onBeforeMount2') })
onMounted(() => { console.log('onMounted') })
onBeforeUpdate(() => { console.log('onBeforeUpdate') })
onUpdated(() => { console.log('onUpdated') })
return () => { return h('div', state.flag) }
}
}
let app = createApp(App, { name: 'Tom', age: 25 })
app.mount('#app')
</script>
定义生命周期类型
component.ts
import { isFunction, isObject, ShapeFlags } from "@vue/shared/src"
import { PublicInstanceProxyHandlers } from "./componentPublicInstance"
export enum LifeCycleHooks {
BEFORE_MOUNT = 'bm',
MOUNTED = 'm',
BEFORE_UPDATE = 'bu',
UPDATED = 'u'
}
export let currentInstance = null
export let setCurrentInstance = (instance) =>{
currentInstance = instance
}
export let getCurrentInstance = () =>{
return currentInstance
}
let uid = 0
export function createComponentInstance(vnode) {
const instance = {
uid: uid++,
vnode,
type: vnode.type,
props: {},
attrs: {},
slots: {},
ctx: {},
data: {},
setupState: {},
proxy: null,
render: null,
subTree: null,
isMounted: false,
update: null,
bm: null,
m: null,
bu: null,
u: null,
}
instance.ctx = { _: instance }
return instance
}
function setupStatefulComponent(instance) {
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
let Component = instance.type
let { setup } = Component
if (setup) {
currentInstance = instance
let setupContext = createSetupContext(instance)
const setupResult = setup(instance.props, setupContext)
currentInstance = null
handleSetupResult(instance, setupResult)
} else {
finishComponentSetup(instance)
}
}
function handleSetupResult(instance, setupResult) {
if (isFunction(setupResult)) {
instance.render = setupResult
} else if (isObject(setupResult)) {
instance.setupState = setupResult
}
finishComponentSetup(instance)
}
function finishComponentSetup(instance) {
let Component = instance.type
if (!instance.render) {
if(!Component.render && Component.template) {
}
instance.render = Component.render
}
}
function createSetupContext(instance) {
return {
attrs: instance.attrs,
slots: instance.slots,
emit: () => {},
expose: () => {}
}
}
将生命周期存储到实例上(重点)
apiLifecycle.ts
import { currentInstance, LifeCycleHooks, setCurrentInstance } from "./component"
export const invokeArrayFns = (fns) =>{
for(let i = 0; i < fns.length; i++) {
fns[i]()
}
}
const injectHook = (type, hook, target) => {
if (!target) {
return console.warn('injection APIs can only be used during execution of setup(), 只能使用在setup中')
} else {
const hooks = target[type] || (target[type] = [])
const wrappedHook = () => {
setCurrentInstance(target)
hook.call(target)
setCurrentInstance(null)
}
hooks.push(wrappedHook)
}
}
const createHook = (lifecycle) => (hook, target = currentInstance) => {
injectHook(lifecycle, hook, target)
}
export const onBeforeMount = createHook(LifeCycleHooks.BEFORE_MOUNT)
export const onMounted = createHook(LifeCycleHooks.MOUNTED)
export const onBeforeUpdate = createHook(LifeCycleHooks.BEFORE_UPDATE)
export const onUpdated = createHook(LifeCycleHooks.UPDATED)
渲染和更新时
render.ts
export function createRenderer(rendererOptions) {
const {
insert: hostInsert,
remove: hostRemove,
patchProp: hostPatchProp,
createElement: hostCreateElement,
createText: hostCreateText,
createComment: hostCreateComment,
setText: hostSetText,
setElementText: hostSetElementText,
nextSibling: hostNextSibling,
} = rendererOptions
const setupRenderEffect = (instance, container) => {
instance.update = effect(function componentEffect() {
const { bm, m, bu, u } = instance
if (!instance.isMounted) {
if (bm) { invokeArrayFns(bm) }
let proxyToUse = instance.proxy
let subTree = instance.subTree = instance.render.call(proxyToUse, proxyToUse)
patch(null, subTree, container)
instance.isMounted = true
if (m) { invokeArrayFns(m) }
} else {
if (bu) { invokeArrayFns(bu) }
const prevTree = instance.subTree
let proxyToUse = instance.proxy
const nextTree = instance.render.call(proxyToUse, proxyToUse)
patch(prevTree, nextTree, container)
if (u) { invokeArrayFns(u) }
}
}, {
scheduler: queueJob
})
}
}
完