本章是翻译的React启蒙系列的第四章,主要将讲述如何使用纯JavaScript语句创建React节点,本章内容依旧非常基础,通过阅读本章内容你将了解React nodes的定义,React.createElement()所需的各参数的实际意义以及部分React事件相关知识。
什么是React nodes?
定义:
React node是一种轻量的,无状态的,不可变的,真实DOM节点的一种虚拟代表。它是React创建的基本元素。
这种虚拟代表被称作Virtual DOM,简言之,React使用React nodes创建虚拟DOM,一个完整的React组件最终可用来创建真实的DOM(或其它结构(如React Native))。
React node可以使用纯JavaScript方式创建也能使用JSX创建,本章我们先详细探讨如何使用纯JavaScript创建React node,这对之后更好的理解JSX很有帮助。本章内容基础,但是对理解React非常重要。
创建React nodes
调用React.createElement(type,props,children)
函数就可以创建一个React node,这个方法的使用类似创建真实DOM节点的方法,下面详细看看这个函数的各个参数
type
(string |React.createClass()
):可以是一个代表HTML元素的字符串,也可以是一个React组件实例(React.createClass()
的实例);props(null|object)
:可以为null
,也可以是一个对象;Children(null | string | React.createClass() | React.createElement())
:可以为null
,如果是Text,其将被转换为文本节点,也可以是一个React node实例 (React.createElement()
)或一个React 组件实例 (React.createClass()
)。
下面是我用这个函数创建了一个React<li>
节点,其中的文本为one
,其id
为li1
var reactNodeLi = React.createElement('li', {id:'li1'}, 'one');
正如前面所说,该函数第一个参数代表你想创建的节点类型,第二个参数代表给该节点传入的参数(props
),第三个代表该React节点的子节点(文本,子元素节点或组件实例)。
为了将此节点 (reactNodeLi
)渲染入DOM中,我还需要调用ReactDOM.render()
方法,代码如下:
//<div id="app"></div>
ReactDOM.render(reactNodeLi,document.getElementById('app'));
其实上面这句代码在执行过程中做了以下一些事情:
生成一个由React nodes组成的Virtual DOM;
利用该Virtual DOM构建一个真实的DOM分支;
在
<div id="app"></div>
处,将该真实DOM分支插入真实DOM中,并当做所插入处的子节点;
真实的DOM如下
//渲染前
<div id="app"></div>
//渲染后
<div id="app">
<li id="li1" data-reactid=".0">one</li>
</div>
以上是使用React.createElement()
基础的例子,使用这种方法,我们也可以创建复杂的结构,下面我将使用此方法模拟html
的无序列表(<ul>
。
// 创建React元素 <li>'
var rElmLi1 = React.createElement('li', {id:'li1'}, 'one');
var rElmLi2 = React.createElement('li', {id:'li2'}, 'two');
var rElmLi3 = React.createElement('li', {id:'li3'}, 'three');
//创建React元素<ul>,并将<li>包含其中
var reactElementUl = React.createElement('ul', {className:'myList'}, rElmLi1,rElmLi2,rElmLi3);
渲染之前,我想展示另外一种创建方式,这种方式用React.createElement()
代替了变量rElmLi*
。
var reactElementUl = React.createElement(
'ul', {
className: 'myList'
},
React.createElement('li', {id: 'li1'},'one'),
React.createElement('li', {id: 'li2'},'two'),
React.createElement('li', {id: 'li3'},'three')
);
上述代码的渲染结果如下:
<ul class="myList" data-reactid=".0">
<li id="li1" data-reactid=".0.0">one</li>
<li id="li2" data-reactid=".0.1">two</li>
<li id="li3" data-reactid=".0.2">three</li>
</ul>
你可以在JSfiddle中细细体味上述代码 点击JSFiddle查看更多
React nodes是一个树形的JavaScript对象,其使用Virtual DOM的形式来表征真实DOM。Virtual DOM随后会在Html页面中被渲染为真实的DOM分支。
本节笔记
React.createElement(type,props,children)
方法中第一个参数type
可以是一个代表真实Html 元素的字符串 (如”li” ),也可以是一个自定义元素 (如”foo-bar” ),还可以是一个React组件实例 (如React.createClass()
的实例 )以下是React支持的标准Html元素
a abbr address area article aside audio b base bdi bdo big blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param picture pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track u ul var video wbr
渲染React nodes到真实的DOM
React提供一个名为ReactDOM.render()
方法用以将React nodes渲染到真实的DOM中(此方法存在于react-dom.js
文件中)。
下面的例子中,我们使用ReactDOM.render()
方法,把React 节点<li>
和<foo-bar>
渲染到了DOM中 点击JSFiddle查看更多
渲染入真实DOM后的HTML代码如下
<body>
<div id="app1"><li class="bar" data-reactid=".0">foo</li></div>
<div id="app2"><foo-bar classname="bar" children="foo" data-reactid=".1">foo</foo-bar></div>
</body>
ReactDOM.render()
做了两件事:渲染React nodes为Virtual DOM;渲染其为真实DOM。
本节还有以下需要注意的点
当把React nodes渲染进某一真实DOM节点中时,会清除该真实DOM节点的所有子元素;
ReactDOM.render()
只是React渲染React nodes到真实DOM中的一种方法,还存在别的渲染方法,比如说你可以在服务器端通过ReactDOMServer.renderToString()
方法将React nodes渲染为节点;当子节点有改变时(依据diff算法),React会重新渲染React nodes到相同的DOM中。
理解props
传入React.createElement(type,props,children)
方法的第二个参数是一个包含键值对的对象(props)。
props
主要有以下几个作用
如果
prop
中某个键与一个已知的Html属性名相同,在最终渲染生成的HTML元素中,其值会作为该元素的该属性的值;prop
可以被用来储存值,用以传递给 React 创建元素或组件;一些特殊的
props
具有特殊的用途 (key
,ref
,Dangerously Set innerHTML
)
总的来说,你可以把props
看做React nodes的配置值,也可以把props
当做 React 元素的属性值。
下例中,我为React的<li>
元素传入了五个props
,其中foo:'bar'
不是标准的HTML属性,其余都是标准的HTML属性。
var reactNodeLi = React.createElement('li',
{
foo:'bar',
id:'li1',
//class 表示为className
//i.e., className
className:'blue',
'data-test':'test',
'aria-test':'test',
//CSS代码采用驼峰式
//i.e., backgroundColor
style:{backgroundColor:'red'}
},
'text'
);
渲染结果如下
<li id="li1"
data-test="test"
class="blue"
aria-test="test"
style="background-color:red;"
data-reactid=".0">
text
</li>
键名为标准的HTML属性的props
项,被渲染后其值是对应属性的值标准的HTML属性,而foo
并非标准HTML属性,因此foo
并未在渲染后的真实DOM中有所表现,不过这个值可以通过下面的方法读取。点击JSFiddle查看更多
通过这两个例子,你肯定近一步理解props
了。
关于props
你还应该了解的事情
React中值为空白的props,渲染后其值为
true
(比如说id=""
渲染后为id="true"
,test
渲染后为test="true"
);props中如果同一属性出现两次,后者的值将生效;
props
中被传入的标准React 元素属性(HTML中真实存在的元素的属性),渲染后依旧是该元素的对应属性值,非标准元素的属性将不会被渲染,如果传入的是一个自定义元素,那么其所有的属性都将被渲染如<x-my-component custom-attribute="foo" />
;React中
class
属性写作className
,for
写作htmlFor
,style属性为写作驼峰式;HTML表单元素 (
<input>
、<textarea></textarea>
等),当其由React创建时,其支持与交互有关的属性value
、checked
、selected
等。key
、ref
、dangerouslySetInnderHtml
属性不存在于真实DOM中,它们在React中有独特的作用;React中所有的属性都被写作驼峰式(如
accept-charset
写做acceptCharset
);以下是React支持的一些属性
accept acceptCharset accessKey action allowFullScreen allowTransparency alt async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge charSet checked classID className colSpan cols content contentEditable contextMenu controls coords crossOrigin data dateTime default defer dir disabled download draggable encType form formAction formEncType formMethod formNoValidate formTarget frameBorder headers height hidden high href hrefLang htmlFor httpEquiv icon id inputMode integrity is keyParams keyType kind label lang list loop low manifest marginHeight marginWidth max maxLength media mediaGroup method min minLength multiple muted name noValidate nonce open optimum pattern placeholder poster preload radioGroup readOnly rel required reversed role rowSpan rows sandbox scope scoped scrolling seamless selected shape size sizes span spellCheck src srcDoc srcLang srcSet start step style summary tabIndex target title type useMap value width wmode wrap
在React中使用内联样式
想要在React中使用内联样式,只需要在React 节点中传入style
属性,并把一个包含CSS属性和对应值的对象赋值给该属性即可。
以下例子可以让你清楚认识这一点。
var inlineStyles = {backgroundColor:'red', fontSize:20};
var reactNodeLi = React.createElement ('div',{style:inlineStyles}, 'styled' )
ReactDOM.render (reactNodeLi, document.getElementById ('app1' ) );
上述代码编译后的结果如下:
<div id="app1">
<div style="background-color:red;font-size:20px;" data-reactid=".0">styled</div>
</div>
上述代码有两点值得注意的
在
fontsize
属性值后,我并未加”px”,做为单位,“px”是React内联样式的默认单位,如果用其他的单位可以类似"2em"
这样写,用引号围起来就可以了;在JavaScript中写内联样式时需要使用小写驼峰式(
backgroundColor
);
本节笔记
有前缀的属性,字母装换为大写 (
-
后的第一个字母大写 );其实在JavaScript中,CSS样式一直都是采用驼峰式的,React也延续了这一习惯;
除了以下CSS属性外,React数值的默认单位都是”px”
columnCount fillOpacity flex flexGrow flexShrink fontWeight lineClamp lineHeightopacity order orphans strokeOpacity widows zIndex zoom
React元素工厂
React 提供一种名为React元素工厂(React element factories)的方法来快速创建React元素。
官方定义:一个 ReactElement 工厂就是一个简单的函数,该函数生成一个带有特殊 type 属性的 ReactElement
其使用方法如下:
//uses
React.DOM.li(props, children);
var reactNodeLi = React.DOM.li({id:'li1'}, 'one');
对比一下我们之前用的方法能让你更清楚
// 使用React.createElement(type, prop, children);
var reactNodeLi = React.createElement('li', {id:'li1'}, 'one');
以下是React提供的所有的内置元素工厂方法
jsa,abbr,address,area,article,aside,audio,b,base,bdi,bdo,big,blockquote,
body,br,button,canvas,caption,cite,code,col,colgroup,data,datalist,dd,
del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,
form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,ins,
kbd,keygen,label,legend,li,link,main,map,mark,menu,menuitem,meta,meter,
nav,noscript,object,ol,optgroup,option,output,p,param,picture,pre,progress,
q,rp,rt,ruby,s,samp,script,section,select,small,source,span,strong,style,
sub,summary,sup,table,tbody,td,textarea,tfoot,th,thead,time,title,tr,track,
u,ul,var,video,wbr,circle,clipPath,defs,ellipse,g,image,line,linearGradient,
mask,path,pattern,polygon,polyline,radialGradient,rect,stop,svg,text,tspa
本节笔记
如果你使用JSX,你可能永远不会用到ReactElement工厂;
React 有一个内置的辅助方法用于创建工厂函数。事实上该方法就是这样的:React.createFactory (type),这个函数用于创建工厂函数,利用这个创建好的函数可以方便的创建其实例。
function createFactory(type)
{ return React.createElement.bind(null, type);
}
var div = React.createFactory('div');
var root = div({ className: 'my-div' });
React.render(root, document.body);`
React中的事件
在React中添加事件和在DOM中添加事件一样方便,在下面的例子中,我把click
和mouseover
事件绑定在了一个Reactdiv
节点上。
var mouseOverHandler = function mouseOverHandler() {
console.log('you moused over');
};
var clickhandler = function clickhandler() {
console.log('you clicked');
};
var reactNode = React.createElement(
'div',
{ onClick: clickhandler, onMouseOver: mouseOverHandler },//在此绑定事件
'click or mouse over'
);
ReactDOM.render(reactNode, document.getElementById('app'));
通过on
就可以绑定对应事件
React为每一个事件绑定了一个被称为SyntheticEvent
的对象,里面包含了该事件的所有细节,其实这个和DOM事件很类似,某个事件的SyntheticEvent
实例,可以通过事件的回调函数访问,如下例。
var clickhandler = function clickhandler(SyntheticEvent) {
console.log(SyntheticEvent);
};
var reactNode = React.createElement(
'div',
{ onClick: clickhandler},
'click'
);
ReactDOM.render(reactNode, document.getElementById('app'));
每一个syntheticEvent
对象实例中都包含了以下属性
bubbles
cancelable
DOMEventTarget currentTarget
defaultPrevented
eventPhase
isTrusted
DOMEvent nativeEvent
void preventDefault()
isDefaultPrevented()
void stopPropagation()
isPropagationStopped()
DOMEventTarget target
timeStamp
type
此外一些事件的syntheticEvent
还具有一些特有属性,比如说onClick
还具有以下属性
altKey
button
buttons
clientX
clientY
ctrlKey
getModifierState(key)
metaKey
pageX
pageY
DOMEventTarget relatedTarget
screenX
screenY
shiftKey
下表按类别列出了事件的syntheticEvent
的特有属性
关于React中的事件还需要注意以下几点
React中的各事件已经规范化,你可以放心的跨浏览器使用;
React事件默认在事件冒泡阶段(bubblling)触发,如果想在事件捕获阶段触发需要在事件名后加上
Capture
(如onClick
变为onClickCapture
);如果你想获知浏览器事件的详情,你可以通过在回调函数中查看
SyntheticEvent
对象中的nativeEvent
值;React实际上并未直接为React nodes添加事件,它使用了event delegation事件委托机制
想要阻止事件冒泡,需要手动调用
e.stopPropagation()
或e.preventDefault()
,不要直接使用returning false
,React其实并没有支持所有的JS事件,不过它还提供额外的生命周期函数以供使用React lifecycle methods.