React:DOM操作

2018-06-19 11:08 更新
      虛擬DOM(virtual DOM)是React的一大亮點。正是因為虛擬DOm,在大多數(shù)的應用場景中,我們都只要關注設置組件的狀態(tài)(setState),不需要直接操作DOM。

那么虛擬DOM到底是什么呢?

其實,虛擬DOM(virtual DOM)是一個模擬DOM樹的JavaScript對象。React使用虛擬DOM來渲染UI,當組件狀態(tài)state有更新時,React會自動調(diào)用組件的render方法重新渲染整個組件的UI。

大多數(shù)時候,我們都應該呆在React的“虛擬瀏覽器”的世界里,因為它性能更加好,并且容易思考。但是,在某些情況下,為了實現(xiàn)某些需要,我們不得不去操作底層的DOM。比如:需要與一個沒有使用React的第三方類庫進行整合,或者執(zhí)行一個React沒有原生支持的操作(canvas)。

我們需要了解ReactDOM.render組件返回的是什么?
它會返回對組件的引用,也就是組件實例(對于無狀態(tài)組件來說,返回null)。

注意:JSX返回的不是組件實例,它只是一個ReactElement對象。

在React內(nèi),它提供了一個可用于處理受其自身控制的DOM節(jié)點的方法,不過要注意的是,這些方法僅在組件聲明周期的特定階段才能被訪問到。

1、refs
要訪問受React控制的DOM節(jié)點,首先必須能夠訪問到負責控制這些DOM的組件,我們可以通過為子組件添加一個ref屬性來實現(xiàn)。

var MyInput = React.createClass({

  render: function(){

    returen (

      <input type="text" ref="myInput" />

    );

  }

});

為子組件添加了ref屬性后,我們就可以通過this.refs.myInput訪問到<input>組件了。

this返回的是當前組件。

注意:賦給每個子組件的ref值在所有子組件中必須是唯一的,也可以說是全局唯一。

到這里,我們已經(jīng)可以訪問到需要的子組件了,然后就可以通過它的getDOMNode()方法來訪問到底層的DOM節(jié)點了。

但是請注意,我們不能在render方法中使用getDOMNode()方法,只有在render方法執(zhí)行之后,并且react已經(jīng)完成了DOM的更新,才能通過 this.refs.city.getDOMNode() 來拿到原生的DOM元素,也可以這樣說,getDOMNode()僅在掛載的組件上有效(掛載:組件已經(jīng)被放進DOM中),否則拋出異常。

一般我們會在 componentDidMount 事件回調(diào)中使用 getDOMNode 方法,當然,componentDidMount內(nèi)部并不是getDOMNode方法的唯一執(zhí)行環(huán)境。事件處理器也是在組件掛載后觸發(fā)的,所以也可以在事件處理器中調(diào)用getDOMNode()方法。

var MyInput = React.createClass({   

  render: function(){   

    return (<input type="text" ref="myInput" onKeyUp={this.handleKeyUp}/>);

  },

  handleKeyUp: function(){   

    var input = this.refs.myInput;   

    console.log(input.value);   

  },   

  componentDidMount: function(){   

    console.log(this.refs.myInput);   

  }   

});   

ReactDOM.render(   

  <MyInput />,   

  document.body   

);

上面的代碼中,給input添加了一個keyup事件處理器,在handleKeyUp()方法里,我們也可以用this.refs.myInput訪問到對應的input元素。
每一個掛載的React組件都有一個 getDOMNode() 方法。

如果不了解React的生命周期,可以看這里:React:組件的生命周期

特別注意:(getDOMNode()方法在v0.14版本中使用會報提醒,而在 v0.15版本中已經(jīng)移除了)。因此,如果你使用的是 v0.15版本及以上的,this.refs.myInput獲取到的已經(jīng)是對應的DOM元素了,不過refs的表現(xiàn)和行為還是和之前的一致的。

2、ReactDOM.findDOMNode()
除了使用refs外,我們還可以使用react-dom提供的 findDOMNode() 方法拿到組件對應的DOM元素。

var MyInput = React.createClass({   

  render: function(){   

    return (<input type="text" ref="myInput" onKeyUp={this.handleKeyUp}/>);

  },

  handleKeyUp: function(){   

    var input = ReactDOM.findDOMNode(this);

    //或者

    input = ReactDOM.findDOMNode(this.refs.myInput);   

    console.log(input.value);   

  } 

});   

ReactDOM.render(   

  <MyInput />,   

  document.body   

);


3、整合非React類庫

要使用非React類庫,保持它們的狀態(tài)和React的狀態(tài)之間的同步是成功整合的關鍵。

假如我們需要使用一個autocomplete類庫:

(function(){   

  var autocomplete = function(params){   

    params = params || {};   

    if(!params.target) return;   

      var parent = params.target;   

      var data = params.data;   

      var list = '';   

      for(var i = 0; i < params.data.length; i++){   

        list += '<li>' + params.data[i] + '</li>';   

      }   

      parent.innerHTML = list;   

      if(params.events){   

        parent.addEventListener('click',function(e){   

          if(e.target.nodeName == 'LI'){   

            params.events.select(e.target.textContent);   

          }   

        });   

      }   

  };   

  window.autocomplete = autocomplete;  

})();

調(diào)用示例代碼:

autocomplte({

  target: document.getElementById('cities'),

  data: [

    '廣州','北京','深圳'

  ],

  events: {

    select: function(city){

      alert('你選擇的城市是' + city);

    }

  }

});

上面的autocomplete函數(shù)需要一個目標DOM節(jié)點、一個用作數(shù)據(jù)展現(xiàn)的字符串清單,以及一些事件監(jiān)聽器。

接下來,我們需要創(chuàng)建一個使用這兩個庫的React組件,然后需要添加一個componentDidMount處理器,在這個處理器內(nèi),可以將autocompleteTarget所指向子組件的底層DOM節(jié)點來連接這兩個接口:

var AutocompleteCities = React.createClass({

  render: function(){

    return (<div id="cities" ref="autocompleteTarget" />);

  },

  getDefaultProps: function(){

    return { 

      data: ['廣州','北京','深圳']

    };

  },

  handleSelect: function(city){

    alert('你選擇的城市是' + city);

  },

  componentDidMount: function(){

    autocomplete({

      target: this.refs.autocompleteTarget,  //也可以使用ReactDOM.findDOMNode(this.refs.autocompleteTarget)

      data: this.props.data,

      events: {

        select: this.handleSelect

      }

    });

  }

});

注意:componentDidMount方法只會為每個DOM節(jié)點調(diào)用一次。因此我們不用擔心一個節(jié)點上兩次調(diào)用autocomplete方法是否會有副作用。

對于簡單的插件,最好是通過將其重寫為React組件的形式來封裝它。

總結
  • 當僅使用虛擬DOM無法滿足需求時,可以考慮ref屬性,它允許訪問指定的元素
  • 在render方法執(zhí)行之后,并且react已經(jīng)完成了DOM的更新(一般是componentDidMount執(zhí)行后)時,可以使用this.refs.name或者ReactDOM.findDOMNode()方法來訪問底層的DOM節(jié)點。
  • 可以將簡單的第三方類庫(非React類庫)重寫為React組件的形式來封裝它。



以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號