引言
从React的渲染流程我们知道,JSX 会先转为一颗 Fiber Tree,然后通过 Renderer
渲染成页面。对于 Web 平台,这个 Renderer
就是 react-dom
,对于 Native 平台,这个 Renderer
就是 react-native
。当然,我们也可以创建我们自己的 Renderer
,将 React 应用渲染到其他目标平台,比如本文中的 Canvas:
下面就来剖析下 Canvas
Renderer
的实现方式。
Canvas Renderer
组件
如图,我们的 Canvas
Renderer
包括 Stage
,Rect
,Circle
,Text
这些组件,其中将他们一些公共的特征抽离成了一个父类 Layer
。
不需要 React,现在的 Canvas
Renderer
已经可以渲染出内容了,比如:
1 | const renderDom = document.getElementById('demo') |
Canvas Renderer 实现方式
我们通过引言中第一个 Demo 来分析 Canvas
Renderer
的实现方式:
1 | // Demo1.jsx |
Demo1 是一个函数组件,返回了 text
、rect
、 circle
这些标签,这些标签需要我们 Canvas
Renderer
来进行渲染,接下来看看 render
函数做了啥:
1 | const reconcilerInstance = Reconciler(HostConfig) |
该函数主要是创建了一个 Stage
对象作为 Reconciler
对象 reconcilerInstance
的 container
,最后调用 reconcilerInstance.updateContainer()
将 Demo1 组件通过 Canvas
Renderer
进行渲染。我们知道 Reconciler
在 React 渲染流程中充当着非常重要的作用,它会计算出哪些组件需要更新,并会将需要更新的信息提交给 Renderer
来处理,而将 Reconciler
和 Renderer
连接起来的秘诀就在 HostConfig
之中:
1 | const HostConfig = { |
HostConfig
中是我们的 Canvas
Renderer
需要实现的一些接口,这里来说明一下:
supportsMutation
当前渲染器是否支持修改节点,毫无疑问这里必须是 true
。
createInstance
该函数会在通过 FiberNode
创建宿主相关的元素时进行调用,返回的元素会保存在 FiberNode
的 stateNode
属性上,参考React的渲染流程。对于 Canvas
Renderer
来说,这里会根据 type
值创建出不同的组件。
appendInitialChild、appendChild、appendChildToContainer、insertBefore
这几个接口都涉及到元素的插入操作,前三个是把元素插到最后面,其中 appendInitialChild
在首次渲染时调用,appendChild
在更新的时候调用,而 appendChildToContainer
则在把元素插入到 container
时使用,对于 Canvas
Renderer
来说,这些接口中均调用 parent.appendChild(child)
即可:
1 | appendChild(child) { |
而 insertBefore
则是把元素插入到某个元素前面,同样,Canvas
Renderer
也有对应的实现:
1 | insertBefore(child, beforeChild) { |
commitUpdate
当组件属性发生变化的时候会调用该函数,Canvas
Renderer
对应的实现方法也比较简单,即更新 instance
的属性即可:
1 | update(props) { |
resetAfterCommit
在React 源码解读之一首次渲染流程这篇文章中已阐明 React 的每次更新过程包括 Render
和 Commit
两大阶段,其中 Render
阶段会计算出 Effect
链表供 Commit
阶段处理,而 resetAfterCommit
这个函数就是在 Commit
阶段执行完 commitMutationEffects
函数后进行调用,此时所有对元素的更新操作已处理完毕,所以这里是一个适合 Canvas
Renderer
调用 container.render()
进行重新渲染的地方。该函数中首先清空了整个画布,然后依次调用子组件的 render
方法:
1 | // Stage.js |
值得一提的是,Remax 也是在这里触发了小程序的更新。
至此,我们的 Canvas
Renderer
的核心实现原理就分析完了,更多内容及 Demo 详见源码。