亲手撸一个React(一):JSX与虚拟DOM
前言react的火热程度已经达到了94.5k个start,本系列文章主要用简单的代码来实现一个react,来了解JSX、虚拟DOM、diff算法以及state和setState的设计。 提到react,当然少不了vue,vue的api设计十分简单 上手也非常容易,但黑魔法很多,使用起来有点虚, 而react没有过多的api,它的深度体现在设计思想上,使用react开发则让人比较踏实、能拿捏的住,这也是我喜欢react的原因之一。 JSX写 const el = <h3 className="title">Hello Javascript</h3> 这样的js代码如果不经过处理会报错,jsx是语法糖,它让这段代码合法化,通过babel转化后是这样的: const el = React.createElement( 'h3',{ className: 'title' },'Hello Javascript' ) 这种例子官网首页也有demo 准备开始开始编码之前,先介绍两个东西: 下面我们开始 简单的搭建parcel这里就不介绍了,一句话概况就是为你生成一个零配置的开发环境。
如果你先麻烦,可以直接下载源码修改。 以上步骤完可能不完整,最好参考parcel里的内容。以上工作完成后,我们需要安装 npm insatll babel-plugin-transform-jsx --save-dev 或者 yarn add babel-plugin-transform-jsx --dev 然后添加 { "presets": ["env"],"plugins": [["transform-jsx",{ "function": "React.createElement" }]] } 上面代码的意思是 使用 React.createElement()现在我们在 const el = <h3 className="title">Hello Javascript</h3>; console.log(el); 我们在什么都不写的情况下,打印看看 打印报错:React没有定义。 这是因为在 ["transform-jsx",{ "function": "React.createElement" }] 上面说过,它会通过 const React = { createElement: function(...args) { return args[0]; } }; const el = <h3 className="title">Hello Javascript</h3>; console.log(el); 上面代码添加了一个 由打印结果可以看出, createElement({ elementName,attributes,children });
现在我们改写一下 const React = { createElement: function({ elementName,children }) { return { tag: elementName,attrs: attributes,children }; } }; 现在可以看到打印结果是: 我们再打印个复杂点的DOM结构: const el = ( <div style="color:red;"> Hello <span className="title">JavaScript</span> </div> ); console.log(el);
和我们想要的结构一样。 ReactDOM.render()要想将虚拟dom转成真实dom并渲染到页面上,就需要调用 ReactDOM.render(<h1>Hello World</h1>,document.getElementById('root')); 这段代码转换后的样子: ReactDOM.render( React.createElement('h1',null,'Hello World'),document.getElementById('root') ); 这时,react会将 现在我们实现 function render(vnode,container) { const dom = createDom(vnode); //将vnode转成真实DOM container.appendChild(dom); } 上面代码中先调用 我们来实现 function createDom(vnode) { if (vnode === undefined || vnode === null || typeof vnode === 'boolean') { vnode = ''; } if (typeof vnode === 'string' || typeof vnode === 'number') { return document.createTextNode(String(vnode)); } const dom = document.createElement(vnode.tag); //设置属性 if (vnode.attrs) { for (let key in vnode.attrs) { const value = vnode.attrs[key]; setAttribute(dom,key,value); } } //递归render子节点 vnode.children.forEach(child => render(child,dom)); return dom; } 由于属性的种类比较多,我们抽出一个setAttribute方法来设置属性: function setAttribute(dom,value) { //className if (key === 'className') { dom.setAttribute('class',value); //事件 } else if (/onw+/.test(key)) { key = key.toLowerCase(); dom[key] = value || ''; //style } else if (key === 'style') { if (typeof value === 'string') { dom.style.cssText = value || ''; } else if (typeof value === 'object') { // {width:'',height:20} for (let name in value) { //如果是数字可以忽略px dom.style[name] = typeof value[name] === 'number' ? value[name] + 'px' : value[name]; } } //其他 } else { dom.setAttribute(key,value); } } 现在 const ReactDOM = { render: function(vnode,container) { container.innerHTML = ''; render(vnode,container); } }; 这里在调用 那么万事具备,我们来测试一下,直接上一个比较复杂的dom结构并加上属性: const element = ( <div className="Hello" onClick={() => alert(1)} style={{ color: 'red',fontSize: 30 }} > Hello <span style={{ color: 'blue' }}>javascript!</span> </div> ); ReactDOM.render(element,document.getElementById('root')); 打开页面,是我们想要的结果:
再看看控制台的dom:
很完美,这是我们想要的东西 后语该demo代码在这里哟~~ 本文叙述了JSX和虚拟DOM,以及将虚拟DOM转成真实DOM的过程,后面的文章会继续叙述react中的组件、生命周期、diff算法和异步setState,敬请期待~ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |