查看原文
其他

[React启蒙系列]React节点

2016-09-13  zhangwang 前端圈

岩石教育-张旺投稿


本章是翻译的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节点的方法,下面详细看看这个函数的各个参数:

1、type (string |React.createClass() ):可以是一个代表HTML元素的字符串,也可以是一个React组件实例(React.createClass()的实例);

2、props(null|object):可以为null,也可以是一个对象;

3、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'));

其实上面这句代码在执行过程中做了以下一些事情:

1、生成一个由React nodes组成的Virtual DOM;

2、利用该Virtual DOM构建一个真实的DOM分支;

3、在<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>

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中:

JavaScript:

//React node, which represents an actual HTML DOM node
var HTMLLi = React.createElement('li', {
       className:'bar'
   }, 'foo');
//React node, which represents a custom HTML DOM node
var HTMLCustom = React.createElement('foo-bar', {
       className:'bar'
   }, 'foo');
//Render the HTMLLI React node to <div id="app1"></div>
ReactDOM.render(HTMLLi, document.getElementById('app1'));
//Render the HTMLCustom React node to <div id="app2"></div>
ReactDOM.render(HTMLCustom, document.getElementById('app2'));

HTML:

<div id="app1"></div>
<div id="app2"></div>

渲染入真实DOM后的HTML代码如下:

<body>
 <div id="app1">
   <li class="bar" data-reactid=".0">
     foo3
   </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。


本节还有以下需要注意的点

1、当把React nodes渲染进某一真实DOM节点中时,会清除该真实DOM节点的所有子元素;

2、ReactDOM.render()只是React渲染React nodes到真实DOM中的一种方法,还存在别的渲染方法,比如说你可以在服务器端通过ReactDOMServer.renderToString()方法将React nodes渲染为节点;

3、当子节点有改变时(依据diff算法),React会重新渲染React nodes到相同的DOM中。

理解props

传入React.createElement(type,props,children)方法的第二个参数是一个包含键值对的对象(props)。

props主要有以下几个作用:

1、如果prop中某个键与一个已知的Html属性名相同,在最终渲染生成的HTML元素中,其值会作为该元素的该属性的值;

2、prop可以被用来储存值,用以传递给 React 创建元素或组件;

3、一些特殊的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中有所表现,不过这个值可以通过下面的方法读取:

Babel:

var Badge = React.createClass({
   render: function() {
       return <div>{this.props.name}</div>;
   }
});
var BadgeList = React.createClass({
   render: function() {
       return (<div>
           <Badge name="Bill" />
           <Badge name="Tom" />
       </div>)
;
   }
});
ReactDOM.render(<BadgeList />, document.getElementById('app'));
console.log(ReactDOM.render(<BadgeList />, document.getElementById('app')));

HTML:

<div id="app"></div>

通过这两个例子,你肯定近一步理解props了。


关于props你还应该了解的事情

1、React中值为空白的props,渲染后其值为true (比如说id=""渲染后为id="true",test渲染后为test="true" );

2、props中如果同一属性出现两次,后者的值将生效;

3、props中被传入的标准React 元素属性(HTML中真实存在的元素的属性),渲染后依旧是该元素的对应属性值,非标准元素的属性将不会被渲染,如果传入的是一个自定义元素,那么其所有的属性都将被渲染如<x-my-component custom-attribute="foo" />;

4、React中class属性写作className,for写作htmlFor,style属性为写作驼峰式;

5、HTML表单元素 (<input>、<textarea></textarea>等),当其由React创建时,其支持与交互有关的属性value、checked、selected等。

6、key、ref、dangerouslySetInnderHtml属性不存在于真实DOM中,它们在React中有独特的作用;

7、React中所有的属性都被写作驼峰式(如accept-charset写做acceptCharset);

8、以下是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>

上述代码有两点值得注意的:

1、在fontsize属性值后,我并未加"px",做为单位,“px”是React内联样式的默认单位,如果用其他的单位可以类似"2em"这样写,用引号围起来就可以了;

2、在JavaScript中写内联样式时需要使用小写驼峰式(backgroundColor)。



本节笔记

  1. 有前缀的属性,字母装换为大写 (-后的第一个字母大写 );

  2. 其实在JavaScript中,CSS样式一直都是采用驼峰式的,React也延续了这一习惯;

  3. 除了以下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

本节笔记

1、如果你使用JSX,你可能永远不会用到ReactElement工厂;

2、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事件绑定在了一个React div节点上。

Babel:

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'));

HTML:

<div id="app"></div>

通过on就可以绑定对应事件。


React为每一个事件绑定了一个被称为SyntheticEvent的对象,里面包含了该事件的所有细节,其实这个和DOM事件很类似,某个事件的SyntheticEvent实例,可以通过事件的回调函数访问,如下例:

Babel:

var clickhandler = function clickhandler(SyntheticEvent) {
 console.log(SyntheticEvent);
};
var reactNode = React.createElement(
 'div',
 { onClick: clickhandler},
 'click'
);
ReactDOM.render(reactNode, document.getElementById('app'));

HTML:

<div id="app"></div>

每一个syntheticEvent对象实例中都包含了以下属性:

bubbles
cancelable
DOMEventTarget currentTarget
defaultPrevented
eventPhase
isTrusted
DOMEvent nativeEvent
void preventDefault()
isDefaultPrevented()
void stopPropagation()
isPropagationStopped()
DOMEventTarget target
timeStamp
type

此外一些事件的syntheticEvent还具有一些特有属性,复制链接查看表格:http://www.reactenlightenment.com/react-nodes/4.7.html。


关于React中的事件还需要注意以下几点

  1. React中的各事件已经规范化,你可以放心的跨浏览器使用;

  2. React事件默认在事件冒泡阶段(bubblling)触发,如果想在事件捕获阶段触发需要在事件名后加上Capture(如onClick变为onClickCapture);

  3. 如果你想获知浏览器事件的详情,你可以通过在回调函数中查看SyntheticEvent对象中的nativeEvent值;

  4. React实际上并未直接为React nodes添加事件,它使用了event delegation(http://domenlightenment.com/#11.14)事件委托机制;

  5. 想要阻止事件冒泡,需要手动调用e.stopPropagation() 或e.preventDefault(),不要直接使用returning false;

  6. React其实并没有支持所有的JS事件,不过它还提供额外的生命周期函数以供使用React lifecycle methods(https://facebook.github.io/react/tips/dom-event-listeners.html)。


【React启蒙系列文章】

一、[React启蒙系列] 初探React

二、[React启蒙系列] 学习React前需要理解的名词

三、[React启蒙系列] React和Babel的基本使用


【您可能感兴趣的文章】

一、手把手教你用react

二、React入门及资源指引

三、利用ESLint检查代码质量

四、构建一个安全的 JavaScript 沙箱

五、入门Webpack,看这篇就够了

六、第三届CSS大会广州找场地啦~~求介绍~~

七、Web Components 是个什么样的东西

八、JavaScript 被忽视的细节



前端圈--打造专业的前端技术会议

为web前端开发者提供技术分享和交流的平台

打造一个良好的前端圈生态,推动web标准化的发展

官网:http://fequan.com

微博:fequancom | QQ群:41378087


长按二维码关注我们

投稿:content@fequan.com

赞助合作:apply@fequan.com

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存