寫在前面
- 在寫 react 程式碼的時候經常遇到如下的報錯
Can't perform a React state update on an unmounted component. This is a no-op......
複製程式碼
- 本篇文章首先回顧一下什麼是記憶體洩露,然後看兩個 demo 觀察 react 出現記憶體洩露的具體情況。
什麼是記憶體洩露
-
程式的執行需要記憶體。只要程式提出要求,作業系統或者執行時(runtime)就必須供給記憶體。
-
對於持續執行的服務程式(daemon),必須及時釋放不再用到的記憶體。否則,記憶體佔用越來越高,輕則影響系統效能,重則導致程式崩潰。
-
不再用到的記憶體,沒有及時釋放,就叫做記憶體洩漏(memory leak)。
JavaScript 中常見的幾種記憶體洩露
- 全域性變數引起的記憶體洩漏
function leaks(){
leak = '***'; //leak 成為一個全域性變數,不會被回收
}
複製程式碼
- 閉包引起的記憶體洩漏
var leaks = (function(){
var leak = '***';// 被閉包所引用,不會被回收
return function(){
console.log(leak);
}
})()
複製程式碼
- dom清空或刪除時,事件未清除導致的記憶體洩漏
document.querySelector("#demo").addEventListener('click', myFunction);
var para1=document.querySelector("#demo");
para1.parentNode.removeChild(para1);
複製程式碼
如果我們在沒有取消 click 方法前去銷燬了 para1 節點,就會造成記憶體洩露。
正確的做法:
document.querySelector("#demo").addEventListener('click', myFunction);
// 我們需要在刪除節點前清除掛載的 click 方法
document.querySelector("#demo").removeEventListener("click", myFunction);
var para1=document.querySelector("p1");
para1.parentNode.removeChild(para1);
複製程式碼
- 延展閱讀,大家有空可以仔細的看看阮一峰老師的相關文章
- www.ruanyifeng.com/blog/2017/0… (阮一峰)
為了更加了解 react 的記憶體洩露問題看看下面幾個 demo
Demo 1:
componentWillMount: function () {
var onLogin = this.props.onLogin || function () {},
onLogout = this.props.onLogout || function () {};
this.on('authChange', function () {
console.log('user authenticated:', this.state.isAuthenticated);
return this.state.isAuthenticated
? onLogin(this.state)
: onLogout(this.state);
}.bind(this));
},
複製程式碼
- 上面的例子是在 Stack Overflow 上看到的,樓主在
componentWillMount
的時候掛載了authChange
事件,然後 react 出現瞭如下的報錯:
- 意思為:我們不能在元件銷燬後設定state,防止出現記憶體洩漏的情況
需要怎麼解決啦?
- 新增如下程式碼即可
componentWillUnmount: function () {
this.off('authChange', this.authChange);
this.authChange = null;
}
複製程式碼
很明顯這種情況就是在 dom 結構銷燬的時候,事件卻沒有清除導致的記憶體洩漏,所以我們需要在
componentWillUnmount
的時候去清除掛載的方法
react 記憶體洩露相關解釋和解決方法
-
這裡就提到了記憶體洩露,當我們在使用事件繫結,setInterval,setTimeOut 或一些函式的時候,但是卻沒有在元件銷燬前清除的時候會造成記憶體洩露。這裡我們手動的再
componentWillUnmount
去清除相關的方法即可。 -
why:
- I would move your function into componentDidMount and add cleanup on componentWillUnmount
- Important: componentWillMount is called on the server and client, but componentDidMount is only called on the client.
- If you’re using eventListeners, setInterval or other functions that needs to be cleaned, put them in componentDidMount. The server will not call componentWillUnmount and is usually the cause of memory leaks.
Demo 2:
- 下面這種就是常見的情況:
this.pwdErrorTimer = setTimeout(() => {
this.setState({
showPwdError:false
})
}, 1000);
複製程式碼
設定了一個timer延遲設定state,然而在延遲的這段時間,元件已經銷燬,則造成此類問題
- 解決方法:
- 利用生命週期鉤子函式:componentWillUnmount
componentWillUnmount(){
clearTimeout(this.pwdErrorTimer);
clearTimeout(this.userNameErrorTimer);
}
複製程式碼
衍生閱讀
關於 promise 請求是否會造成記憶體洩露的問題
1、Does never resolved promise cause memory leak?
- Does never resolved promise cause memory leak?
- 原問題在上面,可以看看