React躬行记(5)——React和DOM

  React实现了一套与浏览器无关的DOM系统,包括元素渲染、节点查询、事件处理等机制。

一、ReactDOM

  自React v0.14开始,官方将与DOM相关的操作从React中剥离,组成单独的react-dom库,从而让React能兼容更多的终端。在引入react-dom库后,就能调用一个全局对象:ReactDOM,虽然在之前的章节中已多次使用该对象,但是都没有给出过多的讲解,本节将对其做重点分析。

  ReactDOM只包含了unmountComponentAtNode()、findDOMNode()、createPortal()和render()等为数不多的几个方法,其中在第2篇中介绍了render()的功能,又在第4篇提到用unmountComponentAtNode()方法移除DOM中已挂载的组件。接下来会讲解剩余的两个方法。

1)findDOMNode()

  当组件被渲染到页面真实的DOM中后,就能通过findDOMNode()方法得到生成的DOM元素,然后就能完成诸如读值、计算尺寸等操作。

  注意,findDOMNode()只能获取已挂载的组件,并且不能用于函数组件。在组件的生命周期中,它只能存在于componentDidMount()和componentDidUpdate()两个回调函数中,在其它地方调用会抛出一个错误,具体如下所示。

class Btn extends React.Component {
  render() {
    ReactDOM.findDOMNode(this);        //抛出错误  
    return ;
  }
  componentDidMount() {
    ReactDOM.findDOMNode(this);        //
  }
}

  在上面的示例中,this指向的是Btn组件实例,在将this传给findDOMNode()方法后,得到了一个; } componentDidMount() { let btn = this.myBtn.current; console.log(btn); // } }

  首先在组件的构造函数中调用React.createRef();再将返回值赋给this.myBtn,这样就能在组件内部的任意位置使用该对象了;然后让this.myBtn成为 ); } componentDidMount() { let btn = this.myBtn; console.log(btn); // } }

  与前一种使用方式最大的不同是解除了对React.createRef()方法的依赖,在回调函数中直接将其参数赋给this.myBtn,就能得到预期的结果,不用再调用一次current属性。

3)字符串

  ref属性的值还可以是字符串,例如下面代码中的"myBtn",通过this.refs.myBtn就能访问到想要的组件实例或DOM元素。

class Btn extends React.Component {
  render() {
    return ;
  }
  componentDidMount() {
    let btn = this.refs.myBtn;
    console.log(btn);           //
  }
}

  不过,官方已经不推荐这种写法了,在未来的版本中有可能会被移除,因此建议改用回调函数的方式。

4)使用场景

  ref属性不仅能像之前示例那样应用于DOM元素上,还能在类组件中使用ref属性,如下代码所示。

class Btn extends React.Component {
  render() {
    return ;
  }
}
class Container extends React.Component {
  render() {
    return  { this.myBtn = btn }}>提交;
  }
  componentDidMount() {
    let btn = this.myBtn;
    console.log(btn);           //Btn组件的实例
  }
}

  Container是Btn的父组件,在其render()方法中,通过回调函数将Btn组件的实例赋给了this.myBtn。

  由于函数组件没有实例,因此不能对其添加ref属性。

5)子组件的DOM元素

  在父组件中,如果要访问子组件的某个DOM元素,那么单靠ref属性是无法实现的,因为ref属性得到的只能是子组件的实例。不过,有一种间接的方式可以实现这个需求,那就是将ref属性和ReactDOM.findDOMNode()配合使用。下面套用上一节使用场景中的Btn和Container两个组件,代码只列出了修改部分,其余都已省略。

class Container extends React.Component {
  componentDidMount() {
    let btn = this.myBtn;
    let dom = ReactDOM.findDOMNode(btn);
    console.log(dom);         //
  }
}

  在componentDidMount()方法中,调用了一次ReactDOM.findDOMNode(),从而得到了子组件所拥有的DOM元素。

三、Fragments

  JSX结构有一个限制,那就是在最外层必须用一个元素包裹,即使这是一个冗余的元素,也得加上。例如为一个

    元素挂载一组元素集合,如下所示。

    class Btns extends React.Component {
      render() {
        return (
          
  • 1
  • 2
  • 3
  • ); } }

      在页面上渲染出的DOM会像下面这样,其中

    元素在此处是没有作用的。

    <ul id="container">
      <div>
        <li>1li>
        <li>2li>
        <li>3li>
      div>
    ul>

      为了避免这种无意义的输出,React引入了Fragments,其结构如下代码所示。只需将最外层的

    元素的开始和结束标签分别改成<>和,就不用在DOM中增加额外的元素了。

    class Btns extends React.Component {
      render() {
        return (
          <>
            
  • 1
  • 2
  • 3
  • ); } }

    1)React.Fragment

      <>和最终会被编译成React.Fragment组件的开始和结束标签,也就是说,前者是后者的语法糖。下面的代码和上一个Fragments的示例是等价的。

    class Btns extends React.Component {
      render() {
        return (
          
            
  • 1
  • 2
  • 3
  • ); } }

      如果要为Fragments添加Keys标识(即为其定义key属性),那么只能用React.Fragment组件包裹子元素。注意,key是React.Fragment组件目前唯一可用的属性。

     

你可能感兴趣的:(React躬行记(5)——React和DOM)