一.首先我们要先了解以下两个概念
1.JSX
它是JavaScript的语法扩展,底层还是JavaScript,JavaScript有的功能它全有。
JSX生产的是React对象,可以将表达式放在花括号里,然后嵌入到JSX中,也就是说JSX能认识HTML标签,在编译之后(babel编译器)就会编程常规的JavaScript对象,也就是本文要说的虚拟DOM转换成真实DOM。
既然说是写在花括号里,那么if语句和for循环里都能写JSX语句,render() 方法就是React的核心,将虚拟DOM转化为真实DOM
import React, { Component } from 'react';
import ReactDom from 'react-dom';
class App extends Component {
render() {
return (
<div className="App">
<h2 id="vDom" style={{ color: "#f40" }}>虚拟DOM</h2>
<div dangerouslySetInnerHTML={{ __html: content }}></div>
</div>
)
}
}
export default App;
2.react-diff算法
大致就是传两个参数,一个是真实的DOM一个是虚拟DOM,比较两者的元素的类型、属性等,发现有不一样就砍了重新加上新的,使得两边一样。
二.虚拟DOM
1.概念
相当于就是JavaScript语句和页面上的DOM元素之间加了一个缓存,将真实的DOM转化为json对象,利用diff算法避免了没必要的操作,从而提高了效率,换句话就是真实的DOM我们获取它时,我们会把我们不需要用到的DOM元素属性都统统获取到,效率很低,利用虚拟DOM就可以避免。
2.原理
一个diff函数有两个参数一个真实DOM一个虚拟DOM,使用递归对组件的文本节点、非文本的DOM节点、属性作比较,如果相同则不更新,如果不同则更新。
对比子节点时,子节点时一个数组,他们可能改变顺序(排序)或者数量发生变化(增删),我们很难确定要和虚拟DOM哪一个做比较,所以我们要给它们设置key,如果有key使用key查找子节点(高性能),如果没key按照DOM类型查找(低性能),
3.虚拟DOM的实现
我们一步一步看:
(1)首先我们需要引入babel(v6版本)
>网页也可以引入,点击这里去复制黏贴一下<
<script src="./js/babel.min.js"></script>
(2)我们在HTML里写我们的JS代码,插入script标签,type是text/babel
<script src="./js/babel.min.js"></script>
<script type="text/babel">
</script>
代码就写在这里面
(3)我们就可以写JSX语句了
let vDOM = (<div id="app" name="app">
hello world!
<ul className= "ul">
<li>虚拟DOM</li>
</ul>
<div>
<p style={{color: "#f40"}}>学习虚拟DOM</p>
</div>
</div>);
我们在末尾console.log(vDOM);查看一下
会报错,说React找不到,当然找不到,我们是自己写的东西,又没有引入任何React的东西,所以需要重写方法。
(4)重写createElement
/*@jsx createElement*/
注:写在标签的顶部,@jsx是babel的自执行指令
/*@jsx createElement*/
let vDOM = (<div id="app" name="app">
hello world!
<ul className="ul">
<li>虚拟DOM</li>
</ul>
<div>
<p style={{ color: "#f40" }}>学习虚拟DOM</p>
</div>
</div>);
function createElement(nodeName, attr, ...args) {
return {
nodeName: nodeName,
attr: attr,
children: [].concat(...args)
}
}
console.log(vDOM);
再次控制台打印,就变成了json格式的虚拟DOM,包括nodeName、attr、children
(5)将虚拟DOM转化为真实DOM在页面中显示,就是自己写一render()方法
需要考虑是否是文本节点
function render(vnode) {
//vnode是纯文本,创建文本节点
if (vnode.split) {
return document.createTextNode(vnode);
}
//不是文本
let node = document.createElement(vnode.nodeName); //节点
let attr = vnode.attr || []; // 属性
Object.keys(attr).forEach((k) => {
node.setAttribute(k, attr[k]);
});
(vnode.children || []).forEach((n) => {
node.appendChild(render(n));//递归
});
return node;
}
let dom = render(vDOM);//将虚拟dom转化成真实dom的方法render()
document.body.appendChild(dom);
最后我们就可以在页面上看到显示