React中的DOM操作
大多数情况下,React的虚拟DOM已经可以创建我们想要的用户体验,而根本不需要直接操作底层真实的DOM,通过组件的复合,把负责的交互聚合为呈现给用户的连贯整体。
但是在某些情况下,比如与一个没有使用React的第三类库的整合,或者执行一个React没有原生支持的操作等等,我们就不得不去操作底层DOM来达到我们的目的。
DOM操作
访问受控制的DOM节点
React提供了一个受其自身控制的方法,这些方法只有在生命周期的相关方法里才有效。我们可以给组件的相关元素添加一个ref属性来实现。
var CanvasComponent = React.createClass({
render:function(){
return (
<canvas ref="canvasEle" />
);
}
});
这样就可以通过this.refs.canvasEle来访问到这个canvas节点了,需要注意的是这里的ref属性必须是唯一的,如果定义了相同的ref也是"canvasEle",那么操作将无效。
一旦访问到了该元素,那么就可以通过getDOMNode()方法来访问底层的DOM节点,需要注意的是,不要在render方法中尝试用该方法,因为在render方法执行完之前,组件还未挂载,该DOM节点不是最有效的,所以可能会报异常。
所以要在组件被挂载后使用,比如componentDidMount或者一些用户操作后的事件处理函数,就像下面这样:
var CanvasComponent = React.createClass({
render:function(){
return (
<canvas ref="canvasEle" />
);
},
componentDidMount:function(){
var oCanvas = this.refs.canvasEle.getNode();
// 现在oCanvas就是我们的canvas节点,可以调用canvas下的相关方法来进行操作
}
});
React的refs和getDOMNode很强大,但是使用它们可能会导致React在性能上的一些问题,所以我们不到在没有其他方式的时候,尽量不要用它们来解决问题。
#####整合非React类库
很多成熟的javaScript类库并没有使用React构建,此时就需要我们自己来进行整合。
比如现在要使用一个autocomplete插件,包含下面的基础代码:
autocomplete({
target:document.getElementById("select"),
data:[
"option1","option2","option2"
],
events:{
select:function(item){
alert("你选择了" + item);
}
}
});
这个类库需要一个DOM节点,一个数组,一个事件的相关对象,所以,这里就用到了DOM操作,刚才提到,在React中使用DOM操作要在组件挂载完成后或一些事件处理函数中完成。
var SelectComponent = React.createClass({
getDefaultProps:function(){
return {
data:[
"option1","option2","option2"
]
};
},
render:function(){
return (
<div id="select" ref="autoCompleteDOM"></div>
);
},
handleSelect:function(item){
alert("你选择了" + item);
},
componentDidMount:function(){
autocomplete({
target:this.refs.autoCompleteDOM.getDOMNode(),
data:this.props.data,
events:{
select:this.handleSelect
}
});
}
});
现在把autocomplete引入到React中了,但是这样还不够,要知道,在这个组件被移除了怎么办,所以引入外部插件时一般需要注意在组件类中再实现一个componentWillUnmount的方法,这样在组件被移除,它会对自身进行清理,从而避免内存泄露等性能问题。
#####侵入式插件
在整合非React类库的时候,我们希望整合的类库仅仅操作的是组件的子元素,但是有时并非如此,此时我们就需要把这些额外的操作在React中规避掉,防止出现DOM被意外修改的错误。处理这种问题,最有效的方法就是把DOM的操控权完全给我们自己,而不是给这些类库。