react初学者——谈谈React.js的核心入门知识

近来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中(也就是渲染),我们需要注意两个 重要的事情:

数据是什么时候改变的

哪一个(些)DOM元素需要被更新

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

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

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

如何在服务端渲染

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

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

一些阅读资料

React’s diff algorithm

The Secrets of React’s virtual DOM

Why is React’s concept of virtual DOM said to be more performant than dirty model checking?

virtual-dom

组件驱动开发

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

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

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

FilterableProductTable

SearchBar

ProductTable

ProductCategoryRow

ProductRow

一个组件应该包含什么

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

因为我们在讨论组件层级,因此在你的组件中也会使用到其他组件。我们首先看下 在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。

一些参考资料:

JSX in depth

Online JSX compiler

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

一些参考资料:

Babel: Learn ES6

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来提供模块。下面我们需要做的 事:选择一个合适的库来打包我们的代码,并且能够让其运行在浏览器上。

因此我们需要一个打包器。目前最流行的解决方案包括两个,分别是Browserify和 Webpack。我们选择使用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.jshintrcpackage.json 

README.md

如何测试React组件

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

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

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

小结

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容