日常知识点集合之react

292 阅读9分钟

1. 生命周期

  • Mounting(挂载阶段)
    • constructor: 构造函数,最先被执行,我们通常在构造函数里初始化state对象或者给自定义方法绑定this
    • getDerivedStateFromProps: static getDerivedStateFromProps(nextProps, prevState),这是个静态方法,当我们接收到新的属性想去修改state,可以使用getDerivedStateFromProps
    • render: render函数是纯函数,只返回需要渲染的东西,不应该包含其它的业务逻辑,可以返回原生的DOMReact组件、FragmentPortals、字符串和数字、Booleannull等内容
    • componentDidMount: 组件装载之后调用,此时我们可以获取到DOM节点并操作,比如对canvas,svg的操作,服务器请求订阅都可以写在这个里面,但是记得在componentWillUnmount中取消订阅
  • Updating(更新阶段)
    • getDerivedStateFromProps: 此方法在更新和挂载阶段都可能会调用
    • shouldComponentUpdate: shouldComponentUpdate(nextProps, nextState),有两个参数nextPropsnextState,表示新的属性和变化之后的state返回一个布尔值true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,我们通常利用此生命周期来优化React程序性能
    • render: 更新阶段也会触发此生命周期
    • getSnapshotBeforeUpdate: getSnapshotBeforeUpdate(prevProps, prevState),这个方法render之后,componentDidUpdate之前调用,有两个参数prevPropsprevState,表示之前的属性和之前的state这个函数有一个返回值,会作为第三个参数传给componentDidUpdate,如果你不想要返回值,可以返回null此生命周期必须与componentDidUpdate搭配使用
    • componentDidUpdate: componentDidUpdate(prevProps, prevState, snapshot),该方法在getSnapshotBeforeUpdate方法之后被调用,有三个参数prevPropsprevStatesnapshot,表示之前的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函数和状态的初始值作为参数,返回一个数组。数组的第一个成员是状态的当前值,第二个成员是发送 actiondispatch函数
  • 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基础

  1. react是组件化的,组件是通过状态更新的
  2. JSX与HTML的区别:在JSX中是驼峰命名法
  3. refs回调
  • React 也支持另一种设置 refs 的方式,称为“回调 refs”。它能助你更精细地控制何时 refs 被设置和解除
  • 不同于传递 createRef() 创建的 ref 属性,你会传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问
  1. setState好处
    1. 封装性好
    2. 数据与展示分离
    3. 级联刷新

5.react-router

  • exact属性:当exactfalse时,根据路由匹配所有组件,如"/" 匹配 “/”、“/home”、“/home/menu”;当exacttrue时,则“/” 仅匹配“/”、无法匹配到“/home”

react router V5版本介绍

中文介绍


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方法接受两个参数:mapStateToPropsmapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action
    • mapStateToProps()建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。它的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。
    • mapDispatchToProps():是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象
      • 如果mapDispatchToProps是一个函数,会得到dispatchownProps(容器组件的props对象)两个参数
      • 如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出

引申:1.redux中如何进行异步操作?

当然,我们可以在componentDidmount中直接进行请求无须借助redux.

但是在一定规模的项目中,上述方法很难进行异步流的管理,通常情况下我们会借助redux的异步中间件进行异步处理.

redux异步流中间件其实有很多,但是当下主流的异步中间件只有两种redux-thunkredux-saga,当然redux-observable可能也有资格占据一席之地,其余的异步中间件不管是社区活跃度还是npm下载量都比较差了.

2.redux的工作流程

整个工作流程:

  1. 首先,用户(通过View)发出Action,发出方式就用到了dispatch方法。
  2. 然后,Store自动调用Reducer,并且传入两个参数:当前State和收到的Action,Reducer会返回新的State
  3. State一旦有变化,Store就会调用监听函数,来更新View。

github中redux教程:github.com/kenberkeley…

7.React Fiber是什么

React Fiber把更新过程碎片化,执行过程如下面的图所示,每执行完一段更新过程,就把控制权交还给React负责任务协调的模块,看看有没有其他紧急任务要做,如果没有就继续去更新,如果有紧急任务,那就去做紧急任务。

维护每一个分片的数据结构,就是Fiber

有了分片之后,更新过程的调用栈如下图所示,中间每一个波谷代表深入某个分片的执行过程,每个波峰就是一个分片执行结束交还控制权的时机。

因为一个更新过程可能被打断,所以React Fiber一个更新过程被分为两个阶段(Phase):第一个阶段Reconciliation Phase和第二阶段Commit Phase

在第一阶段Reconciliation PhaseReact Fiber会找出需要更新哪些DOM,这个阶段是可以被打断的;但是到了第二阶段Commit Phase,那就一鼓作气把DOM更新完,绝不会被打断。

React Fiber中,第一阶段中的生命周期函数在一次加载和更新过程中可能会被多次调用

使用React Fiber之后,一定要检查一下第一阶段相关的这些生命周期函数,看看有没有逻辑是假设在一个更新过程中只调用一次,有的话就要改了。

8.react加载性能优化

  1. HTML 内实现 Loading 态或者骨架屏;
  2. 去掉外联 css
  3. 缓存基础框架;
  4. 使用动态 polyfill
  5. 使用 SplitChunksPlugin 拆分公共代码;
  6. 正确地使用 Webpack 4.0Tree Shaking
  7. 使用动态 import,切分页面代码,减小首屏 JS 体积;
  8. 编译到 ES2015+,提高代码运行效率,减小体积;
  9. 使用 lazyloadplaceholder 提升加载体验。

可参考:React 16 加载性能优化指南

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;