渲染器
简单来说,渲染器的工作就是把 vnode 根据类型变成真实 dom,并挂载、更新到页面上,或者进行卸载删除等操作
渲染器是工作在运行时的
流程
通过h()生成对应的 vNode,vNode 其实就是对象,里面的type属性可以区分类型,比如对应的真实 dom 类型,或者自定义组件类型
vNode 对象要交给渲染器render,渲染器内部实现了一系列的方法,包括插入、删除、更新等操作
下面是精简过的源码部分
const rendererOptions = extend({ patchProp }, nodeOps)
export const render = ((...args) => { let renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions) renderer.render(...args) }) as RootRenderFunction<Element | ShadowRoot>
export function createRenderer< HostNode = RendererNode, HostElement = RendererElement, >(options: RendererOptions<HostNode, HostElement>) { return baseCreateRenderer<HostNode, HostElement>(options) }
function baseCreateRenderer( options: RendererOptions, createHydrationFns?: typeof createHydrationFunctions, ): any {
const render: RootRenderFunction = (vnode, container, namespace) => { if (vnode == null) { if (container._vnode) { unmount(container._vnode, null, null, true) } } else { patch( container._vnode || null, vnode, container, null, null, null, namespace, ) } container._vnode = vnode }
return { render } }
|
h函数
可以使用js来创建vue的虚拟节点(VNode或常说的VDOM),也就是常说的h函数
createVNode()、h()
h函数重载方法很多,只拿出了用的多的两个作为例子
export function h<K extends keyof HTMLElementTagNameMap>( type: K, props?: (RawProps & HTMLElementEventHandler) | null, children?: RawChildren | RawSlots, ): VNode
export function h<P>( type: Component<P>, props?: (RawProps & P) | null, children?: RawChildren | RawSlots, ): VNode
|
调用
const vnode = h( 'h1', { msg: 'hello' }, [ h('span', '子节点内容') ] )
|
h函数是createVNode()的一个别名
原理
原理也很简单,就是返回一个VNode类型的对象
function createVNode( type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT, props: (Data & VNodeProps) | null = null, children: unknown = null, ): VNode { const vnode = { } as VNode
return vnode }
|
使用
完整的渲染函数创建一个VNode的代码如下
Vue.component('anchored-heading', { render: function (createElement) { return createElement( 'h' + this.level, [ createElement('a', { attrs: { name: headingId, href: '#' + headingId } }, this.$slots.default) ] ) }, props: { level: { type: Number, required: true } } })
|
eg2. 创建一个带有作用域插槽的子组件
render: function (createElement) { return createElement('div', [ createElement('child', { scopedSlots: { default: function (props) { return createElement('span', props.text) } } }) ]) }
|