React 16
super
在ES6中,在子类的 constructor
中必须先调用 super
才能引用 this
。
setState (partialState, callback )
有两个参数
- 对象键值对
- 函数:可以获取最新值
在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。
setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数拿到更新后的结果。
setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次 setState , setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。
问题:调用 setState 之后发生了什么
- 在
setState
的时候,React 会为当前节点创建一个updateQueue
的更新列队。 - 然后会触发
reconciliation
过程,在这个过程中,会使用名为 Fiber 的调度算法,开始生成新的 Fiber 树, Fiber 算法的最大特点是可以做到异步可中断的执行。 - 然后
React Scheduler
会根据优先级高低,先执行优先级高的节点,具体是执行doWork
方法。 - 在
doWork
方法中,React 会执行一遍updateQueue
中的方法,以获得新的节点。然后对比新旧节点,为老节点打上 更新、插入、替换 等 Tag。 - 当前节点
doWork
完成后,会执行performUnitOfWork
方法获得新节点,然后再重复上面的过程。 - 当所有节点都
doWork
完成后,会触发commitRoot
方法,React 进入 commit 阶段。 - 在 commit 阶段中,React 会根据前面为各个节点打的 Tag,一次性更新整个 dom 元素。
refs
Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。例:
<form onSubmit={this.handleSubmit}>
<input type='text' ref={(input) => this.input = input} />
</form>
// this.input.value 访问
react-router
-
二级路由
在一级路由内嵌套
-
路由匹配和重定向
// SWitch 包裹,例 <Switch> {/* 重定向 */} <Redirect from="/" to="/home" exact></Redirect> {/*如果都没匹配到,则跳转到下面的 */} <Route component={Error}></Route> </Switch>
-
路由传参和动态路由
// 传入一个动态路由/:id <Route path="/shopcar/:id" component={ Shopcar } /> // to属性增加pathname和search <Link to={{pathname: '/shopcar/001',search: '?a=1&b=2',}}></Link>
-
路由组件守卫
使用高阶组件的形式,将路由组件放在高阶组件返回,通过判断逻辑返回相应的组件
每个路由都有
Enter
和Leave
钩子,用户进入或离开该路由时触发。 -
Route渲染方式 优先级:
children > component > render
-
children
// 无论 location 是否匹配,都会渲染 <Route path='/a' children={({ match }) => ( <Link to='/a'> hello world </Link> )} />
-
render
// 避免重复的无必要的加载,匹配时渲染 <Route path="/home" render={() => { return <div> home </div> }} />
-
component
// 只有 path 匹配时,组件才呈现。 <Route path="/user" component={ component } />
-
-
withRouter
将一个组件包裹进
Route
里面, 然后react-router
的三个对象history, location, match
就会被放进这个组件的props
属性中
生命周期
15和16版本的区别
-
挂载
-
componentWillMount()
在挂载之前被调用
-
constructor()
组件挂载之前,会调用它的构造函数
应在其他语句之前前调用super(props)
,否则无法访问this
-
static getDerivedStateFromProps(props, state)
会在调用 render 方法之前调用,并且在初始挂载及后续更新时(挂载时,接收到新的props,调用了setState和forceUpdate)都会被调用
应返回一个对象来更新 state,如果返回
null
则不更新任何内容。 -
render()
render()
方法是 class 组件中唯一必须实现的方法。
当 render 被调用时,它会检查this.props
和this.state
的变化 -
componentDidMount()
在组件挂载后(插入 DOM 树中)立即调用,可以进行实例化请求
-
-
更新
-
static getDerivedStateFromProps(props, state)
-
componentWillReceiveProps(nextProps)
已挂载的组件接收新的 props 之前被调用
父组件导致组件重新渲染,即使 props 没有更改,也会触发此方法
-
shouldComponentUpdate(nextProps, nextState)
当
props
或state
发生变化时,会在渲染之前调用,默认返回true
,如果返回false
,则会跳过更新
可以将this.state
和nextState
,this.props
和nextProps
比较
此处可以通过返回true
或false
进行性能优化 -
render()
-
getSnapshotBeforeUpdate(prevProps, prevState)
在最近一次渲染输出(提交到 DOM 节点)之前调用
任何返回值将作为参数传递给
componentDidUpdate()
-
componentWillUpdate( nextProps, nextState )
会在渲染之前调用
-
componentDidUpdate(prevProps, prevState, snapshot)
在更新后会被立即调用,首次渲染不会执行此方法。
-
-
卸载
-
componentWillUnmount()
会在组件卸载及销毁之前直接调用
-
-
错误处理
-
static getDerivedStateFromError(error)
在后代组件抛出错误后被调用。 它将抛出的错误作为参数,并返回一个值以更新 state
-
componentDidCatch(error, info)
此生命周期在后代组件抛出错误后被调用
-
高阶组件(HOC)
高阶组件是一个函数,接收一个组件作为参数,并返回一个组件,例如 connect
、 withRouter
返回一个组件,通常是第一个参数,携带着需要的数据返回
如何在Hook中使用HOC ?
使用 Hook 提供的 useState , useEffect 提供的代替方法,代替 class 组件中的 setState、生命周期
使用场景:
- 人员权限访问页面
- 不同类别的列表
suspense
组件
Suspense 让组件“等待”某个异步操作,直到该异步操作结束即可渲染
组件通信
- 父子组件:通过props传递给子组件,子组件通过props接收;子组件通过父组件传递的函数传参给父组件。
- 跨组件通信
- 层层传递
- Context
- 非嵌套组件
- 发布订阅模式
- Redux、Mobx
无状态组件和有状态组件
无状态组件:使用函数直接创建组件,使用方便,易于书写。应避免使用this
有状态组件:通过class创建组件,有生命周期,可以通过this接收状态和属性
属性(props)和状态(state)
组件不可修改属性,但可以修改自己的状态
更新 state 和 props 会触发哪些状态
// 更改state :
// getDerviedStateFromProps
// shouldComponentUpdate
// render
// getSnapshotBeforeUpdate
// componentDidUpdate
// 更改props
// getDerviedStateFromProps
// shouldComponentUpdate
// render
// getSnapshotBeforeUpdate
// componentDidUpdate
// 所触发的生命周期是相同的,但两者的区别是更改props时,生命周期的props和state都是有值的,更改state时,生命周期的第一个参数,也就是 props 值是空的
受控组件和非受控组件
使用setState进行更新状态的称为受控组件
使用refs获取的称为非受控组件
大白话说就是需不需要干涉,不干涉的是非受控组件,干涉的是受控组件
对于表单组件的封装,无论class组件还是针对于类组件,都需要进行表单回显
context
-
第一种写法 (低版本)
//App.js import React from 'react'; import Son from './son';//引入子组件 // 创建一个 theme Context, export const { Provider, Consumer } = React.createContext("默认名称"); export default class App extends React.Component { render() { let name = "小人头" return ( //Provider共享容器 接收一个name属性 <Provider value={name}> <p>父组件定义的值:{name}</p> <Son /> </Provider> ); } } //son.js 子类 import React from 'react'; import { Consumer } from "./App";//引入父组件的Consumer容器 import Grandson from "./grandson.js";//引入子组件 function Son(props) { return ( //Consumer容器,可以拿到上文传递下来的name属性,并可以展示对应的值 <Consumer> {(name) => <div> <p>子组件。获取父组件的值:{name}</p> {/* 孙组件内容 */} <Grandson /> </div> } </Consumer> ); } export default Son; //grandson.js 孙类 import React from 'react'; import { Consumer } from "./App";//引入父组件的Consumer容器 function Grandson(props) { return ( //Consumer容器,可以拿到上文传递下来的name属性,并可以展示对应的值 <Consumer> { (name) => <p>孙组件。获取传递下来的值:{name}</p>} </Consumer> ); } export default Grandson;
-
第二种写法
// 新建global.js import React from 'react' export const ThemeContext = React.createContext('light'); //App.js import React from 'react'; import Toolbar from './toolbar';//引入子组件 import { ThemeContext } from './global' class App extends React.Component { render() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } export default App // Toolbar.js import ThemedButton from './ThemedButton' function Toolbar() { return ( <div> <ThemedButton /> </div> ); } export default Toolbar //ThemedButton.js import React from 'react' import { ThemeContext } from './global' class ThemedButton extends React.Component { static contextType = ThemeContext; render() { return <p>{this.context}</p>; } } export default ThemedButton
类组件中的优化
React.memo
- React.memo 为高阶组件,仅可在函数组件使用
- 仅检查 props 变更,当context变化时仍然会重新渲染
- 默认情况下其只会对复杂对象做浅层对比,如果想要控制对比过程,通过第二个参数传入来实现
- 与
shouldComponentUpdate()
返回值相反
PureComponent
React.PureComponent
中以浅层对比 prop 和 state,和React.component
类似,仅在类组件使用
项目配置less
-
webpack.config.js
// 1 . const lessRegex = /\.less$/; const lessModuleRegex = /\.module\.less$/; // 2 .getStyleLoaders 函数,不要忘了传参lessOptions { loader: require.resolve('less-loader'), options: lessOptions, }, // 3. { test: lessRegex, exclude: lessModuleRegex, use: getStyleLoaders( { importLoaders: 1, sourceMap: isEnvProduction && shouldUseSourceMap, }, 'less-loader' ), sideEffects: true, }, { test: lessModuleRegex, use: getStyleLoaders( { importLoaders: 1, sourceMap: isEnvProduction && shouldUseSourceMap, module: true, getLocalIdent: getCSSModuleLocalIdent }, 'less-loader' ), },
-
npm i less less-loader --save
-
如果还是报错,请将
less-loader===>5.0.0
路径别名
// webpack.config.js
// 1 .
const pathResolve = (url) => {
return path.join(__dirname, url);
};
// 2. 例:
'@': pathResolve('../src')
解决兼容性
// 兼容IE浏览器
// npm i react-app-polyfill --save
// 入口文件中顶部
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
React 17
React和Vue的区别
相同点
- 虚拟DOM,数据驱动,组件化
不同点:
- react使用fiber算法,vue采用diff算法
- react jsx语法,vue 推荐html模板
- react 是单向数据流,vue双向数据绑定
- react MVC,vue MVVM