1. 生命周期
- Mounting(挂载阶段)
constructor
: 构造函数,最先被执行,我们通常在构造函数里初始化state对象或者给自定义方法绑定thisgetDerivedStateFromProps
:static getDerivedStateFromProps(nextProps, prevState)
,这是个静态方法,当我们接收到新的属性想去修改state,可以使用getDerivedStateFromProps
render
:render
函数是纯函数,只返回需要渲染的东西,不应该包含其它的业务逻辑,可以返回原生的DOM
、React
组件、Fragment
、Portals
、字符串和数字、Boolean
和null
等内容componentDidMount
: 组件装载之后调用,此时我们可以获取到DOM
节点并操作,比如对canvas,svg
的操作,服务器请求,订阅都可以写在这个里面,但是记得在componentWillUnmount
中取消订阅
- Updating(更新阶段)
getDerivedStateFromProps
: 此方法在更新和挂载阶段都可能会调用shouldComponentUpdate
:shouldComponentUpdate(nextProps, nextState)
,有两个参数nextProps
和nextState
,表示新的属性和变化之后的state
,返回一个布尔值,true
表示会触发重新渲染,false
表示不会触发重新渲染,默认返回true
,我们通常利用此生命周期来优化React程序性能render
: 更新阶段也会触发此生命周期getSnapshotBeforeUpdate
:getSnapshotBeforeUpdate(prevProps, prevState)
,这个方法在render
之后,componentDidUpdate
之前调用,有两个参数prevProps
和prevState
,表示之前的属性和之前的state
,这个函数有一个返回值,会作为第三个参数传给componentDidUpdate
,如果你不想要返回值,可以返回null
,此生命周期必须与componentDidUpdate
搭配使用componentDidUpdate
:componentDidUpdate(prevProps, prevState, snapshot)
,该方法在getSnapshotBeforeUpdate
方法之后被调用,有三个参数prevProps
,prevState
,snapshot
,表示之前的props
,之前的state
,和snapshot
。第三个参数是getSnapshotBeforeUpdate
返回的,如果触发某些回调函数时需要用到DOM
元素的状态,则将对比或计算的过程迁移至getSnapshotBeforeUpdate
,然后在componentDidUpdate
中统一触发回调或更新状态。
- Unmounting(卸载阶段)
componentWillUnmount
: 当我们的组件被卸载或者销毁了就会调用,我们可以在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作
2.React组件通信如何实现
- 父组件向子组件通讯: 父组件可以向子组件通过传
props
的方式,向子组件进行通讯 - 子组件向父组件通讯:
**props
+回调**的方式,父组件向子组件传递props
进行通讯,此props
为作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到父组件的作用域中 - 兄弟组件通信: 找到这两个兄弟节点共同的父节点,结合上面两种方式由父节点转发信息进行通信
- 跨层级通信: **
Context
**设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言,对于跨越多层的全局数据通过Context
通信再适合不过 - 发布订阅模式: 发布者发布事件,订阅者监听事件并做出反应,我们可以通过引入
event
模块进行通信 - 全局状态管理工具: **借助
Redux
或者Mobx
**等全局状态管理工具进行通信,这种工具会维护一个全局状态中心Store
,并根据不同的事件产生新的状态
3. react中hooks的理解
React Hooks
的意思是:组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来
钩子一律使用use
前缀命名,便于识别。你要使用 xxx 功能,钩子就命名为 usexxx。React 有四个默认的最常用的钩子
useState()
:const [buttonText, setButtonText] = useState("Click me, please")
。useState()
这个函数接受状态的初始值,作为参数,上例的初始值为按钮的文字。该函数返回一个数组,数组的第一个成员是一个变量,指向状态的当前值。第二个成员是一个函数,用来更新状态,约定是set
前缀加上状态的变量名useContext()
:const AppContext = React.createContext({});
如果需要在组件之间共享状态,可以使用useContext()
useReducer()
:const [state, dispatch] = useReducer(reducer, initialState);
它接受Reducer
函数和状态的初始值作为参数,返回一个数组。数组的第一个成员是状态的当前值,第二个成员是发送action
的dispatch
函数useEffect()
:用来引入具有副作用的操作,最常见的就是向服务器请求数据。以前,放在componentDidMount
里面的代码,现在可以放在useEffect()
。接受两个参数。第一个参数是一个函数,异步操作的代码放在里面。第二个参数是一个数组,用于给出Effect
的依赖项,只要这个数组发生变化,useEffect()
就会执行。第二个参数可以省略,这时每次组件渲染时,就会执行useEffect()
。
const Person = ({ personId }) => {
const [loading, setLoading] = useState(true);
const [person, setPerson] = useState({});
//每当组件参数personId发生变化,useEffect()就会执行。组件第一次渲染时,useEffect()也会执行。
useEffect(() => {
setLoading(true);
fetch(`https://swapi.co/api/people/${personId}/`)
.then(response => response.json())
.then(data => {
setPerson(data);
setLoading(false);
});
}, [personId])
if (loading === true) {
return <p>Loading ...</p>
}
return <div>
<p>You're viewing: {person.name}</p>
<p>Height: {person.height}</p>
<p>Mass: {person.mass}</p>
</div>
}
引申:react怎么进行组件复用
组件抽象的技术目前有三种比较主流:
- 高阶组件:属性代理、反向继承
- 渲染属性
- react-hooks
4. react基础
- react是组件化的,组件是通过状态更新的
- JSX与HTML的区别:在JSX中是驼峰命名法
- refs回调
React
也支持另一种设置refs
的方式,称为“回调refs
”。它能助你更精细地控制何时 refs 被设置和解除。- 不同于传递
createRef()
创建的ref
属性,你会传递一个函数。这个函数中接受React
组件实例或HTML DOM
元素作为参数,以使它们能在其他地方被存储和访问
- setState好处
- 封装性好
- 数据与展示分离
- 级联刷新
5.react-router
- exact属性:当
exact
为false
时,根据路由匹配所有组件,如"/" 匹配 “/”、“/home”、“/home/menu”;当exact
为true
时,则“/” 仅匹配“/”、无法匹配到“/home”
import React, { Component } from 'react';
import { BrowserRouter,Route,Switch } from 'react-router-dom'
import App from '../pages/App'
import Introduction from '../pages/Introduction'
class Client extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route component={App} exact path="/" />
<Route path="/intro/:langId" component={Introduction} />
</Switch>
</BrowserRouter>
);
}
}
export default Client;
6. redux
connect
:用于从 UI 组件生成容器组件。connect
的意思,就是将这两种组件连起来。connect
方法接受两个参数:mapStateToProps
和mapDispatchToProps
。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state
映射到 UI 组件的参数(props
),后者负责输出逻辑,即将用户对 UI 组件的操作映射成Action
。mapStateToProps()
:建立一个从(外部的)state
对象到(UI 组件的)props
对象的映射关系。会订阅Store
,每当state
更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。它的第一个参数总是state
对象,还可以使用第二个参数,代表容器组件的props
对象。mapDispatchToProps()
:是connect
函数的第二个参数,用来建立 UI 组件的参数到store.dispatch
方法的映射。也就是说,它定义了哪些用户的操作应该当作Action
,传给Store
。它可以是一个函数,也可以是一个对象- 如果
mapDispatchToProps
是一个函数,会得到dispatch
和ownProps
(容器组件的props对象)两个参数 - 如果
mapDispatchToProps
是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作Action creator
,返回的Action
会由Redux
自动发出
- 如果
引申:1.redux中如何进行异步操作?
当然,我们可以在componentDidmount
中直接进行请求无须借助redux
.
但是在一定规模的项目中,上述方法很难进行异步流的管理,通常情况下我们会借助redux
的异步中间件进行异步处理.
redux
异步流中间件其实有很多,但是当下主流的异步中间件只有两种redux-thunk
、redux-saga
,当然redux-observable
可能也有资格占据一席之地,其余的异步中间件不管是社区活跃度还是npm下载量都比较差了.
2.redux的工作流程
整个工作流程:
- 首先,用户(通过View)发出Action,发出方式就用到了dispatch方法。
- 然后,Store自动调用Reducer,并且传入两个参数:当前State和收到的Action,Reducer会返回新的State
- State一旦有变化,Store就会调用监听函数,来更新View。
github中redux教程:github.com/kenberkeley…
7.React Fiber是什么
React Fiber
把更新过程碎片化,执行过程如下面的图所示,每执行完一段更新过程,就把控制权交还给React
负责任务协调的模块,看看有没有其他紧急任务要做,如果没有就继续去更新,如果有紧急任务,那就去做紧急任务。
维护每一个分片的数据结构,就是Fiber
。
有了分片之后,更新过程的调用栈如下图所示,中间每一个波谷代表深入某个分片的执行过程,每个波峰就是一个分片执行结束交还控制权的时机。
因为一个更新过程可能被打断,所以React Fiber
一个更新过程被分为两个阶段(Phase
):第一个阶段Reconciliation Phase
和第二阶段Commit Phase
。
在第一阶段Reconciliation Phase
,React Fiber
会找出需要更新哪些DOM,这个阶段是可以被打断的;但是到了第二阶段Commit Phase
,那就一鼓作气把DOM
更新完,绝不会被打断。
在React Fiber
中,第一阶段中的生命周期函数在一次加载和更新过程中可能会被多次调用!
使用React Fiber
之后,一定要检查一下第一阶段相关的这些生命周期函数,看看有没有逻辑是假设在一个更新过程中只调用一次,有的话就要改了。
8.react加载性能优化
- 在
HTML
内实现Loading
态或者骨架屏; - 去掉外联
css
; - 缓存基础框架;
- 使用动态
polyfill
; - 使用
SplitChunksPlugin
拆分公共代码; - 正确地使用
Webpack 4.0
的Tree Shaking
; - 使用动态
import
,切分页面代码,减小首屏 JS 体积; - 编译到
ES2015+
,提高代码运行效率,减小体积; - 使用
lazyload
和placeholder
提升加载体验。
9.插件:
- redux-logger:更好的提示
- redux-devtools-extension
参考链接:react面试题
10.国际化语言问题
react语言组件中react-intl已经舍弃addLocalData方法了,可直接使用intlAPI
intl的API相关介绍
react-intl的相关使用方法
import React , { Component }from 'react';
import { Button } from 'antd';
import './App.css';
import {IntlProvider ,FormattedMessage} from 'react-intl'; /* react-intl imports */
// import en from 'react-intl/locale-data/en';//已废弃
// import zh from 'react-intl/locale-data/zh';//已废弃
//addGlobalData已废弃
import zh_CN from "./local/zh_CN"
import en_US from "./local/en_US"
class App extends Component {
constructor(props) {
super(props);
this.state = {
lang: navigator.language.indexOf('zh') !== -1 ? 'zh' : 'en',
};
}
changeLanguage() {
this.setState({
lang: this.state.lang==='zh'?'en':'zh'
})
}
render() {
let messages = {}
messages['en'] = en_US;
messages['zh'] = zh_CN;
return (
<IntlProvider locale={this.state.lang} messages={messages[this.state.lang]}>
<div className="App">
<Button type="primary" onClick={() => this.changeLanguage()}>Button</Button>
<FormattedMessage id="hello" />
</div>
</IntlProvider>
);
}
}
export default App;