几年前,我的一个朋友向我吹捧有个叫做 React 的框架会如何革新 Web 开发。其实一开始我觉得它只是又一个昙花一现的框架罢了。可在之后的时间里,React 名声鹊起,逐渐变得不容小觑了。

也许你和我之前的状况差不多:总是听到 React 这儿好那儿爽,可是真正坐下来学习 React 的时候又毫无头绪。

好消息是我在这里替你总结了,其实 React 只有“五大核心概念”。

不过也请不要误会我的意思,并不是说我一篇文章就能能让你摇身一变成为精通 React 的大神,但如果你打算或者正在学习 React 的话,我至少能帮你理清思路。

现在揭晓这五大核心概念:

  1. 组件
  2. JSX
  3. Props & State
  4. 组件 API
  5. 组件类型

概念一:React 组件的作用

你需要了解有关 React 的第一要点就是——组件。你编写的所有 React 代码基本上就是一个包含许多小组件在内的大组件。

那么到底什么是组件呢?我们可以拿 HTML 标签 <select> 来举一个很恰当的例子。原生的下拉框标签不止包括边框、文本、下拉箭头,它还掌控着自身打开关闭的逻辑。

现在来设想一下你需要构建一个你自定义样式和行为逻辑的 <select>:

这其实就是 React 能够帮你做到的。React 组件能够像原生的 HTML 标签一样输出特定的界面元素,并且也能包括一些元素相关逻辑功能的代码。

现在我们一般会用 ES6 的 Class 语法来声明一个 React 组件,它包含一个能够返回 HTML 的 render 方法。(当然也可以用函数声明,我们在之后会聊到)

class MyComponent extends React.Component {
  render() {
    return <p>Hello World!<p>;
  }
}

概念二:JSX 是什么玩意儿?

是的你没看错,按照上面 React 组件的示例代码,React 的意思就是让我们把 HTML 和 JS 代码全都写在一起。React 是通过一种叫做JSX的语法扩展(X 代表 XML)来实现的。

JSX 乍看起来可能很奇怪,不过你慢慢会习惯的。

是的我知道,按照我们以往的传统,应该尽量把 HTML 和 JavaScript 的代码分开才是。不过看样子现在忘记这教条才是提高你前端开发效率的正道。

我们还是来举几个 JSX 实际应用的例子吧,比如你可以通过 {} 大括号来在 JSX 中显示 JS 变量:

class MyComponent extends React.Component {
  render() {
    return <p>Today is: {new Date()}</p>;
  }
}

你不再需要什么前端模板标签之类的东西了,你可以直接在 JSX 中使用三元运算符一类的逻辑:

class MyComponent extends React.Component {
  render() {
    return <p>Hello {this.props.someVar ?  'World' : 'Kitty'}</p>;
  }
}

顺便提一句,可能你现在对 ES6 还不是特别的了解,那么我推荐你去看看阮老师的ECMAScript 6 入门

概念三:Props & State 又是啥?

你可能会疑惑上个例子里的 this.props.someVar 是从哪里冒出来的。

只要你对 HTML 有所了解,应该能够理解 <a> 标签的 href 属性是什么意思。延伸到 React 当中,属性就被称作 props(properties 的缩写)。组件之间可以通过 Props 进行交互。

class ParentComponent extends React.Component {
  render() {
    return <ChildComponent message="Hello World"/>;
  }
}
class ChildComponent extends React.Component {
  render() {
    return <p>And then I said, “{this.props.message}”</p>;
  }
}

也正因如此,React 当中的数据流是单向的:数据只能从父组件传向子组件,反过来则不行。

可是组件不可能只接受从父组件传来的数据(例如还有用户在 input 当中的输入),这时 state 就派上了用场。

原文的例子实在是不接地气,在这里我情不自禁想要引用一句古诗来解释 props 和 state 之间的区别:

人有悲欢离合,月有阴晴圆缺。

我们可以把一个人的基因性别名字(我知道某些东西其实是可变的但请不要钻牛角尖好吗),月亮的大小重量理解为 props,而随时可变的情感和圆缺则可以被理解为 state.

要注意,组件的 state 同样也能被传入到子组件中作为子组件 prop 的值。你需要明确的就是在 React 当中整个数据流都是向下传递的,包括路由、数据层、各个组件等等,从整个应用的 state 中来并汇聚到一起。

在组建中,我们可以通过一个叫 setState 的方法来修改 state,一般我们都会在事件处理的方法中调用它:

class MyComponent extends React.Component {
  handleClick = (e) => {
    this.setState({clicked: true});
  }
  render() {
    return <a href="#" onClick={this.handleClick}>Click me</a>;
  }
}

一般 React 应用当中的绝大多数数据都是 prop,只有当用户输入内容时才会使用 state 来处理。

注意在上述的代码中,我们使用了自动绑定的语法,如果你想了解更多可以阅读官方文档 Handling Events.

概念四:组件 API

在之前的内容当中我们已经提及了 render 和 setState 两个方法,他们都包含在组件 API 方法之中。还有一个比较有用的方法 constructor,我们一般会在其中初始化 state 并做一些方法的绑定。

除了这三个方法之外,React 还提供了一些列按照特定次序触发的生命周期函数。不过先不需要担心,只有当你深入一些了解 React 之后才有机会使用到它们。

我们并不会在这里展开篇幅讲解 React 的 API,因为学习 React 更主要的目的是学习如何编程和它的构建理念,而不是死记硬背一些无聊的 API 方法。

概念五:组件类型

我们在 React 当中一般按照如下的方法定义一个组件:

class MyComponent extends React.Component {
  render() {
    return <p>Hello World!<p>;
  }
}

在 Class 中我们还可以申明一个组件的许多其他方法,而在更多的情况下我们可以写一种函数式组件。

类似于自定义一个模板标签一样,函数式组件接收一个 props 参数并返回特定的 HTML 内容,不过你当然仍可以在其中调用一些 JS 代码:

const myComponent = props => {
  return <p>Hello {props.name}! Today is {new Date()}.</p>
}

因为通常你的组件可能并不需要多么复杂的交互,也不需要多余的其他方法,用函数式写法可以让你的代码更加简洁。

当然在这样的组件当中你也没有办法使用 setState 方法,也即是说函数式组件没有 state,所以也可以被称作是无状态组件。

当然,如果你接触 React 比较早,可能也见过下面这种写法:

var Greeting = React.createClass({ 

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

不同的组件类型也就延伸出了组件角色的概念,人们在实践过程中开始将组件分为两种角色,一种关注 UI 逻辑,用来展示或隐藏内容;另一种关注数据交互,例如加载服务器端的数据。

这两种组件被称作容器组件和展示组件。分别用来处理不同的业务逻辑:

//presentational component

class CommentList extends React.Component {
  constructor(props) {
    super(props);
  }

  render() { 
    return <ul> {this.props.comments.map(renderComment)} </ul>;
  }

  renderComment({body, author}) {
    return <li>{body}—{author}</li>;
  }
}

//container component

class CommentListContainer extends React.Component {
  constructor() {
    super();
    this.state = { comments: [] }
  }

  componentDidMount() {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments});
      }.bind(this)
    });
  }

  render() {
    return <CommentList comments={this.state.comments} />;
  }
}

这就又有点类似于 view/controller 的概念了。不过说来说去只是构建代码的不同方式而已,区分逻辑当然有其好处(例如分离业务逻辑,更好的代码复用),当然你也可以完全不吃这一套。

高阶组件

最后我们再稍微涉猎一些高阶组件的概念(higher-order components 通常缩写为HOCs)。

其实可以把它理解为一个工厂方法,你可以传入一个组件并得到一个 HOC 返回的附加了更多功能的新组件。HOC 不能直接在 render 方法中调用。如果你想了解更多,可以去看 react-router 当中实际应用的例子

这里还有一篇深入理解 React 高阶组件

总结

  • React 的代码是由一个个的组件构成的。
  • 组件采用了 JSX 语法扩展的写法。
  • 数据流总是从父组件到子组件,除 state 可以通过调用方法修改之外。
  • 组件包含一些特定方法和生命周期函数。
  • 你也完全可以用函数声明只有 render 方法的无状态组件。
  • 区分处理 UI 和数据逻辑的组件是一种很好的开发实践。
  • 高阶组件函数可以传入一个组件并为其赋予更多功能。

信不信由你,目前我们介绍的内容已经涵盖了 React 开发者在日常工作中应用的 90% 的知识。听起来可能有些晦涩抽象,单React当中涉及的内容总能被简化为 functions 和 props.

等到你真正理解这些知识之后,React 也就不会那么可怕了。在你掌握了 React 的模式,瞟一眼就能看懂它的代码之后,也就能自信地装 X:

切,React 已经 out 啦~

更多有关 React 的资源链接:

有任何好的意见建议或有关 React 的想法观点,欢迎在评论区参与讨论。

原文链接:https://www.freecodecamp.org/news/the-5-things-you-need-to-know-to-understand-react-a1dbd5d114a3/,作者:Sacha Greif