Vue.js设计与实现之十一-渲染器的设计

1、设设计写在前面

在Vue.js框架很多功能依赖渲染器实现,计实也是现之渲染框架性能的核心,能够直接影响框架性能。设设计对此,计实Vue.js3的现之渲染渲染器通过快捷路径更新的方式,利用编译器提供的设设计信息提升性能。

2、计实渲染器和响应系统的现之渲染结合

渲染器是用来执行渲染任务的,可以在浏览器平台来渲染真实DOM元素,设设计它还能实现框架跨平台能力。计实

前面已经实现了reactivity的现之渲染源码,为了后续方便讲解后续的设设计操作,对此可以直接使用vue.js3的计实源码导入使用。

<script src="https://unpkg.com/@vue/reactivity@3.0.5/dist/reactivity.global.js" />

这样就可以通过暴露的现之渲染API使用:

const { effect, ref} = VueReactivity;

function renderer(domString, container){

container.innerHTML = domString;

}

const count = ref(1);

effect(()=>{

renderer(`

${ count.value}

`,document.getElementById("app"))

});

count.value++

渲染器把虚拟DOM节点渲染为真实DOM节点的过程叫做挂载,在Vue中mounted钩子函数会在挂载完成时触发,从而实现虚拟DOM的挂载。此外,渲染器还需要一个挂载点作为参数,用于指定具体的云服务器挂载位置,渲染器会将此DOM元素作为容器元素,可以实现内容的渲染。

在Vue.js3中是通过createRenderer函数来创建渲染器renderer,而renderer不仅包含渲染器函数render,还包括服务端渲染使用的hydrate函数。用于创建应用的createApp函数也是渲染器的一部分。

const renderer = createRenderer();

//首次渲染

renderer.render(oldVNode, document.querySelector("#app"));

//第二次渲染

renderer.render(newVNode, document.querySelector("#app"));

在上面代码中,首先调用createRenderer函数创建一个渲染器,通过调用渲染器的renderer.render函数创建新的DOM元素,而后将虚拟DOM节点vnode渲染到挂载点上,这就是挂载。

在同一个挂载DOM上,调用renderer.render函数进行渲染时,渲染器除了实现常规的DOM挂载外,还需要执行页面更新操作,也就是DOM更新。在渲染器会使用vnode与上次渲染的oldVNode进行比较,从而找到并更新变更点,此过程叫做"打补丁"(patch)即更新。

function createRenderer(){

function render(domString, container){

if(vnode){

// 新vnode存在,源码库将其与旧vnode传递给patch函数,进行打补丁

patch(container._vnode, vnode, container);

}else{

if(container._vnode){

// 如果旧节点vnode存在,则需要清空挂载点的DOM元素

container.innerHTML = "";

}

}

// 将vnode存储到container._vnode中

container._vnode = vnode

}

return {

render

}

}

在上面代码中,是render函数的基本实现。patch函数是整个渲染器的核心入口,承载了最重要的渲染逻辑。

patch(n1, n2, container){

/***/

}n1:旧VNode。n2:新VNode。container:挂载容器。3、自定义渲染器

我们看看如何自定义实现渲染器,实现跨平台能力,那就从普通的h1标签开始。

const vnode = {

type:"h1",

children:"pingping"

}

在上面代码中实现了一个简单的vnode,使用type属性描述vnode类型,type属性是字符串类型值时是普通标签。

const vnode = {

type:"h1",

children:"pingping"

}

const renderer = createRenderer();

//首次渲染

renderer.render(vnode, document.querySelector("#app"));

这样,我们就可以渲染为<h1>pingping</h1>,为了完成渲染工作,下面可以补充patch函数。

function createRenderer(){

function patch(n1, n2, container){

//如果n1不存在的时候,意味着挂载,服务器托管需要调用mountElement函数完成挂载

if(!n1){

mountElement(n2, container);

}else{

//n1存在,即打补丁更新DOM节点

}

}

function render(domString, container){

if(vnode){

// 新vnode存在,将其与旧vnode传递给patch函数,进行打补丁

patch(container._vnode, vnode, container);

}else{

if(container._vnode){

// 如果旧节点vnode存在,则需要清空挂载点的DOM元素

container.innerHTML = "";

}

}

// 将vnode存储到container._vnode中

container._vnode = vnode

}

return {

render

}

}

在上面代码中,patch函数中参数n1不存在时,意味着没有旧VNode,此时只需要进行挂载即可。

function mountElement(vnode, container){

const el = document.createElement(vnode.type);

if(typeof vnode.children === "string"){

el.textContent = vnode.children;

}

container.appendChild(el);

}

在上面代码中,简单实现了一个普通标签元素的挂载工作。Vue.js3是个跨平台框架,得益于一个不依赖平台的通用渲染器,而在mountElement函数中依赖了大量浏览器API。想要设计通用渲染器,就必须剥离与平台相关的API,当然可以将这些操作DOM的API作为createRenderer的配置项。

const renderer = createRenderer({

//创建元素

createElement(tag){

return document.createElement(tag);

},

//设置元素的文本节点

setElementText(el, text){

el.textContent = text;

},

//用于给指定父节点添加指定元素

insert(el, parent, achor = null){

parent.insertBefore(el, anchor);

}

})

上面代码中,可以将操作DOM的API封装为一个对象,并把它传递到createRenderer函数,可以通过配置项来取得操作DOM的api。

function createRenderer(options){

const {

createElement,

setElementText,

insert

} = options;

function mountElement(vnode, container){

const el = createElement(vnode.type);

if(typeof vnode.children === "string"){

setElementText(el, vnode.children)

}

insert(el, container);

}

function patch(n1, n2, container){

//...

}

function render(domString, container){

//...

}

return {

render

}

}

现在我们对自定义渲染器有个更深刻的认识,自定义渲染器不是什么黑盒子,只是通过代码抽象,让核心代码不再强依赖于平台特定API,通过自定义配置项实现跨平台。

4、写在最后

在本文中,我们了解到渲染器的作用和实现,知道其是将虚拟DOM转为真实DOM挂载到节点上,在进行节点更新的时候是通过patch实现的。为了实现跨平台渲染,那么就需要用户通过自定义配置项渲染器,从而实现DOM的渲染挂载。

应用开发
上一篇:NVIDIA 与 Ampere Computing 携手创建AICAN服务器平台,共同推动云游戏市场的飞速发展
下一篇:新的数据中心需求重塑企业应用交付