好家伙,

 

前情提要:

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

 现在我们继续

 

1.项目目录

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

胜过我解释给你听一万遍

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

 

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  

 

今天的主角登场了

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
}
}

 

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

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

 

接着往下走

 

3.3.init.js

 

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()

 

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

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

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

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

vm._render()返回vnode

 

接着往下走

3.6._updata

 

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节点的知识点忘了,赶紧复习一下

 删节点操作

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

 

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

4.最终效果

来看看网页

index.html对应内容

网页

由原先的

 变成了

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

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源码学习(五):<templete>渲染第四步,生成虚拟dom并将其转换为真实dom的更多相关文章

  1. Vue源码学习二 ———— Vue原型对象包装

    Vue原型对象的包装 在Vue官网直接通过 script 标签导入的 Vue包是 umd模块的形式.在使用前都通过 new Vue({}).记录一下 Vue构造函数的包装. 在 src/core/in ...

  2. Vue源码学习1——Vue构造函数

    Vue源码学习1--Vue构造函数 这是我第一次正式阅读大型框架源码,刚开始的时候完全不知道该如何入手.Vue源码clone下来之后这么多文件夹,Vue的这么多方法和概念都在哪,完全没有头绪.现在也只 ...

  3. 【Vue源码学习】依赖收集

    前面我们学习了vue的响应式原理,我们知道了vue2底层是通过Object.defineProperty来实现数据响应式的,但是单有这个还不够,我们在data中定义的数据可能没有用于模版渲染,修改这些 ...

  4. Vue源码学习三 ———— Vue构造函数包装

    Vue源码学习二 是对Vue的原型对象的包装,最后从Vue的出生文件导出了 Vue这个构造函数 来到 src/core/index.js 代码是: import Vue from './instanc ...

  5. 最新 Vue 源码学习笔记

    最新 Vue 源码学习笔记 v2.x.x & v3.x.x 框架架构 核心算法 设计模式 编码风格 项目结构 为什么出现 解决了什么问题 有哪些应用场景 v2.x.x & v3.x.x ...

  6. VUE 源码学习01 源码入口

    VUE[version:2.4.1] Vue项目做了不少,最近在学习设计模式与Vue源码,记录一下自己的脚印!共勉!注:此处源码学习方式为先了解其大模块,从宏观再去到微观学习,以免一开始就研究细节然后 ...

  7. Vue 源码学习(1)

    概述 我在闲暇时间学习了一下 Vue 的源码,有一些心得,现在把它们分享给大家. 这个分享只是 Vue源码系列 的第一篇,主要讲述了如下内容: 寻找入口文件 在打包的过程中 Vue 发生了什么变化 在 ...

  8. Vue源码后记-vFor列表渲染(1)

    钩子函数比较简单,没有什么意思,这一节搞点大事情 => 源码中v-for的渲染过程. vue的内置指令包含了v-html.v-if.v-once.v-bind.v-on.v-show等,先从一个 ...

  9. Vue源码学习(一):调试环境搭建

    最近开始学习Vue源码,第一步就是要把调试环境搭好,这个过程遇到小坑着实费了点功夫,在这里记下来 一.调试环境搭建过程 1.安装node.js,具体不展开 2.下载vue项目源码,git或svn等均可 ...

  10. 【Vue源码学习】响应式原理探秘

    最近准备开启Vue的源码学习,并且每一个Vue的重要知识点都会记录下来.我们知道Vue的核心理念是数据驱动视图,所有操作都只需要在数据层做处理,不必关心视图层的操作.这里先来学习Vue的响应式原理,V ...

随机推荐

  1. 从 SpringApplication 认识 Spring 应用启动过程

    一.SpringApplication 是什么? Spring 应用的启动类. 二.SpringApplication 执行了什么? 创建 ApplicationContext 实例 Applicat ...

  2. 从源码级深入剖析Tomcat类加载原理

    众所周知,Java中默认的类加载器是以父子关系存在的,实现了双亲委派机制进行类的加载,在前文中,我们提到了,双亲委派机制的设计是为了保证类的唯一性,这意味着在同一个JVM中是不能加载相同类库的不同版本 ...

  3. Python 标准类库-并发执行之multiprocessing-基于进程的并行

    实践环境 Python3.6 介绍 multiprocessing是一个支持使用类似于线程模块的API派生进程的包.该包同时提供本地和远程并发,通过使用子进程而不是线程,有效地避开了全局解释器锁.因此 ...

  4. 一篇讲懂Java运行类型、编译类型和多态(面向对象语言精髓之一)

    对象:运行类型.编译类型和多态 1.搞清楚面向对象的运行类型和编译类型就掌握了对象的精髓,我们用举个例子 class Father { Father() { System.out.println(&q ...

  5. Linux从文件中逐行读取文件名并将匹配的文件复制到指定目录

    问题应该算挺常见的但是一句话还挺难说清楚,所以百度特别难搜. 场景就是,有一堆以员工名称命名的文件(名称可能还有字母数字等前后缀),现在给定一个员工清单,需要从这些文件中筛选出员工清单上列出的员工的文 ...

  6. .NET 7 新特性全面解析

    在 2021 年 11 月 8 日发布的 .NET 6 当前已经广泛使用.微软团队已经开始着手为.NET 7制定计划和新特性.本文将为您全面解析.NET 7 的新特性,并提供源代码示例. 1. 更好的 ...

  7. PostgreSQL 新手入门指引

    自从MySQL被Oracle收购以后,PostgreSQL 逐渐成为开源关系型数据库的首选. 本文介绍PostgreSQL的安装和基本用法,供初次使用者上手.以下内容基于Debian操作系统,其他操作 ...

  8. 【笔试实战】LeetCode题单刷题-编程基础 0 到 1【二】

    1822. 数组元素积的符号 题目链接 1822. 数组元素积的符号 题目描述 已知函数 signFunc(x) 将会根据 x 的正负返回特定值: 如果 x 是正数,返回 1 . 如果 x 是负数,返 ...

  9. Github入门教程(新版)

    GitHub 的介绍与使用 GitHub 注册一个账号 直接在首页注册即可啦 要注意的是 第一项 username 别人是可见的 后面修改也会比较麻烦,所以起个好名字很重要 个人主页介绍 刚注册好的页 ...

  10. 基于 Probe 的实时全局光照方案(Probe-based Global Illumination)

    目录 Precomputed Probe 预放置 probes 四面体镶嵌(Tetrahedral Tessellations) Indirect Light Cache Volumetric Lig ...