精简react一文上手干项目

286 阅读16分钟
1.react基础知识
1.react-dom

BrowserRouter: 利用h5 Api实现路由切换;

HashRouter: 利用hash实现路由切换;

Suspense: 用来优化页面加载,页面加载时用fallback渲染等待加载组件;

2.react生命周期

第一阶段:装载阶段

  1. constructor()
    
  2. render()
    
  3. componentDidMount()
    

第二阶段:更新阶段

  1. shouldComponentUpdate()
    
  2. render()
    
  3. componentDidUpdate()
    

第三阶段:卸载阶段

  1. componentWillUnmount()
    

constructor生命周期

  1. 当react组件实例化时,是第一个运行的生命周期;
    
  2. 在这个生命周期中,不能使用this.setState();
    
  3. 在这个生命周期中,不能使用副作用(调接口、dom操作、定时器、长连接等);
    
  4. 不能把props和state交叉赋值;
    

componentDidMount生命周期

  1. 相当于是vue中的mounted;
    
  2. 它表示DOM结构在浏览器中渲染已完成;
    
  3. 在这里可以使用任何的副作用;
    

shouldComponentUpdate(nextProps,nextState)生命周期

  1. 相当于一个开关,如果返回true则更新机制正常执行,如果为false则更新机制停止;
    
  2. 在vue中是没有的;
    
  3. 存在的意义:可以用于性能优化,但是不常用,最新的解决方案是使用PureComponent;
    
  4. 理论上,这个生命周期的作用,用于精细地控制声明式变量的更新问题,如果变化的声明式变量参与了视图渲染则返回true,如果被变化的声明式变量没有直接或间接参与视图渲染,则返回false

componentDidUpdate生命周期

  1. 相当于vue中的updated();
    
  2. 它表示DOM结构渲染更新已完成,只发生在更新阶段;
    
  3. 在这里,可以执行大多数的副作用,但是不建议;
    
  4. 在这里,可以使用this.setState(),但是要有终止条件判断。
    

componentWillUnmount生命周期

  1. 一般在这里清除定时器、长连接等其他占用内存的构造器;
    

render生命周期

  1. render是类组件中唯一必须有的生命周期,同时必须有returnreturn 返回的jsx默认只能是单一根节点,但是在fragment的语法支持下,可以返回多个兄弟节点);
    
  2. Fragment碎片写法: <React.Fragment></React.Fragment> 简写成<></>
  3. return之前,可以做任意的业务逻辑,但是不能使用this.setState(),会造成死循环;
    
  4. render()在装载阶段和更新阶段都会运行;
    
  5. 当render方法返回null的时候,不会影响生命周期函数的正常执行。
    

forceUpdate: 页面强制更新,不经过 shouldComponentUpdate();

componentWillReceiveProps: 在子组件内,将要接收props调用(第一次不会调用,更新后才开始调用,可以接收参数props,参数是更新后的值);

unmountComponentAtNode: 组件的卸载(ReactDom.unmountComponentAtNode(gocument.getElementById('...')));

react新的生命周期 - react-17.0.1开始采用

  1. getDerivedStateFromProps:
  2. getSnapshotBeforeUpdate:
3.react-router

一般组件与路由组件: 路由组件就会有history等属性, 默认为undefined 路由组件属性: image.png Link: 用来页面跳转,没有高亮的效果。 NavLink: 用来页面跳转,可以实现高亮的效果,通过activeClassName指定类名, 与defaultChecked类似。

Outlet: 在嵌套路由, 在父级内用来占位, 显示子路由;

Navigate: 路由重定向;

useNavigate: 用来跳转并传值;

useLocation: 用来接收跳转传过来的值; Switch组件: 用来路由中做单独匹配(模糊匹配时,从上到下只匹配一个路由),提高效率。 exact属性: 路由加上后会取消模糊查询机制,变成精准匹配 Redirect组件: 当路由找不到时,做处理的组件 image.png props.children: 父组件标签体内的内容,可以在子组件中的props.childern中找到 image.png 多层路由样式丢失解决办法: image.png 路由传参: params传参,search传参,state传参 image.png image.png image.png 编程式路由跳转: 就是push和replace用法,push会记录路由记录, replace是直接替换掉上一个记录 image.png image.png withRouter : 能够接收一般组件,并给一般组件加工上路由组件的属性,让一般组件也具备路由跳转的功能属性。 image.png BrowerRouter和HashRouter区别: image.png

4.redux

image.png createStore: 用来将state存入store;

reducer(preSatate, action): 传入之前的状态, 传入动作对象, 返回一个新的状态;就是处理状态的纯函数;

Provider: 跨组件传值,避免父子组件层层传递;向子组件暴漏数据(生产者);

Consumer: 跨组件传值,避免父子组件层层传递;接收父组件传值(消费者);

createContext: 解决无需层级关系传递,Provider传递,Consumer接收;

useContext: 使用useContext接受上下文,因为传入的是对象,则接受的也应该是对象;

store.getState: 获取状态;

store.dispath(type, data): 触发的条件和值;

store.subscribe: 检测store中数据的变化后render页面;

connect(参数, 方法)(UI组件): 容器组件,用来链接ui组件和redux的,ui组件只能用来展示,所以必须在外层包裹一个容器组件做数据的处理工作;(注意: 因为react-redux帮做处理了,所以store和其中的state和dispatch就可以直接拿到了) image.png image.png image.png connect(参数对象, {actions...})(Ui组件): react-redux帮忙做了diapatch处理 image.png combineReducers: 合并的总的reduers的集合,合并后是一个总和的集合对象;

纯函数: redux中数组的处理不能用push和unshift的原因,不是纯函数,改变了原来的数组的引用类型数组的指向,改变了原数组,不能用,所以只能用[...oldArr, data];用[...oldArr],原本的oldArr没有改变。 image.png

5.react-css

styled-components: css in js方式的一种; antd按需引入: 参照antd官网,3.xxx版本的详细文档去一步步走即可; antd样式自定义主题: image.png

6.react-hooks

React 一直提倡使用函数组件,但是有一些功能只有类组件具备,比如函数组件没有实例,没有生命周期函数,而 React 16.8 提出的 Hooks 概念可以在不编写 Class 的情况下,使用 React 特性。凡是 use 开头的 React API 都是 Hooks。

Hooks 使用的注意事项:只能在函数内部的最外层调用 Hook,且只能在react的函数组件中调用,不要在其他javascript函数中调用。不要在循环,条件判断或者子函数中调用。只有 Hook 的调用顺序保持一致,React 才能正确地将内部 state 和对应的 Hook 进行关联。React Hooks 内部是链表结构的,如果不按照顺序书写或者某个 useState 没有执行,就会导致指针移动出错,数据存取出错。

  1. useDispatch: 向store传递方法, 与redux搭配使用;

  2. useSelector: 子组件用来访问父组件state中的数据,与redux搭配使用;

  3. useRef: 用来获取ref绑定的dom对象,获取的对象只有个current属性,只在函数式组件用,与createRef的写法一致;

  4. useState: useState 通过在函数组件里调用它来给组件添加一些内部state(函数组件是没有state的,class组件有)。React会在重复渲染时保留这个state。useState会返回一对值:当前状态一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。它类似 class 组件的this.setState,但是它不会把新的 state 和旧的 state 进行合并。

  5. useEffect: 可以看作是componentDidMount,componentDidUpdate,componentWillUnmount三个的组合,这个函数就是用来处理"副作用"的, 举个通俗一点的例子, 假如感冒了本来吃点药就没事了, 但是吃了药发现身体过敏了,而这个"过敏"就是副作用。放到React中,本来只是想渲染DOM展示到页面上,但除了DOM之外还有数据,而这些数据必须从外部的数据源中获取,这个"获取外部数据源"的过程就是副作用。

     useEffect使用时有以下4种情况:
    
     1. 不传递
     useEffect不传递第二个参数会导致每次渲染都会运行useEffect。然后,当它运行时,
     它获取数据并更新状态。然后,一旦状态更新,组件将重新呈现,
     这将再次触发useEffect,这就是问题所在。
         useEffect(()=>{
         console.log(props.number)
         setNumber(props.number)
         }) //所有更新都执行
     2. 传递空数组
         useEffect(()=>{undefined
         console.log(props)
         },[]) //仅在挂载和卸载的时候执行
     3. 传递一个值
         useEffect(()=>{undefined
         console.log(count)
         },[count]) //count更新时执行
     4. 传递多个
         const Asynchronous : React.FC=({number})=>{
         const [number2,setNumber2] = useState(number);
         useEffect(()=>{
             console.log(number)
             setNumber2(number)
         },[number,setNumber2]) //监听props对象number的更改
         //setNumber2是useState返回的setter,所以不会在每次渲染时重新创建它,
         //因此effect只会运行一次
         }
     5. 传递props的对象 传递的useState返回的setter
     6. return 方法
         const timer = setInterval(() => {undefined
         setCount(count + 1)
         }, 1000)
         // useEffect方法的第一个参数是一个函数,函数可以return一个方法,
         //这个方法就是在组件销毁的时候会被调用
         useEffect(() => {
                 return () => {
                 clearInterval(timer)
             }
         }, [])
    
  6. useLayoutEffect: useLayoutEffect有DOM操作的副作用,在DOM更新之后执行和useEffet类似,但是执行时机不同,useLayoutEffect在DOM更新之后执行,useEffect在render渲染结束后执行,也就是说useLayoutEffect比useEffect先执行,这是因为DOM更新之后,渲染才结束或者渲染还会结束。useLayoutEffect执行比较useEffect快,useEffect每次都是完整流程,而useLayoutEffect在vdom后先不渲染,先更新,最后只渲染一次。

  7. useMemo: 用来做缓存用。useMemo使用场景,比如有两个变量(依赖项),只需要在其中一个变量变化时发生变化,否则拿缓存的值;或者其中另一个变量的变化不需要引起重新计算时使用。该属性类似于vue中的计算属性,有返回值。

    const memoizedValue =useMemo(callback,array);
    callback是一个函数用于处理逻辑
    array 控制useMemo重新执行的数组,array改变时才会重新执行useMemo
    不传数组,每次更新都会重新计算
    空数组,只会计算一次
    依赖对应的值当对应的值发生变化时才会重新计算(可以依赖另外一个useMemo 返回的值)
    useMemo的返回值是一个记忆值,是callback的返回值
    

8.useCallback:与useMemo类似;useMemo与useCallback相同,接收一个函数作为参数,也同样接收第二个参数作为依赖列表;useCallback是对传过来的回调函数优化,返回的是一个函数。

被route包裹的组件,可以直接使用props进行路由相关操作,但是没有被route包裹的组件只能用withRouter高阶组件修饰或者使用hooks进行操作

9.useHistory: 跳转路由;

10.useLocation: 得到url对象;

11.useParams: 得到url上的参数;

  1. useRef和useImperativeHandle以及React.forwardRef: image.png
7.redux和hooks搭配使用流程

【useState】-> 【useReducer】-> 【useSelector、useDispatch】 通过useSelector、useDispatch等方法,我们可以用Hooks方法完成Redux的功能,Hooks和Redux之间没有替代关系,应该更好地共存和融合。 React.memo 为高阶组件。它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。

8.react高阶组件
  1. React.memo: 适用于函数组件,但不适用于 class 组件; React.memo()使用场景就是纯函数组件频繁渲染props; React.memo 仅检查 props 变更,且其实现中拥有 useState 或 useContext 的 Hook,当 context 发生变化时,它仍会重新渲染;其实就是在实现shouldComponentUpdate生命周期。
  2. React.PureComponent: 在class组件中使用。React.PureComponent 与 React.Component 唯一的区别在于 Rect.Component 没有实现 shouldComponentUpdate(), 而 React.PureComponent 中以浅层对比 props 和state 的方式来实现了该函数。
  3. withRouterwithRouter的作用就是, 如果我们某个东西不是一个Router, 但是我们要依靠它去跳转一个页面, 比如点击页面的logo, 返回首页。, 这时候就可以使用withRouter来做.withRouter, 作用是将一个组件包裹进Route里面, 然后react-router的三个对象history, location, match就会被放进这个组件的props属性中。被route包裹的组件,可以直接使用props进行路由相关操作,但是没有被route包裹的组件只能用withRouter高阶组件修饰或者使用hooks进行操作
9.react中间件

applyMiddleware: 将所有中间件组成一个数组,依次执行;

redux-thunk: redux-thunk主要对异步运用中间件做一些处理,因为reducer应该是一个纯函数,不能有副作用,只能根据action对state进行更新,因此不能在此发出网络请求。那么借助redux-thunk来在发出action至reducer处理之间借助middleware来进行处理。

10.react分层

State: 数据状态

Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。

View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。

Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。

11.react类型检查

PropTypes检测基础数据类型:

  1. optionalArray: PropTypes.array,
  2. optionalBool: PropTypes.bool,
  3. optionalFunc: PropTypes.func,
  4. optionalNumber: PropTypes.number,
  5. optionalObject: PropTypes.object,
  6. optionalString: PropTypes.string,
  7. optionalSymbol: PropTypes.symbol,

arrOfobjectOf多重嵌套类型检测:

  // An array of a certain type
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
  // An object with property values of a certain type
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),
  //示例
  static propTypes = {
    todoList:PropTypes.arrayOf(PropTypes.shape({
        id:PropTypes.string.isRequired,
        text:PropTypes.string
    }))
  }

shape检测不同对象的不同属性的不同数据类型:

  // An object taking on a particular shape
  optionalObjectWithShape: PropTypes.shape({
    optionalProperty: PropTypes.string,
    requiredProperty: PropTypes.number.isRequired
  }),
  //示例
  static propTypes = {
     object:PropTypes.shape({
         name:PropTypes.string,
         age:PropTypes.number
      })
 }
12.虚拟dom实现原理

虚拟dom是用js模拟一颗dom树,放在浏览器内存中,相当于在js和真实dom中加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。

虚拟dom优点

  1. 虚拟DOM具有批处理和高效的Diff算法,最终表现在DOM上的修改只是变更的部分,可以保证非常高效的渲染,优化性能;
  2. 虚拟DOM不会立马进行排版与重绘操作,对虚拟DOM进行频繁修改,最后一次性比较并修改真实DOM中需要改的部分;
  3. 虚拟 DOM 有效降低大面积真实 DOM 的重绘与排版,因为最终与真实 DOM 比较差异,可以只渲染局部;

虚拟dom缺点:首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢;

React组件的渲染过程

  1. 使用JSX编写React组件后所有的JSX代码会通过Babel转化为 React.createElement执行;
  2. createElement函数对 key和 ref等特殊的 props进行处理,并获取 defaultProps对默认 props进行赋值,并且对传入的子节点进行处理,最终构造成一个 ReactElement对象(所谓的虚拟 DOM)。
  3. ReactDOM.render将生成好的虚拟 DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实 DOM。

虚拟DOM的组成——ReactElementelement对象结构

  1. type:元素的类型,可以是原生html类型(字符串),或者自定义组件(函数或class)
  2. key:组件的唯一标识,用于Diff算法,下面会详细介绍
  3. ref:用于访问原生dom节点
  4. props:传入组件的props,chidren是props中的一个属性,它存储了当前组件的孩子节点,可以是数组(多个孩子节点)或对象(只有一个孩子节点)
  5. owner:当前正在构建的Component所属的Component
  6. self:(非生产环境)指定当前位于哪个组件实例
  7. _source:(非生产环境)指定调试代码来自的文件(fileName)和代码行数(lineNumber);
13. state和props区别
  1. props是从外部传入组件的参数,一般用于父组件向子组件通信,在组件之间通信使用;state一般用于组件内部的状态维护,更新组建内部的数据,状态,更新子组件的props等
  2. props不可以在组件内部修改,只能通过父组件进行修改;state在组件内部通过setState修改;
14. 单向数据流和双向绑定原理

react的单向数据流是指只允许父组件向子组件传递数据,子组件绝对不能修改父组件传的数据,如果想要修改数据,则要在子组件中执行父组件传递过来的回调函数,提醒父组件对数据进行修改。数据单向流让所有的状态改变可以追溯,有利于应用的可维护性; angular中实现了双向数据绑定,代码编写方便,但是不利于维护

15. react组件信息传递
  1. (父组件)向(子组件)传递信息 : porps传值
  2. (父组件)向更深层的(子组件) 进行传递信息 : context
  3. (子组件)向(父组件)传递信息:callback事件
  4. 没有任何嵌套关系的组件之间传值(比如:兄弟组件之间传值): 利用共同父组件context通信、自定义事件
  5. 利用react-redux进行组件之间的状态信息共享 : 组件间状态信息共享:redux、flux、mobx等
  6. 发布订阅: 利用第三方库, PubSubJs
16. jsx语法规则

image.png

17. 类相关基本知识复习

image.png

image.png

18. 函数式组件与类式组件

函数式组件的定义 image.png 类式组件 image.png 复杂组件:类组件中,有state就是复杂的,否则就是简单组件;或者说有状态的就是复杂组件。

19. 组件的四大属性(state, props, refs: 在组件实例组件对象的构造属性; context再说)

类组件中的属性,函数式组件需要用到hooks支持

  1. 初始化state image.png
  2. 修改state事件 image.png
  3. setState用法 image.png
  4. 修改state事件的简化版(es6箭头函数,this向外层查找) image.png
  5. 对props中的属性进行限制的简写方法(static的用法) image.png
  6. 函数式组件拿不到state和refs,但是可以拿到props;除非利用hooks
  7. refs的基础用法(react16.8开始已经废弃, string类型的refs影响效率) image.png
  8. 回调函数形式的refs(利用es6的箭头函数,()和{}都可以省略, 所以只有了一个{}; 页面更新时调两次,第一次为null,第二次才是真正的节点) image.png
  9. 内联函数的refs(不会随着更新时频繁调用) image.png
  10. React.createRef方式创建结点(目前最推荐的一种方式了) image.png
20. react中事件处理
  1. 当事件绑定的元素正是你要操作的元素时,可以省略ref绑定,通过event.target获取到当前元素(其实事件的触发是通过事件委托,委托元素的上级元素进行处理的,即冒泡) image.png
  2. 非受控组件(点击button的时候会执行handleSubmit事件,但是事件是绑定在form上的,承接上面所说事件委托) image.png
  3. 受控组件(推荐, 没有ref, 尽量减少了ref绑定,提高效率; 事件的双向绑定) image.png
  4. 高阶组件-柯里化函数([dataType]:因为字符串形式的不会转成相应的key, 所以用[]包裹一下) image.png
  5. 高阶组件-不用柯里化函数() image.png
21. todoList小案例总结

image.png

22. redux开发者工具

image.png image.png

2.react扩展
1. setState

setState触发更新是异步的,函数式的话在函数体内能够获取到最新的值 image.png image.png

2. lazyLoad

image.png

3. useState

image.png image.png

4. useEffect

image.png

5. refEffect

image.png

6. context

image.png

7. pureComponent

image.png

8. render props

image.png image.png

9. 组件之间通信

image.png