Vue源码学习(五):<templete>渲染第四步,生成虚拟dom并将其转换为真实dom

好家伙,

 

前情提要:

在上一篇我们已经成功将ast语法树转换为渲染函数

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 现在我们继续

 

1.项目目录

代码已开源https://github.com/Fattiger4399/analytic-vue.git手动调试一遍,

胜过我解释给你听一万遍

新增文件:vnode/index.js    vnode/patch.js    lifecycle.js

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 

2.虚拟节点

2.1.什么是虚拟节点?

虚拟节点(Virtual Node)是前端开发中的一个概念,是一个轻量级的JavaScript对象,用来描述真实DOM树中的一个节点(元素)。

虚拟节点包括了该节点的标签名、属性、子节点等信息。

在一些前端框架(比如Vue.js、React等)中,使用虚拟节点来表示整个DOM结构,通过对比新旧虚拟节点,确定发生了哪些变化,并且最小化对真实DOM的操作。

这种方式可以提高性能,避免频繁地直接操作真实DOM带来的性能消耗。

 

2.2.为什么要用虚拟节点?

使用虚拟节点的主要原因是为了提高性能和渲染效率。下面是一些使用虚拟节点的好处:

  1.减少直接操作真实DOM带来的性能损耗:直接对真实DOM进行频繁的增删改查操作会导致浏览器进行重排和重绘,这是非常耗费性能的。

     而使用虚拟节点,可以进行批量的DOM操作,最终只对差异部分进行真实DOM的操作,大大减少了性能损耗

  2.最小化真实DOM的操作次数:虚拟DOM通过对比新旧虚拟节点的差异,找出需要更新的部分,然后只对这部分进行真实DOM的操作,避免不必要的操作。

     这可以减少页面重新渲染的次数,加快页面的响应速度。

  3.提供更灵活的渲染策略:虚拟节点提供了一种对DOM进行抽象的方式,可以根据具体的需求,实现更灵活的渲染策略。

   例如,可以通过操作虚拟节点来实现页面的动态更新、条件渲染、组件化等功能。

  4.跨平台能力:使用虚拟节点,开发者可以将同一套代码渲染到不同的平台(如浏览器、移动端、桌面端等),只需要针对不同平台进行虚拟DOM的适配即可,提高了代码的复用性和跨平台能力。

   总而言之,使用虚拟节点可以优化DOM操作、提高性能、提供灵活的渲染策略,并具有跨平台的能力,这是使用虚拟节点的主要原因。

 

2.3.虚拟节点长什么样?

虚拟节点(Virtual Node)是一个简单的JavaScript对象,用来描述真实DOM中的一个节点(元素)。它包括了节点的标签名、属性、子节点等信息。

{   tag: 'div',  // 标签名   props: {     // 属性     id: 'myDiv',     className: 'container',     style: {       backgroundColor: 'blue',       color: 'white'     }   },   children: [  // 子节点     {       tag: 'h1',       props: {         className: 'title'       },       children: ['Hello, World!']     },     {       tag: 'p',       props: {},       children: ['This is a paragraph']     }   ] }

概括:一个描述节点的对象

 

 

3.从头开始走一遍

各种方法封装的太杂了

感觉有点乱,所以我们要从头开始理清

 

3.1.vue入口文件  src/index.js  

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 

今天的主角登场了

3.2.vnode/index.js

export function renderMixin(Vue) {     Vue.prototype._c = function () {         //创建标签         return createElement(...arguments)     }     Vue.prototype._v = function (text) { //文本         return createText(text)     }     Vue.prototype._s = function (val) {         return val == null?"":(typeof val ==='object')?JSON.stringify(val):val     }     Vue.prototype._render = function () { //render函数变成 vnode         let vm = this         let render = vm.$options.render         let vnode = render.call(this)         // console.log(vnode)         return vnode     } } //vnode只可以描述节点  //创建元素 function createElement(tag,data={},...children){     return vnode(tag,data,data.key,children) } //创建文本 function createText(text){     return vnode(undefined,undefined,undefined,undefined,text) } //创建vnode function vnode(tag,data,key,children,text){     return {         tag,         data,         key,         children,         text     } }

 

我们先来分析创建节点部分

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 我们确定vnode的节点由哪几个属性组成就好,这里是标签名、属性、键值、子节点和文本内容

 

接着往下走

 

3.3.init.js

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 

3.4.mountComponent()方法

mounetComponent()方法在lifecycle.js中定义

import { patch } from "./vnode/patch"  export function mounetComponent(vm,el){     //源码     vm._updata(vm._render())     //(1)vm._render() 将 render函数变成vnode     //(2)vm.updata()将vnode变成真实dom }  export function lifecycleMixin(Vue){     Vue.prototype._updata =function(vnode){         console.log(vnode)         let vm = this         //两个参数 ()         vm.$el = patch(vm.$el,vnode)     } }  //(1) render()函数 =>vnode =>真实dom 

 

_render方法在上方有定义,

Vue.prototype._c = function () {         //创建标签         return createElement(...arguments)     }     Vue.prototype._v = function (text) { //文本         return createText(text)     }     Vue.prototype._s = function (val) {         return val == null?"":(typeof val ==='object')?JSON.stringify(val):val     }     Vue.prototype._render = function () { //render函数变成 vnode         let vm = this         let render = vm.$options.render         let vnode = render.call(this)         // console.log(vnode)         return vnode     }

于是,到此处,我们进入到最核心的两个方法了

 

3.5. _render()

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 

 em,这玩意有点复杂,另外开了一篇来说

Vue源码学习(六):(支线)渲染函数中with(),call()的使用以及一些思考

 一句话概括,将render的作用域指定为Vue实例后运行

这里我们拿到了我们要的虚拟节点

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

vm._render()返回vnode

 

接着往下走

3.6._updata

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 

3.7.来看patch方法

export function patch(oldVnode,vnode){     console.log(oldVnode,vnode)     //(1) 创建新DOM     let el = createEl(vnode)     console.log(el)     //(2) 替换 1) 获取父节点  2)插入 3)删除     let parentEL = oldVnode.parentNode     parentEL.insertBefore(el,oldVnode.nextsibling)     parentEL.removeChild(oldVnode)     return el }  function createEl(vnode){     //vnode 拆解     let {tag,data,key,children,text} = vnode     //判断标签是否为字符串 0:创建标签元素,递归处理子节点   1:文本节点     if(typeof tag === 'string'){         vnode.el = document.createElement(tag)         if(children.length >0){             children.forEach(child => {                 //递归                 vnode.el.appendChild(createEl(child))             });         }     }else{         vnode.el = document.createTextNode(text)     }     return vnode.el }

 

emm,node节点的知识点忘了,赶紧复习一下

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 删节点操作

// 获取旧节点的父节点 let parentEL = oldVnode.parentNode;   // 将新节点插入到旧节点的下一个兄弟节点之前 parentEL.insertBefore(el, oldVnode.nextsibling);  // 从父节点中移除旧节点 parentEL.removeChild(oldVnode);

 

至此,我们的真实dom渲染完成了

 

4.最终效果

来看看网页

index.html对应内容

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

网页

由原先的

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 变成了

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 {{}}模板字符串内容成功渲染

 

 

5.思考

问:{{msg}}是在哪一步变成hello的?

上下文,

<div id="app" style="display: block;color: #000">Hello{{msg}}<h2>张三</h2></div>     <script src="dist/vue.js"></script>     <script>         //umd Vue         // console.log(Vue)         //响应式 Vue         let vm = new Vue({             el: '#app', //编译模板             // data: {             // },             data() {                 // console.log(this)                 return {                     msg: 'hello',                     a: {                         b: 99                     },                     list: [1, 2, 3],                     arr: [{                         a: 1                     }]                 }             }         }) </script>

提示:网页调试结果

Vue源码学习(五):&lt;templete&gt;渲染第四步,生成虚拟dom并将其转换为真实dom

 评论区回答一波

 

发表评论

评论已关闭。

相关文章