一、babel是提供对react jsx语法的支持
我们可以像以前一样脚本中引入babel的js包,实现浏览器对这种jsx的支持,默认的babel碰到jsx这种语法时候,会调用React里的API进行处理,下边示例中,我们没有引入React,则会报错。
这也是为什么 我们使用import导入react包的时候,名字必须为React。
2、稍微做一些改动,我们可以自定义对jsx的渲染,指令是:/** @jsx hyperScript **/,定义一个自己的jsx渲染器,其中hyperScript 表示方法名字。告诉babel在解析碰到jsx时候该如何做?
下面,我们书写这个方法。在jsx中,我们认为每一个虚拟DOM其实就是一个json文件。例如<div id="name"></div>,就会等价于
{ nodeName :"div" , attrbutes : { id : "name"} , children : [] }。下面我们的hyperScript就会长这样
function hyperScript(nodeName , attributes , ...args){
//返回虚拟DOM, 虚拟DOM结构,用[].concat(...args) 连接所有子节点,返回构建完成的JSON。
let children = args.length ? [].concat(...args) : [];
return {nodeName,attributes,children }
}
从下图中可以看出,引入script标签时候的type指定为babel,会在浏览器启动页面时候转换为内置的babel代码
jsx在解析后,参数如下图黑线标记的
3、显然react使用jsx写完的组件,最终被解析为了虚拟的json对象树,如下图:
我们只要把这个虚拟树渲染到真实的dom下就可以了,需要一个render函数,也就是ReactDOM.render函数的功能,这里我们自己定义一个,如下:
本质上就是将虚拟DOM映射成 DOM节点
function render( vnode ){
//如果是文本,直接返回
if(vnode.split){
return document.createTextNode( vnode )
}
//是一个虚拟DOM
let node = document.createElement( vnode.nodeName );
//get属性
let attrs = vnode.attrbutes || {} ;
//给节点加上属性
Object.keys( attrs ).forEach( item => node.setAttribute( item , attrs[item] ) );
//递归处理子元素,children
( vnode.children || [] ).forEach( item => node.appendChild( render(item) ) );
//返回node
return node;
}
4、将返回的真实的dom对象,使用jquery放置到对应的div下即可
二、附上html源代码测试
https://github.com/longyangzz/react-jsx-virtualDom
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="../build/babel.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<div id="example"> 1234556 </div>
<script type="text/babel">
/** @jsx hyperScript **/
const my = <div id="name">Hello, world!</div>
// ReactDOM.render(
// my,
// document.getElementById('example')
// );
var realNode = render(my)
//! 放到页面上的div下,作为虚拟dom的root节点
var oDivs = document.getElementById("example");
$("#example").html(realNode);
function hyperScript(nodeName , attributes , ...args){
let children = args.length ? [].concat(...args) : [];
return {nodeName,attributes,children }
}
function render( vnode ){
//如果是文本,直接返回
if(vnode.split){
return document.createTextNode( vnode )
}
//是一个虚拟DOM
let node = document.createElement( vnode.nodeName );
//get属性
let attrs = vnode.attrbutes || {} ;
//给节点加上属性
Object.keys( attrs ).forEach( item => node.setAttribute( item , attrs[item] ) );
//递归处理子元素,children
( vnode.children || [] ).forEach( item => node.appendChild( render(item) ) );
//返回node
return node;
}
</script>
</body>
</html>