对于一个a.vue文件(组件),它有template,script,style三部分,使用的时候需要先import a from ./a.vue
经过vue-loader
处理后 , a 其实变成了一个对象,template就变成了对象中的render函数,返回的是VNode.其中vnode.type
属性来存储组件的选项对象a
js
//这个组件对应的VNode
{
type:a,
//props:{}
//....
}
现在处理组件转变为处理这个对象,现在只需要聚焦这个对象即可了
在render中的 patch
函数中会根据 type
判断节点类型,其实处理组件跟处理普通标签差不多,当n1为null时,执行 mountComponent
,反之执行 updateComponent
。
组件模版与状态
现在再来讨论一下,这个组件对象里应该包含什么东西,首先对于一个组件,我们要知道它渲染了什么,这里用虚拟DOM表示。Vue规定组件内的render函数的返回值就是虚拟DOM
,所以我们现在可以得到组件的描述-虚拟DOM(之后用subTree代指)
接着组件应该有自己的状态也就是数据,规定是data函数返回的,并且这些数据可以在render函数中通过this访问。
进行组件维护
对于组件我们必须注册一个实例,用来维护组件运行过程中的所有信息
,例如生命周期函数、组件渲染的子树(subTree)、组件是否已经被挂载、组件自身的状态等
应该在第一次渲染也就是mountComponent
注册它的实例。
js
function mountComponent(vnode, container, anchor) {
const { render, state } = vnode.type
const state = reactive(data())
const instance = {
// 组件自身的状态数据,即 data
state,
// 一个布尔值,用来表示组件是否已经被挂载,初始值为 false
isMounted: false,
// 组件所渲染的内容,即子树(subTree)
subTree: null
}
// 将组件实例设置到 vnode 上,用于后续更新
vnode.component = instance
}
结合响应式
接着执行setupRenderEffect
,它的作用就是注册带副作用
的渲染函数.用来完成子组件的自动更新.而这个副作用函数componentUpdateFn
做了什么呢
其实很简单就是利用之前的实例拿到上面的属性isMounted进行判断
如果是false,渲染组件,执行renderComponentRoot
可以拿到subTress,之后在执行patch
进行挂载.
反正更新,首先执行updateComponentPreRender
更新组件实例,再执行patch
父组件导致的更新
上面是子组件自身状态改变自动更新,下面来讨论一下父组件的状态改变导致子组件更新
也就是会触发上面提到的updateComponent
,在里面会比较组件VNode中的props,chilren等属性是否相同,相同代表不需要更新,反正需要更新.接下来会进行下面两步操作
- invalidateJob(instance.update)
- instance.update()
第一步是因为Vue更新粒度是组件级别的,子组件也可能因为自身数据改变触发更新,这样可以避免重复渲染
第二步是触发了子组件的更新渲染
深入探析如何注册组件实例(处理setup)
在这个createComponentInstance
函数中会进行组件实例的创建,创建之后我们还要设置实例才行,如果是组合式api的写法交给这个函数setupComponent
进行设置的.
在这个函数中先初始化props,slots.接着执行核心函数setupStatefulComponent
,它的作用是
创建渲染上下文代理
,虽然实例中会存放对应的属性,例如data,setupState(setup的返回值),props,ctx(选项式api中的methods,computer,inject等),但是为了方便用户,render函数会直接访问实例instance.ctx属性,所以需要对它加一层代理
,用来控制数据的读取及修改.看看他的捕获函数
- get: 访问的key不以 <math xmlns="http://www.w3.org/1998/Math/MathML"> 开头 , 依次判断 s e t u p S t a t e , d a t a , p r o p s , c t x 是否包含这个 k e y . 反正 , 进行判断是不是 V u e 暴露出的 开头,依次判断setupState,data,props,ctx是否包含这个key.反正,进行判断是不是Vue暴露出的 </math>开头,依次判断setupState,data,props,ctx是否包含这个key.反正,进行判断是不是Vue暴露出的xxx属性或方法,再判断是不是在ctx中以$开头的key,最后判断他是不是全局属性
- set: 开发环境修改props会提示
处理setup函数
- 首先会创建
setupContext
对象,他就是setup函数的第二个参数,可以解构出attrs、slots和 emit三个属性以及expose函数。setupContext的目的就是让我们可以在setup函数内部访问组件实例上的属性、插槽、派发事件的方法enit以及暴露组件方法的函数expose. - 执行setup,第一个参数是instance.props,第二个参数是setupContext.返回结果可以是一个对象也可以是一个函数,当返回函数时将被当成render函数.如果setupResult是一个对象,那么对其返回结果做一层代理,把结果赋值给instance.setupState,