组件的实现原理
引入组件实例概念
- state:组件自身的状态数据,即data
- isMounted: 一个布尔值,用来表示组件是否被挂载
- subTree: 存储组件的渲染函数返回的虚拟DOM,即组件的子树(subTree)
组件调用生命周期原理:
function mountComponent(vnode, container, anchor) {
const componentOptions = vnode.type
// 从组件选项对象中取得组件的生命周期函数
const { render, data, beforeCreate, created, beforeMount, mounted, beforeUpdate, updated } = componentOptions
// 在这里调用 beforeCreate 钩子
beforeCreate && beforeCreate()
const state = reactive(data())
const instance = {
state,
props: shallowReactive(props),
isMounted: false,
subTree: null
}
vnode.component = instance
// 创建渲染上下文对象,本质上是组件的代理
const renderContext = new Proxy(instance, {
get(t, k, r) {
// 取得组件自身状态与props数据
const { state, props } = t
// 先尝试读取自身状态数据
if(state && k in state) {
return state[k]
} else if(k in props) {
return props[k]
} else {
console.log('不存在')
}
},
set(t, k, v, r) {
const { state, props } = t
if(state && k in state) {
state[k] = v
} else if(k in props) {
console.warn('props 应该是只读的')
} else {
console.log('不存在')
}
}
})
// 在这里调用 created 钩子
created && created.call(state)
effect(() => {
const subTree = render.call(state, state)
if(!instance.isMounted) {
// 调用 beforeMount 钩子
beforeMount && beforeMount.call(state)
patch(null, subTree, container, anchor)
instance.isMounted = true
// 在这里调用 Mounted 钩子
mounted && momunted.call(state)
} else {
// 在这里调用 beforeUpdate 钩子
beforeUpdate && beforeUpdate.call(state)
patch(istance.subTree, subTree, container, anchor)
// 在这里调用 updated 钩子
updated && updated.call(state)
}
instance.subTree = subTree
}, { scheduler: queueJob })
}
渲染上下文对象: 用于拦截数据状态的读取和设置操作,每当在渲染函数或生命周期钩子中通过this读取数据时,都应该在渲染上下文对象中处理。
setup函数: setup函数的返回值可以是两种类型,如果返回函数,则将该函数作为组件的渲染函数,如果返回数据对象,则将该对象暴露到渲染的上下文中。
组件事件与emit的实现: 检测propsData的key值,判断是否以字符串'on'开头, 如果时,则认为该属性时组件的自定义事件,即使组件没有显示的声明props,也要讲它添加到props中,而不是attrs对象。
插槽: 我们讨论了组件的插槽。它借鉴了 Web Component 中<slot> 标签的概念。插槽内容会被编译为插槽函数,插槽函数的返回值就是向槽位填充的内容。<slot> 标签则会被编译为插槽函数的调用,通过执行对应的插槽函数,得到外部向槽位填充的内容(即虚拟DOM),最后将该内容渲染到槽位中。
注册组件生命周期钩子: 通过 onMounted 注册的生命周期函数会被注册到当前组件实例的 instance.mounted 数组中。为了维护当前正在初始化的组件实例,我们定义了全局变量 currentInstance,以及用来设置该变量的 setCurrentInstance 函数。