摘要:因為節點跨層級的移動操作少到可以忽略不計如果父節點已經不存在,則該節點及其子節點會被完全刪除掉,不會用于進一步的比較。注意官方建議不要進行節點跨層級的操作,非常影響性能。對處于同一層級的節點進行對比。支持異常邊界處理異常。
完全是不可能滴, 這輩子都不可能完全! ? ?? ? -- 來自某非著名碼農
本文總結 React 實用的特性,部分實驗性和不實用的功能將不會納入進來,或許未來可期~
1、setState
面試題
class App extends Component { state = { val: 0 } // 震驚!隔壁老王也有失手的時候~ componentDidMount() { this.setState({val: this.state.val + 1}); console.log(this.state.val); // ? this.setState((prevState) => ({val: prevState.val + 1})); console.log(this.state.val); // ? setTimeout(() => { this.setState({val: this.state.val + 1}); console.log(this.state.val); // ? this.setState({val: this.state.val + 1}); console.log(this.state.val); // ? }, 1000) } render() { returnApp組件
; } }
總結
setState 只在 React 合成事件和鉤子函數中是“異步”的,在原生DOM事件和定時器中都是同步的。
如果需要獲取“異步”場景的 setState 的值 --> this.setState(partial, callback) 在 callback 中拿到最新的值
如果要在“異步”場景保證同步更新多次 setState --> this.setState((prevState, props) => {return newState})
能保證同步更新, 但是外面獲取的值還是之前的值
2、Fragment
before
代碼
export default class App extends Component { render() { return (); } }App組件
這是App組件的內容
效果
after
代碼
export default class App extends Component { render() { return (); } } App組件
這是App組件的內容
效果
總結:使用 Fragment ,可以不用添加額外的DOM節點
3、React性能優化
shouldComponentUpdate
// 舉個栗子: shouldComponentUpdate(nextProps, nextState) { if (nextProps !== this.props) { return true; // 允許更新 } if (nextState !== this.state) { return true; } return false; // 不允許更新 }
PureComponent 組件
使用
// 實現了對 state 和 props 的淺比較 // 相等就不更新,不相等才更新 class App extends PureComponent {}
淺比較源碼
// 實現 Object.is() 方法, 判斷x y是否完全相等 function is(x, y) { // (x !== 0 || 1 / x === 1 / y) 用于判斷 0 和 -0 不相等 // x !== x && y !== y 用于判斷 NaN 等于 NaN return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y ; } // 提取了hasOwnProperty方法,緩存 var hasOwnProperty$1 = Object.prototype.hasOwnProperty; // 返回false為更新,true為不更新 function shallowEqual(objA, objB) { // 如果A和B完全相等,返回true if (is(objA, objB)) { return true; } // 如果A和B不相等,并且不是對象,說明就是普通值,返回false if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { return false; } // 提取A和B的所有屬性 var keysA = Object.keys(objA); var keysB = Object.keys(objB); // 如果長度不相等,返回false if (keysA.length !== keysB.length) { return false; } // 檢測 A 的屬性 和 B 的屬性是否一樣 for (var i = 0; i < keysA.length; i++) { if (!hasOwnProperty$1.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) { return false; } } return true; }
問題:如果使用 pureComponent 只能進行淺比較,如果修改了原數據再更新,就會導致地址值一樣從而不會更新。但實際需要更新。
解決:
手動保證每次都是新的值
使用 immutable-js 庫,這個庫保證生成的值都是唯一的
var map1 = Immutable.Map({ a: 1, b: 2, c: 3 }); // 設置值 var map2 = map1.set("a", 66); // 讀取值 map1.get("a"); // 1 map2.get("a"); // 66
總結:使用以上方式,可以減少不必要的重復渲染。
4、React 高階組件
基本使用
// WrappedComponent 就是傳入的包裝組件 function withHoc(WrappedComponent) { return class extends Component { render () { return; } } } // 使用 withHoc(App)
向其中傳參
function withHoc(params) { return (WrappedComponent) => { return class extends Component { render () { return; } } } } // 使用 withHoc("hello hoc")(App)
接受props
function withHoc(params) { return (WrappedComponent) => { return class extends Component { render () { // 將接受的 props 傳遞給包裝組件使用 return; } } } }
定義組件名稱
function withHoc(params) { return (WrappedComponent) => { return class extends Component { // 定義靜態方法,修改組件在調試工具中顯示的名稱 static displayName = `Form(${getDisplayName(WrappedComponent)})` render () { return; } } } } // 封裝獲取包裝組件的 displayName 的方法 function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || "Component"; }
原文鏈接5、render props
原文太長,直接上 鏈接6、React 懶加載
官網真香, 建議大家將 React 官網過一遍~
react-loadable
import Loadable from "react-loadable"; import Loading from "./components/loading" const LoadableComponent = Loadable({ loader: () => import("./components/home"), loading: Loading, }); export default class App extends Component { render() { return (); } }App組件
Suspense 和 lazy
import React, {Component, Suspense, lazy} from "react"; import Loading from "./components/loading"; const LazyComponent = lazy(() => import("./components/home")); export default class App extends Component { render() { return (); } }App組件
}>
區別
react-loadable 是民間 --> 需要額外下載引入
Suspense 和 lazy 是官方 --> 只需引入
react-loadable 支持服務器渲染
Suspense 和 lazy 不支持服務器渲染
總結:使用 create-react-app 會將其多帶帶提取成一個bundle輸出,從而資源可以懶加載和重復利用。
7、虛擬DOM diff算法虛擬DOM diff算法主要就是對以下三種場景進行優化:
tree diff
對樹進行分層比較,兩棵樹只會對同一層次的節點進行比較。(因為 DOM 節點跨層級的移動操作少到可以忽略不計)
如果父節點已經不存在,則該節點及其子節點會被完全刪除掉,不會用于進一步的比較。
注意:
React 官方建議不要進行 DOM 節點跨層級的操作,非常影響 React 性能。
在開發組件時,保持穩定的 DOM 結構會有助于性能的提升。例如,可以通過 CSS 隱藏或顯示節點,而不是真的移除或添加 DOM 節點。
component diff
如果是同一類型的組件,按照原策略繼續比較 virtual DOM tree(tree diff)。
對于同一類型的組件,有可能其 Virtual DOM 沒有任何變化,如果能夠確切的知道這點那可以節省大量的 diff 運算時間,因此 React 允許用戶通過 shouldComponentUpdate() 來判斷該組件是否需要進行 diff。
如果不是,直接替換整個組件下的所有子節點。
element diff
對處于同一層級的節點進行對比。
這時 React 建議:添加唯一 key 進行區分。雖然只是小小的改動,性能上卻發生了翻天覆地的變化!
如: A B C D --> B A D C
添加 key 之前: 發現 B != A,則創建并插入 B 至新集合,刪除老集合 A;以此類推,創建并插入 A、D 和 C,刪除 B、C 和 D。
添加 key 之后: B、D 不做任何操作,A、C 進行移動操作,即可。
建議:在開發過程中,盡量減少類似將最后一個節點移動到列表首部的操作,當節點數量過大或更新操作過于頻繁時,在一定程度上會影響 React 的渲染性能。
總結
React 通過制定大膽的 diff 策略,將 O(n3) 復雜度的問題轉換成 O(n) 復雜度的問題;
React 通過分層求異的策略,對 tree diff 進行算法優化;
React 通過相同類生成相似樹形結構,不同類生成不同樹形結構的策略,對 component diff 進行算法優化;
React 通過設置唯一 key的策略,對 element diff 進行算法優化;
建議,在開發組件時,保持穩定的 DOM 結構會有助于性能的提升;
建議,在開發過程中,盡量減少類似將最后一個節點移動到列表首部的操作,當節點數量過大或更新操作過于頻繁時,在一定程度上會影響 React 的渲染性能。
原文鏈接8、Fiber
Fiber 是為了解決 React 項目的性能問題和之前的一些痛點而誕生的。
Fiber 的核心流程可以分為兩個部分:
可中斷的 render/reconciliation 通過構造 workInProgress tree 得出 change。
不可中斷的 commit 應用這些 DOM change。
異步實現不同優先級任務的協調執行:
requestIdleCallback: 在線程空閑時期調度執行低優先級函數;
requestAnimationFrame: 在下一個動畫幀調度執行高優先級函數;
總結
可切分,可中斷任務。
可重用各分階段任務,且可以設置優先級。
可以在父子組件任務間前進/后退切換任務。
render方法可以返回多元素(即可以返回數組)。
支持異常邊界處理異常。
原文鏈接:9、Redux
https://mp.weixin.qq.com/s/uD...
https://juejin.im/post/5a2276...
作用: 集中管理多個組件共享的狀態
特點: 單一數據源、純函數、只讀state
redux 核心模塊定義:
store.js
import { createStore, applyMiddleware } from "redux"; // 異步actions使用的中間件 import thunk from "redux-thunk"; // redux開發chrome調試插件 import { composeWithDevTools } from "redux-devtools-extension"; import reducers from "./reducers"; export default createStore(reducers, composeWithDevTools(applyMiddleware(thunk)));
reducers.js
import { combineReducers } from "redux"; import { TEST1, TEST2 } from "./action-types"; function a(prevState = 0, action) { switch (action.type) { case TEST1 : return action.data + 1; default : return prevState; } } function b(prevState = 0, action) { switch (action.type) { case TEST2 : return action.data + 1; default : return prevState; } } // 組合兩個reducer函數并暴露出去 export default combineReducers({a, b});
actions.js
import { TEST1, TEST2 } from "./action-types"; // 同步action creator,返回值為action對象 export const test1 = (data) => ({type: TEST1, data}); export const test2 = (data) => ({type: TEST2, data}); // 異步action creator,返回值為函數 export const test2Async = (data) => { return (dispatch) => { setTimeout(() => { dispatch(test2(data)); }, 1000) } };
action-types.js
export const TEST1 = "test1"; export const TEST2 = "test2";
組件內使用:
App.jsx
import React, { Component } from "react"; import PropTypes from "prop-types"; import { connect } from "react-redux"; import {test1, test2Async} from "./redux/actions"; class App extends Component { static propTypes = { a: PropTypes.number.isRequired, b: PropTypes.number.isRequired, test1: PropTypes.func.isRequired, test2Async: PropTypes.func.isRequired, } componentDidMount() { const { a, b, test1, test2Async } = this.props; // 測試 test1(a + 1); test2Async(b + 1); } render() { return (App組件); } } /* =============== redux相關代碼 ================== */ // 將狀態數據映射為屬性以props方式傳入組件 const mapStateToProps = (state) => ({a: state.a, b: state.b}); // 將操作狀態數據的方法映射為屬性以props方式傳入組件 const mapDispatchToProps = (dispatch) => { return { test1(data) { dispatch(test1(data)); }, test2Async(data) { dispatch(test2Async(data)); } } } // connect就是一個典型的HOC export default connect(mapStateToProps, mapDispatchToProps)(App); /* // 上面寫的太復雜了,但是好理解。而以下就是上面的簡寫方式 export default connect( (state) => ({...state}), { test1, test2Async } )(App); */
index.js
// 入口文件的配置 import React from "react"; import ReactDOM from "react-dom"; import { Provider } from "react-redux"; import App from "./App"; import store from "./redux/store"; ReactDOM.render(, document.getElementById("root"));
總結:
我們會發現使用 Redux 會變得更加復雜,以及多了很多模板代碼(例如: action creators)
但是,這是 Redux 能幫助我們更好操作狀態,追蹤和調試錯誤等。
并且 Redux 有著一整套豐富的生態圈,這些你都能在 官方文檔 找到答案
總之,目前比起世面上 mobx 等庫,更適用于大型項目開發~
10、未來可期其實還有很多技術沒有說,像 context 和 React Hooks 等,但受限于筆者的眼界,目前沒有發現大規模使用的場景(如果有,請小伙伴們指正),所以就不談了~有興趣的小伙伴去找找看吧~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109272.html
摘要:負載均衡就是用來幫助我們將眾多的客戶端請求合理的分配到各個服務器,以達到服務端資源的充分利用和更少的請求時間。如下面的配置復制代碼這樣可以完美繞過瀏覽器的同源策略訪問的屬于同源訪問,而對服務端轉發的請求不會觸發瀏覽器的同源策略。 性能優化是一門大學問,本文僅對個人一些積累知識的闡述,歡迎下面補充。 拋出一個問題,從輸入url地址欄到所有內容顯示到界面上做了哪些事? 1.瀏覽器向 DN...
摘要:負載均衡就是用來幫助我們將眾多的客戶端請求合理的分配到各個服務器,以達到服務端資源的充分利用和更少的請求時間。如下面的配置復制代碼這樣可以完美繞過瀏覽器的同源策略訪問的屬于同源訪問,而對服務端轉發的請求不會觸發瀏覽器的同源策略。 性能優化是一門大學問,本文僅對個人一些積累知識的闡述,歡迎下面補充。 拋出一個問題,從輸入url地址欄到所有內容顯示到界面上做了哪些事? 1.瀏覽器向 DN...
摘要:作者滬江前端開發工程師本文原創翻譯,有不當的地方歡迎指出。管理數據,而提供服務器上的數據,因此應用于處理網絡請求。結論使用建立的應用都是模塊化的會成為其中一個模塊,庫是另一個模塊。原文原創新書移動前端高效開發實戰已在亞馬遜京東當當開售。 作者:Oral (滬江Web前端開發工程師)本文原創翻譯,有不當的地方歡迎指出。轉載請指明出處。 當你問起有關AJAX與React時,老司機們首先就會...
閱讀 2759·2021-09-24 10:34
閱讀 1862·2021-09-22 10:02
閱讀 2251·2021-09-09 09:33
閱讀 1457·2021-08-13 15:02
閱讀 3270·2020-12-03 17:10
閱讀 1180·2019-08-30 15:44
閱讀 2144·2019-08-30 12:58
閱讀 3229·2019-08-26 13:40