react项目配置(类组件、函数式组件)

类组件

1、事件绑定

class App extends React.Component {

    // 定义事件
    handleClick1(val, e) {
    	// 阻止默认行为
        e.preventDefault();

        //阻止冒泡
        e.stopPropagation();
        
        console.log(val, e);
    }

    // 定义事件第二种方式
    handleClick2(val3, e) {
        console.log(val3);
    }

    render() {
        return (
            <div className={style.App}>
                <button onClick={this.handleClick1.bind(this, "传参数")}>事件绑定</button>
                <button onClick={(e) => this.handleClick2(11, e)}>事件绑定第二中方式</button>
            </div>
        );
    }
}

注意:react中阻止冒泡和默认行为和原生是一样的 e.preventDefault(); e.stopPropagation();

2、setState

  1. setState 是浅合并 (Object.asign), 所以在更新 state 状态时,如果是对象,一定要展开再合并新的数据
state = {
	obj: {
		a: 1,
		b: 2,
		c: 3
	}
}
setState({
	obj: {
		...this.state.obj,
		b: 5
	}
})
  1. setState 在修改 list 数组的时候,如果是 React.Component 可是直接在数据的基础上修改,如果是 React.PureComponent ,就只能深拷贝一份数组再进行修改,因为 PureComponent 对比的是地址值
  2. 多次 setState render只会走一次
  3. 在类组件中 React.Component 下,setState 走一次, 就会render 一次页面更新,所以尽量使用 React.PureComponent
  4. setState 是异步的,取值要在第二个参数里面
setState({
	msg: "修改msg的值"
}, () => {
	this.state.msg  // 取值
})

3、组件通讯

react 中父组件传值给子组件 和 子组件传值给父组件,都是通过 props 来实现的

父组件给子组件传值,父组件给子组件传递属性即可

// 父组件中
 render() {
      return <div>
          <Son name="父传值给子"></Son>
      </div>
  }

// 子组件中接值
this.props.name

子组件传值给父组件,父组件给子组件传递方法即可

// 父组件中
getChildValue(val) {
	console.log("父组件拿到子组件的值", val)
}

render() {
   return <div>
        <Son onGetChildValue={this.getChildValue.bind(this)}></Son>
    </div>
}

// 子组件中
handle() {
     this.props.onGetChildValue("子组件的值传给父组件")  // 子组件拿到父组件传过来的方法直接调用,把值传过去
 }
 
 render() {
     return <div>
         <button onClick={this.handle.bind(this)}>点击给父组件值</button>
     </div>
 }

子组件接值验证和默认值的定义

// props 验证值的类型 和 默认值
Son.propTypes = {
    name: (props) => {
        if(typeof props.name !== "string") {
            return new Error("期望一个字符串")
        }
        return null
    }
}
Son.defaultProps = {
    name: "默认值"
}

子组件接值验证借助第三方库

// 1、下载 proptypes
yarn add proptypes

// 2、引用 
import propType from "proptypes"

// 子组件接值校验 (使用)
Son.propTypes = {
	name: propType.string,  // 校验字符串
	age: propType.number,   // 校验数字
	list: propType.array,   // 校验数组
	obj: propType.object    // 校验对象
}

4、插槽

react中的插槽的使用也是 props

// 1、默认插槽
父组件
 render() {
   return <div>
        <Slot>
            <div>before</div> // 需要在子组件展示的内容
        </Slot>
    </div>
}
子组件
 render() {
   const { children } = this.props
    return <div>
        {children}
    </div>
}




// 2、具名插槽
父组件
render() {
    return <div>
        <Slot>
            <div data-id="before">before</div>  // 父组件传过去两处在子组件需要展示的内容
            <div data-id="after">after</div>
        </Slot>
    </div>
}

子组件占位,展示父组件的内容
 render() {
 	// 子组件从 props 得到 父组件传进来的 自定义属性为 data-id, 然后判断展示在不同的位置
   const { children } = this.props
    let beforeVNode, afterNode
    children.forEach(v => {
        if("before" === v.props['data-id']) {
            beforeVNode = v
        }else{
            afterNode = v
        }
    })
    return <div>
        {beforeVNode}  // 虚拟Dom用插值表达式来解析
        Slot
        {afterNode}
    </div>
}


// 3、作用域插槽 (借助的就是子传值给父组件)
父组件
render() {
   return <div>
        <Slot customClick={(val) => console.log(val)}></Slot>
    </div>
}

子组件
 render() {
   return <div>
        <button onClick={() => this.props.customClick(this.state.msg)}>作用域插槽</button>
    </div>
}

5、样式隔离

创建 xxx.module.scss, 在jsx引入

// 在js中
import style from "./xxx.module.scss"
render() {
	return <div className={ style.box }>
		111
	</div>
}
// css中
.box{
	.....
} 

6、ref 的使用 (相当于函数式组件中的 useRef)

ref 获取dom

const div1 = React.createRef()  // 定义的 div1 和标签中的 ref 的值是一样的

componentDidMount() {
	console.log(div1)  // 获取 dom
}

render() {
	return <div>
		<div ref={div1}>111</div>
	</div>
}

ref 获取组价实例

const divComponent = React.createRef()  // 定义的 divComponent 和标签中的 ref 的值是一样的

componentDidMount() {
	console.log(divComponent)  // 获取组件实例
}

render() {
	return <div>
		<Son ref={divComponent}></Son>
	</div>
}

函数式组件

1、函数式组件没有生命周期
2、函数式组件没有 this
3、函数式组件通过 hook 来完成各种操作
4、函数式组件本身的函数体相当于 render 函数
5、props 在函数的第一个参数接受

7、定义变量,修改变量

import { useState } from "react"
let [msg, setMsg] = useState('')  // 定义msg变量
// 修改msg变量
function changeMsg() {
	setMsg('修改msg变量的值')
}

return <div>
	{ msg }  // 使用变量
	<button onClick={() => changeMsg()}>修改变量</button>
</div>

8、useEffect 副作用函数

1、不传第二个参数 = componentDidMount 和 componentDidUpdate
2、第二个参数传空数组 = componentDidMount
3、第二个参数数组里面放某个数据 = watch 监听
4、第一个参数函数体里面返回值是一个方法 = componentDidUnMount

useEffect(function() {}, [])

9、useMemo

useMemo 类似于 vue 中的计算属性, 第二个参数和 useEffect 的参数用法是一样的

// 1、定义常量的时候可以用 useMemo 包裹一下,表示只在创建的时候创建一次, 更新的时候就不创建了
const computedObj = useMemo(function() {
   return {
        name: "张三",
        age: 18
    }
}, [])

// 2、当做计算属性来使用,依赖写在第二个参数的数组里面
const computedObj = useMemo(() => msg + ":" + text, [msg, text])  // 表示只有 msg 和 text 两个变量改变的时候,useMemo 才会重新运行

10、useCallback

包裹方法使用的hook,第二个参数和 useMemo、useEffect 规则是一样的

11、useRef (相当于类组件的 createRef)

import { useRef, useEffect } from "react"
const div1 = useRef()
useEffect(() => {
	console.log(div1.current)  // 获取dom节点
}, [])
return <div>
	<div ref={div1}>111</div>
</div>

函数式组件没有组件实例,不能使用传统的 useRef 获取, 但是可以使用 forwardRef 获取想要的

// 子组件, 通过 forwardRef 高阶组件包裹一下,会传过来 props 参数 和 ref,用ref包裹需要在父组件中需要的标签
import React from 'react'  
function children(props, ref) {
    return <div ref={ref}>
        children
    </div>
}
export default React.forwardRef(children)

// 父组件
const childrenDom = useRef(null)
setTimeout(() => {
    console.log(childrenDom)
}, 300)

return (
    <div className="App">
         <Childrens ref={childrenDom}></Childrens>
     </div>
 );

12、高阶组件(相当于vue中的mixins)

高阶组件就相当于 vue 中的 mixins 混入, 说白了就是往使用高阶组件的组件的 props 里面 放一些公共的属性和方法

在 utils 里面建一个高阶组件 HigherOrderComponent.jsx

//  HigherOrderComponent.jsx
import { useState } from "react"

// 高阶组件的封装, Component 是:使用高阶组件的的原始组件,props 是原始组件本身的 props
export default function HeightComponent(Component) {  // 暴露出去一个方法
    return function Heightsa(props) {   // 返回一个方法

        // 高阶组件封装的属性值(每个使用高阶组件的组件都可以使用)
        const [heightVal] = useState("高阶函数封装的属性")

        // 高阶组件封装的方法(每个使用高阶组件的组件都可以使用)
        const heightMethods = () => {
            console.log("高阶函数封装的方法");
        }

        return <>
        	// 把传进来的组件加上高阶组件包装之后的 props 返回出去,使用这个高阶组件的组件就有了上面的属性和方法
            <Component {...props} heightVal={heightVal} heightMethods={heightMethods}></Component>
        </>
    }
}

组件的使用者

import HeightComponent from "../utils/HigherOrderComponent.jsx"
const App = (props) => {
	const { heightVal, heightMethods } = props  // 拿到了高阶组件的属性和方法了
	return <div>
		App
	</div>
}
export default HeightComponent(App)  // 只用高阶组件包装一下再暴露出去就可以了

13、react中性能优化( useMemo、useCallback、React.memo )

13-1、React.memo(相当于类组件的 PureComponent )

memo 包裹子组件的引入,在父组件更新的时,子组件可以减少不必要的更新操作

基本数据类型,只触发一次子组件的更新;引用数据类型变更会一直触发子组件的更新

// 父组件
import React, { memo } from "react"
import Son from "./Son.jsx"
const Sons = memo(Son)    // memo 包裹一下子组件的引入

export default function Father() {
	return <div>
		<Sons></Sons>   // 子组件的使用位置
	</div>
}

// 子组件
import React from "react"
export default function Son() {
 	return <div>
		son
	</div>
}

13-2、useMemo

1、useMemo 用于计算属性
2、useMomo 用于包裹不是 useState 定义的引用数据类型

useMemo 用于计算属性

export default function App() {
	let obj = useMemo(() => ({name: "张三", age: 18}), [name])  // obj的变化依赖于 name 变量的变化而变化
	return <div>
		
	</div>
}

定义引用数据类型的常量时,用 useMemo 包裹一下

export default function App() {
	let obj = useMemo(() => ({name: "张三", age: 18}), [])  // 这样定义的变量,即使传给子组件,也不会触发子组件的不必要更新
	return <div>
		
	</div>
}

13-3、useCallback

并不是所有的函数式组件在定义方法的时候都需要使用 useCallback , 这样是滥用 useCallback
而是在父组件往子组件传递自定义方法的时候才需要,避免子组件不必要的渲染

export default function App() {
	const func = useCallback(function() {
		
	}, [])
	return <div>
		<Son func={func}></Son>
	</div>
}

13、react中的状态管理工具

13-1、mobx的使用

13-1-1、mobx5 + 类组件
  1. yarn add -D react-app-rewired customize-cra @babel/plugin-proposal-decorators
  2. 根目录创建 config-overrides.js 文件
// config-overrides.js
const { override, addDecoratorsLegacy } = require("customize-cra")
module.exports = override(addDecoratorsLegacy())
  1. 根目录创建 .babelrc 文件
// .babelrc
{
    "plugins": [
        [
            "@babel/plugin-proposal-decorators", 
            {"legacy": true}
        ]
    ]
}
  1. 修改 package.json
"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test"
}

以上4部就是配置装饰器

  1. yarn add -D mobx@5 mobx-react@5
  2. 按照模块化创建 store
// 1、store 目录里面创建index.js 作为store  的根文件
import MainStore from "./mainStore"       // 引入的模块化 store 文件
import FooterStore from "./footerStore"   // 引入的模块化 store 文件
import { configure } from "mobx"

configure({
    enforceActions: "always"   // 开启严格模式
})

class Store {
    constructor() {
        this.MainStore = new MainStore(this)
        this.FooterStore = new FooterStore(this)
    }
}
export default Store


// 2、index.js 平级创建 mainStore.js 文件
import { action, observable, runInAction } from "mobx";

class MainStore {
    constructor(store) {
        this.store = store;
    }

    // 定义数据
    @observable list = [
        {
            id: 1, name: "张三", age: 18
        },
        {
            id: 2, name: "李四", age: 18
        },
        {
            id: 3, name: "王五", age: 18
        }
    ]

    // 修改 mobx 中的数据
    @action.bound changeList(payload) {
        this.list = payload
    }

    // 异步修改mobx中的数据
    @action.bound asyncChangeList(payload) {
        setTimeout(() => {
            
            runInAction(() => {
                this.list = payload
            })
            
        }, 1000)
    }
}
export default MainStore




// 3、index.js 平级创建 footerStore.js 文件
import { action, observable } from "mobx";

class FooterStore {
    constructor(store) {
        this.store = store;
    }

	// 定义变量
    @observable msg = "张三"

	// 定义计算属性
    @computed get computedRelyOn() {
        return this.comRelyOn + "许潇"  // 定义的计算属性表示 comRelyOn 发生变化,computedRelyOn 就跟着变化
    }

	// 修改变量
    @action.bound changeMsg() {
        this.msg = Math.random()
    }
}
export default FooterStore
  1. 组件中使用和修改mobx中的数据
import React from 'react'
import Son from "../son/Son"
import { inject, observer } from "mobx-react"
   
@inject("MainStore", "FooterStore")   // 引入模块里面的数据到组件中
@observer
export default class Father extends React.Component {  // 注意,只能使用Component,不能使用 PureComponent
    // mobx修改数组
    changeList =() => {
        const { changeList, list } = this.props.MainStore
        const newList = [...list]
        newList.push({
            id: Date.now(),
            name: "许潇",
            age: Date.now()
        })
        changeList(newList)
    }

    // mobx修改字符串
    changeMsg = () => {
        const { changeMsg } = this.props.FooterStore
        changeMsg()
    }
  
    render() {

        const { MainStore, FooterStore } = this.props

        return <div>
            Father
            <h3>mobx中的list渲染</h3>
            <div>
                {MainStore.list.map(v => (
                    <li key={v.id}>{v.name} -- {v.age}</li>
                ))}
            </div>
            <button onClick={this.changeList}>通过增加mobx中的值来修改list数组</button>


            <h3>mobx中的字符串渲染</h3>
            <div>{FooterStore.msg}</div>
            <button onClick={this.changeMsg}>通过增加mobx中的值来修改msg字符串</button>
            <hr />
            <Son></Son>
        </div>
    }
}

// 异步修改 mobx 里面的状态
import React from 'react'
import { inject, observer } from 'mobx-react'
   
@inject("MainStore")
@observer
export default class Son extends React.Component {
    changeList = () => {
    	// 要不修改 mobx 里面的值
        const { asyncChangeList, list } = this.props.MainStore
        const newList = [...list]
        newList.push({
            id: Date.now(),
            name: Date.now(),
            age: 22
        })
        asyncChangeList(newList)
    }
  
    render() {
        return <div>
            Son

            <button onClick={this.changeList}>异步修改mobx中的值是父组件的list数组重新渲染</button>
        </div>
    }
}
13-1-2、mobx6 + 函数式组件
  1. yarn add mobx mobx-react-lite
  2. store/index.js 文件中
import { useContext, createContext } from "react"
// 引入的模块化 store
import footerStore from "./footerStore"
import headerStore from "./headerStore"

class store {
    constructor() {
        this.headerStore = new headerStore(this)
        this.footerStore = new footerStore(this)
    }
}

const context = createContext(new store())

export default function useStore() {
    return useContext(context)
}
  1. store/headerStore.js 文件中
// 模块化 headerStore.js 文件中
import { makeAutoObservable, runInAction } from "mobx";

export default class headerStore {
    constructor(store) {
        this.store = store;
        // 将所有的变量改成 装饰 @observable , 将所有的方法改成 @action , 将所有的 get 修饰的 改成 计算属性 @computed get 
        makeAutoObservable(this, {}, { autoBind: true })
    }

    // 定义字符串
    msg = "初始值"
    // 修改字符串
    changeMsg(payload) {
        this.msg = payload
    }
    // 依赖msg字符串的计算属性
    get replyMsg() {
        return this.msg + "徐小平"
    }
    // 异步修改数组
    changeList(list) {
        setTimeout(() => {
            runInAction (() => {  // 异步修改数据要写在 runInAction 方法里面
                this.list = list
            })
        }, 1000)
    }
}
  1. 组件中使用
import { observer } from 'mobx-react-lite'  // observer 将整个方法包裹,store状态变化,包裹之后就监测到了
import useStore from "../../store"   // 引入创建的 store/index.js 仓库

export default observer(function App() {
	
	const { footerStore, headerStore } = useStore()  // 拿到了 store 仓库里面的数据和方法

	useEffect(function() {
		console.log(footerStore.属性)
		console.log(footerStore.方法)
	}, [])

	return <div>
		{footerStore.属性}
	</div>
})
  1. mobx6 持久化数据
// 1、安装包
yarn add -D mobx-persist-store

// 2、在 store 模块化仓库中
import { makeAutoObservable, runInAction, action } from "mobx";
import { makePersistable, isHydrated } from "mobx-persist-store";

export default class footerStore {
    constructor(store) {
        this.store = store;
        makeAutoObservable(this, {}, { autoBind: true })
        
		// 以下是持久化仓库的配置
        makePersistable(this, {
            // 在构造函数内使用 makePersistable
            name: "UserStore", // 保存的name,用于在storage中的名称标识,只要不和storage中其他名称重复就可以
            properties: ["list"], // 要保存的字段,这些字段会被保存在name对应的storage中,注意:不写在这里面的字段将不会被保存,刷新页面也将丢失:get字段例外。get数据会在数据返回后再自动计算
            storage: window.localStorage, // 保存的位置:看自己的业务情况选择,可以是localStorage,sessionstorage
            // 。。还有一些其他配置参数,例如数据过期时间等等,可以康康文档,像storage这种字段可以配置在全局配置里,详见文档
        }).then(
            action((persistStore) => {
                // persist 完成的回调,在这里可以执行一些拿到数据后需要执行的操作,如果在页面上要判断是否完成persist,使用 isHydrated
                // console.log(persistStore);
            })
        );
    }
    // 注意这个字段的使用:在页面useEffect的时候,如果你没有添加依赖数组(初始化执行),那么你可能拿不到computed计算的数据(原因是这时候还没有persist结束),所以
    // 这个属性还是比较重要的,因为它可以在页面上做判断,当页面load完成,get数据计算完成之后,isHydrated会置为true
    get isHydrated() {
        return isHydrated(this);
    }


    // 定义数组
    list = [
        { id: 1, name: "张三" },
        { id: 2, name: "李四" },
    ];
    // 异步修改数组
    changeList(list) {
        setTimeout(() => {
            runInAction(() => {
                this.list = list;
            });
        }, 1000);
    }
}

// 3、组件中使用和原来是一样的

14、react 中的 v6 版本的路由

yarn add -D react-router-dom

1、在index.js文件中:

import { BrowserRouter } from "react-router-dom";
root.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>
)
// 这样所有的组件都可以使用路由,包括app.js组件

2、app.js文件

import "./App.scss";
import { Routes, Route } from "react-router-dom";
import { lazy, Suspense } from "react";
import { AuthRoute } from "../components/beforeEachRouter/AuthRoute";  // 类似于vue中的路由守卫,利用高阶组件
import PrivateRouter from "../router/PrivateRouter";  // 根据数据库里面的数据生成路由表,权限控制

// 路由懒加载 需要 Suspense 包裹
const Login = lazy(() => import("../views/login/Login"));
const Layout = lazy(() => import("./layout/Layout"));
const NoPage = lazy(() => import('../views/NoPage/NoPage'));

function App() {
    return (
        <Routes>
            <Route path="/login" element={
                    <Suspense fallback={<div>...</div>}>
                        <Login></Login>
                    </Suspense>
                }>
            </Route>
            
            // AuthRoute 高阶组件,有token就进入系统,没有token进不去
            <Route path="/" element={<Suspense fallback={<div>...</div>}><AuthRoute><Layout /></AuthRoute></Suspense>}>
                {PrivateRouter()}   // 权限路由
                // 路由重定向,表示当前在 / 页面下,就自动跳转到 /index 页面
                <Route path="/" element={<Navigate to="/index" />} />
                <Route path="*" element={<Suspense fallback={<div>...</div>}><NoPage></NoPage></Suspense>}></Route>  // 404页面                       
            </Route>

        </Routes>
    );
}

export default App;

3、在需要展示的嵌套组件中使用

<Outlet />

4、路由跳转传参(三种方式)

const navigate = useNavigate()
navigate('/page1')
// 4-1、searchParams 传参
navigate('/page1?name=张三&age=18')
    // 接参
	let [searchParams, setSearchParams] = useSearchParams()
	searchParams.get('name')
	searchParams.get('age')


// 4-2、params 传参
     // 路由表配置上
      {
        path: "/page1/:name/:age",//注意这里 可以占位多个
        element: <Page1 />
      }
      navigate('/page1/张三/18')
      // 接参
      const params = useParams()
      params.name
      params.age


// 4-3、state 传参
	navigate('/page1', {
		state: { name: '张三', age: 18 },
		replace: true
	})
	// 接参
	const location = useLocation()
	location.state.name
	location.state.age

15、react-transition-group 动画使用

15-1、单个元素使用

// isShow 表示显示和隐藏  timeout 表示到时间卸载掉元素  unmountOnExit 卸载元素
<CSSTransition in={ isShow } timeout={300} classNames='single' unmountOnExit>
   <div className={Style.box}></div>
</CSSTransition>

对应的css

// 单个元素
/* 入场动画之前 */
.single-enter{
    opacity: 0;
    transform: translateY(300px);
}
/* 入场动画过程之后 */
.single-enter-active{
    transition: 300ms;
    opacity: 1;
    transform: translateY(0px);
}

/* 出场动画之前 */
.single-exit{
    opacity: 1;
    transform: translateY(0px);
}
/* 出场动画过程之后 */
.single-exit-active{
    transition: 300ms;
    opacity: 0;
    transform: translateY(300px);
}

15-2、列表使用

<ul>
    <TransitionGroup>
        {list.map((v, index) => (
        	// CSSTransition 中的 key 要用数组中的唯一值,不要用索引,否则会出现离场动画问题
            <CSSTransition key={v.id} timeout={500} classNames="list-animate" appear={true} unmountOnExit>
                <li className={Style.li1}>
                    <p className={Style.p1}>{v.name}</p>
                    <Button size='mini' onClick={() => handleDelete(index)}>删除</Button>
                </li>
            </CSSTransition>
        ))}
    </TransitionGroup>
</ul>

对应的css

// 列表动画
.list-animate-enter{
    opacity: 0;
}
.list-animate-enter-active{
    opacity: 1;
    transition: all 0.5s;
}
.list-animate-exit{
    opacity: 1;
}
.list-animate-exit-active{
    opacity: 0;
    transition: all 0.5s;
}

15-3、路由组件切换使用

// 1、使用 SwitchTransition 组件包裹 CSSTransition 
// 2、SwitchTransition 组件中 mode='out-in' 表示先离场在进入
// 3、CSSTransition 组件中的 key 表示唯一值,所以用了 useLocation() 中的 key
// 4、不要直接使用 <Outlet /> , 要使用  const outlet = useOutlet() => {outlet} 代替

<SwitchTransition mode='out-in'>
   <CSSTransition key={location.key} classNames="fade" timeout={500}>
        {outLet}
    </CSSTransition>
</SwitchTransition>

对应的css

.fade-enter {
	opacity: 0;
	transform: translateX(-50px);
}
.fade-enter-active {
	opacity: 1;
	transform: translateX(0%);
	transition: all 500ms;
}
.fade-exit {
	opacity: 1;
    transform: translateX(0);
}
.fade-exit-active {
	opacity: 0;
    transform: translateX(-50px);
	transition: all 500ms;
}

16、react + v6 路由 权限控制

代码地址: https://gitee.com/xu-xiao1/react-permissions

17、反向代理 跨域请求接口配置

下载安装:http-proxy-middleware
创建文件 src/setupProxy.js, 文件名称不能改动

// src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
    app.use(
        '/api',  // api开头的进行代理
        createProxyMiddleware({
             target: 'https://backend-server.com', // 目标后端服务器地址
             changeOrigin: true, // 改变源到目标服务器
             pathRewrite: {
                '^/api': '', // 重写路径,去除/api部分
             },
        })
    );
};

18、react 打包优化

1、关掉 map 文件
在根目录下创建 .env文件,里面写入:

GENERATE_SOURCEMAP = false

2、配置 craco.config.js 文件(类似于vue.config.js)
2-1、安装 @craco/craco

yarn add @craco/craco

2-2、修改 package.json文件

 "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },

2-3、项目根目录(和package.json平级)创建 配置文件 craco.config.js

// craco.config.js
const path = require("path");

module.exports = {
    devServer: {
        port: 8000
    },

    webpack: {
        alias: {
            '@': path.resolve(__dirname, "src")
        },

        configure(webpackConfig) {
            if (webpackConfig.mode === 'production') {
                if (webpackConfig.optimization == null) {
                    webpackConfig.optimization = {}
                }
                webpackConfig.optimization.splitChunks = {
                    chunks: 'all',//同步或异步的模块都进行分包处理
                    
                    cacheGroups: {
                        antd: {
                            name: 'antd-chunk',
                            test: /antd/,//匹配到antd的都抽离到一个单独的文件中,下面类似
                            priority: 100,//优先级越高,较先匹配
                        },
                        reactDom: {
                            name: 'react-dom-chunk',
                            test: /react-dom/,
                            priority: 99,
                        },
                        reactRouterDom: {
                            name: 'react-router-dom-chunk',
                            test: /react-router-dom/,
                            priority: 98,
                        },
                        echarts: {
                            name: 'echarts',
                            test: /echarts/,
                            priority: 97,
                        },
                        vendors: {
                            name: 'vendors-chunk',
                            test: /node_modules/,
                            priority: 95,
                        },
                    },
                }
            }
            return webpackConfig
        },
    },
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值