(給前端大全加星標,提升前端技能)
作者:code秘密花園 公號 / ConardLi
缺少 Keyimgreact官方文檔是這樣描述key的:
Keys可以在DOM中的某些元素被增加或刪除的時候幫助React識別哪些元素發生了變化。因此你應當給數組中的每一個元素賦予一個確定的標識。
react的diff算法是把key當成唯一id然後比對組件的value來確定是否需要更新的,所以如果沒有key,react將不會知道該如何更新組件。你不傳 key 也能用是因為 react 檢測到子組件沒有 key 後,會默認將數組的索引作為 key。react根據key來決定是銷毀重新創建組件還是更新組件,原則是:
key相同,組件有所變化,react會只更新組件對應變化的屬性。key不同,組件會銷毀之前的組件,將整個組件重新渲染。重複 Keywarningimg從上面提到的 key 的作用可以知道,如果出現兩個相同的 key,則渲染可能出現異常。
錯誤案例:img常見的錯誤是,在使用 antd 的 table 組件時,每個列的 dataIndex 屬性同時也會作為 key,注意兩個列的 dataIndex 不要相同。
P 標籤包含內聯元素warningimg在 HTML5 中,標準制定者重新定義了HTML元素的分類,並根據這一新的分類定義了元素的內容模型(Content Model) -- 對於一個元素而言,哪些子元素是合法的,而哪些子元素是非法的。需要注意的是,HTML5中的這種元素分類與inline、block沒有任何關係,任何元素都可以在CSS中被定義為display:inline或者display:block。另外,除了這7大分類,還存在一些較小的分類,如Palpable、Script-Supporting等。
imgMetadata
顧名思義,Metadata元素意指那些定義文檔元數據信息的元素 — 其作用包括:影響文檔中其它節點的展現與行為、定義文檔與其它外部資源之間的關係等。以下元素屬於Metadata:base, link, meta, noscript, script, style, template, title。
Flow
所有可以放在body標籤內,構成文檔內容的元素均屬於Flow元素。因此,除了base, link, meta, style, title等只能放在head標籤內的元素外,剩下的所有元素均屬於Flow元素。
Sectioning
Sectioning意指定義頁面結構的元素,具體包含以下四個:article, aside, nav, section。
Heading
所有標題元素屬於Heading,也即以下6個元素:h1, h2, h3, h4, h5, h6。
Phrasing
所有可以放在p標籤內,構成段落內容的元素均屬於Phrasing元素。因此,所有Phrasing元素均屬於Flow元素。在HTML5標準文檔中,關於Phrasing元素的原始定義為:
Phrasing content is the text of the document, as well as elements that mark up that text at the intra-paragraph level. Runs of phrasing content form paragraphs.
對於這一定義,個人認為不應當使用「text」這一容易引起誤解的詞,事實上,一個元素即使不是文本,只要能包含在p標籤中成為段落內容的一部分,就可以稱之為Phrasing元素。比如:audio、video、img、select、input等元素(經測試,這些元素都可以放置在p標籤中)。一個不太精確的類比是:HTML5中的Phrasing元素大致就是HTML4中所定義的inline元素。Phrasing元素內部一般只能包含別的Phrasing元素。
Embedded
所有用於在網頁中嵌入外部資源的元素均屬於Embedded元素,具體包含以下9個:audio, video, img, canvas, svg, iframe, embed, object, math。
Interactive
所有與用戶交互有關的元素均屬於Interactive元素,包括a, input, textarea, select等。
內容模型(Content Model)
根據以上元素分類,HTML5標準文檔定義了任何元素的內容模型 — 對於該元素而言,何種子元素才是合法的。
對於p元素而言,其內容模型為Phrasing, 這意味著p元素只接受Phrasing元素為子元素,而對於像div這樣的非Phrasing元素則並不接受。類似的,li元素的內容模型為Flow,因此任何可以放置在body中的元素都可以作為li元素的子元素。
錯誤案例img直接寫 html 元素時我們可能會有意識的避免 p 標籤包含 div,使用 antd 時有些組件可能會不太注意,比如 Divider 是使用 div 實現的,不能作為 p 標籤的子元素。
頁面可能正常解析,但不符合語義。這是因為瀏覽器自帶容錯機制,對於不規範的寫法也能夠正確的解析,各瀏覽器的容錯機制不同,所以儘量按規範來寫。
Props 類型錯誤warning組件接收的 props 類型與預定義的不符。
錯誤案例以上的 case 最容易產生這種 warning,當我們定義了一個高階組件,此組件是對已有 From 組件的一個封裝,同時我們額外接收一個 param 參數來做一個其他事情,其他的參數我們要傳回 Form。這時如果不做額外的操作,param 參數也會被傳入 Form 組件,它是一個意外的參數,這就會讓 React 拋出 warning,我們可以做下面的處理:
componentWillReceiveProps 棄用warningimgcomponentWillReceiveProps這些生命周期經常被誤解或濫用,它們的潛在濫用可能會對異步渲染造成更大的問題,未來其會被逐漸棄用,現在使用如果沒有加 UNSAFE_ 前綴,則會在控制臺拋出錯誤。
React Fiber 引入了異步渲染,有了異步渲染之後,React 組件的渲染過程是分時間片的,不是一口氣從頭到尾把子組件全部渲染完,而是每個時間片渲染一點,然後每個時間片的間隔都可去看看有沒有更緊急的任務(比如用戶按鍵),如果有,就去處理緊急任務,如果沒有那就繼續照常渲染。
根據 React Fiber 的設計,一個組件的渲染被分為兩個階段:第一個階段(也叫做 render 階段)是可以被 React 打斷的,一旦被打斷,這階段所做的所有事情都被廢棄,當 React 處理完緊急的事情回來,依然會重新渲染這個組件,這時候第一階段的工作會重做一遍;第二個階段叫做 commit 階段,一旦開始就不能中斷,也就是說第二個階段的工作會穩穩噹噹地做到這個組件的渲染結束。
兩個階段的分界點,就是 render 函數。render 函數之前的所有生命周期函數(包括 render)都屬於第一階段,之後的都屬於第二階段。在 React v16.3 之前,render 之前的生命周期函數(也就是第一階段生命周期函數)包括這些:
componentWillReceiveProps上面提到的濫用,其實就是在這些生命周期中產生了副作用,這些生命周期都應該是純函數,不應該產生任何副作用。到了 React v16.3,React 乾脆引入了一個新的生命周期函數 getDerivedStateFromProps,這個生命周期函數是一個 static 函數,在裡面根本不能通過 this 訪問到當前組件,輸入只能通過參數,對組件渲染的影響只能通過返回值。沒錯,getDerivedStateFromProps 應該是一個純函數,React 就是通過要求這種純函數,強制開發者們必須適應異步渲染。
錯誤案例已棄用寫法:
class ExampleComponent extends React.Component {
state = {
isScrollingDown: false,
};
componentWillReceiveProps(nextProps) {
if (this.props.currentRow !== nextProps.currentRow) {
this.setState({
isScrollingDown:
nextProps.currentRow > this.props.currentRow,
});
}
}
}推薦寫法:
class ExampleComponent extends React.Component {
state = {
isScrollingDown: false,
lastRow: null,
};
static getDerivedStateFromProps(props, state) {
if (props.currentRow !== state.lastRow) {
return {
isScrollingDown: props.currentRow > state.lastRow,
lastRow: props.currentRow,
};
}
return null;
}
}
getSnapshotBeforeUpdate 無返回值warning如果組件實現了 getSnapshotBeforeUpdate() 生命周期,則它的返回值將作為 componentDidUpdate() 的第三個參數 「snapshot」 參數傳遞。否則此參數將為 undefined。
getSnapshotBeforeUpdate() 在最近一次渲染輸出(提交到 DOM 節點)之前調用。它使得組件能在發生更改之前從 DOM 中捕獲一些信息(例如,滾動位置)。此生命周期的任何返回值將作為參數傳遞給 componentDidUpdate()。此用法並不常見,但它可能出現在 UI 處理中,如需要以特殊方式處理滾動位置的聊天線程等。
錯誤案例已棄用寫法:
componentWillUpdate(nextProps, nextState) {
if (this.props.list.length < nextProps.list.length) {
this.previousScrollOffset =
this.listRef.scrollHeight - this.listRef.scrollTop;
}
}
componentDidUpdate(prevProps, prevState) {
if (this.previousScrollOffset !== null) {
this.listRef.scrollTop =
this.listRef.scrollHeight -
this.previousScrollOffset;
this.previousScrollOffset = null;
}
}在上面的示例中,componentWillUpdate用於讀取DOM屬性。但是,使用異步渲染時,「render」階段生命周期(如componentWillUpdate和render)和「commit」階段生命周期(如componentDidUpdate)之間可能會有延遲。如果用戶在此期間進行了諸如調整窗口大小的操作,則scrollHeight從中讀取的值componentWillUpdate將不準確。
正確用法:
getSnapshotBeforeUpdate(prevProps, prevState) {
if (prevProps.list.length < this.props.list.length) {
return (
this.listRef.scrollHeight - this.listRef.scrollTop
);
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot !== null) {
this.listRef.scrollTop =
this.listRef.scrollHeight - snapshot;
}
}
誤用純函數 Renderwarningimg上面我們提到 render 函數也屬於 render 階段的生命周期,所以它一定也要是純函數,有時候為了方便我們會在 render 函數中做一些狀態更改,這種用法是錯誤的。
錯誤案例img上面的案例中,在 render 中根據 hash 值對狀態做了更改,正確的用法是這種操作應該在狀態初始化時完成,而不是在 render 函數中。
react hot loaderimg這個是 react-hot-loader 的一個 bug,react-hot-loader react-dom 補丁對其進行了修復 https://www.npmjs.com/package/react-hot-loader#hot-loaderreact-dom
安裝 @hot-loader/react-dom ,在 webpack 配置中通過 alias 將 @hot-loader/react-dom 指向 react-dom 即可。
【Mobx】observableArraywarningimg不同於 sort 和 reverse 函數的內置實現,observableArray.sort 和 observableArray.reverse 不會改變數組本身,而只是返回一個排序過/反轉過的拷貝。在 MobX 5 及以上版本中會出現警告。推薦使用 array.slice().sort() 來替代。
錯誤案例store.data.sort((a, b) => a.status - b.status);上面的代碼不會直接改變 array,推薦下面的寫法:
store.data = store.data.slice().sort((a, b) => a.status - b.status);覺得本文對你有幫助?請分享給更多人
推薦關注「前端大全」,提升前端技能
點讚和在看就是最大的支持❤️