likes
comments
collection
share

h方法的实现

作者站长头像
站长
· 阅读数 38

hello 大家好,🙎🏻‍♀️🙋🏻‍♀️🙆🏻‍♀️

我是一个热爱知识传递,正在学习写作的作者,ClyingDeng 凳凳!

继续,在给组件实例复制完成后,就需要去调用render实现组件的渲染逻辑。数据和视图双向绑定,如果数据变化,视图要随之更新。

调用render

我们可以看到在组件挂载中,是通过给setupRenderEffect这个函数传入虚拟节点、实例、容器等参数实现组件的渲染逻辑的。

h方法的实现

setupRenderEffect中,主要核心是调用render,数据变化重新调用render。 在调用render之前需要判断一下组件是否是初始化挂载:如果是初始化,就调用实例的render;如果不是,就需要走更新逻辑。

const componentUpdateFn = () => {
 let { proxy } = instance // render中的参数
 if (!instance.isMounted) { // 组件初始化流程
   // 调用render方法(渲染页面的时候会进行取值操作,那么取值的时候进行依赖收集,收集对应的effect,属性变化会重新执行)
   // 给实例的subtree赋值
     const subTree = instance.subTree = instance.render.call(proxy, proxy) 
   instance.isMounted = true
 } else {
     // 更新
 }
}

componentUpdateFn中的初始化逻辑已经填补完成。那么怎么调用这个方法呢?这样我们就需要创建一个渲染effect。

const setupRenderEffect = (initialVNode, instance, container) => {
  const componentUpdateFn = () => {
  // ...
  }
  //   创建渲染的响应式effect
  const effect = new ReactiveEffect(componentUpdateFn)
  //   变化就更新   
  const update =  effect.run.bind(effect)
  // 默认调用更新方法 执行componentUpdateFn
  update()
}

通过引入之前完成effect API中的ReactiveEffect函数,创建一个响应式渲染effect,默认就先执行一遍componentUpdateFn

引用一下打包好的代码,执行一下: h方法的实现

对,是出现报错了!

我们在完成调用实例render后,它会去执行用户传入render中的h方法,但问题就是h函数我们并没有提供啊!

h方法的实现 既然可以执行componentUpdateFn方法,那么接下来要做的就是,在执行instance.render时候调用我们自己的h方法。

h 方法的基本实现

h方法的初始化是在runtime-core文件夹中的index文件,通过export { h } from './h'对外暴露的。 h方法就写在如下图的位置:

h方法的实现

h的参数可以有很多种写法,和createElement类似第一个参数是类型,第二个参数可能是属性也可能是孩子,第三个参数是孩子。

export function h(type, propsChildren, children) {
  const l = arguments.length
  if (l === 2) {
    // ...
  } else {
   // ...
  }
}

arguments.length = 2

h接收的参数是两个的情况:

  • 情况1: h('div',{color:red})
  • 情况2: h('div',h('span'))
  • 情况3: h('div','hello')
  • 情况4: h('div',['hello','world'])

针对以上情况,第二个参数可能传递的是属性也可能是孩子。 如果第二个参数是属性,在创建vnode的时候,就按照顺序传递,依旧放到createVNode的第二个参数上; 如果第二个参数是孩子就其组成数组,放到createVNode的第三个属性上; 第二个不是属性是数组或字符串情况应将在创建元素的时候将其放到createVNode的第三个孩子属性上。

if (l === 2) {
 if (isObject(propsChildren) && !isArray(propsChildren)) {
   // 情况1
   if (isVNode(propsChildren)) {
     // 情况2
     return createVNode(type, null, [propsChildren])
   }
   // 情况3
   return createVNode(type, propsChildren)
 } else {
   // 情况4
   return createVNode(type, null, propsChildren)
 }
}

arguments.length >= 3

h接收的参数是大于等于三个的情况:

  • 情况1: h('div',{},'hello')
  • 情况2: h('div',{},['hello', 'hello']) ==> 情况1,2都转成情况2处理
  • 情况3: h('div',{},h('span'),h('span')) ==>h('div', {}, [h('span')])
if (l > 3) {
  // 情况3
  // 将其第三位后面的参数都包装成第三位参数
  children = Array.prototype.slice.call(arguments, 2)
} else if (l === 3 && isVNode(children)) { // 情况1/2
  children = [children]
}
return createVNode(type, propsChildren, children)

这样我们的h方法就基本完成了✌️✌️✌️。

查看结果

实例调用render方法,给subTree完成赋值后,我们可以输出subTree验证一下:🔜

h方法的实现

可以看到subTree上的类型、孩子、属性等都可以看出已经出现了🤪

感兴趣的朋友可以关注 手写vue3系列 专栏或者点击关注作者ClyingDeng哦(●'◡'●)!。 如果不足,请多指教。

转载自:https://juejin.cn/post/7193505420483231803
评论
请登录