React.js 基础概念一览 已翻译 100%

oschina 投递于 2017/11/06 11:04 (共 15 段, 翻译完成于 11-17)
阅读 3218
收藏 59
2
加载中

去年我写过一本100多页的学习 React.js 的书,今年要挑战一下自己把那本书精炼成Medium上的一篇文章。

这篇文章不会包含什么是 React 或者为什么要学习 React 这样的内容,这篇文章是对于已经熟悉 JavaScript 和 DOM API 基础的人员的一个实践入门。

下面的所有代码示例都有标签索引,示例只是各种概念的一个展示。大多数的示例都可以用一种更好的方法写出来。

soaring
翻译于 2017/11/06 22:16
0

准则 #1: React 的所有内容都是关于组件的

React 是围绕可重用组件的概念设计的,定义所有的小组件,然后将小组件合成大组件。

所有组件不管大小都是可重用的,而且也是跨工程的。

一个组件的最简单的形式是一个 JavaScript 函数:

// Example 1
// https://jscomplete.com/repl?j=Sy3QAdKHW
function Button (props) {
  // Returns a DOM element here. For example:
  return <button type="submit">{props.label}</button>;
}
// To render the Button component to the browser
ReactDOM.render(<Button label="Save" />, mountNode)

标签 button 里面的大括号的意思在后边说明,ReactDOM 也将在后边内容解释。如果想要测试这个例子和后边所有的代码例子,你需要上边的 render 函数。

ReactDOM.render 的第二个参数是 React 将要挂载的目标 DOM 元素。在 jsComplete REPL 中,只能使用特殊的变量 mountNode 。

Example 1 的注意事项:

  • 组件名必须首字母大写。由于需要处理混合的 HTML 元素和 React 元素,所以组件名必须首字母大写,小写的名称留给 HTML 元素。事实上,如果把上边的 React 组件名字改为 “button” , ReactDOM 将会忽略该函数并渲染一个普通的空 HTML 按钮。

  • 像 HTML 元素一样,每个组件都接收一个属性列表,在 React 里面这个列表被称作 props 。通过函数组件,可以对 props 传递任何数据。

  • 上边的 Button 组件返回里面写了一段很奇怪的类似 HTML 的代码,这段代码不是 JavaScript 代码,也不是 HTML 代码,也不是 React.js 。这种代码格式如此流行以至于 React 应用将其作为默认的代码语言。这种语言叫 JSX ,是 JavaScript 的一个扩展。 JSX 也是一种折中的语言!尝试在上边代码中返回另一种 HTML 元素,看一下 JSX 是怎样被支持的(例如返回一个文本输入框)。

soaring
翻译于 2017/11/06 23:12
0

准则 #2: 什么是flux的JSX


上面的例子1可以用没有JSX的纯React.js编写,如下所示:

// 示例2 - 没有JSX的React组件 // https://jscomplete.com/repl?j=HyiEwoYB-
function Button (props) {
  return React.createElement(
    "button",
    { type: "submit" },
    props.label
  );
}
// 使用Button,你会做类似的事情 ReactDOM.render(
  React.createElement(Button, { label: "Save" }),
  mountNode
);

createElement函数是React顶级API的主要函数。这是你需要学习的7件事情中的1件。这就是React API的多小。
就像DOM本身具有一个document.createElement函数来创建一个由标签名称指定的元素一样,React的createElement函数是一个更高级的函数,可以执行document.createElement的操作。但它也可以用来创建一个元素来表示一个React组件。当我们使用上面例2中的Button组件时,我们做了后者。
与document.createElement不同的是,React的createElement接受第二个参数后的动态数量的参数来表示创建的元素的子元素。所以createElement实际上创建了一棵tree

亚林瓜子
翻译于 2017/11/13 15:38
0

下边是一个创建树的例子:

// Example 3 -  React’s createElement API
// https://jscomplete.com/repl?j=r1GNoiFBb
const InputForm = React.createElement(
  "form",
  { target: "_blank", action: "https://google.com/search" },
  React.createElement("div", null, "Enter input and click Search"),
  React.createElement("input", { name: "q", className: "input" }),
  React.createElement(Button, { label: "Search" })
);
// InputForm uses the Button component, so we need that too:
function Button (props) {
  return React.createElement(
    "button",
    { type: "submit" },
    props.label
  );
}
// Then we can use InputForm directly with .render
ReactDOM.render(InputForm, mountNode);

其中有一些值得注意的地方:

  • InputForm 是一个 React 元素,不是一个 React 组件。这就是为什么在 ReactDOM.rander 中直接使用 InputForm 而不写成 <InputForm /> 的原因。

  • 函数 React.createElement 在前两个参数后面还可以接收多个参数。从第三个参数开始的所有参数组成要创建的元素的子元素列表。

  • 因为是 JavaScript 代码,React.createElement 函数可以嵌套调用。

  • 如果要创建的元素没有属性或者 props 需要设置,React.createElement 的第二个参数可以为空。

  • HTML 元素可以和 React 组件混用,可以将 HTML 元素看成是 React 的内建组件。

  • React API 很贴近 DOM API ,所以在 input 元素里面需要使用 className 代替 class 。由于 React API 很优秀,窃希望 React API 可以成为 DOM API 一部分。

soaring
翻译于 2017/11/07 22:56
0

上边的代码是包含 React 库浏览器解析的代码,浏览器并不处理任何 JSX 代码。人类喜欢处理 HTML 而不是一大堆的函数 createElement 调用 (想想一下只能用 document.createElement 创建网页)。这就是 JSX 存在的原因。上边的代码可以使用一个非常类似 HTML 的语法来实现,而不是通过 React.createElement 实现:

// Example 4 - JSX (compare with Example 3)
// https://jscomplete.com/repl?j=SJWy3otHW
const InputForm =
  <form target="_blank" action="https://google.com/search">
    <div>Enter input and click Search</div>
    <input name="q" className="input" />
    <Button label="Search" />
  </form>;
// InputForm "still" uses the Button component, so we need that too.
// Either JSX or normal form would do
function Button (props) {
  // Returns a DOM element here. For example:
  return <button type="submit">{props.label}</button>;
}
// Then we can use InputForm directly with .render
ReactDOM.render(InputForm, mountNode);

上边的代码有个需要注意的地方:

上边的代码并不是 HTML 代码,里面依旧用 className 代替 class

虽然上边的代码类似 HTML ,但应该被认为是 JavaScript ,代码的结尾加了分号。

上边的代码就是(示例4) JSX 。传给浏览器的代码是示例 4 编译之后的版本示例 3 。示例 4 变成示例 3 需要使用预处理器将 JSX 代码转为 React.createElemnt 版本的代码。

通过 JSX 采用类似 HTML 的语法书写 React 组件是一个很好的想法。

标题的单词 “Flux” 使用来押韵的,“Flux”也是一个非常流行的 Facebook 应用框架。最著名的一个实现版本是 Redux 。Flux 完美的适用于 React 交互模式。

顺便说一下,JSX 可以独立使用,而不只是依赖 React 。

soaring
翻译于 2017/11/08 22:36
0

准则 #3 在 JSX 的任何地方都可以使用 JavaScript 表达式

可以将任何 JavaScript 表达式包含在大括号里放在 JSX 代码里面。

// Example 5 -  Using JavaScript expressions in JSX
// https://jscomplete.com/repl?j=SkNN3oYSW
const RandomValue = () => 
  <div>
    { Math.floor(Math.random() * 100) }
  </div>;
// To use it:
ReactDOM.render(<RandomValue />, mountNode);

任何 JavaScript 表达式都可以放到 JSX 代码中的大括号里面类似于 JavaScript 模板语法中的插入语法${}。

JSX 内嵌 JavaScript 代码的限制:只能是表达式。例如不能使用 if 语句但是使用三元表达式是可以的。

JavaScript 变量也是表达式,所以当组件接收 props 列表( props 是可选的,RandomValue 组件没有),可以把 props 放到大括号里面嵌入 JSX 中,上边的 Button 组件就是这么做的(示例1)。

soaring
翻译于 2017/11/08 21:51
0

JavaScript 对象也是表达式,所以 JavaScript 对象也可以包含在大括号里面嵌入 JSX 代码中。这样虽然有点像两个大括号,但仅仅是一个对象放到了一个大括号里面。在 React 里面传递 CSS 对象的时候就是这么用的:

// Example 6 - An object passed to the special React style prop
// https://jscomplete.com/repl?j=S1Kw2sFHb
const ErrorDisplay = ({message}) =>
  <div style={ { color: 'red', backgroundColor: 'yellow' } }>
    {message}
  </div>;
// Use it:
ReactDOM.render(
  <ErrorDisplay 
    message="These aren't the droids you're looking for" 
  />,
  mountNode
);

上边代码值得注意的是:1. message 是如何从 props 中解构出来。2.上边用一个对象来作为样式属性的值是一种特殊情况 (非HTML,只是类似DOM API),对象中定义样式值和在 JavaScript 定义样式值是一样的。

React 元素是一个函数调用,也是一个表达式,所以也可以放到 JSX 里面:

// Example 7 - Using a React element within {}
// https://jscomplete.com/repl?j=SkTLpjYr-
const MaybeError = ({errorMessage}) =>
  <div>
    {errorMessage && <ErrorDisplay message={errorMessage} />}
  </div>;
  
// The MaybeError component uses the ErrorDisplay component:
const ErrorDisplay = ({message}) =>
  <div style={ { color: 'red', backgroundColor: 'yellow' } }>
    {message}
  </div>;
// Now we can use the MaybeError component:
ReactDOM.render(
  <MaybeError
    errorMessage={Math.random() > 0.5 ? 'Not good' : ''}
  />,
  mountNode
);

上边例子中,当有错误消息字符串传递给 MaybeError 组件的时候,MaybeError 组件会显示 ErrorDisplay 组件。{true}、{false}、{undefined} 和 {null} 在 React 里面是有效元素,不渲染任何事物。

JavaScript 集合方法 (map, reduce, filter, concat 等) 返回的是表达式,所以也可以在 JSX 里面调用所有的集合方法。

// Example 8 - Using an array map inside {}
// https://jscomplete.com/repl?j=SJ29aiYH-
const Doubler = ({value=[1, 2, 3]}) =>
  <div>
    {value.map(e => e * 2)}
  </div>;
// Use it
ReactDOM.render(<Doubler />, mountNode);

上边例子中注意如何给 prop 属性 value 设置默认值。上边例子里面在一个 div 标签里面输出里一个数组表达式,这在 React 里面是可以的,React 会把数组里面每个元素都放到一个文本节点中。

soaring
翻译于 2017/11/09 23:53
0

准则 #4: 可以使用JavaScript 类书写React组件

对于简单的需求简单的函数组件很合适,有时候需要更复杂功能的组件。React也支持JavaScript类语法来创建组件。下面的例子就是示例1的类语法书写方式:

// Example 9 - Creating components using JavaScript classes
// https://jscomplete.com/repl?j=ryjk0iKHb
class Button extends React.Component {
  render() {
    return <button>{this.props.label}</button>;
  }
}
// Use it (same syntax)
ReactDOM.render(<Button label="Save" />, mountNode);



类语法是比较简单的,定义一个React组件类需要继承自React.Component(另一个需要学习的React上层API)。React类必须定义一个返回虚拟DOM对象的render()函数。每次使用上边的Button类的时候(例如<Button ... />),React会实例化一个基于该类组件的对象并将该对象放到DOM树里面。

每一个组件实例化的时候都会接收一个包含所有传递给该组件的所有数据的实例属性props,所以可以在JSX里面使用this.props.label来渲染上边示例的输出。

上边例子是使用该类组件生成的一个实例,可以按需求定制化该实例。例如可以用JavaScript constructor函数在组件创建之后定制实例,如下:

// Example 10 -  Customizing a component instance
// https://jscomplete.com/repl?j=rko7RsKS-
class Button extends React.Component {
  constructor(props) {
    super(props);
    this.id = Date.now();
  }
  render() {
    return <button id={this.id}>{this.props.label}</button>;
  }
}
// Use it
ReactDOM.render(<Button label="Save" />, mountNode);

也可以在类里面定义类原型函数并在任何需要的地方包括在要返回的JSX输出中使用该函数:

// Example 11 — Using class properties
// https://jscomplete.com/repl?j=H1YDCoFSb
class Button extends React.Component {
  clickCounter = 0;
handleClick = () => {
    console.log(`Clicked: ${++this.clickCounter}`);
  };
  
  render() {
    return (
      <button id={this.id} onClick={this.handleClick}>
        {this.props.label}
      </button>
    );
  }
}
// Use it
ReactDOM.render(<Button label="Save" />, mountNode);



示例11需要注意的地方有:

handleClick函数使用了JavaScript中新的语法类字段语法。这种语法虽然在草案阶段,但由于多种原因这种方法是获取组件的挂载实例的最好选择(感谢箭头函数)。上边的代码想要运行需要使用类似于babel的编译器并且配置识别stage-2(或者类字段语法)选项编译之后才可以运行。JSComplete REPL预先做了配置。

clickCounter也是用类字段语法定义的实例变量,这样书写就完全可以不使用类构造函数。

当把handleClick函数赋给React属性onClick的时候并没有调用该函数,仅仅是传递了handleClick函数的一个引用。在该地方写成函数调用是React编程中经常遇到的一个错误。

// Wrong:
onClick={this.handleClick()}
// Right:
onClick={this.handleClick}



soaring
翻译于 2017/11/13 23:13
0

准则 #5:React 事件:与 DOM 事件的两个不同点

处理 React 元素里面的事件的时候,与 DOM API 相比有两个重要的不同:

所有 React 元素属性(包括事件)都使用驼峰命名规则而不是小写,例如应该写成 onClick 而不是 onclick 。

需要传递实际的 JavaScript 函数索引作为 React 元素事件处理函数而不是字符串,例如应该写成 onClick={handleClick} 而不是 onClick=“handleClick” 。

React 用一个 React 对象包装 DOM 事件以优化事件处理性能。React 在事件处理器里面仍可以获得所有 DOM 事件对象的方法。React 将包装后的对象传递每一个事件调用。例如,想禁止 form 表单默认的提交事件可以采用下面的做法:

// Example 12 - Working with wrapped events
// https://jscomplete.com/repl?j=HkIhRoKBb
class Form extends React.Component {
  handleSubmit = (event) => {
    event.preventDefault();
    console.log('Form submitted');
  };
  
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <button type="submit">Submit</button>
      </form>
    );
  }
}
// Use it
ReactDOM.render(<Form />, mountNode);
soaring
翻译于 2017/11/07 16:42
0

准则#6:每个React组件都有一个故事

以下内容仅适用于类组件(那些扩展了React.Component的组件)。函数组件有一个稍微不同的故事。

  1. 首先,我们为React定义一个模板,以从组件创建元素。

  2. 然后,我们指示React在某处使用它。例如,在另一个组件的rendercall中,或者使用ReactDOM.render

  3. 然后,React实例化一个元素并给它一组我们可以通过this.props访问的props。那些props正是我们在上面第二步所传递的。

  4. 由于它是全部的JavaScript,因此将调用构造函数方法(如果已定义)。这是我们所说的第一个组件生命周期方法。

  5. React然后计算渲染方法(虚拟DOM节点)的输出。

  6. 由于这是React首次渲染元素,因此React将与浏览器(代表我们使用DOM API)在那里显示元素。这个过程通常被称为挂载中

  7. React然后调用另一个称为componentDidMount的生命周期方法。例如,我们可以使用这种方法在DOM上做一些我们现在知道存在于浏览器中的东西。在此生命周期方法之前,我们所使用的DOM都是虚拟的。

  8. 一些组件故事在这里结束。其他组件由于各种原因从浏览器DOM卸载。就在后者发生之前,React调用另一个生命周期方法componentWillUnmount

  9. 任何挂载元素的状态可能会改变。该元素的父母可能会重新呈现。无论哪种情况,被挂载的元素都可能会收到一组不同的props。神奇的反应发生在这里,我们实际上开始需要在这一点上的React!在此之前,我们根本就不需要React

  10. 这个组件的故事在继续,但是在这之前,我们需要了解这个我所说的状态

亚林瓜子
翻译于 2017/11/14 14:31
0
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
返回顶部
顶部