转载

React的一些概念

对于初学React的同学而言,这并不是一件易事。就拿我自己来说,都不知道从何下手,应该如何去学习才能开始使用React。就算你对React不陌生,学习React也常会碰到一些瓶颈。比如说新颖的概念、开发工具的使用、抽象的名词、快速变化的生态环境等等。也就是说,一旦开始学习React,你会发觉要学的东西越来越多,甚至可能还没开始碰到React就被这些东西给吓跑了(特别是对于初学者,听到这些东东就傻眼了)。

这篇文章不是来介绍怎么学习React,而是要让初学者或没学过React的同学对React的一些重要概念有所了解。

元素(Element)

Element对于我们来说并不陌生,中文常称之为 元素 ,也就是HTML里的标签元素。比如:

<h1>W3cplus</h1> <p>Des...</p> 

这里的 <h1><p> 等等就是元素。这些元素都可以添加一些属性,比如说 classidstyle 以及一些自定义的属性 data-* 。这我们所说的Element是HTML,对于我们来说很好理解,但是...

React的Element是写在JavaScript的普通对象(Plain Object)

在理解这句话之前,先来看看下面的示例,就算你是初次接触React,也应该能猜到这是一个导航元素,而且其字体的颜色是 blue

var app = <Nav color="blue" /> 

这看起来像是HTML的语法,其官方的叫法是 JSX ,和我们所了解的HTML是完全不同的。这个 <Nav color="blue" /> 不是HTML的DOM节点,而是一个对象(Plain Object)。

在React中,其实要我们用物件去描述Element。下面的示例用来阐述React如何用物件描述一个按钮元素(button element),可以把 type 想成一般的 tag 名称、 props 想成是写在 tag 里的属性( attributes )。

{     type:"button",     props: {         className: "btn btn-primary",         children: {             type: "b",             props: {                 children: "OK!"             }         }     } } 

但这种编码方式降低了其可读性,所以我们把它包装成像我们熟悉的HTML的语法(也就是JSX),但千万记得,不能别一般的HTML搞混了,底下这段JSX实质上是一个对象。

<button className="btn btn-primary">     <b>OK!</b> </button> 

在当今的前端开发上,常常需要大量的操作DOM,没处理好的话,效率是非常低的。这也就是React不希望我们这样做。

React要我们用物件去描述Element,然后它自动帮我们处理DOM,这样的开发方式叫作声明式编程(Declarative Programming),也就是React说的 What (描述想呈现的Elemnt),而不是说的 How (要怎么去处理DOM)。

如此一来,React开发上主要工作是处理JavaScript的物件,这除了比操作DOM来得更轻量且方便之外,这也是JavaScript常做的事情。也就是说 学习React其实也就是在学习JavaScript,就是处理描述Element的物件 (如果学过React的同学,就知道这就是React中的 state )。

数据呈现:JSX

前面提到过了,React的ELement虽然看上去像HTML,其实它的专业术语称之为JSX。那么在学习React之前,很有必要要了解一下JSX。

了解JSX

JSX可以看做是JavaScript的拓展,有点类似于XML。使用React可以进行JSX语法到JavaScript的转换。它不是新语言,也没不用改变JavaScript的语法,只是对JavaScript的拓展。当然,使用React开发并不一定非要使用JSX( 感兴趣的可以看看这篇译文 )。

前面说过,JSX语法跟XML或者HTML很相似,但有一些细节需要注意:

  • 属性表达式 : 要使用JavaScript表达式作为属性值,只需把这个表达式用一对大括号 {} 包裹起来,不要用引号 ""
  • 嵌套其他组件 :只需要引入相应组件,并将其作为一个标签插入JSX即可
  • Style属性 :我们可以在JSX内部写CSS,不过要遵循一下格式
  • HTML转义 : React默认会进行HTML转义,用以避免XSS攻击
  • 注释 :只需要在一个标签的子节点内(非最外层)小心地用 {} 包围要注释的部分即可
  • JSX最外层标签必须是唯一的
  • React的JSX里约定分别使用首字母大、小写来区分本地组件的类和HTML标签
  • 由于JSX就是JavaScript,一些JavaScript的标识符,比如 classfor 这样的不建议作为XML属性名,作为替代,React DOM使用 classNamehtmlFor 来做对应的属性

上面清单所提到的一些示例:

// JSX内部写CSS格式  React.render(     <button color="blue">         <b style={color:"white",display: "block"}></b>     </button> );  // HTML转义  var content = "<strong>content</strong>>";  React.render(     <div dangerouslySetInnerHtml = {{__html:content}}></div>,     document.body );  // HTML不转义  var content = "<strong>content</strong>"; React.render(     <div>{content}</div>,      document.body ); 

咱们一起再来看看一个完整的示例:

var TopTitle = React.createClass({     getDefaultProps: function(){         return {             titleContent: "TopTitleBar"         };     },     render: function() {         return (             <h1 id="topTitle" className="J_title">{this.props.titleContent}</h1>         );     } }); 

React数据模型:props和state

在React中有两种类型的数据 模型propsstate 。那么这两个概念对于学习React也很重要。

this.props

React是通过内置虚拟DOM来操作,这个组件的输入被称为 props (也就是properties缩写)。通过JSX语法进行参数传递。在组件中,这些属性是不可以直接改变的,也就是说 this.props 是只读的。

this.state

React把用户界面当作简单的状态机。把用户界面想像成拥有不同状态,之后渲染这些状态,这样可以轻松让用户界面和数据保持一致。

React里,只需要更新组件的 state ,然后根据新的 state 重新渲染用户界面(不要操作DOM)。React来决定如何最高效的更新DOM。

在React组件内部,通过 this.state 来获取 state 值。但在使用的过程中千万要注意,不要尝试使用 this.state.xxx = "xxx" 来强制修改 state 的值,这样将会引起一系列错误。就算需要修改 state 值,在React中是提供了一些操作 state 的方法:

setState()

setState() 不会立即改变 this.state ,而是创建一个即将处理的 state 转变。在调用该方法之后获取 this.state 的值可能会得到现有的值,而不是最新设置的值。

setState() 总是触发一次重绘,除非 shouldComponentUpdate() 中实现了条件渲染逻辑。如果使用可变的对象,但又不能在 shouldComponentUpdate() 中实现这种逻辑,仅在新 state 和之前的 state 存在差异的时候调用 setState() 可避免不必要的重新渲染。

replaceState()

类似于 setState() ,只不它只是负责删除已经存在的 state 键。

render

render() 方法是必须的。顾名思义,它负责组件的渲染工作。当 render 被调用时,会去检测 this.propsthis.state ,并且返回一个元素(这个元素可以使原生的HTML Dom也可以是React Dom,或是你自己定义的一些复合的组件模块,当然也可以返回 nullfalse 来表明你不希望做任何渲染工作)。每次被调用时, render 方法都会返回'相同'的结果,它不会去读、写DOM或是和浏览器做交互(例如使用 setTimeout )。

注意:切记要保证 render 方法的洁净,不可以在 render 方法中修改组件的 state 。如果你希望和浏览器交互或者做更多事情,请在 componentDidMount() 方法或者其他生命周期的方法中去实现。

React组件

知道React中Element是什么,那了解React中的Component(组件)也就很好理解了。在React中是通过Element去描述Component,其本质上也可以说Component是Element。

React组件的特色

React中的Component有两大特色:

  • 封装Element tree(利于前端模块化)
  • 可以使用JavaScript Function、Class来写

下在是使用ES6的Arrow Function写的一个DeleteAccount组件:

const DeleteAccount = () => ({     type: "div",     props: {         children: [{             type: "p",             props: {                 children: "Are you sure?"             }         },{             type: DangerButton,             props: {                 children: "Yep"             }         },{             type: Button,             props: {                 color: "blue",                 children: "Cancel"             }         }]     } }); 

type 的描述可以知道其本身是一个 div 元素, props 告诉他的子组件(children component)包含 p 元素及 DangerButtonButton 组件(前面提到过, type 并不仅是一般的 tag 标签,也可以是一个React组件)。

当然,也可以使用JSX来提高React组件的可读性:

const DeleteAccount = () => (     <div>         <p>Are you sure?</p>         <DangerButton>Yep</DangerButton>         <Button color="blue">Cancel</Button>     </div> ); 

这里不是要告诉大家怎么学习React的语法,而是要告诉大家学习React最重要的事情: 使用对象(Plain Object)去描述React的组件(Component)或元素(Element)

组件说明和生命周期

当我们调用 React.createClass() 方法创建一个组件类时,必须提供一个包含 render 方法的对象作为实参,当然也可以包含其他一些生命周期方法,不过他们是可选的。下面让我们按照生命周期的顺序,来依次介绍一下这些生命周期方法。

简单了解一下React组件的生命周期。React的组件生命周期,就如同人一样,也有生老病死,其大致可以分为七个周期:

React的一些概念

  • Mounting : 取得组件的初始参数和状态,并且进行第一次的渲染
  • DOM Muntings Complete : 渲染完成后,DOM也会跟着更新,使用者可以看到最新的画面
  • Mounted : 组件已经更新完成,等待其他变化
  • Receiving state : 组件的状态 state 已经被更新,并且重新渲染了,所以回到第 2 个周期
  • Unmounting : 组件即将被移除
  • Unmounted : 组件的死亡阶段

除了知道React组件的生命周期之外,还需要了解生命周期相关的方法。

React的一些概念

在实际开发当中,那又要如何使用这些方法呢?其实在开发中,可以直接在 class 中使用这些方法,这样你就可以拦截到组件的变化,并且会执行后面相应的动作,如:

var Demo = React.createClass ({     getInitialState: function () {         return {             name: "W3cplus"         };     },     getDefaultProps: function () {         return {             myName: "大漠"         };     },     statics: {         isUndefined: function (str) {             return str === undefined;         },         isNumber: function (num) {             return typeof(num) === "number";         }     },     propTypes: {         myName: React.PropTypes.string     },     componentWillReceiveProps: function () {         console.log("组件收到props!");     },     componentDidUpdate: function () {         console.log("render渲染更新完成!");     },     componentWillUpdate: function () {         console.log("收到新的props或者state!");     },     shouldComponentUpdate: function () {         console.log("收到新的props或者state!即将进行render方法");     },     componentWillMount: function () {         console.log("初始化render执行前!");     },     componentDidMount: function () {         console.log("初始化render渲染完成!");     },     componentWillUnmount: function () {         console.log("DOM被移除");     },     render: function () {         console.log("渲染开始!");         return (             <div className="container">                 <h1>Hello {this.state.name}</h1>                 <h2>I'm {this.state.myName}</h2>             </div>         );     } }); 

比如下面这个组件示例:

class Counter extends React.Component {     constructor(props, context) {         super(props, context);         this.state = { value: 0 };     }      componentWillMount() {         console.log('1. Mounting: componentWillMount');     }      componentDidMount() {         console.log('3. Mounted: componentDidMount');     }      componentWillReceiveProps(nextProps, nextContext) {         console.log('4. Recieving Props: componentWillReceiveProps');     }      shouldComponentUpdate(nextProps, nextState, nextContext) {         console.log('5. Recieving State: shouldComponentUpdate');         return true;     }      componentWillUpdate(nextProps, nextState, nextContext) {         console.log('5. Recieving State: componentWillUpdate');     }      componentDidUpdate(prevProps, prevState, prevContext) {         console.log('3. Mounted: componentDidUpdate');     }      componentWillUnmount() {         console.log('5. Recieving State: componentWillUpdate');     }      render() {         console.log('render');         return (<h1>{this.state.value}</h1>);     } } 

下面简单介绍了这些方法的使用,如果你想了解更详细的信息,可以阅读 官方文件 。

getInitialState()

  • 组件首次挂载之前调用一次(仅执行一次)
  • 用户初始化 this.state
  • 其返回值将作为 this.state 的初始值

getDefaultProps()

  • 当组件类被创建时调用一次并缓存其返回值
  • 用于设置 this.props 的初始值
  • 如果其父组件没指定 props 中的某个键,那么 getDefaultProps() 的返回对象中的相应属性将会合并到 this.props 中作为初始值。也就是说,当父集没有传入 props 时, getDefaultProps() 方法可以保证 this.props.xxx 有默认值,并且它的返回值将被缓存,我们可以直接使用 props 而不必重复编写一些无意义的代码
  • 由于该方法在任何实例创建之前调用,因此它不能依赖于 this.props ,其返回值将在全部实例中共享

propTypes

  • 随着应用不断壮大,确保组件被正确使用变得非常有必要。 propTypes 用于验证传入到组件的 props ,以此来判断传入数据的有效性。当向 props 中传入无效数据时,JavaScript控制台会抛出警告
  • React.PropTypes 验证器支持下面这些类型。但为了保证性能,建议只在开发环境下验证 propTypes

React.PropTypes 验证器支持的类型:

PropTypes : {     'arrayKey' : React.PropTypes.array , // 数组;     'boolKey' : React.PropTypes.bool , // 布尔值;     'funcKey' : React.PropTypes.func , // 函数;     'numberKey' : React.PropTypes.number , // 数字;     'objKey' : React.PropTypes.object , // 对象;     'stringKey' : React.PropTypes.string , // 字符串;     'nodeKey' : React.PropTypes.node , //所有可以被渲染的对象(数字、字符串、DOM及包含上述三种类型的数组)     'eleKey' : React.PropTypes.element , // React元素;     'arrayOfKey' : React.PropTypes.arrayOf(React.PropTypes.number) , // 指定类型构成的数组     'oneOfKey' : React.PropTypes.oneOf(['one','two']) , // 用来限制oneOfKey的值只能接受指定值     'objWithShap' : React.PropTypes.shape({     color : React.PropTypes.string,     width : React.PropTypes.number      }), // 用于验证特定形状的参数对象     ...     // 以上这些prop在默认情况下都是可以忽略的。     // 当在任意类型后面加上'isRequired',来使该prop不可为空。如:     'appId' : React.PropTypes.number.isRequired,     'anyTypeKey' : React.PropTypes.any.isRequired, // 用于验证不可为空的任意类型     'customProp' : function(props, propName, componentName) {         if (!/matchme/.test(props[propName])) {             return new Error('认证失败')         }     } } 

mixins数组

  • 组件是React里复用代码的最佳方式, mixins 数组可以让我们在多个组件间复用一些方法。大致等同于将 mixins 数组中的组件方法引入到 this 下,以便我们可以直接调用
  • 如果一个组件使用了多个 mixin ,并且有多个 mixin 定义了同样的生命周期方法,所有这些生命周期方法都保证会被执行到。其执行顺序是:先按照 mixin 引入顺序执行 mixin 里面的方法,最后执行组件内定义的方法

statics

  • statics 对象允许我们定义一些能够被组件类调用的静态方法
  • 在这里定义的方法都是静态的,所以我们可以在任何组件实例创建之前调用它们
  • 这些方法不能获取组件的 propsstate ,但是却可以接受参数

componentWillMount

  • 在初始化渲染执行前立即调用且仅调用一次
  • 服务端和客户端都只会调用一次。如果在这个方法内部调用 setState 来改变 this.state 的值,更新后的 this.state 值将被 render 方法感知到,并且 render 只会执行一次,尽管 state 改变了

componentDidMount

  • 在初始化 render 渲染方法之后,立即调用一次,仅客户端有效(服务端不会调用)
  • 在生命周期中的这个时间点,组件拥有一个DOM展现,可以通过 this.getDOMNode() 来获取相应的DOM节点。我们可以在这个方法中与其他JavaScript框架进行集成、处理一些渲染后的逻辑(比如说绑定一些事件等)、发送Ajax请求或是设置定时器方法( setTimeout/setInterval )等

componentWillReceiveProps

  • 组件每次收到新的 props 时调用,初始化渲染时不会被调用
  • 该方法可以作为React在 prop 传入之后, render() 方法执行之前更新 state 的合适时机。老的 props 可以通过 this.props 获取到
  • 在初始化渲染时,该方法不会被调用。在该方法中调用 this.setState()

shouldComponentUpdate

  • 在收到新的 props 或者 state ,将要渲染之前调用
  • 当我们确定新的 propsstate 不需要导致组件更新时,应在此方法中 return false; ,也就是说当 shouldComponentUpdate() 返回 false 时, render() 方法不会被执行( componentWillUpdate()componentDidUpdate() 都将不会被调动),直到下一次 state 改变。默认情况下, shouldComponentUpdate() 总是返回 true
  • 该方法在初始化渲染时不会被调用,且在使用 forceUpdate() 时也不会被调用

componentDidUpdate

  • 在组件更新已经同步到DOM中之后立刻被调用。也就是在除了初始化 render 之后
  • 使用该方法可以在组件更新之后操作DOM元素。基本等同于 componentDidMount() ,唯一不同在于首次初始化 render 渲染完成后将执行 componentDidMount() 方法,而之后每次渲染完成都会执行 componentDidUpdate() 方法。我们可以使用 this.getDOMNode() 来访问DOM节点
  • 该方法不会在初始化渲染的时候调用

componentWillUnmount

  • 在组件从DOM中移除的时候立刻被调用
  • 我们可以在该方法中执行任何必要的清理,比如无效的定时器,或者清除在 componentDidMount() 中创建的DOM元素等

对于React组件的生命周期来说,我们需要了解的是:

  • React的组件是有生命周期的
  • 生命周期的每一个阶段,都会调用相对应的方法
  • 操作这些方法,可以拦截React组件的变化,并且做相应的处理

总结

当我们开发Web网站或应用时,前端架构变得越来越复杂,有大量的 DOM需要处理的状况之下,性能就开始会出现问题。使用React则可以帮助我们省下处理DOM的麻烦,因为使用React的话,从头到尾我们都没有必要去操作真正的DOM,我们要做的事情仅仅是处理物件(描述Element、Component的物件)。

因为React生态系统过于庞大,需要学习的东西越来越多,但要记住一件很重要的事情, 学习React其实也就是学习JavaScript 。同时使用React开发会使用到最基本的JavaScript及Web API(XHR或Fetch),当然如果你想快速完成一个案例或应用,那么React是一个很好的选择。

另外,React还有很多有趣的东西值得讨论和学习,而这篇文章只是涉及了React最基础的部分,也向大家阐述了学习React最重要的一件事情: React是处理物件而不是处理DOM

React的一些概念

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《 图解CSS3:核心技术与案例实战 》。

原文  http://www.w3cplus.com/react/react-beginner-intro.html
正文到此结束
Loading...