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