React项目实战

(一) 创建项目

  1. npm i create-react-app -g 安装脚手架(需要node14以上)

  2. create-react-app my-app 创建项目

  3. 用vscode打开项目my-app

  4. npm start 运行项目

    win7安装node14:

  5. 若安装有nvm, 先卸载

  6. 下载node14解压到c盘

  7. 右键我的电脑 => 属性 => 高级系统设置 => 环境变量 => 系统变量 => 双击Path

  8. 查看有Path里node的路径, 将路径改为

    C:\node-v14.15.3-win-x64;NODE_SKIP_PLATFORM_CHECK=1
    

(二) 路由配置

(1) 配置路由步骤

  1. npm i react-router-dom 安装路由插件
  2. 创建组件: 首页,目的地,提交需求,发现,我的
  3. 在app.js配置路由
    1. Router 最外层需要用Router进行包裹(只需一次)
    2. Routes 路由组件包裹层
    3. Route 用来配置路由,包括路由地址和路由组件
    4. 重定向使用Navigate
// App.js
import { BrowserRouter as Router, Routes, Route,Navigate} from "react-router-dom";
import Home from "./pages/home/index"; 
import Demand from "./pages/demand/index";   

function App() {
  return (
    <div className="app">
      <Router>
        <Routes> 
          {/*重定向*/}
          <Route path="/" element={<Navigate to="/house"/>} />
          <Route path="/house" element={<Home />}>房子</Route>
          <Route path="/demand" element={<Demand />}> 需求 </Route> 
        </Routes> 
      </Router>
    </div>
  );
}

export default App;

(2) 子路由配置

// App.js配置
import { BrowserRouter as Router, Routes, Route} from "react-router-dom";
import Home from "./pages/home/index";
// Home子路由组件
import Detail from "./pages/home/detail";
import List from "./pages/home/list";

import Demand from "./pages/demand/index";  

function App() {
  return (
    <div className="app">
      <Router>
        <Routes>
         
          <Route path="/house" element={<Home />}>
            {/* 嵌套路由(子路由)配置 */}
            <Route path="detail" element={<Detail />} />
              {/*访问/home,默认显示list*/}
            <Route index element={<List />} />
          </Route>

          <Route path="/demand" element={<Demand />}> 需求 </Route> 
        </Routes> 
      </Router>
    </div>
  );
}

export default App;
// home.jsx 父路由
import { Outlet } from "react-router-dom";

function Index() {
  return (
    <div>
      <h3>父组件</h3>
      <hr />
      <Outlet />
    </div>
  );
}

export default Index;

(3) 重定向

  1. react-router-dom v6以前的版本, 有一个Redirect组件可以实现

  2. v6的重定向, 官方给出的参考是

    import { useEffect } from "react";
    import { useNavigate } from "react-router-dom";
    
    function Redirect({ to }) {
      let navigate = useNavigate();
      useEffect(() => {
        navigate(to);
      });
      return null;
    }
    
    // usage
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/events" element={<Users />} />
      <Route
        path="/dashboard"
        element={<Redirect to="/events" />}
      />
    </Routes>; 
    

(4) 编程式导航

(三) 添加预处理语言支持

  1. 添加scss支持(不要安装node-sass, 有很多问题)
npm install sass
  1. 添加less支持
    1. npm i less less-loader@6 less-loader版本太高则不兼容
    2. npm run eject 暴露webpack配置
    3. 在webpack.config.js修改散出地方, 把sass修改为了less
// 1. 在64行中,将
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
修改成下面的代码(若想保留sass则复制后再修改)
const lessRegex = /\.(less)$/;
const lessModuleRegex = /\.module\.(less)$/;

// 2. 在502行, 将sass修改为less, 如果想保留sass就复制一份出来进行修改
{
    test: sassRegex,
        exclude: sassModuleRegex,
            use: getStyleLoaders(
                {
                    importLoaders: 3,
                    sourceMap: isEnvProduction
                    ? shouldUseSourceMap
                    : isEnvDevelopment,
                },
                'sass-loader'
            ),
                // Don't consider CSS imports dead code even if the
                // containing package claims to have no side effects.
                // Remove this when webpack adds a warning or an error for this.
                // See https://github.com/webpack/webpack/issues/6571
                sideEffects: true,
},
    // Adds support for CSS Modules, but using SASS
    // using the extension .module.scss or .module.sass
    {
        test: sassModuleRegex,
            use: getStyleLoaders(
                {
                    importLoaders: 3,
                    sourceMap: isEnvProduction
                    ? shouldUseSourceMap
                    : isEnvDevelopment,
                    modules: {
                        getLocalIdent: getCSSModuleLocalIdent,
                    },
                },
                'sass-loader'
            ),
    }, 

(四) 导入ant-design

官网地址

(五) 父子组件通信

// 父组件
import React from 'react';
import Son from './Son'

class Father extends React.Component{
    constructor() {
        super();
        this.getMsg = this.getMsg.bind(this);
    }

    // 接收子组件传过来的数据
    getMsg(data) {
        console.log(data);
    }

    render() {
        return(
            <div>
                <h3>父组件</h3>
                <hr/>
                {/* 给子组件传递属性和方法 */}
                <Son msg="hello son" getMsg={this.getMsg}/>
            </div>
        )
    }
}

export default Father;

// 子组件
import React from 'react';

class Son extends React.Component{
    constructor() {
        super();
    }

    render() {
        let {msg,getMsg} = this.props;
        return(
            <div>
                {msg}<br/>
                {/* 调用父组件传过来的方法,并传递数据 */}
                <button onClick={()=>{getMsg('哈哈哈哈哈')}}>发送消息</button>
            </div>
        )
    }
}

export default Son;			

(六) axios 拦截器

import axios from "axios"; 
let isDev = process.env.NODE_ENV === 'development';
let baseURL;
if (isDev) {
    baseURL = "http://localhost:3006";
} else {
    baseURL = "http://huruqing.cn:3006";
}
const service = axios.create({
  baseURL,
  timeout: 10 * 60 * 1000,
});

//4. 请求拦截
service.interceptors.request.use(
  (config) => {
    // do something
    return config;
  },
  (err) => {
    return Promise.reject(err);
  }
);

service.interceptors.response.use(
  (res) => {
    const data = res.data;
    if (data.code != 666) {
      return Promise.reject(data.msg);
    }
    return data;
  },
  (err) => {
    return Promise.reject(err);
  }
);

const get = (url, data = {}) => {
  return service.get(url, { params: data });
};
const post = (url, data = {}) => {
  return service.post(url, data);
};

export default {
  get,
  post,
};

(七) 静态页面

  1. 引入icont
  2. 使用短路径

    在React项目使用短路径, 跟 ../../../../地狱模式说拜把

    项目的根目录下创建jsconfig.json文件,并添加以下代码。

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}

// 使用
import axios from "../../../utils/request";   // 以前的写法
import axios from "utils/request";   
  1. 图片的引用问题

最佳实践: 写静态的时候, 如果图片来自网上, 随便用网上的一张图片暂代, 等        拿到数据再使用变量代替

方式1: 支持完整路径
<img sr="https://mall.s.maizuo.com/aaa.png"/>
方式2: 使用import
import bg from 'assest/img/bg.png'
 <img src={bg} alt=""/>
  1. 渲染还可以这样写
{
	list.map(item=>(
  <ul>
  	<li>11111</li>
    <li>22222</li>
  </ul>
  ))
}

// 类似vue的template的做法
{
	list.map(item=>( 
  <>
  	<div>11111</div>
    <div>22222</div> 
   </> 
  ))
}


  1. react渲染html字符串(类似vue的v-html指令)
let htmlStr = `<ul class="nav fcc bg-fff">
      <li class="fg1 ifcc item active">推荐•路线</li>
      <li class="fg1 ifcc item">推荐•路线</li>
      <li class="fg1 ifcc item">推荐•路线</li>
    </ul>`

export default function() {
	return (
   		<div className="pt-15 bg-fff" dangerouslySetInnerHTML={{ __html:htmlStr}}></div> 
	)
}

(八) classnames

类似vue绑定class的操作

// 判断条件为true时会给div添加class类 on
<div className={classnames("flex jc-sa",{on:判断条件}}></div>

// 实际应用
import React from 'react'; 
import classnames from 'classnames';
export default function() {
    let curr = '01';
    return(
        <div className="demo-index">
           <li className={classnames("flex f18 jc-sb",{on: curr==='01'})}>111</li>
           <li  className={classnames("flex f18 jc-sb",{on: curr==='02'})}>222</li>
           <li  className={classnames("flex f18 jc-sb",{on: curr==='03'})}>333</li>
        </div>
    )
}

(九) Hooks

推荐链接: React useState() 使用指南

react hooks介绍

  1. 在函数组件中没有state(状态), 也没有生命周期
  2. react hooks是react中引入新特性,它可以让react函数组件也拥有状态

知识点

  1. useState 定义状态
  2. useEffect 副作用
  3. useNavigate 导航
  4. 其它hooks

(1) useState

  1. 在函数组件中使用useState来定义响应式数据, 接收一个参数(变量的默认值)
  2. 使用格式 let [value,setValue] = useState(initValue)
    • value要定义的变量名称
    • setValue用来修改value的方法
    • initValue初始值

01 value是基本数据类型

import React, { useState } from 'react';
export default function() {
    // 定义响应式数据keyword,keyword的默认值是 ''
    let [keyword,setKeyword] = useState(''); 
    return(
        <div>
            <button onClick={()=>{setKeyword('哈哈哈哈哈')}}>修改关键字</button>
            <p>{keyword}</p> 
        </div>
    )
}

02 value是对象

import React, { useState } from "react";

function Demo() {
  let [obj, setObj] = useState({
    username: "张三",
    age: 18,
  });

  const handleClick = (key, value) => {
    // 需要传入一个新的对象
    setObj({
      ...obj,
      [key]: value,
    });
  };

  return (
    <div>
      <p>
        {obj.username}
		<button onClick={() => { handleClick("username", "李四"); }} > 修改姓名 </button>
      </p>
      <p>
        {obj.age} 
		<button onClick={() => { handleClick("age", 20); }} > 修改年龄 </button>
      </p>
    </div>
  );
}

export default Demo;

03 value是数组

import React, { useState } from "react";

function Demo() {
  let [list, setList] = useState([
    { username: "张三", finished: 17 },
    { username: "李四", finished: 18 },
    { username: "王五", finished: 19 },
  ]);

  const update = (item)=> {
    item.finished = !item.finished;
    // 需要传入一个新的数组
    setList([...list]);
  }
 
  return (
    <ul>
      {list.map((item, index) => (
        <li key={index}>
          <span>{item.username} </span>
          {item.finished?'已完成':'未完成'}
          <button onClick={()=>{update(item)}}>修改状态</button>
        </li>
      ))}
    </ul>
  );
}

export default Demo;

04 useState依赖原来的值

let [count,setCount] = useState(0);
// 在原来的基础上加1
setCount(val=>val+1);

(2) useEffect

  1. 中括号内的变量发生了改变, 就会触发回调函数
  2. 请求后台接口可以放在useEffect的函数里执行, 中括号为空或者不要中括号
    let [keyword,setKeyword] = useState(''); 
    useEffect(()=> {
        console.log(2222);
      // 当keyword发生变化时,会触发回调函数
    },[keyword])

(3) useNavigate

import {useNavigate} from 'react-router';
 // 跳转到/home/detail
 navigate('/home/detail'});
//  // 跳转到/home/detail并传参参数
 navigate('/home/detail',{ 
      state: {
        id: item.id
      }
})

useMemo

类似vue的computed

(十) 路由跳转和传参

路由跳转:

1. 使用 <Link to="xxx">xxx</Link>
2. 通过 navigate进行跳转
 - 一个参数: 目的地路由
 - 两个参数: 目的路由, state数据

(1) 通过state传参

传递数据

// 列表页面
import {useNavigate} from 'react-router';

function House({ houseList}) {
  const navigate = useNavigate();
  // 跳转到详情
  const toDetail = (item) => { 
    navigate('/home/detail',{
      // state里放要传递的参数
      state: {
        id: item.id
      }
    })
  }

接收参数

// 详情页面
import {useLocation} from 'react-router';
function List() {
    const location = useLocation();
    console.log(location);
    return ( <div>详情</div> );
}
export default List;

(2) 动态路由传参

配置动态路由(app.js)

 <Route path="/demo2/:id" element={<Demo2 />} />

路由跳转并传参(demo.jsx);

import React from "react";
import { Link } from "react-router-dom";
import {useNavigate} from 'react-router'

function Demo() {
  const navigate = useNavigate();
  return (
    <div>
      <Link to="/demo2/2222">去demo2</Link>
      <button onClick={()=>{navigate('/demo2/3333')}}>去demo2</button>
    </div>
  );
}

export default Demo;

接收参数

import React from "react";
import {useParams} from 'react-router';

function Demo2() {
  const params = useParams();
  console.log(params); 
  return (
    <div>
      <p>{params.id}</p>
    </div>
  );
}

export default Demo2;

(十一) redux

本例gitee地址: git@gitee.com:huruqing/react-redux-demo.git

新版redux,使用hooks https://react-redux.js.org/tutorials/quick-start

(1) redux使用场景

mobx也是用来进行跨组件通信, 比redux更简单一些 一句话: 状态共享, 跨组件通信

某个组件的状态,需要共享
某个状态需要在任何地方都可以拿到
一个组件需要改变全局状态
一个组件需要改变另一个组件的状态

(2) 核心概念

  1. **Store: **Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。

Redux 提供createStore这个函数,用来生成 Store。

  1. **State: **状态, 存放在store里
  2. Action
    1. action是个对象, 描述当前发生的事情
    2. Action Creator: View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator。
    3. 派发action: store.dispatch() 是 View 发出 Action 的唯一方法。
  3. Reducer
    1. Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
    2. Reducer 是一个纯函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。上面的createStore接受 Reducer 作为参数,生成一个新的 Store
    3. 纯函数: 纯函数是函数式编程的概念,必须遵守以下一些约束。
      1. 特点: 相同的输入会有相同的输出
      2. 不得改写参数
      3. 不能调用系统 I/O 的API(就是输入输出)
      4. 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果

(3) 项目中redux步骤

知识目录
  1. 创建store
  2. 获取状态
  3. 修改状态
  4. 模块化
  5. 持久化
1. 安装redux和react-redux

npm i redux react-redux

2. 创建store和相关的东西
  1. src下新建 /redux/index.js
  2. 初始化state、声明action、声明reduer
  3. 创建store并导出
import {createStore} from 'redux';

// 1.state的初始值
const initState = {
    count: 0
}

// 2.声明action creator 并导出,payload是派发action时传进来的数据
export const updateCount = function(payload) {
    // 此函数返回一个action
    return {
        type: 'UPDATE_COUNT',
        payload,
    }
}

// 3.声明reducer
const reducer = function(state=initState,action) {
    let {type,payload} = action;
    switch(type) {
        case 'UPDATE_COUNT':
            return {
                ...state,
                count: state.count+payload
            }
        default:
            return state;    
    }
}

let store = createStore(reducer);
export default store;
3. 挂载store
// index.js(src目录下的)
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Provider} from 'react-redux';
import store from 'redux/index'

ReactDOM.render(
  <React.StrictMode>
  	// 挂载store
    <Provider store={store}>
    <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
); 
reportWebVitals();
4. 获取和修改store的状态

把普通react组件改造成react高阶组件

import React from "react"; 
// 1 导入action creator等相关东西
import { updateCount } from "redux/index";  
import { bindActionCreators } from "redux"; 
import { connect } from "react-redux";

function Demo(props) {
  return ( 
      <div>
          {/* 5.获取count的值 */}
          <p>count的值: {props.count}</p>

           {/* 修改count的值 */}
           <button onClick={()=>{props.updateCount(props.count+1)}}>修改count</button>
      </div>
  );
}

// 2.把store里state转变成props来使用,后面访问state时就使用props.xxx来进行访问
// @params state store里面所有状态
function mapStateToProps(state) {
  return {
    // 组件使用props.count来访问count状态
    count: state.count 
  }
}
// 3.把dispatch转变成props来使用
function mapDispatchToProps(dispatch) {
  return {
    // bindActionCreators的作用:把action creator和dispatch绑定起来
    updateCount: bindActionCreators(updateCount,dispatch)
  }
}

// 4.connect方法的作用: 把普通组件Demo变高阶组件
export default connect(mapStateToProps,mapDispatchToProps)(Demo);

(4) redux 模块化

  1. 新增cart模块, 在里面定义state,action,reducer并导出
// demo模块

// 1.state的初始值
const initState = {
    count: 10
}

// 2.声明action creator 并导出,payload是派发action时传进来的数据
export const updateCount = function(payload) {
    // 此函数返回一个action
    return {
        type: 'UPDATE_COUNT',
        payload,
    }
}

// 3.声明reducer
export default function(state=initState,action) {
    let {type,payload} = action; 
    switch(type) {
        case 'UPDATE_COUNT':
            return {
                ...state,
                count: payload
            }
        default:
            return state;    
    }
}

// user模块
// 1.usermok的初始化状态
const initState = {
  phone: "",
  token: "",
};

// 2.相关action creator
export const updatePhone = function (payload) {
  // 返回一个action
  return {
    type: "UPDATE_PHONE",
    payload,
  };
};
export const updateToken = function (payload) {
  return {
    type: "UPDATE_TOKEN",
    payload
  };
};

// 3.声明reducer
export default function (state = initState, action) {
  let { type, payload } = action; 
  switch (type) {
    case "UPDATE_PHONE":
      return {
        ...state,
        phone: payload,
      };
    case "UPDATE_TOKEN":
      return {
        ...state,
        token: payload,
      };

    default:
      return state;
  }
}

// 合并reducer, /src/redux/index.js
import {createStore} from 'redux';
import demoReducer from './demo';
import userReducer from './user';
import { combineReducers } from 'redux';

// 合并reducer
let reducers = combineReducers({
    demo: demoReducer,
    user: userReducer
})

export default createStore(reducers);

  1. 合并reducer, 改造/redux/index.js 代码
import {createStore} from 'redux';
import demoReducer from './demo';
import userReducer from './user';
import { combineReducers } from 'redux';

// 合并reducer
let reducers = combineReducers({
    demo: demoReducer,
    user: userReducer
})

export default createStore(reducers);

(5) redux 持久化

1. 安装持久化插件 redux-persist
npm i redux-persist
2. 使用插件对store进行持久化操作

改造 /redux/index.js 代码, 对reducer和store进行持久化配置

3. 应用持久化后的store

改造 /src/index.js, 实现持久化

*************************模块化之前持久化**************************
 // src/redux/index.js 
import { createStore } from "redux";
import {persistStore, persistReducer} from 'redux-persist';
import storageSession from 'redux-persist/lib/storage/session';

// 初始状态
const initState = {
  count: 0, 
}; 
 
export const updateCount = (payload) => {
  return {
    type: "UPDATE-COUNT",
    payload,
  };
};

// 修改状态的一个方法, 叫reducer, 当用户派发了一个action, 不管是什么action, 这里都会接收到 
const reducer = (state = initState, action) => {
  let { type, payload } = action;
  switch (type) {
    case "UPDATE-COUNT":
      return {
        ...state,
        count: state.count + payload,
      };
      break; 
    default: {
      return state;
    }
  }
};

const storageConfig = {
    key: 'root', // 必须有的
    storage:storageSession, // 缓存机制
    blacklist: [] // reducer 里不持久化的数据,除此外均为持久化数据
}
const newReducer = persistReducer(storageConfig, reducer);
export let store = createStore(newReducer);
export let newStore = persistStore(store);

/**
 * 模块之前的持久化
 * src/index.js
 */ 
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import "antd-mobile/dist/antd-mobile.css";
import { Provider } from "react-redux";
import {store,newStore} from "./redux/index";
import { PersistGate } from "redux-persist/lib/integration/react";

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <PersistGate persistor={newStore}>
        <App />
      </PersistGate>
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
); 
reportWebVitals();
*************************模块之后的持久化**************************
// src/redux/index.js代码
import {createStore} from 'redux';
import demoReducer from './demo';
import userReducer from './user';
import { combineReducers } from 'redux';


// 1.导入相关插件
// persistStore持久化store, 持久化reducer
import {persistStore, persistReducer} from 'redux-persist';
// storageSession缓存
import storageSession from 'redux-persist/lib/storage/session';

// 2.store持久化设置
const storageConfig = {
    key: 'root', // 必须有的
    storage:storageSession, // 缓存机制
    blacklist: [] // reducer 里不持久化的数据,除此外均为持久化数据
}

// 3.合并reducer
let reducers = combineReducers({
    demo: demoReducer,
    user: userReducer
})

// 4.给reducer应用持久化配置
const newReducers = persistReducer(storageConfig, reducers);
// 5.创建store
const store = createStore(newReducers);
// 6.对store进行持久化并导出
export const persistor = persistStore(store);
// 7.导出store
export default store;


// src/index.js代码
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux"; 

// 8.持久化
import store, { persistor } from "./redux/index";
import { PersistGate } from "redux-persist/lib/integration/react";

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
        {/* 用持久化后的store包裹<App/> */}
      <PersistGate persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);
reportWebVitals();

(6) 异步action,使用redux-thunk实现

  1. 安装redux-thunk npm i redux-thunk
  2. 改造/redux/index.js
// 核心代码如下
//1.applyMiddleware 加载中间件
import { createStore,applyMiddleware } from "redux";
//2.导入异步中间件thunk
import thunk from 'redux-thunk';
 
const store = createStore(newReducers,applyMiddleware(thunk)); 
  1. 创建异步action

改造/redux/demo.js

// 1.state的初始值
const initState = {
  count: 10,
};

// 2.声明action creator 并导出,payload是派发action时传进来的数据
// export const updateCount = function(payload) {
//     // 此函数返回一个action
//     return {
//         type: 'UPDATE_COUNT',
//         payload,
//     }
// }

export const updateCount = (payload) => {
  // 异步action, 允许返回一个函数,使用dispatch派发action
  return (dispatch) => {
    setTimeout(() => {
      dispatch({
        type: "UPDATE_COUNT",
        payload,
      });
    }, 1000);
  };
};

// 3.声明reducer
export default function (state = initState, action) {
  let { type, payload } = action;
  switch (type) {
    case "UPDATE_COUNT":
      return {
        ...state,
        count: statepayload,
      };
    default:
      return state;
  }
}

(7) axios拦截器给http请求头添加token

配置后再查看首页的请求是否都带上了token

import axios from 'axios'; 
// 1.导入redux
import redux from 'redux/index';

 
const service = axios.create({ 
    baseURL: 'http://132.232.87.95:3006',  
    timeout: 10 * 60 * 1000 //10分钟  
}) 
service.interceptors.request.use(
    (config) => {
        // 2.获取store
        let store = redux.getState();
        // 3.把token放入请求头
        config.headers['user-token'] = store.user.token;
        return config;
    },
    (err) => {
        return Promise.reject(err);
    }
) 

service.interceptors.response.use(
    (res) => {
        const data = res.data; 
        if (data.code != 666) {  
            return Promise.reject(data.msg);
        }
        return data;
    },
    (err) => {
        return Promise.reject(err);
    }
)


const get = (url,data={})=> {
    return service.get(url,{params: data});
}
const post = (url,data={})=> {
    return service.post(url,data);
}
 
export default {
    get,post
};

(十二) Mobx

介绍

  1. mobx是react用来跨组件通信的一个插件, 它的核心是依赖注入
  2. 具体做法就是:
    • 先创建一个(或多个)可观察的仓库
    • 在需要使用仓库状态的组件, 使用enject方法将这个仓库注入到它的props里, 就可以使用仓库的状态了
    • 若组件要修改仓库的状态, 则需要将组件用observer方法成包装高级组件

(1) 安装依赖

npm install mobx mobx-react

(2) 配置store

// src/store/index.js

  1. makeAutoObservable 使一个普通类的属性和方法变成一个响应式的状态库

  2. 函数前面加get, 类似vuex的getters, 用来根据已有属性计算出一个新的属性

   import * as mobx from "mobx";
   class Store {
     count = 0;
   
     constructor() {
       mobx.makeAutoObservable(this);
     }
   
     add() {
       this.count++;
     }
     minus() {
         this.count--;
     } 
   
     // 求平方根
     get sqrt() {
       return Math.sqrt(this.count);
     }
   }
   
   export default new Store();

(3) 挂载

​ // src/index.js

​ Provider: 提供数据

import React from "react";
import ReactDOM from "react-dom";
import "./index.scss";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "mobx-react";
import store from "store";

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);

reportWebVitals();

(4) 使用和修改

  1. inject 将store注入到props里
  2. observer 将组件变成高级组件, 可以修改仓库状态
  3. 在不需要修改状态的组件里, 只需要注入即可
import { inject, observer } from "mobx-react";
import React from "react";

function Demo({ store }) {
  return (
    <div style={{margin:'30px'}}>
      <button onClick={()=>{store.add()}}>+</button>
      <button>{store.count}</button>
      <button onClick={()=>{store.minus()}}>-</button>
      <p>
        开平方根: {store.sqrt.toFixed(2)}
      </p>
    </div>
  );
} 
export default inject("store")(observer(Demo));

错误: Support for the experimental syntax 'decorators-legacy' isn't currently enab -- 装饰器@

[报错显示:Parsing error: This experimental syntax requires enabling one of the following parser plugin(s): “decorators-legacy”, “decorators”.](https://blog.csdn.net/lfy_wybss/article/details/122079178)

(十三) 性能优化

知识点

  1. shouldComponentUpdate
  2. PureComponent
  3. useMemo

(1) shouldComponentUpdate

类(class)组件性能9优化可以使用shouldCompoentUpdate进行优化

// nextProps下一个属性(新的属性),nextState下一个状态(新的状态)   
shouldComponentUpdate(nextProps, nextState) { 
    // 根据新的属性或者状态决定要不要更新页面
   if(xxx) {
       return true;
	} else {
       return false
   }
}

(2) PureComponent

类(class)组件, 可以让其继承React.PureComponent来实现优化

import React from "react"; 
class Demo extends React.PureComponent {
  constructor(props){
    super(props);
  } 

  render() {
    console.log(this.props);
    return (<div>性能优化</div>)
  }
} 
export default Demo;

(3) memo

函数组件可以使用React.memo进行包装以实现优化

import React from "react";
function Scene(props) { 
  return <div>{props.name}</div>;
}
export default React.memo(Scene);

https://www.jianshu.com/p/b3d07860b778