React踩坑筆記 —— React中的Dom操作
阿新 • • 發佈:2018-12-29
目錄
React中操作Dom的四種方式
- 通過
‘Refs’
操作非受控元件《使用Ref》; - 通過事件處理器的預設引數
event
,獲取EventTarget
《React事件系統》; - 通過原生JavaScript
Dom選擇器
; - 通過
Dom操作外掛
,例如Jquery
《React整合第三方庫》;
為什麼要操作Dom
在一般的網頁應用中,像使用原生JavaScript、Jquery開發的應用中,Dom操作是很常見的。
而在典型的React資料流中,props
是父元件與子元件互動的唯一方式(單向資料流)。所以,為了修改子元件,你需要傳遞新的props
去重新渲染它。
然而,在某些情況下,我們需要脫離對React資料流動的依賴,去強制修改子元件。這些被修改的子元件,可能是一個React元件例項,也可能是實際的Dom元素。
Refs
便是在這些情況下,React提供的推薦解決方式。值得注意的是,像event.target
、dom選擇器
、dom操作外掛
也是能夠使用的方式,但是它們存在侷限性,比如:
- 只能夠操作實際Dom元素;
- 相同元件可以被多次渲染,
id選擇器
無法正常工作。
而Refs
class元件
可以被使用,而function無狀態元件
不能被使用,因為它無法被例項化)
什麼時候操作Dom
這裡有一些典型的使用情況:
- 焦點獲取、文字選擇、媒體播放等動態行為;
- 觸發強制動畫;
- 整合第三方庫;
對於任何可宣告性完成的事情避免使用Refs
。 例如,在Dialog
元件中通過傳遞isOpen
屬性,來代替暴露open()
和close()
方法。
須知
- React不會知道React以外的方式對Dom做出的改變,它基於自己內部的表現來決定如何更新,如果一個Dom節點同時被React以外的方式操作,那麼React將變的混亂,並且無從恢復。
- 比如:用React以外的方式對Dom內容修改,在props或state發生改變——元件更新後被覆蓋;
- 比如:用React以外的方式對Dom內容的修改依賴於元件更新前的狀態,元件更新後沒有達到預期效果;
- 為了解決這個問題,你可以通過狀態改變來記錄每一步操作,以便在更新後重現它。
- 最乾脆的方式就是,不讓React有理由去更新這個Dom,從而阻止元件更新帶來的衝突。例如空的
<div />
,這個<div />
不需要props
和state
的支撐,所以React沒有理由去更新它,可以留給Dom操作外掛
或選擇器
自由的管理這部分Dom。 - 記得在元件解除安裝時移除外掛註冊的事件監聽,避免記憶體洩漏。
- 另外,React推薦使用Refs或React event system來操作Dom,最好不要依賴
ID選擇器
,因為相同的元件可能會被多次渲染,使得整個document
中存在多個元素具有相同id
屬性。(詳情可見《document中id屬性不唯一時,id選擇器如何工作》)
注意
- 在Props和State發生改變的時候,元件會發生更新,至於最終如何被更新,可以檢視《差分演算法》
- document維護著整棵React元素樹,“差分演算法” 便是基於document比較React元素樹或元素子樹的演算法;
- 通過
Refs
、event.target
、dom選擇器
、Dom操作外掛
對Dom節點的操作將儲存在document中,並參與 “差分演算法”,然而:元件更新
將會對該元件對應的React元素子樹執行差異演算法,如果新的狀態和屬性沒有影響到這些Dom操作,那麼操作將被保留,否則對Dom的操作會消失
或變得混亂
;元件解除安裝(路由)
,將會解除安裝元件,同時所包含的Dom節點會被銷燬、子元件也會被解除安裝,所以之前對Dom的操作也會消失(除非你專門記錄它:可以使用父元件state
或storage
)頁面重新整理
將會產生新的document,之前對Dom的操作將會消失(除非你專門記錄它:只能使用storage
)。
- Element被建立後,被諸如
Node.insertBefore()
之類的操作插入document之前,是沒有意義的,通過選擇器document.querySelector()
或document.getElementById()
只能獲得null
,所以在document渲染完成前,操作Dom是無意義的。