完全是不可能滴, 这辈子都不可能完全!        – 来自某非著名码农本文总结 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() { return <h2>App组件</h2>; }}总结setState 只在 React 合成事件和钩子函数中是“异步”的,在原生DOM事件和定时器中都是同步的。如果需要获取“异步”场景的 setState 的值 –> this.setState(partial, callback) 在 callback 中拿到最新的值如果要在“异步”场景保证同步更新多次 setState –> this.setState((prevState, props) => {return newState}) 能保证同步更新, 但是外面获取的值还是之前的值2、Fragmentbefore代码export default class App extends Component { render() { return ( <div> <h2>App组件</h2> <p>这是App组件的内容</p> </div> ); }}效果after代码export default class App extends Component { render() { return ( <Fragment> <h2>App组件</h2> <p>这是App组件的内容</p> </Fragment> ); }}效果总结:使用 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’); // 1map2.get(‘a’); // 66总结:使用以上方式,可以减少不必要的重复渲染。4、React 高阶组件基本使用// WrappedComponent 就是传入的包装组件function withHoc(WrappedComponent) { return class extends Component { render () { return <WrappedComponent />; } }}// 使用withHoc(App)向其中传参function withHoc(params) { return (WrappedComponent) => { return class extends Component { render () { return <WrappedComponent />; } } }}// 使用withHoc(‘hello hoc’)(App)接受propsfunction withHoc(params) { return (WrappedComponent) => { return class extends Component { render () { // 将接受的 props 传递给包装组件使用 return <WrappedComponent {…this.props}/>; } } }}定义组件名称function withHoc(params) { return (WrappedComponent) => { return class extends Component { // 定义静态方法,修改组件在调试工具中显示的名称 static displayName = Form(${getDisplayName(WrappedComponent)}) render () { return <WrappedComponent {…this.props}/>; } } }}// 封装获取包装组件的 displayName 的方法function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || ‘Component’;}原文链接5、render props原文太长,直接上 链接 官网真香, 建议大家将 React 官网过一遍6、React 懒加载react-loadableimport 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 ( <div> <h2>App组件</h2> <LoadableComponent /> </div> ); }}Suspense 和 lazyimport 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 ( <div> <h2>App组件</h2> <Suspense fallback={<Loading />}> <LazyComponent /> </Suspense> </div> ); }}区别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、FiberFiber 是为了解决 React 项目的性能问题和之前的一些痛点而诞生的。Fiber 的核心流程可以分为两个部分:可中断的 render/reconciliation 通过构造 workInProgress tree 得出 change。不可中断的 commit 应用这些 DOM change。异步实现不同优先级任务的协调执行:requestIdleCallback: 在线程空闲时期调度执行低优先级函数;requestAnimationFrame: 在下一个动画帧调度执行高优先级函数;总结可切分,可中断任务。可重用各分阶段任务,且可以设置优先级。可以在父子组件任务间前进/后退切换任务。render方法可以返回多元素(即可以返回数组)。支持异常边界处理异常。原文链接: https://mp.weixin.qq.com/s/uD...https://juejin.im/post/5a2276...9、Redux作用: 集中管理多个组件共享的状态特点: 单一数据源、纯函数、只读stateredux 核心模块定义:store.jsimport { 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.jsimport { 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.jsimport { 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.jsexport const TEST1 = ’test1’;export const TEST2 = ’test2’;组件内使用:App.jsximport 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 ( <div>App组件</div> ); }}/* =============== 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就是一个典型的HOCexport 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( <Provider store={store}> <App /> </Provider>, document.getElementById(‘root’));总结:我们会发现使用 Redux 会变得更加复杂,以及多了很多模板代码(例如: action creators)但是,这是 Redux 能帮助我们更好操作状态,追踪和调试错误等。并且 Redux 有着一整套丰富的生态圈,这些你都能在 官方文档 找到答案总之,目前比起世面上 mobx 等库,更适用于大型项目开发10、未来可期其实还有很多技术没有说,像 context 和 React Hooks 等,但受限于笔者的眼界,目前没有发现大规模使用的场景(如果有,请小伙伴们指正),所以就不谈了有兴趣的小伙伴去找找看吧