前言
一直想写点关于 Virtual Dom 的东西。以前刚开始接触 react 的时候就已经接触到了 Virtual Dom 和 diff 算法的概念,但一直觉得这些都是很复杂,很高深的东西。后面使用 react,使用 Vue 多了,逐渐觉得这些概念没那么陌生了。但还是觉得各种开源的 Virtual Dom,React 等对 Virtual Dom 的实现的源码不太容易读懂,源码的注释不全是一大障碍,直到遇到了 snabbdom,才开始觉得有深究其源码的冲动。
为什么是 snabbdom?
因为 snabbdom 是用 typescript 写的,如果有了解过 typescript 的话就会知道,各种类型、参数、接口、函数和类的定义都非常严谨,如果是按照 typescript 的规范来写,加上命名规范的话,根本不需要写注释就可以让人轻松读懂代码。微软发明的 typescript 填补了 javascript 这种弱语言的各种不规范的缺点,让人可以像强语言一样来写 JS 真的很了不起。Snabbdom 另外一个特点就是代码组织、模块化非常清晰。让人更容易上手。还有,如果有看过 Vue 源码 的话就会发现,新版的 Vue 使用的 Virtual Dom 就源于 snabbdom。
为什么需要 Virtual Dom?
我们先来看看来自一个比较早的几乎与 react 同时实现 virtual dom开源库: https://github.com/Matt-Esch/virtual-dom 对virtual dom 介绍。
virtual-dom
A JavaScript DOM model supporting element creation, diff computation and patch operations for efficient re-renderingMotivation
Manual DOM manipulation is messy and keeping track of the previous DOM state is hard. A solution to this problem is to write your code as if you were recreating the entire DOM whenever state changes. Of course, if you actually recreated the entire DOM every time your application state changed, your app would be very slow and your input fields would lose focus.virtual-dom is a collection of modules designed to provide a declarative way of representing the DOM for your app. So instead of updating the DOM when your application state changes, you simply create a virtual tree or VTree, which looks like the DOM state that you want. virtual-dom will then figure out how to make the DOM look like this efficiently without recreating all of the DOM nodes.
virtual-dom allows you to update a view whenever state changes by creating a full VTree of the view and then patching the DOM efficiently to look exactly as you described it. This results in keeping manual DOM manipulation and previous state tracking out of your application code, promoting clean and maintainable rendering logic for web applications.
其大意,什么是 Virtual Dom 呢?Virtual Dom 就是一个 Dom 的模型或者我们习惯叫的虚拟 Dom,支持虚拟 Dom 元素创建,到虚拟 Dom 元素比较变化计算,到把变化应用到真实 Dom 元素,最后实现 Dom 的重新高效渲染。动机是什么呢?能够解决 Dom 的操控难、前后变化难以跟踪、Dom 的高效局部更新问题和其实还有两个上面没提到的痛点被解决了,就是可以支持 server side 渲染及支持 Native 端渲染(react-native,weex 和小程序的实现依赖于 Virtual Dom)。
Virtual Dom 主要涉及的概念
- VNode(virtual node / virtual tree)--虚拟 DOM。
- H(hyperscript)--最原始的定义是 “Create HyperText with JavaScript.”,在snabbdom 里面,h 函数是生成虚拟 DOM 的函数。而不是生成 HyperText 超文本或者实实在在的 Dom节点。
- Patch--使用 diff 算法来比较旧 VNode 及新的 VNode 之间的差异然后执行 Patch Operation 或者叫 Patch 函数来高效更新 Dom 节点。
- htmlDomApi--一些操作 Dom 函数的工具集合,在 Patch 函数里面会大量用到。
- Thunk--在 react 全家桶中经常会看到 Thunk 这概念,在计算机语言中是传名调用的意思,但如果按我个人的理解就是中间件的概念,更通俗一点就是在某函数的每次输入与输出之间,预加一个自定义的拦截处理函数(thunk 函数),先对输入的参数进行预处理再作为输入,这样当然输出的结果就依赖于 thunk 函数了。react-thunk 中间件的作用也类似,让 redux 可以支持 dispatch 一个异步函数(不使用的话,只能支持dispatch 同步的 action)。而在 snabbdom 里,thunk 主要用于优化性能,在每次 patch 之前先拦截一下判断 VNode 状态数据有没有变化,如果没有变化就不需要执行 patch 操作了。
- Hooks--一些勾子函数,可以绑定到 VNode 里面的整个生命周期里面,使得支持类似 Vue 这样的框架的生命周期之间相应关联。