React中文

1.Hello World

开始React最简单的方式是在CodePen上使用HelloWorld例子。你不需要安装任何东西,你只要在新的标签页中打开并和我们一起看例子。如果你想要使用一个本地开发环境,可参看Installation页。

最小的React例子如下:

ReactDOM.render(

  <h1>Hello,world!</h1>,

document.getElementById('root')

);

它要在页面中渲染一个内容为“Hello World”的标题。

接下来的几部分将逐步向你介绍如何使用React.我们将examine React apps的构建块:elements 和 components.一旦你掌握他们,你就能用小的可复用的零件pieces创建复杂的apps.

一个JavaScript便笺

React是一个JavaScript库,因此它假设你已经对JavaScript语言有一个基本的理解。如果你不是很自信,我们建议你复习下你的JavaScript知识,这样你会很容易跟上讲解。

在例子中我们也使用一些ES6语法。因为它比较新,我们会尽量少使用它,但是我们鼓励你熟悉arrow functions,classes,template literals,let 和const声明。你可以使用Babel REPL检查ES6代码编译成了什么。

2.Introducing JSX

考虑到变量声明:

const element=<h1>Hello,world!</h1>;

这个有趣的标签语法是一个字符串或者HTML;

它被称为JSX,它是JavaScript的语法扩展。在React中我们推荐使用它来描述UI应该像什么。JSX可能会使你想起来模板语言,但它来自JavaScript的衍生。

JSX产生React "elements".在下一部分中,我们将探索渲染它们到DOM中。下面,你将找到你开始React必须的JSX基础。

在JSX中植入的表达式

通过用大括号包裹将任何JavaScript表达式植入在JSX中

例如,2+2,user.firstName和formatName(user)都是有效的表达式:

function formatName(user){

     return user.firstName+'  '+user.lastName;

}

const user={

     firstName:'Harper',

    lastName:'Perez'

};

const element={

  <h1>

  Hello,{formatName(user)}!

</h1>

};

ReactDOM.render(

element,

document.getElementById('root')

);

在CodePen上试一下吧。

为了便于理解,我们将JSX拆分成多行。这样做,不是必须的,我们也推荐用圆括号包含它避免自动插入分号的陷阱。

JSX也是一个表达式

编译之后,JSX表达式变成有规律的JavaScript对象。

这意味着你可以替代if语句和for循环使用JSX,指定它为变量,作为参数接收它和从函数中返回它。

function getGreeting(user){

     if(user){

         return <h1>Hello,{formatName(user)}!</h1>;     

     }

    return <h1>Hello,Stranger.</h1>;

}

指定JSX的属性

你可能使用引号指定字符串值为属性:

const element=<div tabIndex="0"></div>;

你也可以在属性中用大括号包裹一个JavaScript表达式:

const element=<img src={user.avatarUrl}></img>;

在属性中包含JavaScript表达式的大括号不要加引号。否则JSX会将属性当作一个字符串来对待,而不是一个表达式。在一个属性中,你要么使用引号,要么使用大括号,不要两个都使用。

用JSX指定子节点

如果一个标签是空的,你可能会立即关闭它,像XML:

const element=<img src={user.avatarUrl}/>;

JSX标签可能包含子节点:

const element=(

       <div>

            <h1>Hello!</h1>

           <h2>Good to see you here.</h2>

      <div>

);

警告:

相比于HTML,JSX更贴近JavaScript,React DOM 使用camelCase 属性命名习俗,而不是HTML属性命名规则。

例如,class在JSX中改为className,tabindex改为tabIndex。

JSX阻止注入攻击

在JSX中包裹用户输入是安全的。

const title=response.potentiallyMaliciousInput;

const element=<h1>{title}</h1>;

默认渲染它们前React DOM避免任何值包裹在JSX中。这样能确保决不注入在应用中不是明确书写的任何内容。在渲染前一切都会被转换为字符串。这样有助于XSS(cross-site-scripting)攻击。

JSX代表对象

Babel 编译JSX到React.createElement()调用。

这两个例子是完全相同的:

const element=(

     <h1 className="greeting">

             Hello ,world!

      </h1>

);

const element=React.createElement(

      'h1',

      {className:'greeting'},

      'Hello ,world!'

);

React.createElement()执行几个检查来帮助你写没有缺点的代码但是本质上它创建一个像这样的对象:

const element={

      type:'h1',

      props:{

            className:'greeting',

            children:'Hello ,world'

      }

};

这些对象被称为“React elements”.你可以认为他们是你在屏幕上想看到的勾画。React读取这些对象,使用它们构建DOM并保持他们是最新的状态。

在下一节我们将探索渲染React元素到DOM中去。

提示:

我们推荐查找一个Babel语法命名空间作为你编辑器的选择,这样ES6和JSX代码会正确的高亮显示。

3.渲染元素。

Elements是最小的React apps构建块。

一个元素描述你在屏幕上想看到的内容。

const element=<h1>Hello,world></h1>;

不像浏览器DOM元素,React元素是一个简单的对象,很容易被创建。React DOM注重更新DOM匹配React 元素。

便签:

人们可能会混淆elements和一个更为熟知的components的概念。我们将会在下一节介绍components.Elements是components组成元素,我们鼓励你读完这节再往前跳。

渲染一个元素到DOM中

在你的HTML文件中某个地方有一个</div>

<div id="root"></div>

我们调用这个“root”DOM节点,因为React DOM管理其中的一切。

使用React构建的应用一般都有一个root DOM节点。如果你集成React到一个已存在的app,你可能有随你喜欢一样多的孤立的roor DOM节点。

渲染一个React元素到一个root DOM节点中,必须把两个都传给ReactDOM.render();

const element=<h1>Hello,world</h1>;

ReactDOM.render(

    element,

   document.getElementById('root')

);

在CodePen中尝试运行下。

会在页面上展示“Hello World”

更新渲染了的元素

React元素是不变的。一个element一旦创建,便不能改变其子节点或属性。一个element就像一部电影里的一个画面:在某个时间点上描绘UI。

按照我们所知道的,更新UI的唯一方式是创建新元素,并传递它给ReactDOM.render()。

思考一下滴答响闹钟的例子:

function tick(){

    const element=(

         <div>

                <h1>Hello,world!</h1>

               <h2>It is {new Date().toLocaleTimeString()}.</h2>

        </div>

     );

     ReactDOM.render(

           element,

           document.getElementById('root')

     );

}

setInterval(tick,1000);

在CodePen中尝试运行

每秒中由setInterval()回叫信息执行ReactDOM.render()

便签

实际上,大部分React apps仅执行一次ReactDOM.render()。在接下来的几个小节中,我们将学习这些代码是怎样被压缩在有状态的组件中的,stateful components.

我们建议你不要跳过任何主题因为他们彼此依赖。

React只更新必要的内容

React DOM 对比元素及其子节点和之前的,只适用必要的引起DOM销毁状态的更新部分。

你可以在浏览器中检查上述例子进行验证。

即使我们在每个滴答声创建一个描述整个UI树的元素,内容已变更的文本节点会被React DOM更新。

依我们的经验,思考UI应该怎样在给定的时间查看,而不是怎样随时间改变消除一系列bug

4.组件和属性

Components 将UI拆分成独立可复用的零件,可以单独考虑每个零件。

概述,components就像JavaScript的函数。他们接受武断的输入(called "props")和返回描述应该出现在屏幕上内容的元素。

函数和类组件

Functional and Class Components

定义一个组件最简单的方法是写一个JavaScript函数:

function Welcome(props){

      return <h1>Hello,{props.name}</h1>;

}

这个函数是一个有效的React component 因为它接收一个带数据的“props”对象参数并返回一个React元素。因为他们字面上是JavaScript函数,我们叫这些组件为"functional"

你也可以使用一个ES6 类来定义一个组件:

class Welcome extends React.Component{

     render(){

         return <h1>Hello,{this.pops.name}</h1>;

   }

}

以上两个组件从React的观点来看是等价的。

在接下来的几个小节中我们将讨论类增加的几个特性。目前,我们将先使用函数组件保证简洁。

渲染一个组件

以前,我们只鼓励表示DOM标签的React元素:

const element=<div />;

然而,元素也可以表示拥护自定义的组件:

const element=<Welcome name="Sara" />;

React看到表示用户自定义组件的元素时,会传递一个JSX属性对象给这个组件。我们称这个对象为“props”.

例如,这些代码在页面中渲染“Hello,Sara”:

function Welcome(props){

 return <h1>Hello,{props.name}</h1>;

}

const element=<Welcome name="Sara"/>;

ReactDOM.render(

       element,

     document.getElementById('root')

);

在CodePen上试试吧

让我们着重说明下在例子中发生了什么:

1.我们用<Welcome name="Sara" />元素来调用ReactDOM.render();

2.React 用{name:'Sara'}作为props来调用Welcome组件;

3.我们的Welcome组件返回<h1>Hello,Sara</h1>元素作为结果;

4.React DOM 有效更新DOM适用<h1>Hello,Sara</h1>;

警告:

一般以首字母大写命名组件。

例如,<div />表示一个DOM标签,但是<Welcome />表示一个组件和要求Welcome在环境中。

组成组件

组件可以在他们的输出中提及其他组件。这让我们从不同的具体实现中提取出相同的组件抽象。一个按钮,一个表单,一个对话,一个屏幕:在React apps,所有的那些都被表达为组件。

例如,我们创建一个App组件,用来多次渲染Welcome.

function Welcome(props){

     return <h1>Hello,{props.name}</h1>;

}

function App(){

  return (

     <div>

        <Welcome name="Sara" />

        <Welcome  name ="Cahal" />

       <Welcome name="Edite " ?>

     </div>

 );

}

ReactDOM.render(

    <App />;

    document.getElementById('root')

);

在CodePen上试试吧

代表性地,新的React apps在最顶层有一个App组件。然而如果你集成React到一个已经存在的App中,你可能要从一个像按钮一样的小组件自下而上开始,逐渐在页面层级的最高处运行你的方法。

警告:

组件必须返回一个root元素,这是因为我们增加一个<div>包含所有<Welcome />元素

提炼组件

大胆把组件拆成更小的组件。

例如,思考下Comment组件:

function Comment(props){

    return(

          <div className="Comment">

                <div className="UserInfo">

                       <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name}/>

                       <div className="UserInfo-name">{props.author.name}</div>

                </div>

                <div className="Comment-text">{props.text}</div>

                <div className="Comment-date">{formatDate(props.date)}</div>

          </div>

    );

}

在CodePen上试试吧。

它接受一个author(对象),text(字符串),日期(时间)属性在社交媒体网站描述评论。

这个组件对于改变是机警的因为全耦合,它也难以复用其中的个体部分。让我们从中提取几个组件。

首先我们提取Avatar:

function Avatar(props){

       return (

             <img className="Avatar"  src={props.user.avatarUrl} alt={props.user.name} /> 

      );

}

Avatar不需要知道它将被渲染到Comment中。所以我们将给它的属性一个一般的名字:user而不是author。

我们建议从组件本身的角度去命名属性,而不是它将被使用的上下文。

我们现在简化组件一丁点:

function Comment(props){

      return (

            <div  className="Comment">

                   <div className="UserInfo">

                         <Avatar user={props.author} />

                         <div className="UserInfo-name">{props.author.name}</div> 

                  </div>

                  <div className="Comment-text">{props.author.name}</div>

                  <div className="Comment-date">{formatDate(props.date)}</div>

            </div>

      );

}

接下来,我们将提取一个UserInfo组件,Avatar紧挨着user`s name:

function UserInfo(props){

      return(

           <div className="UserInfo">

                   <Avatar user={props.user} />

                  <div className="UserInfo-name">

                           {props.user.name}

                  </div>

            </div>

     );

}

让我们更进一步简化Comment:

function Comment(props){

      return (

            <div className="Comment">

                   <UserInfo user={props.author} />

                   <div className="Comment-text">

                      {props.text}

                  </div>

                  <div className="Comment-date">

                    {formatDate(props.date)}

                  </div> 

           </div>

     );

}

在CodePen中试试吧。

起初提取组件似乎看起来像grunt 重组代码,但在大型app中复用组件可以成功调节。一个好的做法是如果你的UI的一部分被多次使用(Button,Panel,Avatar),或者足够复杂,复用组件是一个很好的选择。

属性是只读的

无论你是以一个函数或者是一个类的形式声明一个组件,都决不能修改它的属性。思考sum函数:

function sum(a,b){

    return a+b;

}

像这样的函数被称为纯函数,因为它们不视图修改它们的输入,总是返回和输入一样的结果。

与此相反,这个函数不是纯函数,因为它改变自己的输入:

function withdraw(account,amount){

      account.total-=amount;

}

React 是相当灵活的,但是它有一个严格的规则:

All React components must act like pure functions with respect to their props.

所有React组件必须向纯函数一样尊重它们的属性。

当然,应用UI是动态的,随时间变化的。在下一节,我们将介绍"state"新概念。在这个规律下,State允许React组件因户行为响应,网络响应,其他任何的随时改变它们的输出。

5.状态和生命周期

思考前面小节的一个时钟滴答例子。

到目前为止我们仅学习了更新UI的一个方法。

我们调用ReactDOM.render()来更新渲染的输出:

function tick(){

    const element=(

         <div>

              <h1>Hello,world!</h1>

              <h2>It is {new Date().toLocaleTimeString()}.</h2>

         </div>

    );

    ReactDOM.render(

        element,

        document.getElementById('root')

   );

}

setInterval(tick,1000)

在CodePen中试试吧。

在这节中,我们将学习如何真正地复用和封装Clock组件。它将建立自己的计时器和每秒中更新自己。

我们从如何封装clock开始:

function Clock(props){

    return(

          <div>

                <h1>Hello,world!</h1>

                <h2>It is {props.date.toLocaleTimeString()}.</h2>    

         </div>

    );

}

function tick(){

     ReactDOM.render(

        <Clock date={new Date()} />,

        document.getElementById('root') 

    );

}

setInterval(tick,1000);

在CodePen中试试吧。

然而它遗漏了一个至关重要的需求:Clock建立计时器每秒钟更新UI是Clock实现细节的事实。

理想地,我们想要写一次,Clock更新自己。

ReactDOM.render(

    <Clock />,

    document.getElementById('root')

);

为了实现这个,我们需要给Clock组件加“state”。

State和props是相似的,但是它是私有的,完全被组件控制的。

我们之前提到过被定义成类的组件有一些附加特性。

Local state is exactly that:a feature available  only to classes.

本地State正是如此:仅适用类的特性。

将函数转换为类

用以下五步,你可以将像Clock的函数转换为类:

1.Create an ES6 class with the same name that extends React.Component.

用相同的名字创建一个ES6类继承React.Component.

2.Add a single empty method to it called render().

添加一个render()空方法。

3.Move the body of the function into the render() method.

将函数体放在render()方法中。

4.Replace props with this.props in the render() body.

在render()方法体中用this.props替换掉props.

5.Delete the remaining empty function declaration.

删除剩下的空函数声明。

class Clock extends React.Component{

        render(){

              return(

                    <div>

                        <h1>Hello,world!</h1>

                       <h2>It is {this.props.date.toLocaleTimeString()}.</h2> 

                   </div>

              ); 

       }

}

在CodePen上试试吧。

Clock现在被定义为一个类而不是函数。

这让我们可以使用像本地State和生命周期挂钩的特性。

添加本地State到一个类。

我们用三步将date从props移至steps:

1)Replace this.props.date with this.state.date in the render()method:

在render()方法中用this.state.date替换this.props.date.

class Clock extends React.Component{

      render(){

           return(

                <div>

                      <h1>Hello,world</h1>

                     <h2>It is {this.state.date.toLocaleTimeString()}.</h2> 

               </div>

           );   

      }

}

2)Add a class constructor that assigns the initial this.state:

添加一个类构造函数,指定初始的this.state:

class Clock extends React.Component{

    constructor(props){

         super(props);

         this.state={date:new Date()};

    }

    render(){

        return(

              <div>

                    <h1>Hello,world!</h1>

                    <h2>It is {this.state.date.toLocaleTimeString()}.</h2> 

             </div>

       );

   }

}

注意我们怎样将props传递给基础构造函数:

constructor(props){

     super(props);

     this.state={date:new Date()};

}

类组件总会调用带有props的构造函数。

3)Remove the date prop from the <Clock /> element:

ReactDOM.render(

    <Clock />,

    document.getElementById('root')

);

稍后我们将计时器代码添加到组件本身上。

结果像这样:

class Clock extends React.Component{

     constructor(props){

           super(props);

          this.state={date:new Date()};

     }

     render(){

         return(

             <div>

                   <h1>Hello,world!</h1>

                   <h2>It is {this.state.date.toLocaleTimeString()}.</h2>

             </div>

         );

     }

}

ReactDOM.render(

      <Clock />,

       document.getElementById('root')

);

在CodePen试试吧。

接下来,我们将给Clock创建计时器并每秒钟更新一下自己。

添加生命周期到类中

在有许多组件的应用中,组件被销魂腾出的空间是很重要的。

一旦Clock组件被渲染到DOM中,我们就创建一个计时器。在React称之为"mounting"。

一旦由Clock组件产生的DOM被移除,我们也想清楚计时器。在React中称之为“unmounting”。

当组件mount和unmount时,我们可以在组件类的特定方法中运行一些代码:

class Clock extends React.Component{

       constructor(props){

            super(props);

            this.state={date:new Date()};

       }

       componnetDidMount(){

        }

        componentWillUnmount(){

       }

       render(){

            return(

                  <div>

                        <h1>Hello,world!</h1>

                        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>

                  </div>

            ); 

      }

}

这些方法被称为“lifecycle hooks”。

组件输出被渲染至DOM后,componentDidMount()运行。这是一个放置计时器的好地方:

componentDidMount(){

   this.timerID=setInterval(

      ()=>this.tick(),1000 

   );

}

注意我们怎样正确地将timer ID 保存在this上。

While this.props is set up by React itself and this.state has a special meaning,you are free to add additional fields to the class manually if you need to store something that is not used for the visual output.

this.props由React自己创建,this.state有特定意义,如果你需要存储一些不会输出的字段,你可以随意手动添加给类。

没有在render()中使用的内容不会在state中。

我们将在componentWillUnmount()生命循环钩中销毁计时器

componentWillUnmount(){

   clearInterval(this.timerID);

}

最后,我们将实现每秒钟运行一次的tick()方法。

它将用this.setState()计划组件本地State的更新:

class Clock extends React.Component{

     constructor(props){

         super(props);

         this.state={date:new Date()};

     }

     componentDidMount(){

         this.timerID=setInterval(

              ()=>this.tick(),

              1000

          );

     }

     componentWillUnmount(){

         clearInterval(this.timerID); 

     }

     tick(){

         this.setState({

             date:new Date()

          });

     }

     render(){

           return(

                  <div>

                        <h1>Hello,world!</h1>

                        <h2>It is {this.state.date.toLocaleTimeString}.</h2>

                  </div>

           );

     }

}

ReactDOM.render(

      <Clock />,

       document.getElementById('root')

);

在CodePen上试试吧。

现在时钟每秒钟可以滴答一次了。

我们快速扼要重述下正在进行的事情和方法被调用的顺序:

1)当<Clock />被传送给ReactDOM.render()时,React调用Clock组件的构造函数。因为Clock需要展示当前时间,它需要用一个包含当前时间的对象初始化this.state。我们以后将更新这个状态。

2)然后React调用Clock组件的render()方法。这是React怎样得知什么应该显示在屏幕上。然后React更新DOM匹配Clock的render的输出。

3)当Clock输出被插入到DOM时,React调用componentDidMount()生命循环钩。在这个方法中,Clock组件要求浏览器建立一个计时器每一秒调一次tick()

4)浏览器每秒钟调用一次tick()方法。在这个方法中,Clock组件计划调用包含当前时间对象的setState()方法更新一次UI。由于setState()方法调用,React知道状态已经发生变化,再次调用render()方法得知什么应该显示在屏幕上。这次,render()方法的this.state.date将是不同的,因此render输出将包含更新时间。React相应更新DOM。

5)如果Clock组件从DOM中移除,React调用componentWillUnmount()生命循环钩子因此计时器被停止。

正确使用状态

关于setState()有三个你应该知道的事情。

不直接修改状态

例如,不重复渲染组件:

this.state.comment='Hello';//wrong

相反,使用setState():

this.setState({comment:'Hello'});

The only place where you can assign this.state is the constructor.

你可以声明this.state的唯一地方是构造函数。

状态更新可能是异步。

React可能将多个setState()批量更新。

因为this.props和this.state可能被异步更新,所以你不应该依赖他们的值去计算下一个状态。

例如,以下代码更新计数器可能会失败:

//Wrong

this.setState({

    counter:this.state.counter +this.props.increment

});

修正以上代码,使用setState()的第二种方式:接受一个函数而不是对象。这个函数将接受以前的状态作为第一个参数,在更新的时候属性会被作为第二个参数:

this.setState(

     (prevState,props)=>({counter:prevState.counter+props.increment})

);

以上我们使用一个箭头函数,但是它也可以传入标准函数:

this.setState(function(prevState,props){

        return {

             counter:prevState.counter +props.increment 

       };

});

State更新被合并

当你调用setState()时,React融合你提供的对象给当前的状态。

例如,你的状态可能包含几个独立的变量:

constructor(props){

  super(props);

  this.state={

      posts:[],

      comments:[]

   }

}

那么你可以分开调用setState()独立更新他们:

componentDidMount(){

    fetchPosts().then(

         response=>{

            this.setState({

                 posts:response.posts  

          }); 

        }

    );

    fetchComments().then(

        response=>{

              this.setState(

                  comments:response.comments

              ); 

        }

    );

}

The merging is shallow,so this.setState({comments}) leaves this.state.posts intact,but completely replaces this.state.comments.

合并是浅合并,因此this.setState({comments})保持this.state.posts原封不动,但是可以完全替换this.state.comments.

数据下流

不管是父组件还是子组件都无法知晓某个组件是有状态还是没有状态,他们也不关心它是作为函数被定义的,还是作为类被定义的。

这是因为state经常被称为本地的或封装的。除了拥有并可设置它的组件外,其他任何组件都是不可访问的。

A component may choose to pass its state down as props to its child components:

一个组件可以选择将它的状态作为属性传递给它的子组件:

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

这也适用在用户定义组件。

<FormattedDate date={this.state.date} />

FormattedDate组件在属性中接受date,并不清楚它来自Clock的state,还是Clock的props,还是手动输入的:

function FormattedDate(props){

   return <h2>It is {props.date.toLocaleTimeString()}.</h2>

}

在CodePen上试试吧。

这普遍被称为“自上而下‘或”单向“数据流。任何状态总是被一些特定的组件所拥有,由state衍生的任何数据或UI仅影响组件树下的他们。

如果你将组件树想象成属性瀑布,每个组件状态就像一个新增的水资源,将它随意加在一个位置但是也向下。

为了展示所有组件都是真实孤立的,我们创建一个渲染 <Clock>树的App组件:

function App(){

     return(

          <div>

              <Clock />

              <Clock />

               <Clock />

         </div>

     );

}

ReactDOM.render(

     <App  />,

      document.getElementById('root')

);

在CodePen试试吧。

每个Clock建立自己的计时器并独立更新。

在React apps中,组件是否有状态被认为是组件可能随时间变化的实现细节。你可以在有状态的组件中使用无状态的组件,反之亦然。


长篇大论,阅读和查找体验不好,已拆分成小节发表,之后会按小节进行更新,三人行必有我师焉,请多指教。。。。。。

























































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值