React启蒙认识React nodes

本章是翻译的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,其idli1

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','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'),{id: 'li2'},'two'),{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,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,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中有所表现,不过这个值可以通过下面的方法读取。点击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创建时,其支持与交互有关的属性valuecheckedselected等。

  • keyrefdangerouslySetInnderHtml属性不存在于真实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>

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

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

  2. 在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','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,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

本节笔记

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中添加事件一样方便,在下面的例子中,我把clickmouSEOver事件绑定在了一个Reactdiv节点上。

点击JSFiddle查看更多

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实例,可以通过事件的回调函数访问,如下例。

点击JSFiddle查看更多

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.

参考

全书
原文

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom