React文档 - 学习重点

React使用虚拟DOM(Virtual DOM)解决DOM操作的诸多问题。

问题

Web网站因操作DOM的性能问题导致交互动画出现卡顿。

根源

Web应用基于DOM,浏览器解析html在内存中生成DOM结构、接受CSS对样式进行调整。

过程受html结构和样式复杂性影响极大。

DOM操作的同步特性,降低了浏览器解析html文档的整体效率,一旦出现阻塞,整个页面就失去响应。

出发点

浏览器渲染html的频率60FPS(16毫秒/帧),js能否在16毫秒完成DOM操作,取决于程序复杂性。

因此,产生跳帧的问题既不确定也避免不了。

用户感受到不流畅或不连贯再所难免。

网页的大部分样式属性都是由CPU直接处理,无法利用GPU图形加速技术。

解决方案

去DOM

手机App FlipBoard给出一个史无前例的方案,抛弃DOM。

将html当作图片进行渲染。

将整个网站用HTML5 <canvas> 标签绘制成图像(使用javaScript)。

<canvas> 元素本身没有图形绘制能力,仅仅是图形容器。

此方案在图形绘制方面能充分利用硬件加速技术,流畅性与Native app并无二致。

FlipBoard方案存在很大争议,canvas是一种位图,不存在语义。

没有CSS动态效果、超链接功能、不具备自适应特性、无法通过搜索引擎检索,适用场景非常窄。

多线程

Mozilla开发了Servo项目,一个多线程并行处理的浏览器。

多线程浏览器、一个网页由多个线程处理,保证主线程能在16毫秒完成。

异步操作DOM

JavaScript操作DOM时,不再使用同步方式,使用事件将它交给Event Loop处理单元。

React Virtual DOM

React中每个组件都有状态,是一种可观察的状态,能观察到数据何时发生变化。

React知道何时重新渲染。

React利用diff算法检查Virtual DOM与DOM间的差异,决定是否需要重新渲染。

项目历史

React源于Facebook内部项目,旨在为Instagram网站开发一个JavaScript MVC框架。

2013年5月开源React。

React设计独特、性能出众、代码逻辑清晰、备受众多开发者关注。

最终,React变成Web应用、前后端通用的UI解决方案。

编写一套UI代码,运行于多种客户端上。

React环境

学习React语法时,可借助在线编码环境快速学习。

https://codesandbox.io/s/new

https://codepen.io/gaearon/pen/oWWQNa?editors=0010

React语法

从表现来看是一种js与html混写的语法。

React JSX代码块type="text/babel"。

React除核心功能包,还包含一个Browser.js,将JSX语法转成JavaScript。

ReactDOM.render(<App />, rootElement);

ReactDOM.render是React最常用接口,将模板转成html插入到DOM。

JSX语法

JSX语法书写html无需加引号,html可直接引用JS对象。

function App() {
  var list = ['Lee', 'Rain', 'Nick'];
  return (
    <div className="App">
      <h1>Hello</h1>
      <img src='/my-profile' />
  {
    list.map(function(n){
          return <span>{n}</span>
        })
  }
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

示例说明:

第三行是一个html标签,标签是模板的根元素,一个JSX块只有一个根元素。

注:html的class属性应改名为className。

第四行是一个标准的html元素写法。

第五行是一个img标签,JSX语法要求标签要有完整的关闭标识,img结束位置的斜杠(/)不能少。

第六行,使用一对大括号引用js变量。

第七行,结尾处不需要分号(;)。

第八行,引入一个html元素,元素中使用大括号引用js变量。

最后一行,引用App标签,使用ReactDOM.render插入到DOM。

React开发者相对比较活跃,版本随时间推移改动较大。

示列中React API用法可能与后续版本存在差异。

学习React API,最佳方式查看React文档:

www.reactjs.org/docs

React 组件

React允许将函数与html元素封装成组件,最终,当作HTML标签进行引用。

html标签中的属性可做为参数传给组件。

function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.says}
    </button>
  );
}

ReactDOM.render(<App says='word'></App>, document.getElementById("root"));

React children

若把React组件当作html标签,那么,它就该有html所有的特点,如:拥有子元素。

function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  );
}

ReactDOM.render(<App says='word'><h2>song</h2></App>, document.getElementById("root"));

参考:

https://reactjs.org/docs/forwarding-refs.html#forwarding-refs-to-dom-components

React PropTypes

若对属性进行类型限制,可通过PropTypes进行定义。

import PropTypes from 'prop-types';

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

Greeting.propTypes = {
  name: PropTypes.string
};

参考:

https://reactjs.org/docs/typechecking-with-proptypes.html#___gatsby

React获取DOM

通过ref给html元素命名,使用refs['hefName']对元素进行访问。

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

参考:

https://reactjs.org/docs/refs-and-the-dom.html

React State

React组件都有状态,状态发生变化,DOM会被重新渲染。

 <button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Click me
  </button>

参考:

https://reactjs.org/docs/hooks-state.html

React forms

form表单出现的地方,往往都有复杂的用户交互需求。此时回归html本身,如:在html元素上定义事件,然后,调用React属性。

https://reactjs.org/docs/forms.html

组件生命周期

React组件生命周期包括三个阶段:

Mounting

创建组件实例,将其插入DOM时,顺序调用以下方法:

constructor()
static getDerivedStateFromProps()
render()
componentDidMount()

Updating

当props或state被更改后引发更新,重新渲染组件,将顺序调用以下方法:

static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()

Unmounting

从DOM中删除组件时调用此方法:

componentWillUnmount()

参考:

https://reactjs.org/docs/react-component.html#the-component-lifecycle

错误处理

渲染期间、生命周期方法或子组件构造函数发生错误时,将调用以下方法:

static getDerivedStateFromError()
componentDidCatch()

参考:

https://reactjs.org/docs/react-component.html#the-component-lifecycle

Ajax渲染组件

如需要使用Ajax获取数据渲染组件,应在componentDidMount方法中发起AJAX请求,使用setState更新组件。

参考:

https://reactjs.org/docs/faq-ajax.html#example-using-ajax-results-to-set-local-state