深入React技术栈从几个维度去介绍 React。一是作为 View 库,它怎么实现组件化,以及它背后的实现原理。二是扩展到 Flux 应用架构及重要的衍生品 Redux,它们怎么与 React 结合做应用开发。三是对 React 与 server 的碰撞产生的一些思考。四是讲述它在可视化方面的优势与劣势。 本书适合有一定经验的前端开发人员阅读。

1.6 React 与 DOM

前面已经介绍完组件的组成部分了,但还缺少最后一环,那就是将组件渲染到真实 DOM 上。从 React 0.14 版本开始,React 将 React 中涉及 DOM 操作的部分剥离开,目的是为了抽象 React,同时适用于 Web 端和移动端。ReactDOM 的关注点在 DOM 上,因此只适用于 Web 端。

在 React 组件的开发实现中,我们并不会用到 ReactDOM,只有在顶层组件以及由于 React 模型所限而不得不操作 DOM 的时候,才会使用它。

1.6.1 ReactDOM

ReactDOM 中的 API 非常少,只有 `findDOMNode`、`unmountComponentAtNode` 和 `render`,我们就以 API 的角度来讲讲它们的用法。

findDOMNode

上一节我们已经讲过组件的生命周期,DOM 真正被添加到 HTML 中的生命周期方法是 componentDidMount 和 componentDidUpdate 方法。在这两个方法中,我们可以获取真正的 DOM 元素。React 提供的获取 DOM 元素的方法有两种,其中一种就是 ReactDOM 提供的 `findDOMNode`。

DOMElement findDOMNode(ReactComponent component)

当组件被渲染到 DOM 中后,findDOMNode 返回该 React 组件实例相应的 DOM 节点。它可以被用于获取表单的 value 以及 DOM 的测量上。例如,假设要在当前组件加载完时获取当前 DOM,就可以使用 findDOMNode:

import React, { Component } from 'react';

class App extends Component {

  componentDidMount() {

    // this 为当前组件的实例

    const dom = findDOMNode(this);

  }

render() {}}

如果在 render 中返回 null,那么 findDOMNode 也返回 null。findDOMNode 只对已经挂载的组件有效。

涉及到复杂操作时,还有非常多的原生 DOM API 可以用。但是需要严格限制场景,在使用之前多问自己为什么要操作 DOM。

render

为什么说只有在顶层组件我们不得不使用 ReactDOM,因为我们要把 React 渲染的 Virtual DOM 渲染到浏览器的 DOM 当中,这就要使用 render 方法了。

ReactComponent render(

  ReactElement element,

  DOMElement container,

  [function callback]

)

render 方法把元素挂载到 container 中,并且返回 element 的实例(即 ref 引用)。当然,如果是无状态组件,render 会返回 null。当组件装载完毕时,callback 就会被调用。

当组件在初次渲染之后再次更新时,React 不会把整个组件重新渲染一次,而会用它高效的 DOM diff 算法做局部的更新。这也是 React 最大的亮点之一!

此外,与 render 相反,React 还提供了一个很少使用 unmountComponentAtNode 方法来做 unMount 的操作。

1.6.2 ReactDOM 不稳定方法

ReactDOM 中有两个不稳定方法,其中一个方法与 render 方法颇为相似。讲起它,还得从我们常用的 Dialog 组件在 React 中的实现讲起。

我们先来回忆一下 Dialog 组件的特点,它是不在文档流中的弹出框,一般会绝对定位在屏幕的正中央,背后有一层半透明的遮罩。因此,它往往直接渲染在 document.body 下,然而我们并不知道如何在 React 组件外进行操作。这就要从实现 Dialog 的思路以及涉及 DOM 部分的实现讲起。

这里我们引入 Portal 组件,这是一个经典的实现,最初的实现来源于 React Bootstrap 组件库中的 Overlay Mixin,后来使用越来越广泛。我们截取关键部分的源码:

import React from 'react';

import ReactDOM, { findDOMNode } from 'react-dom';

import CSSPropertyOperations from 'react/lib/CSSPropertyOperations';

export default class Portal extends React.Component {

  constructor() {

    // ...

  }

 openPortal(props = this.props) {

    this.setState({ active: true });

    this.renderPortal(props);

    this.props.onOpen(this.node);

  }

  closePortal(isUnmounted = false) {

    const resetPortalState = () => {

      if (this.node) {

        ReactDOM.unmountComponentAtNode(this.node);

        document.body.removeChild(this.node);

      }

      this.portal = null;

      this.node = null;

      if (isUnmounted !== true) {

        this.setState({ active: false });

      }

    };

    if (this.state.active) {

      if (this.props.beforeClose) {

        this.props.beforeClose(this.node, resetPortalState);

      } else {

        resetPortalState();

      }

   ... ...

      • 温馨提示:
      • 在微信、微博等APP中下载时,会出现无法下载的情况
      • 这时请选择在浏览器中打开,然后再请下载浏览

发表回复

后才能评论