javascript基础修炼(10)——VirtualDOM和基本DFS

1. Virtual-DOM是什么
Virtual-DOM,即虚拟DOM树。浏览器在解析文件时,会将html文档转换为document对象,在浏览器环境中运行的脚本文件都可以获取到它,通过操作document对象暴露的接口可以直接操作页面上的DOM节点。但是DOM读写是非常耗性能的,很容易触发不必要的重绘和重排,为了更好地处理DOM操作,Virtual-DOM技术就诞生了。Virtual-DOM就是在javascript中模拟真实DOM的结构,通过数据追踪和状态对比来减少对于真实DOM的操作,以此来提高程序的效率的一种技术。
Virtual-DOM技术是前端高性能的基石,它是真实document对象的抽象,通过对比新旧Virtual-DOM的区别,找出发生变化的DOM节点,再利用算法得到相对更合理的DOM节点修改方案,最终再将方案应用在document对象上来改变页面的展示内容。
主流前端SPA框架都离不开【
Virtual-DOM模型 +DOM-Diff算法 + 生命周期钩子】这样的核心模型。
2. Virtual-DOM的基本结构
在上一篇博文《javascript基础修炼(9)——MVVM中双向数据绑定的基本原理》中,我们通过document.getElementById()从真实DOM中获得了带有自定义属性的待解析结构,这里是有一些问题的,实际的过程是先解析模板字符串得到虚拟DOM树,最后生成真实的DOM树。
实际上我们在使用SPA框架时所编写的html模板,并没有被直接当做DOM片段加载到页面上使用,而是将文件当做字符串读入到程序中,然后通过解析来生成Virtual-DOM树,接着通过SPA框架的渲染函数来生成必要的片段后才生成真实的DOM节点。例如我们要生成下文示例的HTML片段(为了方便演示,示例中只涉及了类名和文本节点):
<body class="main">
<div class="sideBar">
<ul class="sideBarContainer">
<li class="sideBarItem">sidebar-1</li>
<li class="sideBarItem">sidebar-2</li>
<li class="sideBarItem">sidebar-3</li>
</ul>
</div>
<div class="mainContent">
<div class="header">header-zone</div>
<div class="coreContent">core-content</div>
<div class="footer">footer-zone</div>
</div>
<div class="rightSide">暂未开发</div>
</body>
我们需要构建出一个简易模型来表达上面的结构:
virtualDom = {
name:"body",
props:{
className:"main"
},
children:[{
name:"div",
props:{...},
children:[...]
},{
name:"div",
props:{...},
children:[...]
},{
name:"div",
props:{...},
children:[...]
}]
}
建立一个生成虚拟节点的辅助函数:
//构建DOM节点的辅助函数
function h(name, props, children) {
return {
name:name,
props:props,
children:children
}
}
//手动生成virtual-DOM
var tree = h('body',{className:'main'},[
h('div',{className:'sideBar'},[
h('ul',{className:'sideBarContainer'},[
h('li',{className:'sideBarItem'},['sidebar-1']),
h('li',{className:'sideBarItem'},['sidebar-2']),
h('li',{className:'sideBarItem'},['sidebar-3']),
])
]),
h('div',{className:'mainContent'},[
h('div',{className:'header'},['header-zone']),
h('div',{className:'coreContent'},['core-content']),
h('div',{className:'footer'},['footer-zone']),
]),
h('div',{className:'rightSide'},['暂未开发'])
]);
通过上面的方法得到的tree对象就涵盖了模板片段中的结构和关键信息。实际开发中并不需要像上面一样手动来填写DOM结构,可以将模板字符串挂载到离线DOM节点上,然后在递归解析的同时来构建Virtual-DOM就可以了。
3. 使用DFS从Virtual-DOM生成DOM
至此我们完成了模板的编译,也得到了Virtual-DOM对象,但它似乎并没有什么用处,毕竟我们已经完成了对模板的解析,渲染出页面没什么问题,其实Virtual-DOM对于首屏来说并没有什么特别重要的意义,它的价值在模型和视图发生变化时才会体现。上一篇博文的末尾我们已经提到了更新视图时的效率问题,当数据模型发生变化后,我们需要一个方法来收集所有需要修改的DOM,并为之提供高效的修改方式(你总不能一有变化就把整个网页重新渲染,或者让数据模型各自去修改各自绑定的DOM吧)。那么为了能够收集所有DOM节点的变化,我们就需要遍历所有节点。
对数据结构和算法有一定了解的读者很容易想到,遍历解析一个Virtual-DOM实际上就是对其进行先序深度优先遍历(Pre-Order Depth-First-Search),本节中,我们先预热一下,使用这种方式来复现一下DOM结构。
function dfswalking(tree) {
var _childrenLength;
//执行动作
if (typeof tree.children[0] === 'string') {
console.log(`<${tree.name} class="${tree.props.className}">${tree.children[0]}</${tree.name}>`);
} else {
console.log(`<${tree.name} class="${tree.props.className}"> -->`);
for(var i = 0, _childrenLength = tree.children.length; i < _childrenLength; i++){
dfswalking(tree.children[i]);
}
}
}
本例中仅打印出字符串的方式来展示,可以在控制台看到输出结果:

下一篇博文中将分析如何通过domDiff(oldTree, newTree)的方法通过同样的遍历方法来收集变化并批量更新视图。
4. 声明
本篇只是部分原理的学习笔记,并不代表框架真实源码的实现逻辑。
javascript基础修炼(10)——VirtualDOM和基本DFS的更多相关文章
- javascript基础修炼(11)——DOM-DIFF的实现
目录 一. 再谈从Virtual-Dom生成真实DOM 二. DOM-Diff的目的 三. DOM-Diff的基本算法描述 四. DOM-Diff的简单实现 4.1 期望效果 4.2 DOM-Diff ...
- javascript基础修炼(8)——指向FP世界的箭头函数
一. 箭头函数 箭头函数是ES6语法中加入的新特性,而它也是许多开发者对ES6仅有的了解,每当面试里被问到关于"ES6里添加了哪些新特性?"这种问题的时候,几乎总是会拿箭头函数来应 ...
- javascript基础修炼(2)——What's this(上)
目录 一.this是什么 二.近距离看this 三. this的一般指向规则 四. 基本规则示例 五. 后记 开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一.thi ...
- javascript基础修炼(4)——UMD规范的代码推演
javascript基础修炼(4)--UMD规范的代码推演 1. UMD规范 地址:https://github.com/umdjs/umd UMD规范,就是所有规范里长得最丑的那个,没有之一!!!它 ...
- javascript基础修炼(7)——Promise,异步,可靠性
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 别人是开发者,你也是 Promise技术是[javascript异步编程]这个话题中非常重要的,它一度让我感到熟悉 ...
- JavaScript基础入门10
目录 JavaScript 基础入门10 正则表达式 为什么使用正则表达式? 正则表达式的应用场景 如何创建一个正则表达式 基础语法 具有特殊意义的转义字符 量词 字符类 贪婪模式 练习 邮箱验证 中 ...
- javascript基础修炼(9)——MVVM中双向数据绑定的基本原理
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 概述 1.1 MVVM模型 MVVM模型是前端单页面应用中非常重要的模型之一,也是Single Page Appl ...
- javascript基础修炼(1)——一道十面埋伏的原型链面试题
在基础面前,一切技巧都是浮云. 题目是这样的 要求写出控制台的输出. function Parent() { this.a = 1; this.b = [1, 2, this.a]; this.c = ...
- javascript基础修炼(3)—What's this(下)
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 这一期主要分析各种实际开发中各种复杂的this指向问题. 一. 严格模式 严格模式是ES5中添加的javascript的 ...
随机推荐
- 4.23 Linux(3)
2019-4-23 19:03:53 买的服务器第三天感觉超爽!! 发现学习Linux超爽,有种操作的快感!!!!!是Windows比不了的!! 阿里巴巴镜像源 : https://opsx.alib ...
- python print 中文重定向失败
一直以来认为解决python字符集编码,不一定需要通过sys.setdefaultencoding.因为既然python实现过程中,默认禁用了该操作,说明是不推荐的. 通过不断的字符转换,也cover ...
- Restful levels&HATEOAS
RESTful: Rest是一种软件架构风格.设计风格,而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等 ...
- SEED实验——Environment Variable and Set-UID Program实验报告
任务一:操作环境变量 实验过程一: 用printenv或env打印出环境变量. 在终端输入命令,显示结果如下图所示: 经过实验发现,printenv和env均可输出当前系统的环境变量.不同的是prin ...
- ORACLE 查询近一天,近半小时内的数据
SELECT 字段 FROM 表名 WHERE 时间字段 BETWEEN SYSDATE-1 AND SYSDATE; //查询一天内的数据 sysdate+1 加一天sysdate+1/24 ...
- JDK 1.8判断集合种的元素是否存在相同
List<String> str=new ArrayList<>(); str.add("a"); str.add("a"); str. ...
- Retrofit 实现获取往里圆角图片,且传值到另一个页面
记得加网络权限 java包: // compile 'jp.wasabeef:glide-transformations:3.0.1' implementation 'com.squareup.ret ...
- 【转载】C++ ,C#数据类型对照
C++ C#=====================================WORD ushortDWORD uintUCH ...
- 分享我在 vue 项目中关于 api 请求的一些实现及项目框架
本文主要简单分享以下四点 如何使用 axios 如何隔离配置 如何模拟数据 分享自己的项目框架 本文主要目的为以下三点 希望能够帮到一些人 希望能够得到一些建议 奉上一个使用Vue的模板框架 我只是把 ...
- 深入分析volatile的实现原理
synchronized是一个重量级的锁,虽然JVM对它做了很多优化,而下面介绍的volatile则是轻量级的synchronized.如果一个变量使用volatile,则它比使用synchroniz ...