谈谈React.js的核心入门知识

jopen 9年前

来自:http://wwsun.me/posts/react-getting-started.html


近来React.js变得越来越流行,本文就来谈一谈React.js的入门实践,通过分析一些常用的概念,以及提供一些入门 的最佳编程编程方式,仅供参考。

首先需要搞懂的是,React并不是一个框架,React提供了一些新颖的概念、库 和编程原则让你能够同时在服务端和客户端编写快速、紧凑、漂亮的代码来构建 你的web应用。

如果你使用React,那么可能会涉及到一些常用的概念或技术,包括:

  • ES6 React
  • 虚拟DOM(virtual DOM)
  • 组件驱动开发(component-driven development)
  • 不变性(immutability)
  • 自上而下的渲染(top-down rendering)
  • 渲染路径和优化
  • 打包工具, ES6, 构建请求, debugging, 路由等
  • 同构React(isomorphic React)

什么是React.js

React.js不是一个框架

在整个Web应用的MVC架构中,你可以将React看作为视图层,并且是一个高效 的视图。React提供了和以往不一样的方式来看待视图,它以组件开发为基础。 对React应用而言,你需要分割你的页面,使其成为一个个的组件。也就是说,你的 应用是由这些组件组合而成的。

你可以通过分割组件的方式去开发复杂的页面或某个功能区块,并且组件是可以 被复用的。这个过程大概类似于用乐高积木去瓶装不同的物体。我们称这种编程方式称为 组件驱动开发

React的一大特点是其所拥有的虚拟DOM,它让页面渲染变得非常的高效,并且比直接 操纵DOM变得更为可控。这两大特点的组合使得React具有强大的自上而下的页面渲染 能力。

好了,React的有两个特点:组件化和高效的虚拟DOM,但是为什么它这么被看好呢? 因为React更多的是一种概念层面的东西,而库是其次的。也有很多其他遵从了这些思想的第三方实现。和每一个编程概念一样,React尤其 独有的解决方案、工具和工具。但这里并不会深入的去讨论他们,而是关注React本身。

Virtual DOM

为了跟踪模型层的变化,并且将其应用到DOM中(也就是渲染),我们需要注意两个 重要的事情:

  1. 数据是什么时候改变的
  2. 哪一个(些)DOM元素需要被更新

对于(1)而言,React提供了一个观察者模型用于替代传统的脏检查(dirty checking), 也就是持续的检查模型的变化。这也就是解释了为什么React不需要计算哪些发生 了改变的原因,因为它会立即知道。这个过程减少了计算量,并它应用程序变得 更平滑。但这里真正有趣的是,React是如何管理DOM操纵的

对于DOM改变(2)而言,React在内存中构建了DOM的树形表示,并且计算出哪个 DOM元素应该被改变。对浏览器而言,DOM操纵是比较耗费性能的,因此我们更倾向于 让其变得最小化。幸运的是,React视图尽可能少的触及到DOM元素。给予对象表示而言, 更少的DOM操纵意味着计算会更快,因此DOM改变也被尽可能的减少。

React在底层实现了一个diffing算法,该算法使用DOM的树形表示法,当某个 节点发生变化(标记为dirty)时它会重新计算整个子树,你会注意到你的模型发生 了改变,因为整个子树在之后会被重新渲染。关于该算法的详细分析可以参考这篇 文章

谈谈React.js的核心入门知识

如何在服务端渲染

因为React在DOM表示时使用了一个虚拟(假的)DOM,因此借助于这种方式使得在服务端 渲染输出HTML称为可能(不借助于JSDom, PhantomJS等)。React还能智能的识别出 服务端渲染出来的页面标记,并在客户端只为这些标记添加事件处理器,这对构建 同构web app非常有用。

有意思的是,React渲染出来的HTML标记都包含了data-reactid属性,这有助于 React中追踪DOM节点。

一些阅读资料

  1. React’s diff algorithm
  2. The Secrets of React’s virtual DOM
  3. Why is React’s concept of virtual DOM said to be more performant than dirty model checking?
  4. virtual-dom

组件驱动开发

对于component-driven development而言,你在一个模板中是看不到整个网站的。 虽然在一开始你可能会遇到一些困难,但是如果进一步的使用这种思路,你会发现 它易于理解,易于维护,并且容易测试。

如何使用React的方式来思考组件开发

下面我们来看如何实现组件驱动开发这一理念。我们看一个例子,这个例子来源于 thinking in react 这篇文章。对于构建一个可过滤的产品列表而言,通常其包括如下的组件结构:

  • FilterableProductTable
    • SearchBar
    • ProductTable
      • ProductCategoryRow
      • ProductRow
      </li> </ul> </li> </ul>

      谈谈React.js的核心入门知识

      一个组件应该包含什么

      首先,理想的,我们应该遵守单一责任原则 来设计你的组件。当你发下你的组件应该做的更多的时候,你可以考虑将其分割为 更小的组件集合。

      因为我们在讨论组件层级,因此在你的组件中也会使用到其他组件。我们首先看下 在ES5中组件代码是什么样子的:

      var HelloComponent = React.createClass({        render: function() {          return <div>Hello {this.props.name}</div>;      }  });

      如果使用ES6,你的组件代码可以这样写:

      class HelloComponent extends React.Component {      render() {      return <div>Hello {this.props.name}</div>;    }  }

      JS和JSX

      正如你说看到的,我们的组件是JS和HTML代码的混合,你可能会觉得这很糟糕,因为 MVC一直在教我们尽可能的隔离视图和控制逻辑。但另一方,这种混合获得另一个层面的 单一责任,他使得组件更加的灵活和可重用。

      当然,在React中你也可以使用纯JS来编写你的组件:

      render () {      return React.createElement("div", null, "Hello ",          this.props.name);  }

      是的,你会发现这很麻烦,没有使用HTML来得直观。因此React提供了JSX (JavaScript eXtension)语法让你能够在JS中书写HTML代码。

      render () {      return <div>Hello {this.props.name}</div>;  }

      什么是JSX

      JSX在ECMAScript的基础上提供了类似于XML的扩展。 JSX和HTML有点像,但也有不一样的地方。例如,HTML中的class属性在JSX中 为className。其他不一样的地方,你可以参考FB的HTML Tags vs. React Components 这篇文章。

      但是由于浏览器原生并不支持JSX,因此我们需要将其编译为JS,有很多方法能够 完成这个任务,后面我们会提到这些方法。此外,Babel也能够讲JSX编译为JS。

      一些参考资料:

      1. JSX in depth
      2. Online JSX compiler
      3. Babel: How to use the react transformer

      组件还应该包括什么

      每个组件都应该包括一些内部状态,处理逻辑,和事件处理器(例如按钮点击、输入改变), 当然也包括一些内部的样式。

      你会遇到{this.props.name}这样的代码片段,这意味着你可以通过属性的方式 先组件内传递数据,例如<MyComp name='weiwei sun' />。这让组件变得可重用, 并且能够自上而下的向嵌套的组件传递数据。

      示例代码如下:

      class UserName extends React.Component {      render() {      return <div>name: {this.props.name}</div>;    }  }    class User extends React.Component {      render() {      return <div>          <h1>City: {this.props.user.city}</h1>          <UserName name={this.props.user.name} />        </div>;    }  }    var user = { name: 'John', city: 'San Francisco' };    React.render(<User user={user} />, document.body);

      React拥抱ES6

      在React中尝试编写ES6是个非常不错的开始,React并不是一开始就支持ES6的, 而是从v0.13.0开始支持的。你会经常用到的ES6特性包括类、箭头函数、consts 和模块。例如,我们会经常从继承React.Component类开始编写我们的组件。

      还有一点需要注意的是,并不是每个浏览器都支持ES6,因此目前情况下,我们需要 使用一些工具将我们编写的ES6代码转换为ES5代码,我推荐使用Babel

      一些参考资料:

      1. Babel: Learn ES6
      2. React ES6 announcement

      组件生命周期

      每个React组件在加载时都有特定的生命周期,在此期间不同的方法会被执行。 下面简单介绍React组件的生命周期:

      componentWillMount

      该方法会在组件render之前执行,并且永远只执行一次。

      componentDidMount

      该方法会在组件加载完毕之后立即执行。此时,组件已经完成了DOM结构的渲染, 并可以通过this.getDOMNode()方法来访问。

      componentWillReceiveProps

      组件接收到一个新的prop时会被执行,且该方法在初始render时不会被调用。

      shouldComponentUpdate

      在组件接收到新的props或state时被执行。

      componentWillUpdate

      在组件接收到新的props或者state但还没有render时被执行。 在初始化时不会被执行。

      componentDidUpdate

      在组件完成更新后立即执行。在初始化时不会被执行。 一般会在组件完成更新后被使用。

      componentWillUnMount

      在组件从DOM中unmount后立即执行。该方法主要用来执行一些必要的清理任务。

      关于生命周期的具体内容,你可以参考官方文档

      在打包时使用Webpack和Babel

      我们会经常用到一些工具,首先一个是node.js的模块系统和它的包管理工具npm。 我们会编写node风格的代码来require我们需要的东西。并且react本身也是一个独立的 npm包。

      通常你有两种选择,commonJS或者ES6:

      var React = require('react/addon');    var MyComponent = React.createClass({   // do something  });    module.exports = MyComponent;

      或者

      import React from 'react/addons';  class MyComponent extends React.Component {   // do something use es6  }  export default MyComponent;

      例如,我们会使用debug模块来调试, 使用superagent模块来编写请求。

      现在,我们有了Node的依赖管理系统,并且使用npm来提供模块。下面我们需要做的 事:选择一个合适的库来打包我们的代码,并且能够让其运行在浏览器上。

      因此我们需要一个打包器。目前最流行的解决方案包括两个,分别是BrowserifyWebpack。我们选择使用Webpack,因为Webpack 更适合于React社区。

      Webpack是如何工作的

      Webpack用于打包我们的代码,并且包含进我们需要的包,然后输出为浏览器可运行的 文件。因为我们使用JSX和ES6,因此我们需要相应的工具来将其转换为ES5。事实上, Babel能够同时做这两件事。使用Webpack能够很轻松的完成这些任务,因为Webpack 是面向配置的。

      使用如下命令开始:

      npm init  npm install webpack --save-dev  npm install babel --save-dev  npm install babel-loader --save-dev

      然后创建webpack.config.js文件,我们需要使用ES5来编写该文件,因为它是 webpack的配置文件。一个典型的配置方式如下:

      var path = require('path');    module.exports = {      entry: path.resolve(__dirname, '../src/client/scripts/client.js'),    output: {      path: path.resolve(__dirname, '../dist'),      filename: 'bundle.js'    },      module: {      loaders: [        {          test: /src\/.+.js$/,          exclude: /node_modules/,          loader: 'babel'        }      ]    }  };

      运行webpack命令你可以执行打包流程。这之后你可以只在页面中包含bundle.js即可。 如下:

      <script src='bundle.js'></script>

      (提示:你可以使用node-static来存放你的静态资源文件,使用npm install -g node-static来安装,并使用static .来启动)。

      项目结构

      一个典型的项目结构你可以参考这个仓库

      config/        app.js      webpack.js (js config over json -> flexible)  src/      app/ (the React app: runs on server and client too)      components/        __tests__ (Jest test folder)        AppRoot.jsx        Cart.jsx        Item.jsx      index.js (just to export app)      app.js    client/  (only browser: attach app to DOM)      styles/      scripts/        client.js      index.html    server/      index.js      server.js  .gitignore  .jshintrc  package.json    README.md

      如何测试React组件

      对于React组件的测试,这里推荐使用Jest, Jest也是由非死book提供的测试框架,并且有很多强大的特性,但这里并会详细的 介绍它们。

      关于Jest,我推荐你阅读和尝试来自非死book的Tutorial

      对于ES6代码的测试,你可以参考 React ES6 Testing

      小结

      本文简单介绍了React的基础原理,一些相关的编程技术。后续还会整合一些资料 谈一谈Flux和同构。

      Statement

      本文翻译自:https://blog.risingstack.com/the-react-way-getting-started-tutorial/ 有增删改。

      References

      1. https://blog.risingstack.com/the-react-way-getting-started-tutorial/
      2. https://github.com/RisingStack/react-way-getting-started
      3. http://非死book.github.io/react/docs/component-specs.html