在VUE中它可以将一个大的页面拆分为多个部分,每一个部分都可以作为单独 的组件,这些组件共同组成完整的页面。组件化的实现同样需要渲染 器的支持,接下来我们看下组件化需要怎么做。
本篇的相关的解析,我们先用文字描述逻辑,然后再转换成代码实现
组件的结构
TS创建组件的definecomponent只是用来赋予类型,这里我们不关注。 实际我们定义的结构和最后解析到虚拟DOM环节时的结构是这样:
// MyComponent 是一个组件,它的值是一个选项对象
const MyComponent = {
name: 'MyComponent',
// 配置时写法
data() {
return { foo: 1 }
},
//返回明确的render函数
render() {
return {
type: 'div',
children: '文本'
}
}
}
// MyComponent1 是一个组件,它的值是一个选项对象
const MyComponent1 = {
name: 'MyComponent1',
props: { 05 title: String 06 },
//组合式写法
setup(props) {
// setup中返回的函数当render函数
return () => {
return {
type: 'div',
children: ['文本',props.title]
}
}
},
// 或者返回明确的render函数 和上面setup返回 只需要有一个就可以
render() {
return {
type: 'div',
children: '文本'
}
}
}
这两种写法代表了2和3 option配置式和Composition api组合式写法(这里为了方便理解,返回值直接虚拟DOM化展示了),创建的组件。
组件在虚拟DOM中类型
我们虚拟DOM篇,看到我们的虚拟DOM有:
// 该 vnode 用来描述普通标签
const vnode = {
type: 'div'
// ...
}
// 该 vnode 用来描述文本节点
const vnode = {
type: Text
// ...
}
等类型,从这里我们能想到 那肯定有type是组件的类型。
那对于组件类型的VNode它的type我们直接把存储组件的选项对 象放上去,也就是说VNode.type是一个对象时那它肯定就是有状态组件(函数组件后面讲)。
也就是说最后的使用可能是这样:
// 用来描述组件的 VNode 对象,type 属性值为组件的选项对象
const CompVNode = {
type: MyComponent
}
// 调用渲染器来渲染组件
renderer.render(CompVNode, document.querySelector('#app'))
render就是我们虚拟DOM篇讲的render,renderer渲染器是为了做跨平台(这里不细讲这个)
挂载和更新函数的实现
那虚拟DOM篇我们的mountComponent和patchComponent具体实现是没写的,在这篇章我们能看到他们的实现。
首次render时那一定是挂载 mountComponent,那我们先来实现它,我们看下实现它需要几步,我们先用文字描述逻辑,然后再转换成代码。
mountComponent
- 首先我们要取出组件的各个属性配置,组件对象我们知道存到type上了,所以我们从type上把data函数、render函数、setup函数、props属性配置(别名propsOption)、和其他生命周期函数。
- 接下来就是创建组件操作了,在这之前我们先调用beforeCreate创建之前的生命周期函数
- 然后调用data函数获取state,这个state需要是响应式数据,所以我们用reactive包一下。
- 然后根据组件的props就是propsOption,和实际传递的props(vnode.props)来解析出props和attrs
- 然后获取一下插槽const slots = vnode.children || {};
- 然后我们创建模拟一个组件实例instance,存一些属性,这个组件实例属性在卸载和更新、生命周期存储等操作都会用到。
- instance上我们存储state和props(它通过shallowReactive变成了响应式数据,组件props变更时把它改了,如果当前组件用了它,通过这个响应式来触发组件更新),isMounted表示是否被挂载,subTree存储render函数返回的VNode,slots插槽,mounted数组存储setup中onMounted函数。
- 然后我们处理onMounted往实例instance上存储,因为vue组件的实例化过程也就是mountComponent是同步的,同一时间只会有一个调用,所以我们可以用一个全局变量currentInstance存一下组件实例,等setup结束后再置空,然后onMounted的实现就直接
插槽数据解析: