react生命周期及hooks

生命周期(新旧对比)
旧版生命周期

旧版生命周期 指的是 React 16.3 及其之前的版本。

在这里插入图片描述

新版生命周期

在这里插入图片描述

static getDerivedStateFromProps
  • static getDerivedStateFromProps(nextProps,prevState):接收父组件传递过来的 props 和组件之前的状态,返回一个对象来更新 state 或者返回 null 来表示接收到的 props 没有变化,不需要更新 state.

  • 该生命周期钩子的作用: 将父组件传递过来的 props 映射 到子组件的 state 上面,这样组件内部就不用再通过 this.props.xxx 获取属性值了,统一通过 this.state.xxx 获取。映射就相当于拷贝了一份父组件传过来的 props ,作为子组件自己的状态。注意:子组件通过 setState 更新自身状态时,不会改变父组件的 props

  • 配合 componentDidUpdate,可以覆盖 componentWillReceiveProps 的所有用法

  • 该生命周期钩子触发的时机:

    1. 在 React 16.3.0 版本中:在组件实例化、接收到新的 props 时会被调用
    2. 在 React 16.4.0 版本中:在组件实例化、接收到新的 props 、组件状态更新时会被调用
	// 根据新的属性对象派生状态对象
  // nextProps:新的属性对象 prevState:旧的状态对象
  static getDerivedStateFromProps(nextprops, state) {
    console.log('props',nextprops);
    // 返回一个对象来更新 state 或者返回 null 来表示接收到的 props 不需要更新 state
    if (nextProps.parentName !== 'parent info') {
      return {
        info: nextprops.parentName,
        // 注意:这里不需要把组件自身的状态也放进来
      };
    }
    return null;
  }

getSnapshotBeforeUpdate
  • getSnapshotBeforeUpdate(prevProps, prevState):接收父组件传递过来的 props 和组件之前的状态,此生命周期钩子必须有返回值,返回值将作为第三个参数传递给 componentDidUpdate。必须和 componentDidUpdate 一起使用,否则会报错。
  • 该生命周期钩子触发的时机 :被调用于 render 之后、更新 DOMrefs 之前
  • 该生命周期钩子的作用: 它能让你在组件更新 DOMrefs 之前,从 DOM 中捕获一些信息(例如滚动位置)
  • 配合 componentDidUpdate, 可以覆盖 componentWillUpdate 的所有用法
  • demo:每次组件更新时,都去获取之前的滚动位置,让组件保持在之前的滚动位置
 getSnapshotBeforeUpdate() {
    // 返回更新内容的高度
    return this.wrapper.current.scrollHeight;
  }
componentDidUpdate(prevProps, prevState, prevScrollHeight) {// 在这里拿到高度
    this.wrapper.current.scrollTop =
      this.wrapper.current.scrollTop +
      (this.wrapper.current.scrollHeight - prevScrollHeight);
  }
版本迁移
  • componentWillMountcomponentWillReceivePropscomponentWillUpdate 这三个生命周期因为经常会被误解和滥用,所以被称为 不安全(不是指安全性,而是表示使用这些生命周期的代码,有可能在未来的 React 版本中存在缺陷,可能会影响未来的异步渲染) 的生命周期

  • React 16.3 版本:为不安全的生命周期引入别名 UNSAFE_componentWillMountUNSAFE_componentWillReceivePropsUNSAFE_componentWillUpdate。(旧的生命周期名称和新的别名都可以在此版本中使用)

  • React 16.3 之后的版本:为 componentWillMountcomponentWillReceivePropscomponentWillUpdate 启用弃用警告。(旧的生命周期名称和新的别名都可以在此版本中使用,但旧名称会记录 DEV 模式警告)

性能优化

我们都知道,react 是数据驱动视图的变化,即是通过reder来渲染视图,当数据(即状态)变化时,我们的页面就应当重新渲染。但是应用复杂之后就会出现这种情况:一个父组件 A 下面包含了多个子组件 B、C、D。假如 B、C 组件用到了父组件 A 的某个属性,子组件 D 却没有用到这个属性,当父组件的这个属性改变的时候,他下面的子组件 B、C 组件重新渲染,但是子组件 D 本不需要重新渲染,但是他没办法,他也被重新渲染了。这就造成了性能浪费了。说这么多,不如我们来看个例子:

// 父组件
import React, { Component } from 'react'
import { Button } from 'antd'
import Son1 from './son1'
import Son2 from './son2'
import Son3 from './son3'

interface Istate {
  info1: string
  info2: string
}
export class Parent extends Component<Istate> {
  state: Istate = {
    info1: 'info1',
    info2: 'info2',
  }
  info1Change = () => {
    this.setState({
      info1: 'info1被改变了...',
    })
  }
  render() {
    return (
      <div>
        <p>父组件</p>
        <Button onClick={this.info1Change}> 点击更改info1</Button>
        <Son1 info1={this.state.info1} />
        <Son2 info2={this.state.info2} />
      </div>
    )
  }
}

export default Parent

// 子组件1
import React, { Component } from 'react'

interface Iprops {
  info1: string
}

class Son1 extends Component<Iprops> {
  render() {
    console.log('son1重新渲染了....')
    return (
      <div>
        <p>我是son1</p>
        <p>{this.props.info1}</p>
      </div>
    )
  }
}
export default Son1

// 子组件2
import React, { Component } from 'react'

interface Iprops {
  info2: string
}

class Son2 extends Component<Iprops> {
  render() {
    console.log('son2重新渲染了....')
    return (
      <div>
        <p>我是son2</p>
        <p>{this.props.info2}</p>
      </div>
    )
  }
}
export default Son2

上面这个例子,父组件提供了两个值:info1info2,其中 Son1 组件只用到了 info1Son2 组件只用到了 info2。我们在父组件中,点击了按钮改变了 info1 的值,父组件必须重新渲染,因为它自身状态改变了,Son1 也应该重新渲染,因为它依赖于 info1,而 Son2 是否应该重新渲染呢?按道理,它不应该重新渲染,因为 info2 没有改变,但是当我们每次点击按钮改变 info1 的时候,Son1Son2 都重新渲染了,这就明显存在问题了。

shouldComponentUpdate

在上面 👆 生命周期章节,我们讲到了shouldComponentUpdate这个生命周期钩子,它接收两个参数,一个是下一次的 props 和下一次的 state,在这里,我们拿到下一次的 props(nextProps)和当前的 props 进行比较,根据我们的场景,返回一个 bool 变量,返回 true,则表示要更新当前组件,返回 false 则表示不更新当前组件。

import React, { Component } from 'react'

interface Iprops {
  info2: string
}

class Son2 extends Component<Iprops> {
  // 利用生命周期 shouldComponentUpdate 进行比较
  shouldComponentUpdate(nextProps: Iprops, nextState: any) {
    if (nextProps.info2 === this.props.info2) return false
    return true
  }
  render() {
    console.log('son2重新渲染了....')
    return (
      <div>
        <p>我是son2</p>
        <p>{this.props.info2}</p>
      </div>
    )
  }
}
export default Son2

当我们再次点击按钮更改info1的值,发现Son2就不会再重新渲染了。

PureComponet

react为我们提供了PureComponet的语法糖,用它也可以用作组件是否渲染的比较。它的原理就是内部实现了shouldComponentUpdate。让我们用PureComponet来改造一下刚刚的Son2组件:

import React, { PureComponent } from 'react'

interface Iprops {
  info2: string
}

class Son2 extends PureComponent<Iprops> {
  render() {
    console.log('son2重新渲染了....')
    return (
      <div>
        <p>我是son2</p>
        <p>{this.props.info2}</p>
      </div>
    )
  }
}
export default Son2

再次点击按钮改变info1的值,发现Son2也不会渲染了。

虽然PureComponent帮我们很好的实现了shouldComponentUpdate,但是它也是有缺点的。它只能用作对象的浅层比较,也就是它只会进行一层比较,当我们的数据是嵌套的对象或者数组的时候,它就无法比较了。所以PureComponent最好只用于展示型组件

除了以上缺点以外,PureComponent还有一些另外值得我们注意的地方:

当我们给PureComponent包裹的子组件传入一个立即执行函数的时候,父组件的状态改变的时候,这个子组件始终会重新渲染:

<Son2 info2={this.state.info2} change={() => {}} />

这个问题的出现是因为 change 这个函数每次都会执行,所以导致 Son2 组件每次都会重新渲染。这个问题的解决方法很简单,有两种方法:

  • 第一种,将这个立即执行函数,抽取到类方法上,并且在constructor bind this:
constructor(props: any) {
    super(props)
    this.change = this.change.bind(this)
  }

  state: Istate = {
    info1: 'info1',
    info2: 'info2',
  }
  info1Change = () => {
    this.setState({
      info1: 'info1被改变了...',
    })
  }
  change() {}
  render() {
    return (
      <div>
        <p>父组件</p>
        <Button onClick={this.info1Change}> 点击更改info1</Button>
        <Son1 info1={this.state.info1} />
        <Son2 info2={this.state.info2} change={this.change} />
      </div>
    )
  }
  • 第二种,利用箭头函数将立即函数抽取成类属性:
state: Istate = {
    info1: 'info1',
    info2: 'info2',
  }
  info1Change = () => {
    this.setState({
      info1: 'info1被改变了...',
    })
  }
  change = () => {}
  render() {
    return (
      <div>
        <p>父组件</p>
        <Button onClick={this.info1Change}> 点击更改info1</Button>
        <Son1 info1={this.state.info1} />
        <Son2 info2={this.state.info2} change={this.change} />
      </div>
    )
  }
Memo

刚刚我们介绍了PureComponent,但是这只是用于class组件,当我们用函数组件时,react 也给我们提供了一种方式:memo

import React, { memo } from 'react'
interface Iprops {
  info2: string
}
const Son3: React.FC<Iprops> = (props) => {
  console.log('son3重新渲染了....')
  return (
    <div>
      <p>我是Son3</p>
      <p>{props.info2}</p>
    </div>
  )
}

export default memo(Son3)

不过使用 memo 的时候也有PureComponent的限制,我们仍然需要注意。

hooks

随着react 16.8版本的出现,hooks也问世了。hooks解决了class组件饱受诟病的众多问题,比如绑定this的问题、组件的逻辑复用的问题等等。其实起初我对hooks并不十分感冒,因为那个时候笔者连class写法都还没掌握,再学个这玩意简直徒增烦恼。后来没办法,团队里的小伙伴都开始使用hooks,所以我也被动学习了一波。这不写不知道,一写就真香!!!所以赶紧学起来吧

useState

记住一个点,useState返回两个参数,一个是state(也就是我们的state)、一个是用于更新state的函数。其实你叫啥名都行,不过咱们为了给 hooks 一个标志,大多采用如下写法:

const [name, setName] = useState(initState)

好了,掌握这个就行了,我们来使用一下:(别忘记了,我们现在只需要写函数式组件了哦)

import React, { useState } from 'react'
import { Button } from 'antd'

const Home: React.FC<Iprops> = ({ dispatch, goodsList }) => {
  const [info, setInfo] = useState('init info')
  return (
    <div>
      <p>{info}</p>
      <Button onClick={() => setinfo('改变info')}> 点击更改info</Button>
    </div>
  )
}
export default Home

当我们初次进入 home 页面时,页面上会显示 info 的初始值:init info,然后当我们点击按钮,调用 setInfo,然后 info 的值就被改变了。就是这么简单。

useEffect

useEffect 其实只比复杂了那么一点。它合成了 calss 组件中的componentDidMountcomponentDidUpdatecomponentWillUnmount。 我们很容易就明白,它是用来执行副作用的,最最常见的副作用就是异步请求。

下面我们用它来实现class组件的componentDidMount的用法:

const Home: React.FC<Iprops> = ({ dispatch, goodsList }) => {
  // 获取商品列表
  const getList = () => {
    dispatch({
      type: `${namespace}/getGoodsList`,
    })
  }
  useEffect(() => {
    getList() // 调用方法发起异步请求
  }, [])

  return (
    <div style={{ marginTop: '5px', marginLeft: '400px', marginRight: '400px' }}>
      <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-around' }}>
        {goodsList.map((item, index) => {
          return <Card hoverable style={{ width: 240 }} cover={<img alt='example' src={item} />}></Card>
        })}
      </div>
    </div>
  )
}

const mapStateToProps = (model) => ({
  goodsList: model[namespace].goodsList,
})

export default connect(mapStateToProps)(Home)

上面的getList就是咱们发起异步请求的方法,我们在useEffect里面使用了它,同时我们还传入了一个[],就表示我们只需在页面初始化的时候发起请求,这样使用就相当于class组件的componentDidMount

接下来我们再来实现class组件的componentDidUpdate的用法:

import React, { useState, useEffect } from 'react'
import { connect } from 'react-redux'
import { Button, Card } from 'antd'
import { namespace } from '../models/model1'

interface Iprops {
  goodsList: any[]
  dispatch: any
}

const Home: React.FC<Iprops> = ({ dispatch, goodsList }) => {
  const [info, setInfo] = useState('init info')

  // 获取商品列表
  const getList = () => {
    dispatch({
      type: `${namespace}/getGoodsList`,
    })
  }
  useEffect(() => {
    getList()
  }, [info])

  return (
    <div style={{ marginTop: '5px', marginLeft: '400px', marginRight: '400px' }}>
      <p>我是home页</p>
      <p>{info}</p>
      <Button onClick={() => setInfo('改变info')}> 点击更改info</Button>

      <div style={{ display: 'flex', flexDirection: 'row' }}>
        {goodsList.map((item, index) => {
          return <Card hoverable style={{ width: 240 }} cover={<img alt='example' src={item} />}></Card>
        })}
      </div>
    </div>
  )
}

const mapStateToProps = (model) => ({
  goodsList: model[namespace].goodsList,
})

export default connect(mapStateToProps)(Home)

看上面,我们希望点击按钮时改变 info 时,它会自动再去发起请求,从而刷新页面(也就是说,goodsList 的数据依赖于 info)。可以看见,我们这里还是利用了useEffect的第二个参数,只不过这次我们传入的是[info],意思就是告诉useEffect,如果 info 的值发生改变了,就去发起请求。这相当于我们在class组件的componentDidMount钩子。

接下来还有最后一个class组件的componentWillUnmount的用法了。这个就更简单了,我们只需要在useEffect return 一个回调函数,就可以用来清除上一次副作用留下的副作用了:

....
 useEffect(() => {
    getList()
    return () => dispatch({ type: `${namespace}/clearData` })
  }, [])
....
useRef

这个hook更简单了,它就是用来拿到子组件的实例的,相当于class组件的React.createRef()

import React, { useState, useEffect, useRef } from 'react'
import { connect } from 'react-redux'
import { Button, Card } from 'antd'
import { namespace } from '../models/model1'
import Son from './components/son'

interface Iprops {
  goodsList: any[]
  dispatch: any
}

const Home: React.FC<Iprops> = ({ dispatch, goodsList }) => {
  const sonRef = useRef(null) // 在这里新建一个子组件的ref
  const [info, setInfo] = useState('init info')

  // 获取商品列表
  const getList = () => {
    conson.log(sonRef.current) // 在这里就可以通过sonRef拿到子组件
    dispatch({
      type: `${namespace}/getGoodsList`,
    })
  }
  useEffect(() => {
    getList()
  }, [info])

  return (
    <div>
      <p>我是home页</p>
      <p>{info}</p>
      <Button onClick={() => setInfo('改变info')}> 点击更改info</Button>
      <div style={{ display: 'flex', flexDirection: 'row' }}>
        {goodsList.map((item, index) => {
          return <Card hoverable style={{ width: 240 }} cover={<img alt='example' src={item} />}></Card>
        })}
      </div>
      <Son />
    </div>
  )
}

const mapStateToProps = (model) => ({
  goodsList: model[namespace].goodsList,
})

export default connect(mapStateToProps)(Home)
useContext

useContext这个hook的作用也很简单,它可以让我们在函数组件中使用Context,而且它还解决了以前我们需要利用Consumer包裹组件的问题:

// context.js
import React from 'react'
const { Provider, Consumer } = React.createContext(null) //创建 context 并暴露Provider和Consumer
export { Provider, Consumer }

// 父组件
import React from 'react'
import Son from './son'
import { Provider } from './context'
class Father extends React.Component {
  constructor(props) {
    super(props)
  }
  state = {
    info: 'info from father',
  }
  render() {
    return (
      <Provider value={this.state.info}>
        <div>
          <Son />
        </div>
      </Provider>
    )
  }
}
export default Father

class 组件里面,我们要想拿到 Context 里面的值,必须通过 Consumer 包裹组件:

// 子组件
import React from 'react'
import { Consumer } from './context'
class Son extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    return (
      <Consumer>
        {(info) => (
          // 通过Consumer直接获取父组件的值
          <div>
            <p>父组件的值:{info}</p>
          </div>
        )}
      </Consumer>
    )
  }
}
export default Son

有了 useContext,就只需要这样:

// 子组件
import React from 'react'
funcion Son() {
  const info = useContext(Context)
  render() {
    return (
       <p>父组件的值:{info}</p>
    )
  }
}
export default Son

我们可以看到上面直接使用 React.useContext(Context) 就可以获得 context,而在之前的版本中需要像这样才能获取 <Consumer>({vlaue} => {})</Consumer> ,这极大的简化了代码的书写。

useMemo

先来看看官网给出的用法:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])

根据官网的解释和这个用法可以看出,在 ab 的变量值不变的情况下,memoizedValue的值不变。即是:useMemo函数的第一个入参函数不会被执行,从而达到节省计算量的目的(有点像vue的计算属性)。那它有什么用呢?通常来说可以用作性能优化的手段。我们来看一个例子:

// 父组件
import React, { useState } from 'react'
import { Input } from 'antd'
import Son1 from './son1'

interface Iprops {}

const Home: React.FC<Iprops> = () => {
  const [info, setInfo] = useState('')
  const [visible, setVisible] = useState(true)

  const onVisible = () => {
    setVisible((visible) => !visible)
  }
  const changeInfo = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value
    setInfo(value)
  }

  return (
    <div style={{ marginTop: '5px', marginLeft: '400px', marginRight: '400px' }}>
      <p>{info}</p>
      <Input onChange={(e) => changeInfo(e)}></Input>
      <Son1 onVisible={onVisible} />
    </div>
  )
}

export default Home

// 子组件
import React from 'react'
import { Button } from 'antd'

interface Iprops {
  onVisible: () => void
}
const Son1: React.FC<Iprops> = ({ onVisible }) => {
  console.log('我被重新渲染了....')
  return (
    <div>
      <Button onClick={() => onVisible()}>button</Button>
    </div>
  )
}
export default Son1

在父组件中,有个Input输入框,每次输入新的值,父组件的info的值就会发生改变,同时我们发现子组件每次都会重新渲染,即使我们子组件没用到info的值,那是因为setInfo导致父组件重新渲染了,也导致onVisible每次都变成一个新的值,所以引起子组件重新渲染。那么有的同学就会说,可以利用React.memo,我们来试一试:

import React, { memo } from 'react'
import { Button } from 'antd'

interface Iprops {
  onVisible: () => void
}
const Son1: React.FC<Iprops> = ({ onVisible }) => {
  console.log('我被重新渲染了....')
  return (
    <div>
      <Button onClick={() => onVisible()}>button</Button>
    </div>
  )
}
export default memo(Son1)

然后我们随便在输入框输入新的值,我们发现,子组件仍然会重新渲染,为什么呢?那是因为这里的props.onVisible是一个函数,它是一个引用类型的值,当父组件重新渲染onVisible 这个函数也会重新生成,这样引用地址变化就导致对比出新的数据,子组件就会重新渲染。所以我们需要缓存onVisible这个函数,即是:我们只需要创建一遍这个函数,以后父组件重新渲染的时候,onVisible的值仍然是第一次渲染的值,这样子组件才不会重新渲染。这个时候我们就用到了useMemo

import React, { useState } from 'react'
import { Input } from 'antd'
import Son1 from './son1'

interface Iprops {}

const Home: React.FC<Iprops> = () => {
  const [info, setInfo] = useState('')
  const [visible, setVisible] = useState(true)

  const onVisible = useMemo(() => {
    return () => {
      setVisible((visible) => !visible)
    }
  }, [])
  const changeInfo = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value
    setInfo(value)
  }

  return (
    <div style={{ marginTop: '5px', marginLeft: '400px', marginRight: '400px' }}>
      <p>{info}</p>
      <Input onChange={(e) => changeInfo(e)}></Input>
      <Son1 onVisible={onVisible} />
    </div>
  )
}

export default Home

可以看到,我们利用useMemoonVisible缓存起来了,我们在useMemo的第二个参数传入了一个[],表明它只会在渲染时执行一次,这里的用法跟useEffect一样,[]传入依赖项,当依赖项改变时,我们缓存的值才会重新计算。再次在输入框输入新的值,我们发现子组件不渲染了。

useMemo 一般用于计算比较复杂的场景

useCallback

如果掌握了useMemo,那掌握 useCallback简直不在话下。我们先来看看定义:

const memoizedCallback = useCallback(() => {
  doSomething(a, b)
}, [a, b])

ab 的变量值不变的情况下,memoizedCallback 的引用不变。即:useCallback 的第一个入参函数会被缓存,从而达到渲染性能优化的目的。是不是跟useMemo很像?useMemo是缓存值,useCallback一个是缓存函数的引用。也就是说 useCallback(fn, [deps]) 相当于 useMemo(() => fn, [deps])。我们现在用 useCallback 来改造一下刚刚上面 👆 那个例子:


....
const Home: React.FC<Iprops> = () => {
  const [info, setInfo] = useState('')
  const [visible, setVisible] = useState(true)

  // const onVisible = useMemo(() => {
  //   return () => {
  //     setVisible((visible) => !visible)
  //   }
  // }, [])
  const onVisible = useCallback(() => {
    setVisible(visible => !visible)
  }, [])
  const changeInfo = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value
    setInfo(value)
  }

  return (
    <div style={{ marginTop: '5px', marginLeft: '400px', marginRight: '400px' }}>
      <p>{info}</p>
      <Input onChange={(e) => changeInfo(e)}></Input>
      <Son1 onVisible={onVisible} />
    </div>
  )
}

export default Home
自定义 hook

借助于react提供的基础hook,我们通常也可以自定义hookreact规定我们自定义hook时,必须以use开头。我们来尝试自定义一个控制对话框的hook:

import { useState } from 'react'

type returnd = [boolean, (visible?: boolean) => void]

const useVisible = (initVisible = false): returnd => {
  const [visible, setVisible] = useState(initVisible)
  function onVisible(value?: boolean) {
    const newValue = value === undefined ? !visible : value
    setVisible(newValue)
  }
  return [visible, onVisible]
}

export default useVisible

首先我们利用useState声明了visiblesetVisible,然后我们定义了onVisible这个函数用来更改visible,接着我们返回[visible, onVisible]。然后我们来看看如何使用:

import { Button, Modal } from 'antd'
import useVisible from '../hooks/useVisible'

const Home: React.FC = () => {
  const [visible, setVisible] = useVisible(false)

  const modalShow = (value: boolean) => {
    setVisible(value)
  }

  return (
    <div style={{ marginTop: '5px', marginLeft: '400px', marginRight: '400px' }}>
      <Button type='primary' onClick={() => modalShow(true)}>
        Open Modal
      </Button>
      <Modal title='Basic Modal' visible={visible} onOk={() => modalShow(false)} onCancel={() => modalShow(false)}>
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Modal>
    </div>
  )
}

export default Home

就像我们使用其他hook一样方便。我们在写业务(搬砖)的过程中,我们可以尝试去将一些可复用的逻辑或者操作封装为我们自己的hook,这才是hooks的强大之处。

项目配置

我们在开发react的时候,不免需要一些配置,例如别名、跨域等等。vue给我们提供了一个vue.config.js用于配置,那么react项目呢?我们需要用到react-app-rewiredcustomize-cra:

yarn add react-app-rewired -D
yarn add customize-cra -D

安装完之后,我们需要更改一下我们的package.json文件:

....
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test --env=jsdom",
    "eject": "react-scripts eject"
  },
....

接着我们需要在项目的根目录新建一个config-overrides.js文件。接下来我们对项目进行一些配置:

/* config-overrides.js */

const path = require('path')
const { override, addWebpackResolve, fixBabelImports, overrideDevServer } = require('customize-cra')
const { addReactRefresh } = require('customize-cra-react-refresh')
// 配置开发环境跨域
const devServerConfig = () => (config) => {
  return {
    ...config,
    port: 3000,
    proxy: {
      '/mock/158/airi': {
        target: 'https://api.guaik.org',
        changeOrigin: true,
        secure: false,
      },
    },
  }
}

module.exports = {
  webpack: override(
    // 热加载
    addReactRefresh(),
    // 配置路径别名
    addWebpackResolve({
      alias: {
        '@': path.resolve(__dirname, 'src'),
      },
    }),
    // antd 按需加载
    fixBabelImports('import', {
      libraryName: 'antd',
      libraryDirectory: 'es',
      style: true,
    })
  ),
  devServer: overrideDevServer(devServerConfig()),
}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值