文档

1. 快速入门

1.1. 安装

React是灵活的,可以在各种项目中使用。您可以使用它创建新的应用程序,但是您也可以在不重写的情况下逐步将其引入到现有的代码中。

下面是一些开始的方式:

  • 尝试React
  • 创建新的应用
  • 添加React到现有的应用

1.1.1. 尝试React

//TODO

1.1.2. 创建新的应用

Create React App是开始构建一个新的React应用程序的最佳方式。它设置了您的开发环境,以便您可以使用最新的JavaScript特性,提供良好的开发体验,并面向产品优化您的应用程序。您需要在您的机器上有大于等于6的Node。

npm install -g create-react-app
create-react-app my-app

cd my-app
npm start

如果您已经安装了npm 5.2.0+,您可以使用npx代替。

npx create-react-app my-app

cd my-app
npm start

Create React App不处理后端逻辑或数据库,它只是创建了一个前端构建途径,所以您可以使用它与任何后端。它在底层使用像Babel和webpack这样的构建工具,但是是零配置工作。

当您准备部署到生产时,运行npm run build将在build文件夹中创建您的应用程序的优化构建。你可以从它的README和用户指南中学到更多关于Create React App的信息。

1.1.3. 添加React到现有的应用

//TODO

1.2. Hello World

最简单的开始React的方法是使用CodePen上的Hello World示例代码。你不需要安装任何东西,您可以在另一个选项卡中打开它,并在我们浏览示例时跟随它。如果您更愿意使用本地开发环境,请参考安装页面。

最小的React例子看起来像这样:

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);

它在页面上渲染一个标题:“Hello, world!”。

接下来的几节将逐步介绍你使用React。我们将介绍React应用程序的构建块:元素和组件。一旦你掌握了它们,你就可以从小的可重复使用的部分创建复杂的应用程序。

1.2.1. 关于JavaScript的提示

React是一个JavaScript库,因此它假定您对JavaScript语言有基本的了解。如果你感觉不太自信,我们建议你更新你的JavaScript知识,这样你就可以更容易地跟上。

我们还在示例中使用了一些ES6语法。我们尽量少使用它,因为它仍然相对较新,但是我们鼓励您熟悉arrow functions、classes、template literals、let和const语句。您可以使用Babel REPL检查ES6代码编译的内容。

1.3. 介绍JSX

思考这个常量声明:

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

这个有趣的标签语法既不是字符串也不是HTML。

它被称为JSX,它是JavaScript的语法扩展。我们推荐在React中使用它来描述UI应该是什么样的。JSX可能会使您想起一种模板语言,但它具有JavaScript的全部功能。

JSX产生React“元素”。我们将在下一节中探索将它们呈现给DOM。下面,您可以找到开始React需要的JSX的基础知识。

//TODO

1.4. 渲染元素

元素是React应用程序的最小构建块。

一个元素描述了您希望在屏幕上看到的内容:

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

与浏览器DOM元素不同,React元素是普通对象,而且创建起来很便捷。React DOM负责更新DOM以匹配React元素。

注意:

有人可能会混淆元素与更广为人知的“组件”的概念。我们将在下一节介绍组件。元素是由组件组成的,我们鼓励您在跳过前阅读本节。

1.4.1. 将一个元素渲染到DOM中

假设在HTML文件中某个位置有一个< div >:

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

我们将此称为“根”DOM节点,因为它内部的所有内容都由React  DOM管理。

React构建的应用程序通常只有一个根DOM节点。如果您正在将React集成到一个现有的应用程序,那么您可能会有正如您所需要的许多单独的根DOM节点。

要将一个React元素呈现到一个根DOM节点中,请将两个参数传递给ReactDOM.render():

const element = <h1>Hello, world</h1>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

Try it on CodePen

它在页面上显示“Hello,world”。

1.4.2. 更新已渲染的元素

//TODO

1.5. 组件和属性

组件让您将UI分割成独立的可重用的块,并独立地考虑每一个块。

从概念上讲,组件就像JavaScript函数。他们接受任意输入(称为“props”)并返回描述什么应该出现在屏幕上的React元素。

1.5.1. 功能的和类组件

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

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

这个函数是一个有效的React组件,因为它接受一个单独的“props”(它代表属性)数据对象参数,并返回一个React元素。我们称这些组件为“功能性的”,因为它们实际上是JavaScript函数。

您还可以使用ES6类来定义组件:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

上述两个组件从React的角度来说是等价的。

类有一些额外的特性,我们将在下一节中讨论。在此之前,我们将使用功能组件,因为他们比较简洁。

1.5.2. 渲染一个组件

先前,我们只遇到代表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')
);

Try it on CodePen

让我们回顾一下在这个例子中发生了什么:

  1. 我们调用ReactDOM.render()并传入<Welcome name="Sara" />元素。
  2. React用{name: 'Sara'}作为属性来调用Welcome组件。
  3. 我们的Welcome组件返回一个<h1>Hello, Sara</h1>元素作为结果。
  4. React DOM有效地更新DOM以匹配<h1>Hello, Sara</h1>。

告诫

总是用大写字母开始组件名称。

例如,<div />表示一个DOM标记,但是<Welcome />表示一个组件,且需Welcome在范围内。

1.5.3. 构成组件

组件可以在其输出中涉及其他组件。这使我们可以对任何级别的细节使用相同的组件抽象。一个按钮,一个表单,一个对话框,一个屏幕: 在React应用程序中,所有这些都被普遍表示为组件。

例如,我们可以创建一个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')
);

Try it on CodePen

通常,新的React应用程序在最顶部有一个App组件。然而,如果你整合React到一个现有的应用程序,你可以通过一个类似于Button的小组件开始自底向上,然后逐渐地进入到视图层级的顶端。

1.5.4. 提取组件

不要害怕将组件分割成更小的组件。

例如,请思考以下Comment组件:

function Comment(props) {
  return (
    <div className="comment">
      <div className="user-info">
        <img className="avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="user-info-name">
          {props.author.name}
        </div>
      </div>
      <div className="comment-text">
        {props.text}
      </div>
      <div className="comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

Try it on CodePen

它接受author(一个对象)、text (一个字符串)和date(一个日期)作为props,并在社交媒体网站上描述评论。

由于所有的嵌套,这个组件可能很难更改,而且很难重用它的个别部分。让我们从它中提取一些组件。

首先,我们将提取Avatar:

function Avatar(props) {
  return (
    <img className="avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

Avatar不需要知道它是在Comment中渲染的。这就是为什么我们给它提供了一个更通用的属性名:user而不是author。

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

现在我们可以让Comment简化一点:

function Comment(props) {
  return (
    <div className="comment">
      <div className="user-info">
        <Avatar user={props.author} />
        <div className="user-info-name">
          {props.author.name}
        </div>
      </div>
      <div className="comment-text">
        {props.text}
      </div>
      <div className="comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

接下来,我们将提取一个UserInfo组件,该组件将在用户名旁边渲染一个Avatar:

function UserInfo(props) {
  return (
    <div className="user-info">
      <Avatar user={props.user} />
      <div className="user-info-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>
  );
}

Try it on CodePen

首先,提取组件似乎是一种枯燥乏味的工作,但是拥有一个可重用组件的调色板在更大的应用程序中是有回报的。一个好的经验法则是,如果你的UI的一部分被多次使用(Button、Panel、Avatar),或者它自己足够复杂(App、Avatar),那么它是可重用组件的一个好的候选者。

1.5.5. props是只读的

//TODO

1.6. 状态和生命周期

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

到目前为止,我们只学习了一种更新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);

Try it on CodePen

在本节中,我们将学习如何使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);

Try it on CodePen

然而,它忽略了一个关键的要求:为Clock设置一个计时器,每秒钟更新UI应该是Clock的实现细节。

理想情况下,我们想一旦写了它,就让Clock自己更新:

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

要实现这一点,我们需要向Clock组件添加“state”。

状态类似于属性,但它是私有的,由组件完全控制。

我们之前提到过,定义为类的组件有一些额外的特性。本地状态就是这样:只提供给类的特性。

1.6.1. 将函数转换为类

您可以在五个步骤内将像Clock这样的功能组件转换为一个类:

  1. 创建一个具有相同名称的ES6类,它继承React.Component。
  2. 添加一个名为render()的空方法。
  3. 将函数体移动到render()方法中。
  4. 在render()方法体中,用this.props替换props。
  5. 删除剩余的空函数声明。
class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

Try it on CodePen

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

这使我们可以使用其他特性,例如本地状态和生命周期钩子。

1.6.2. 给类添加本地状态

我们将三个步骤把date从props移动到state:

1. 在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. 添加一个类构造器,它分配初始的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. 从<Clock / >元素中移除日期属性:

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

Try it on CodePen

接下来,我们将让Clock设置自己的计时器,并每秒钟更新一次。

1.6.3. 向类中添加生命周期方法

在有许多组件的应用程序中,当组件被销毁时,释放组件占用的资源是非常重要的。

我们想每当Clock第一次被渲染到DOM时设置一个计时器。在React中这被称为“挂载”。

我们还想每当Clock产生的DOM被移除时清除计时器,在React中这被称为“ 卸载”。

我们可以在组件类上声明特殊方法,当组件挂载和卸载时运行一些代码:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {

  }

  componentWillUnmount() {

  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

这些方法被称为“生命周期钩子”。

componentDidMount()钩子在组件输出被渲染到DOM之后运行。这是一个设置计时器的好地方:

componentDidMount() {
  this.timerID = setInterval(
    () => this.tick(),
    1000
  );
}

注意我们如何在this中保存定时器ID。

虽然this.props是由React自己设置的,并且this.state有特殊的含义,但是如果您需要存储不用于可视化输出的内容,您可以自由地在类中手动添加其他字段。

如果您不在render()中使用某些东西,它也不应该出现在状态中。

我们将在componentWillUnmount()生命周期钩子中拆除计时器:

componentWillUnmount() {
  clearInterval(this.timerID);
}

最后,我们将实现一个名为tick()的方法,Clock组件将每秒钟运行一次。

它将使用this.setState()来调度更新组件的本地状态:

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

Try it on CodePen

现在时钟每秒钟运行一次。

让我们快速回顾一下正在发生的事情以及调用方法的顺序:

//TODO

1.7. 事件处理

React元素的事件处理非常类似于DOM元素的事件处理。有一些句法上的差异:

  • React事件以camelCase命名,而不是lowercase。
  • 使用JSX,您传递一个函数作为事件处理程序,而不是字符串。

例如,HTML:

<button onclick="activateLasers()">
  Activate Lasers
</button>

在React中稍微不同:

<button onClick={activateLasers}>
  Activate Lasers
</button>

另一个不同之处在于,您不能返回false阻止React中的默认行为。您必须明确地调用preventDefault。例如,使用纯HTML,为了阻止打开新页面的默认链接行为,您可以编写:

<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

在React中,这可以被替换成:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

这里,e是一个合成事件。根据W3C规范,React定义了这些合成事件,因此您不必担心跨浏览器的兼容性。查看SyntheticEvent参考指南了解更多信息。

当使用React时,通常不需要调用addEventListener在元素创建之后将侦听器添加到DOM元素。相反,只要在最初渲染元素时提供一个侦听器。

当您使用ES6类定义组件时,通常的模式是将事件处理程序定义成类的方法。例如,这个Toggle组件渲染一个按钮,让用户在“ON”和“OFF”状态之间切换:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Try it on CodePen

//TODO

猜你喜欢

转载自my.oschina.net/u/2539475/blog/1582161
今日推荐