React上手(一):JSX与VirtualDOM
时间:2023-04-05 14:30:55
HTML5
前言React的热度已经达到94.5k启动。本系列文章主要通过简单的代码实现一个react来理解JSX、虚拟DOM、diff算法以及state和setState的设计。说到react,当然少不了vue。vue的api设计非常简单好用,但是黑魔法很多,用起来有点空洞。React没有太多的api。它的深度体现在设计理念上。使用react开发是让人更踏实,更容易管理,这也是我喜欢react的原因之一。JSX写入反应较少。JSX,什么是JSX,我看一个例子。现在有如下代码:constel=HelloJavascript这样的js代码如果不经过处理就会报错,jsx是语法糖,它使得这段代码合法,经过babel改造后是这样的:constel=React.createElement('h3',{className:'title'},'HelloJavascript')this官网首页也有demo。在开始编码之前,首先介绍两个东西:parcel和babel-plugin-transform-jsx。稍后,我们将使用parcel构建一个开发项目。babel-plugin-transform-jsx是Babel的插件,它将jsx语法转换为React.createElement(...)。接下来,让我们开始简单地构建parcel。这里就不介绍了。一句话概括就是为你生成一个零配置的开发环境。yarnglobaladdparcel-bundler或npminstall-gparcel-bundler新建一个项目文件夹,这里命名为simple-react,在simple-react中执行yarninit-y或npminit-y生成package.json并创建索引.html创建src文件夹,然后在src下创建index.js,然后将index.js导入index.html。如果嫌麻烦,可以直接下载源码修改。以上步骤可能不完整,最好以包裹内的内容为准。以上工作完成后,我们需要安装babel-plugin-transform-jsx:npminsatllbabel-plugin-transform-jsx--save-dev或者yarnaddbabel-plugin-transform-jsx--dev然后添加.babelrc文件,并在文件中添加以下代码:{"presets":["env"],"plugins":[["transform-jsx",{"function":"React.createElement"}]]}上面代码的意思是使用transform-jsx插件,通过React.createElement方法配置解析JSX。当然你也可以使用React.createElement和自定义方法,比如preact使用的h方法。React.createElement()现在我们开始在index.js中编码。先写代码:constel=HelloJavascript;控制台日志(EL);我们不写任何东西就打印出来看看el是什么。打印错误:React未定义。这是因为在.babelrc文件中,我们使用的代码有效:["transform-jsx",{"function":"React.createElement"}]如上所述,它会将React.createElement方法传递给TranslateJSX,然后我们给这个方法:让我们改变一下刚才的代码:constReact={createElement:function(...args){returnargs[0];}};constel=HelloJavascript;console.log(el);上面的代码添加了一个React对象,并为其添加了createElement方法,现在再执行一下,看看有什么打印:从打印结果可以看出,当jsx使用React.createElement方法进行翻译时,createElement方法应该是这样的:createElement({elementName,attributes,children});elementName:dom对象的标签名称,如div、span等attributes:当前dom对象的属性集合,如class、id等children:所有子节点现在我们重写createElement方法,使键名更简单:constReact={createElement:function({elementName,attributes,children}){return{tag:elementName,attrs:attributes,children};}};现在你可以看到打印结果是:让我们打印一个更复杂的DOM结构:constel=(HelloJavaScript
);console.log(el);与我们想要的结构相同。其实上面打印的是虚拟DOM。现在我们要做的就是如何将虚拟DOM转化为真实的DOM对象,并显示在浏览器上。ReactDOM.render()需要调用ReactDOM.render才能将虚拟dom转换为真实dom并渲染到页面上,例如:ReactDOM.render(
HelloWorld
,document.getElementById('根'));转换后的代码如下所示:ReactDOM.render(React.createElement('h1',null,'HelloWorld'),document.getElementById('root'));这时候react会将
HelloWorld
挂载到id为root的dom下,这样就可以显示在页面上了。现在我们实现渲染方法:functionrender(vnode,container){constdom=createDom(vnode);//将vnode转化为真实DOMcontainer.appendChild(dom);}上面代码中,首先调用createDom将虚拟dom转化为真实DOM,然后挂载到容器下。让我们实现createDom方法:functioncreateDom(vnode){if(vnode===undefined||vnode===null||typeofvnode==='boolean'){vnode='';}if(typeofvnode==='string'||typeofvnode==='number'){returndocument.createTextNode(String(vnode));}constdom=document.createElement(vnode.tag);//设置属性if(vnode.attrs){for(letkeyinvnode.attrs){constvalue=vnode.attrs[key];setAttribute(dom,key,value);}}//递归渲染子节点vnode.children.forEach(child=>render(child,dom));returndom;}因为属性的种类很多,我们抽取一个setAttribute方法来设置属性:functionsetAttribute(dom,key,value){//classNameif(key==='className'){dom.setAttribute('类',值);//event}elseif(/on\w+/.test(key)){key=key.toLowerCase();dom[键]=值||'';//样式}elseif(key==='style'){if(typeofvalue==='string'){dom.style.cssText=value||'';}elseif(typeofvalue==='object'){//{宽度:'',height:20}for(letnameinvalue){//如果是数字,px可以忽略dom.style[name]=typeofvalue[name]==='number'?值[名称]+'px':值[名称];}}//其他}else{dom.setAttribute(key,value);现在render方法已经完全实现,我们将创建一个ReactDOM对象并将render方法挂在上面:constReactDOM={render:function(vnode,container){container.innerHTML='';渲染(vnode,容器);}};这里在调用render之前加了一句container.innerHTML='',就不解释了,相信大家明白了,万事俱备,我们来测试一下,直接加一个比较复杂的dom结构,加属性:const元素=(
alert(1)}style={{color:'red',fontSize:30}}>你好javascript! );ReactDOM.render(element,document.getElementById('root'));打开页面,就是我们想要的结果:看控制台的dom:完美,这就是我们想要的后记,demo代码在这里~~本文介绍了JSX和virtualDOM,以及转换的过程virtualDOM转化为realDOM,后面的文章会继续介绍react中的组件、生命周期、diff算法和异步setState,敬请期待~