React高级指南

高级指南

 

 

 1.深入JSX:

从本质上讲,JSX 只是为 React.createElement(component, props, ...children) 函数提供的语法糖。

因为 JSX 被编译为 React.createElement 的调用,所以 React 库必须在你 JSX 代码的作用域中。

import React from 'react'

用户定义组件必须以大写字母开头

当一个元素类型以小写字母开头,它表示引用一个类似于 <div> 或者 <span> 的内置组件,会给 React.createElement 方法传递 'div' 或者 'span' 字符串。以大写字母开头的类型,类似于 <Foo />,会被编译成 React.createElement(Foo) ,对应于自定义组件 或者在 JavaScript 文件中导入的组件。

JSX 类型不能是表达式

return <components[props.storyType] story={props.story/>; 错误

 const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;         正确

JavaScript 表达式作为 props(属性)

你可以传递任何一个用 {} 包裹的 JavaScript 表达式作为 props(属性)。

{1+2+3} 但是 {if(..)else... 和 for(..)} 不是表达式

 

果你没给 prop(属性) 传值,那么他默认为 true 。下面两个 JSX 表达式是等价的:

<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

属性扩展

如果你已经有一个 object 类型的 props,并且希望在 JSX 中传入,你可以使用扩展操作符 ... 传入整个 props 对象。这两个组件是等效的:

function App1() {
  return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}


JSX会删除每行开头和结尾的空格,并且也会删除空行。邻接标签的空行也会被移除,字符串之间的空格会被压缩成一个空格,因此下面的渲染效果都是相同的:

JavaScript 表达式作为 Children(子元素)

通过使用 {} 包裹,你可以将任何的 JavaScript 元素而作为 children(子元素) 传递

Booleans, Null, 和 Undefined 被忽略

在有条件性渲染 React 元素时非常有用。如果 showHeader 为 true 时,<Header />会被渲染:

<div>
  {showHeader && <Header />}
  <Content />
</div>

需要注意的是“falsy”值,例如数值 0 ,仍然会被 React 渲染。例如,这段代码不会按照你预期的发生,因为当 props.messages 是一个空数组时 0 会被打印:

<div>
  {props.messages.length &&
    <MessageList messages={props.messages} />
  }
</div>

要修复这个问题,确保 && 之前的表达式总是布尔值:

<div>
  {props.messages.length > 0 &&
    <MessageList messages={props.messages} />
  }

2.使用 PropTypes 进行类型检查

 

3.静态类型检查

 

4.Refs 和 DOM

在常规的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改子元素,你需要用新的 props 去重新渲染子元素。然而,在少数情况下,你需要在常规数据流外强制修改子元素。被修改的子元素可以是 React 组件实例,或者是一个 DOM 元素。在这种情况下,React 提供了解决办法。

何时使用 Refs

下面有一些正好使用 refs 的场景:

  • 处理focus、文本选择或者媒体播放
  • 触发强制动画
  • 集成第三方DOM库

如果可以通过声明式实现,就尽量避免使用 refs 。

在 DOM 元素上添加 Ref

为 类(Class) 组件添加 Ref

Refs 与 函数式组件

 

5.不受控组件

在大多数情况下,我们推荐使用受控组件来实现表单。在受控组件中,表单数据由 React 组件负责处理。另外一个选择是不受控组件,其表单数据由 DOM 元素本身处理。

要编写一个未控制组件,你可以使用一个 ref 来从 DOM 获得 表单值,而不是为每个状态更新编写一个事件处理程序。

默认值

在 React 渲染生命周期中,表单元素中的 value 属性将会覆盖 DOM 中的 value 。在不受控组件中,你可能希望 React 有初始值,但保留后续更新不受控制。在这种情况下,你需要使用 defaultValue 属性而不是 value 属性。

同样, <input type="checkbox"> 和 <input type="radio"> 支持 defaultChecked,而 <select> 和 <textarea> 支持 defaultValue

 

6.优化性能

使用生产版本而不是使用开发版本

避免重新渲染

React 构建并维护渲染 UI 的内部表示。它包括你从组件中返回的 React 元素。这些内部状态使得 React 只有在必要的情况下才会创建DOM节点和访问存在DOM节点,因为对 JavaScript 对象的操作是比 DOM 操作更快。这被称为”虚拟DOM”,React Native 也是基于上述原理。

当组件的 props 和 state 改变时,React 通过比较新返回的元素 和 之前渲染的元素 来决定是否有必要更新DOM元素。当二者不相等时,则更新 DOM 元素。

 

如果你知道在某些情况下你的组件不需要更新,那么你可以在 shouldComponentUpdate返回 false 来跳过整个渲染过程,包括在这个组件和后面调用的 render() 。

在大多数情况下,您可以不用手写 shouldComponentUpdate() ,而是从 React.PureComponent 继承。 这相当于用当前和以前 props(属性) 和 state(状态) 的浅层比较来实现shouldComponentUpdate() 。

 

应用 shouldComponentUpdate

如果你想要你的组件仅当 props.color 或 state.count 发生改变时需要更新,你可以通过 shouldComponentUpdate 函数来检查:

class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

在这种情况下,shouldComponentUpdate 函数仅仅检查 props.color 或者 state.count 是否发生改变。如果这些值没有发生变化,则组件不会进行更新。如果你的组件更复杂,你可以使用类似于对 props 和 state 的所有属性进行”浅比较”这种模式来决定组件是否需要更新。这种模式非常普遍,因此 React 提供了一个 helper 实现上面的逻辑:继承 React.PureComponent 。因此,下面的代码是一种更简单的方式实现了相同的功能:

class CounterButton extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

大多数情况下,你可以使用 React.PureComponent 而不是自己编写 shouldComponentUpdate 。但 React.PureComponent 仅会进项浅比较,因此如果 props 或者 state 可能会导致浅比较失败的情况下就不能使用 React.PureComponent 。

避免这类问题最简单的方法是不要突变(mutate) props 或 state 的值。例如,上述 handleClick 方法可以通过使用 concat 重写:

handleClick() {
  this.setState(prevState => ({
    words: prevState.words.concat(['marklar'])
  }));
}

 

7.不使用 ES6

 createReactClass({..

8.不使用 JSX

每一个 JSX 元素都是调用 React.createElement(component, props, ...children) 的语法糖,因此,任何你使用 JSX 来做事都可以通过纯 JavaScript 实现。

 

9.一致性比较(Reconciliation)

React提供声明式API,因此在每次更新中你不需要关心具体的更改内容。这使得编写应用更加容易,但是这样使得你对 React 内部具体实现并不了解,这篇文章介绍了在 React 的 “diffing” 算法中我们所作出地决择,以使得组件的更新是可预测的并且可以适用于高性能应用。

 

 

10.Context

在多层级间传递的时候不一定非要死磕属性来一层一层的传,这里介绍一下Context

 

11.片段(fragments)

动机

一个常见模式是为一个组件返回一个子元素列表。以这个示例的 React 片段为例:

class Table extends React.Component {
  render() {
    return (
      <table>
        <tr>
          <Columns />
        </tr>
      </table>
    );
  }
}

为了渲染有效的 HTML , <Columns /> 需要返回多个 <td> 元素。如果 <Columns /> 的 render() 函数里面使用一个父级 div ,那么最终生成的 HTML 将是无效的。

class Columns extends React.Component {
  render() {
    return (
      <div>
        <td>Hello</td>
        <td>World</td>
      </div>
    );
  }
}

在 <Table /> 组件中的输出结果:

<table>
  <tr>
    <div>
      <td>Hello</td>
      <td>World</td>
    </div>
  </tr>
</table>

所以,我们介绍 Fragment

使用

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

在正确的 <Table /> 组件中,这个结果输出如下:

<table>
  <tr>
    <td>Hello</td>
    <td>World</td>
  </tr>
</table>


简写语法

有一个新的,更短的语法可以用来声明 片段(fragments) 。 它看起来像空标签:

There is a new, shorter syntax you can use for declaring fragments. It looks like empty tags:

class Columns extends React.Component {
  render() {
    return (
      <>
        <td>Hello</td>
        <td>World</td>
      </>
    );
  }
}



12.插槽(Portals)

 

ReactDOM.createPortal(child, container)

第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 片段(fragment)。第二个参数(container)则是一个 DOM 元素。

用法

通常来说,当你从组件的 render 方法返回一个元素时,它将被作为子元素被装载到最近父节点 DOM 中:

render() {
  // React 装载一个新的 div,并将 children 渲染到这个 div 中
  return (
    <div>
      {this.props.children}
    </div>
  );
}

然而,有时候将子元素插入到 DOM 节点的其他位置会有用的:

render() {
  // React *不* 会创建一个新的 div。 它把 children 渲染到 `domNode` 中。
  // `domNode` 可以是任何有效的 DOM 节点,不管它在 DOM 中的位置。
  return ReactDOM.createPortal(
    this.props.children,
    domNode,
  );
}

对于 portal 的一个典型用例是当父组件有 overflow: hidden 或 z-index 样式,但你需要子组件能够在视觉上 “跳出(break out)” 其容器。例如,对话框、hovercards以及提示框:

 

这包含事件冒泡。一个从 portal 内部会触发的事件会一直冒泡至包含 React tree 的祖先。假设如下 HTML 结构:

 

 

13.错误边界(Error Boundaries)

部分 UI 中的 JavaScript 错误不应该破坏整个应用程序。 为了解决 React 用户的这个问题,React 16引入了一个 “错误边界(Error Boundaries)” 的新概念。

错误边界是 React 组件,它可以在子组件树的任何位置捕获 JavaScript 错误,记录这些错误,并显示一个备用 UI ** ,而不是使整个组件树崩溃。 错误边界(Error Boundaries) 在渲染,生命周期方法以及整个组件树下的构造函数中捕获错误。

 

果一个类组件定义了一个名为 componentDidCatch(error, info): 的新生命周期方法,它将成为一个错误边界:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

而后你可以像一个普通的组件一样使用:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>


componentDidCatch() 方法机制类似于 JavaScript catch {},但是针对组件。仅有类组件可以成为错误边界。实际上,大多数时间你仅想要定义一个错误边界组件并在你的整个应用中使用。

注意错误边界(Error Boundaries) 仅可以捕获其子组件的错误。错误边界无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会向上冒泡至最接近的错误边界。这也类似于 JavaScript 中 catch {} 的工作机制。

componentDidCatch 参数

error 是被抛出的错误。

info 是一个含有 componentStack 属性的对象。这一属性包含了错误期间关于组件的堆栈信息。

//...
componentDidCatch(error, info) {
  
  /* Example stack information:
     in ComponentThatThrows (created by App)
     in ErrorBoundary (created by App)
     in div (created by App)
     in App
  */
  logComponentStackToMyService(info.componentStack);
}

//...

 

14.Web 组件(Web Components)

React 和 web组件 被用以解决不同问题。Web组件为可重用组件提供了强大的封装能力,而React则是提供了保持DOM和数据同步的声明式库。二者目标互补。作为开发者,你可以随意地在Web组件里使用React,或者在React里使用Web组件,或都有。

个人注:(其实感觉Web组件是React这种组件化库的一个子集)

 

15.高阶组件(Higher-Order Components)

在React中,高阶组件是重用组件逻辑的一项高级技术。高阶组件并不是React API的一部分。高阶组件源自于React生态。

具体来说,高阶组件是一个函数,能够接受一个组件并返回一个新的组件。

 

16.与其他库整合(Integrating with Other Libraries)

17.可访问性(Accessibility)

 

18.代码拆分(Code-Splitting)

 

 

 

 

参考:

 

 

Inheritance with the prototype chain

Inheriting properties

JavaScript objects are dynamic "bags" of properties (referred to as own properties). JavaScript objects have a link to a prototype object. When trying to access a property of an object, the property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached.

Following the ECMAScript standard, the notation someObject.[[Prototype]]is used to designate the prototype of someObject. Since ECMAScript 2015, the [[Prototype]] is accessed using the accessors Object.getPrototypeOf() and Object.setPrototypeOf(). This is equivalent to the JavaScript property __proto__ which is non-standard but de-facto implemented by many browsers.

It should not be confused with the func.prototype property of functions, which instead specifies the [[Prototype]] to be assigned to all instances of objects created by the given function when used as a constructor. The Object.prototype property represents the Object prototype object.

Here is what happens when trying to access a property:

posted @ 2018-04-24 11:21  hh9515  阅读(253)  评论(0编辑  收藏  举报