原文链接:http://reactkungfu.com/2015/10/the-difference-between-virtual-dom-and-dom/
DOM
Just to get things straight - DOM stands for Document Object Model and is an abstraction of a structured text. For web developers, this text is an HTML code, and the DOM is simply called HTML DOM. Elements of HTML become nodes in the DOM.
So, while HTML is a text, the DOM is an in-memory representation of this text.
/* HTML是一个文本,DOM是这个文本在内存表现形式。*/
Compare it to a process being an instance of a program. You can have multiple processes of the same one program, just like you can have multiple DOMs of the same HTML (e.g. the same page loaded on many tabs).
The HTML DOM provides an interface (API) to traverse and modify the nodes. It contains methods like getElementById
or removeChild
. We usually use JavaScript language to work with the DOM, because… Well, nobody knows why :).
/* HTML DOM提供了一些接口来遍历和修改节点,其中包含getElementById或removeChild等方法。 但是我们通常使用JavaScript语言来处理DOM。*/
So, whenever we want to dynamicly change the content of the web page, we modify the DOM:
var item = document.getElementById("myLI");
item.parentNode.removeChild(item);
document
is an abstraction of the root node, while getElementById
, parentNode
and removeChild
are methods from HTML DOM API.
/* document
是对于根节点的一个抽象表示,而getElementById
, parentNode
and removeChild
是HTML DOM API的方法。 */
Issues
The HTML DOM is always tree-structured - which is allowed by the structure of HTML document. This is cool because we can traverse trees fairly easily. Unfortunately, easily doesn’t mean quickly here.
/* HTML DOM是树形结构。 这使得我们很容易地对树形结构进行遍历。 但是,这里的容易遍历并不意味着效率也同样很高。 */
The DOM trees are huge nowadays. Since we are more and more pushed towards dynamic web apps (Single Page Applications - SPAs), we need to modify the DOM tree incessantly and a lot. And this is a real performance and development pain.
/* 在当今DOM树十分巨大。随着动态web程序发展,我们需要不断地大量地修改DOM树。 这是性能和开发工作的痛点。 */
BTW. I myself managed to create a web page with a source of 5GB+. It wasn’t even that hard.
Consider a DOM made of thousands of div
s. Remember, we are modern web developers, our app is very SPA! We have lots of methods that handle events - clicks, submits, type-ins… A typical jQuery
-like event handler looks like this:
- find every node interested on an event
- update it if necessary
/* 上述是现代的网页开发人员经常遇到的两种事件处理程序。1.查找对应事件的节点。2.如果有必要,更新该节点。*/
Which has two problems:
- It’s hard to manage. Imagine that you have to tweak an event handler. If you lost the context, you have to dive really deep into the code to even know what’s going on. Both time-consuming and bug-risky.
- It’s inefficient. Do we really need to do all this findings manually? Maybe we can be smarter and tell in advance which nodes are to-be-updated?
/* 这带来两个问题,1.管理难度大。假如你必须处理某事件,当你失去了上下文,就必须深入到代码中,才能知道发生了什么。既耗时又有bug风险。2.开发效率低。我们是否真的需要手动完成所有这些工作?也许我们可以更聪明,提前告知哪些节点需要更新? */
Once again, React comes with a helping hand. The solution to problem 1 is declarativeness. Instead of low-level techniques like traversing the DOM tree manually, you simple declare how a component should look like. React does the low-level job for you - the HTML DOM API methods are called under the hood. React doesn’t want you to worry about it - eventually, the component will look like it should.
/* React对于问题1的解决方案是这样的,你不需要使用手动遍历DOM树这样的底层技术,而是简单地通过声明组件的外观。 React为你做了底层的工作。 */
But this doesn’t solve the performance issue. And this is exactly where the Virtual DOM comes into action.
/* 但是通过声明组件这并不能解决性能问题。性能问题是下面虚拟DOM完成的工作。 */
Virtual DOM
First of all - the Virtual DOM was not invented by React, but React uses it and provides it for free.
The Virtual DOM is an abstraction of the HTML DOM. It is lightweight and detached from the browser-specific implementation details. Since the DOM itself was already an abstraction, the virtual DOM is, in fact, an abstraction of an abstraction.
/* 虚拟DOM是对HTML DOM的抽象。它是轻量级的,并从浏览器特定的实现细节中分离出来。 由于DOM本身已经是一个抽象,所以虚拟DOM实际上是抽象的抽象。 */
Perhaps it’s better to think of the virtual DOM as React’s local and simplified copy of the HTML DOM. It allows React to do its computations within this abstract world and skip the “real” DOM operations, often slow and browser-specific.
/* 虚拟DOM可以看作是HTML DOM的React本地化和简化副本。 React可以在这个抽象的世界中执行它的计算,跳过“真正的”通常是缓慢的和特定于浏览器的DOM操作。 */
There’s no big difference between the “regular” DOM and the virtual DOM. This is why the JSX parts of the React code can look almost like pure HTML:
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
In most cases, when you have an HTML code and you want to make it a static React component, all you have to do is:
- Return the HTML code in
render
- Replace
class
attribute name toclassName
- becauseclass
is a reserved word in JavaScript.
/* 你有一个HTML代码,你想使它成为一个静态的React组件时,可以进行上述两步:1.在render
中返回HTML代码。2.将class属性名称替换为className。 */
There are more, rather minor, differences between the DOMs:
- Three attributes of the virtual DOM that don’t appear in “real” DOM -
key
,ref
anddangerouslySetInnerHTML
. See more. - React-ish virtual DOM introduced a few more constraints.
ReactElement vs ReactComponent
When talking about the virtual DOM, it’s important to see the difference between these two.
ReactElement
This is the primary type in React. React docs say:
A
ReactElement
is a light, stateless, immutable, virtual representation of a DOM Element.
/* ReactElement是React中的主要类型。文档中说:ReactElement是一个的轻量的,无状态的,不可变的,DOM元素的虚拟表示。 */
ReactElements lives in the virtual DOM. They make the basic nodes here. Their immutability makes them easy and fast to compare and update. This is the reason of great React performance.
/* ReactElements存在于虚拟DOM中。它们是基本的节点。他们的不变性使他们可以方便快捷地进行比较和更新。这是React性能强大的原因。 */
What can be a ReactElement
? Almost every HTML tag - div
, table
, strong
… If you wish, see the whole list.
Once defined, ReactElements can be render into the “real” DOM. This is the moment when React ceases to control the elements. They become slow, boring DOM nodes:
/* 一旦定义,ReactElements可以渲染到“真实”的DOM。在此时刻,React停止控制元素。它们被转化成DOM节点。 */
var root = React.createElement('div');
ReactDOM.render(root, document.getElementById('example'));
// If you are surprised by the fact that `render`
// comes from `ReactDOM` package, see the Post Scriptum.
JSX compiles HTML tags to ReactElements. So this is equivalent to the above:
var root = <div />;
ReactDOM.render(root, document.getElementById('example'));
Once again - ReactElements are the basic items in React-ish virtual DOM. However, they are stateless, therefore don’t seem to be very helpful for us, the programmers. We would rather work on the class-like pieces of HTML, with kind-of-variables and kind-of-constants - don’t we? And here we come to…
/* ReactElements是React-ish虚拟DOM中的基本项目。但是,他们是无状态的,所以对程序员来说,我们似乎不是很有帮助。我们宁愿使用类似变量和常量来处理类的HTML片段。 */
ReactComponent
What differs ReactComponent
from ReactElement
is - ReactComponents are stateful.
/* ReactComponent与ReactElement不同之处在于 - ReactComponents是有状态的。 */
We usually use React.createClass
method to define one:
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
Your HTML-like blocks returned from render
method can have a state. And the best thing is (I bet you already know it, this is why React is so cool!) - whenever the state changes, the component is rerendered:
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
ReactComponents turned out to be a great tool for designing dynamic HTML. They don’t have the access to the virtual DOM, but they can be easily converted to ReactElements:
/* ReactComponents是设计动态HTML的好工具。 他们没有虚拟DOM的访问权限,但可以很容易地将其转换为ReactElements。 */
var element = React.createElement(MyComponent);
// or equivalently, with JSX
var element = <MyComponent />;
What makes the difference?
ReactComponents are great, we would love to have plenty of them since they are easy to manage. But they have no access to the virtual DOM - and we would like to do as much as possible there.
/* ReactComponents很容易管理,但无法访问虚拟DOM。 */
Whenever a ReactComponent
is changing the state, we want to make as little changes to the “real” DOM as possible. So this is how React deals with it. The ReactComponent
is converted to the ReactElement
. Now the ReactElement
can be inserted to the virtual DOM, compared and updated fast and easily. How exactly - well, that’s the job of the diff algorithm. The point is - it’s done faster than it would be in the “regular” DOM.
/* 每当ReactComponent组件改变状态时,我们希望尽可能少地改变“真实”的DOM。 这里就是React所做的事: ReactComponent转换为ReactElement。 当ReactElement被插入到虚拟DOM中时,可以简单快速地比较和更新。 这是由差异算法来实现的。 重点是 - 它比“常规”DOM的速度要快。 */
When React knows the diff - it’s converted to the low-level (HTML DOM) code, which is executed in the DOM. This code is optimised per browser.
/* 当React监听到DOM的差异 - ReactElement会被转换成根据浏览器进行优化的在DOM中执行的代码。 */
Summary
Is virtual DOM really the feature to boast on the main page? I would say so. In practise, React performance is absolutely high, and the virtual DOM is surely very helpful here.
/* 在实际运行中,React性能绝对高 */
PS. In case you didn’t notice - since the last week, when React 0.14 was released, the DOM-related parts of React were extracted from the react
package. Now there is a separate package react-dom
. You can read more in the newest changelog.