react笔记

《React学习》

vite脚手架

npm create vite

npm install @vitejs/plugin-react

MeiTuan-React

npm install react-router-dom zustand immer zustand-devtools

​ npm i react-redux @reduxjs/toolkit

npm i react-redux @reduxjs/toolkit react-router-dom dayjs cassnames antd-mobile axios

vite react Redux Rtk router antd

假数据开发 json-server

创建目录 server

​ —data.js

​ --配置package启动 : “server”: “json-server ./server/data.json --port 8888”

清楚初始默认

Normalize.css

npm install normalize.css

配置路径配置@/

什么是@别名路径?

通过@替代src路径,方便开发过程中的路径查找访问

npm install vite @vitejs/plugin-react

放入vite.config.js

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:5000',
        changeOrigin: true,
      },
    },
  },
});
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),  // 路径别名配置
    },
  },
  server: {
    port: 3000,  // 设置开发服务器端口
    proxy: {
      '/api': {
        target: 'http://localhost:5000',
        changeOrigin: true,
      },
    },
  },
});

配置jsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",  // 基础路径,通常为项目根目录
    "paths": {
      "@/*": ["src/*"]  // 路径别名配置
    }
  },
  "include": ["src"]  // 包含的文件夹
}

Hooks

useState

​ 使用useState

useState它是组件状态变量 可以用来组件的数组调用使用

当使用useState时,我们可以通过调用React.useState来创建一个状态变量。useState返回一个数组,其中第一个元素是当前状态的值,第二个元素是用于更新状态的函数。

osnt [number,Setnumber]=React.unsetet(内容)

import React from "react";

const MyuseState = () => {
  let i = 1;
  const onClick = () => {
    i++;
    return i % 2 == 0
      ? setText("My text: hallo React")
      : setText("My text: hello world");
  };

  const [text, setText] = React.useState("My text: hallo world");
  return (
    <div>
      <h1>我是Usestate初始:{text}</h1>
      <button onClick={onClick}>点击修改文本</button>
    </div>
  );
};
export default MyuseState;

useEffect

useEffect 它是异步执行变更状态的 ,UseEffect它是一个异步执行的,从Js 的dom渲染之前调用,但是在浏览器渲染完成之后调用.它会在 React 完成 DOM 渲染和组件初次挂载之后执行。

配合一个useSteat来监听状态变化从而作出改变 它能够坚挺依赖项变化,并执行对应的回调函数

基本语法:

useEffect(()=>{},[依赖项])

​ 如果不想调用渲染一次直接吧依赖项为[],哪些状态需要更改就要存入它的依赖项

设置为[]对象就只会初始执行, 因为没有依赖项

问:为什么初始化渲染执行了两次useEffect的log 如果开启了$\color{red} 严格模式 $的话是会执行 两次 

答: 因为初始化会直接渲染一次

  1. 在组件首次渲染时,useEffect 会执行一次,因为这是组件的初始化过程。
  2. 当你点击 "点击+1" 按钮时,cont 状态发生变化,这会导致 useEffect 再次执行。
import React from "react";

const MyuseEffect = () => {
  const [cont, Setcont] = React.useState(0);
  const add = () => {
    Setcont(cont + 1);
  };
  React.useEffect(() => {
    console.log("useEffect执行了");
  }, [cont]);
  return (
    <div style={{ width: 200, height: 200 }}>
      <h1>我是MyuseEffect组件</h1>
      <p>{cont}</p>
      <button onClick={add}>点击+1</button>
    </div>
  );
};

export default MyuseEffect;

更新天数demo

​ usetate 配合useEffect时间日期更新组件 Demo

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import App from "./components/App";

const TodayDate = () => {
  const [date, setDate] = useState(() => {
    const dt = new Date();
    return {
      year: dt.getFullYear(),
      month: dt.getMonth(),
      day: dt.getDate(),
    };
  });

  useEffect(() => {
    const interval = setInterval(() => {
      const newDate = new Date();
      setDate({
        year: newDate.getFullYear(),
        month: newDate.getMonth(),
        day: newDate.getDate(),
      });
    }, 1000 * 60 * 60 * 24); // 每 24 小时更新一次

    // 组件卸载时清除定时器
    return () => clearInterval(interval);
  }, []);

  return (
    <h1 style={{ textAlign: "right", color: "blue" }}>
      {date.year}-{date.month + 1}-{date.day}
    </h1>
  );
};

const rootElement = ReactDOM.createRoot(document.getElementById("root"));
rootElement.render(
  <React.StrictMode>
    <TodayDate />
    <App />
  </React.StrictMode>
);

useLayoutEffect

​ 使用场景,例如显示title文本 ,useLayoutEffect是在浏览器重新绘制屏幕之前触发.

useRef

使用场景 需要获取dom的元素节点就可以使用useRef这个钩子,说白了就是获取dom节点标签的,

  1. 保存可变状态:
    • useRef 可以用于保存可变的值,而不会触发组件的重新渲染。这在某些情况下很有用,例如保存上一次的 prop 值、计时器 ID 等。
  • 跨渲染周期保存值:

  • useRef 保存的值在组件的整个生命周期内都是持续存在的,不会随着组件的重新渲染而丢失。这在某些情况下很有用,例如保存上一次的搜索关键词、滚动位置等。

使用方法:

  const RefH1 = useRef(null);//创建后在return的标签里面写入ref接受

      <h1 ref={RefH1}>我是MyuseEffect组件</h1>

这样就可以获取到标签了

三个注意项:

请不要把ref的值,当作别人的依赖对象!

forwardRef

获取组件的其中的一个domOfChilider(dom的节||孩子)

useContext

使用场景,当多个子组件开始传递数据,你需要手动props传递过去,子传父 层层套props,这样非常麻烦且不易读! 这个时候我们就可以使用useContext这个hooks来实现一次使用 多个子节点都可以调用数据.

useContext 使用方法:

1.创建useContext 这个hooks的方法

import React from "react";
const TextContext = React.createContext({ name: "我是初始值", age: "NaN" });
export default TextContext;

2.调用切 创建Provider(供应者)也就是值,我们需要什么参数我们就可以传递给这个Provider的value值,可以是一个变量,一个对象或者是数组,最常用的还是我们react的hooks的useState值,传递过去.

例:

  <TextContext.Provider value={"我是父组件"}> 
        <MyuseState />
        <MyuseEffect />
      </TextContext.Provider> 

我们传递过去了一个字符串如果我们的子组件想调用这个value的值需要以下操作就可以使用.

我们的useContext里面是可以嵌套多个传递数据的,useContext() 总是在调用它的组件 上面 它向上搜索,$\color{red} 寻找最近的 provider。 $ 不考虑 调用 useContext() 的组件中的 provider。

usememo

useTransition

使用方法:

cosnt [isPending,starTransition]=useTransition();
用starTransition将执行的数据包裹就行了

演示React真正意义上比Vue厉害的地方。

用于性能,用于用户体验。

特性:并发更新(fiber架构),

16.8版本之后

并发更新

1.fiber架构 (数据结构)

2.浏览器空闲时间 requestIdleCallback的思想

因为requestIdleCallback这个东西兼容不好,react目前使用的是

usei d

创建的是独一无二的id

这个展示不需要使用

useImperativeHandle

作用:自定义转发的ref

应用场景:我不希望开发者直接dom,你给我定义方法就好.

可以指定ref返回的值

三个Effect

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

useEffectuseInsertionEffectuseLayoutEffect 这三个 React Hooks 都是用于处理副作用的,但它们之间有一些重要的区别:

  1. 执行时机:
    • useEffect: 在浏览器完成页面渲染后执行,即在 DOM 更新之后。
    • useInsertionEffect: 在 DOM 更新之前执行,优先级高于 useEffect
    • useLayoutEffect: 在 DOM 更新后,但在浏览器绘制之前执行,优先级高于 useEffect
  2. 用途:
    • useEffect: 常用于处理订阅、事件监听、数据获取等副作用。
    • useInsertionEffect: 适用于在 DOM 插入时修改样式的场景,如注入全局样式。
    • useLayoutEffect: 适用于同步测量和操作 DOM 的场景,如设置页面滚动位置。
  3. 性能影响:
    • useEffect: 由于在浏览器绘制后执行,对性能影响较小。
    • useInsertionEffect: 由于在 DOM 更新之前执行,可能会造成一些视觉上的闪烁。
    • useLayoutEffect: 由于在浏览器绘制之前执行,可能会阻塞页面渲染,对性能影响较大。
  4. 错误处理:
    • useEffect: 如果 effect 函数返回一个清理函数,React 会在组件卸载时调用它。
    • useInsertionEffect: 目前没有官方文档介绍如何处理错误和清理。
    • useLayoutEffect: 与 useEffect 类似,可以返回一个清理函数。

总的来说,useEffect 是最常用的副作用钩子,适用于大多数需要处理副作用的场景。useInsertionEffect 主要用于样式注入,而 useLayoutEffect 则主要用于同步测量和操作 DOM 的场景,需要谨慎使用以避免阻塞页面渲染。

Redux

使用步骤:

  1. 定义一个 reducer 函数(根据当前想要做的修改返回一个新的状态)
  2. 使用createStore方法传入 reducer函数 生成一个store实例对象
  3. 使用store实例的 subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知)
  4. 使用store实例的dispatch方法提交action对象 触发数据变化(告诉reducer你想怎么改数据)
  5. 使用store实例的getState方法 获取最新的状态数据更新到视图中

​ 配套工具 在用react中redux ,官方要求安装连个其他插件 redux Toolikit 和react-redux

​ 官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式

简化store的配置方式 ---- 内置immer支持可变式状态修改。 内置thunk更好的异步创建

​ 2.react-redux 用来链接Redux和React组件的中间件

​ 获取数据

​ ---------------------------------------->

​ react – react-redux. – react组件

															   <--------------------------------------

​ 更新状态

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

整体路径熟悉

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

React-Redux

它是React组件与Redux有交互可以从store读取一些state,可以通过dispatch actions 来更新store

Redux术语

actions是个什么鬼东西呢?

actions它是一个用于type字段里面使用的,白话就是告诉你我的地址

action 是一个具有 type 字段的普通 JavaScript 对象。你可以将 action 视为描述应用程序中发生了什么的事件.

type 字段是一个字符串,给这个 action 一个描述性的名字,比如"todos/todoAdded"。我们通常把那个类型的字符串写成“域/事件名称”,其中第一部分是这个 action 所属的特征或类别,第二部分是发生的具体事情。

action 对象可以有其他字段,其中包含有关发生的事情的附加信息。按照惯例,我们将该信息放在名为 payload 的字段中。

Readux Toolkit

​ Readux Toolkit用于简化我们编写Redux的繁琐过程,让我提升开发效率

​ npm i react-redux @reduxjs/toolkit

useSelector(获取数据), useDispatch(派发,发送)

import { createSlice } from "@reduxjs/toolkit";
const userSilice = createSlice({
  name: "Panda",
  initialState: {
    //初始数据
    name: "X",
    gender: "男",
    age: 18,
  },
  reducers: {
    //操作数据
     SetName: (state, action) => { //state是传入的值,action是我们操作的方法
      state.name = action.payload;
    },
  },
});
export default userSilice;

RTK Query

文档https://redux-toolkit.js.org/tutorials/rtk-query

创建RTK Query

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

// 使用 createApi 创建 API
const myApi = createApi({
  // reducerPath 是用来存储 API 状态的 slice 名称
  reducerPath: "myApi",

  // baseQuery 用于配置基础 URL,这里使用 fetchBaseQuery 简化请求
  baseQuery: fetchBaseQuery({
    baseUrl: "https://api.oioweb.cn/api/common/", // 基础 URL
  }),

  // endpoints 定义 API 的端点
  endpoints: (builder) => ({
    // 定义一个名为 getApiValue 的查询端点
    getApiValue: builder.query({
      // query 函数返回查询的相对路径
      query: () => "today", // 当调用 getApiValue 时,请求 'https://api.oioweb.cn/api/common/today'
    }),
  }),
});

// 导出自动生成的 hook,用于在组件中调用111
export const { useGetApiValueQuery } = myApi;

// 导出 API slice,用于 store 配置
export default myApi;

#### 导入到Store仓库
import { configureStore } from "@reduxjs/toolkit";
import BannanaStore from "./bannana"; // 导入你的自定义 reducer,处理与 banana 相关的状态
import myApi from "./creatApi"; // 导入创建的 API slice,用于处理 API 请求

// 配置 Redux store
export const Store = configureStore({
  // 定义 store 中的 reducers
  reducer: {
    // 自定义的 reducer,管理与 banana 相关的状态
    myBannanaStore: BannanaStore,

    // API slice 的 reducer,存储 API 请求的状态和缓存数据
    [myApi.reducerPath]: myApi.reducer,
  },

  // 配置 middleware
  middleware: (getDefaultMiddleware) =>
    // 使用默认的 middleware,并添加 API slice 的 middleware
    getDefaultMiddleware().concat(myApi.middleware),
});

// 1.	import { configureStore } from "@reduxjs/toolkit":
// 	•	从 Redux Toolkit 导入 configureStore 函数,用于创建 Redux store。
// 	2.	import BannanaStore from "./bannana":
// 	•	导入自定义的 BannanaStore reducer,这个 reducer 负责管理与 banana 相关的状态。
// 	3.	import myApi from "./creatApi":
// 	•	导入通过 createApi 创建的 API slice。这个 slice 处理 API 请求的状态和缓存数据。
// 	4.	export const Store = configureStore({...}):
// 	•	使用 configureStore 函数创建 Redux store。
// 	5.	reducer:
// 	•	定义 store 中的 reducers。
// 	•	myBannanaStore: 你的自定义 reducer,用于处理 banana 状态。
// 	•	[myApi.reducerPath]: myApi.reducer: 将 API slice 的 reducer 添加到 store 中,myApi.reducerPath 是 API slice 状态在 store 中的路径。
// 	6.	middleware:
// 	•	配置 middleware。
// 	•	getDefaultMiddleware(): 获取 Redux Toolkit 提供的默认 middleware。
// 	•	.concat(myApi.middleware): 将 API slice 的 middleware 添加到默认 middleware 中。myApi.middleware 用于处理 API 请求的生命周期。

// 这样配置的 store 包含了你的自定义 reducer 和 API slice 的 middleware,确保可以正确地管理应用的状态和处理 API 请求。

App组件使用

import { useDispatch, useSelector } from "react-redux";
import { addBannana, lessBandada } from "./store/bannana";
import { useGetApiValueQuery } from "./store/creatApi";

function App() {
  const count = useSelector((state) => state.myBannanaStore.count);
  const dispatch = useDispatch();
  const { data, isSuccess, isLoading } = useGetApiValueQuery(); // 解构获取所需的状态和数据
  console.log(data);

  return (
    <div>
      <p>{count}</p>
      <p>
        <button
          onClick={() => {
            dispatch(addBannana(10));
          }}
        >
          +
        </button>
        <button
          onClick={() => {
            dispatch(lessBandada(10));
          }}
        >
          -
        </button>
      </p>
      <ul>
        {isLoading && <p>Loading...</p>}
        {isSuccess &&
          data.result.news.map((item, index) => (
            <li key={index}>{item}</li> // 使用圆括号包裹 map 的回调函数
          ))}
      </ul>
    </div>
  );
}

export default App;

从0-1 Readux

分析我们的需求和任务
需求:创建一个状态管理的仓库管理我们的bannan,写入几个方法=》 add ,less, input来控制or少的nbumber 

创建 bannanaStore

import { createSlice } from "@reduxjs/toolkit"; //创建 切片
export const BannanaStore = createSlice({
  name: "MyBannanaStore", //仓库名称
  initialState: {
    //状态值
    count: 0, //数量
    price: 2, //价钱
  },
  reducers: {
    addBannana: (state) => {
      //加1
      state.count += 1;
    },
    lessBannana: (state) => {
      state.count -= 1;
      //     减1
    },
    restore: (state, action) => {
      state.count = action.payload;
      //自定义归0?
    },
    diyPrice: (state, action) => {
      state.price += action.payload;
      //自定义加价
    },
  },
});
export const { addBannana, lessBannana, restore, diyPrice } =
  BannanaStore.actions;
export default BannanaStore.reducer;

创建仓库后我们需要把它导入到全局仓库中

import { configureStore } from "@reduxjs/toolkit";
import {} from "react-redux";
import bannanaStore from "./bannanaStore";
export const store = configureStore({
  reducer: {
    MyBannanaStore: bannanaStore,
  },
});

在mian里面引入stoure

// import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import { Provider } from "react-redux";
import { store } from "./store/stote.jsx";

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

使用与操作

import { useSelector, useDispatch } from "react-redux";
import {
  addBannana,
  lessBannana,
  diyPrice,
  restore,
} from "./store/bannanaStore";
import { useState } from "react";

function App() {
  const dispatch = useDispatch();
  const count = useSelector((state) => state.MyBannanaStore.count);
  const price = useSelector((state) => state.MyBannanaStore.price);
  const [priceInput, setPriceInput] = useState(0);

  return (
    <>
      <div>Bannana: {count}</div>
      <div>Price: ${price}</div>
      <p>
        <button
          onClick={() => {
            dispatch(addBannana());
          }}
        >
          Add
        </button>
        <button
          onClick={() => {
            dispatch(lessBannana());
          }}
        >
          Less
        </button>
        <button
          onClick={() => {
            dispatch(restore(0));
          }}
        >
          Restore
        </button>
        <input
          type="number"
          value={priceInput}
          onChange={(e) => setPriceInput(Number(e.target.value))}
          style={{ margin: 20 }}
        />
        <button
          onClick={() => {
            dispatch(diyPrice(priceInput));
          }}
        >
          Set Price
        </button>
      </p>
    </>
  );
}

export default App;

异步操作

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

美团案例

创建路由

zustand

npm i zustand

安装zustand   npm i zustand
immer npm install zustand immer
果您还打算使用zustand-devtools和zustand-persist等附加中间件,您可以运行以下命令来安装它们:
npm install zustand-devtools zustand-persist

初次使用

首先我们先定义一个变量后创建一个状态,用create,然后在内部参数为一个对象(

{value:0},

定义一个方法:()=>set(state)=>({value:state.value+1}),

removeAllBears: () => set({ Mynumber: 0 }),

)

import React from "react";
import { create } from "zustand"; //导入我们状态库
												创建
const useZustandStore = create((set) => ({
  Mynumber: 0, 对象里面创建
  increasePopulation: () => set((state) => ({ Mynumber: state.Mynumber + 1 })),
  removeAllBears: () => set({ Mynumber: 0 }),
}));

const Myzustand = () => {
  const { Mynumber, increasePopulation, removeAllBears } = useZustandStore();

  return (
    <>
      <p>我是zustand: {Mynumber}</p>
      <button onClick={increasePopulation}>增加数字</button>
      <button onClick={removeAllBears}>重置数字</button>
    </>
  );
};

export default Myzustand;

useShallow

当你需要从存储中订阅一个计算状态时,推荐的方式是使用选择器。

计算选择器将在输出根据 Object.is 改变时导致重新渲染。

在这种情况下,你可能希望使用 useShallow 来避免如果计算值总是浅相等于前一个值时的重新渲染。

// import React from "react";
import { useShallow } from "zustand/react/shallow";
import MyZustand from "./store";
console.log(MyZustand);
const Childern1 = () => {
  const { count, increment, decrement } = MyZustand(
    useShallow((state) => ({ 
      count: state.count,
      increment: state.increment,
      decrement: state.decrement,
    }))
  );
  console.log("Childern1");
  return (
    <>
      <div>
        <h1>{count}</h1>
        <button onClick={increment}>+</button>
        <button onClick={decrement}>-</button>
      </div>
    </>
  );
};
const Childern2 = () => {
  const color = MyZustand((state) => state.color);
  console.log("Childern2");
  return (
    <>
      <div>
        <h1>{color}</h1>
      </div>
    </>
  );
};
function App() {
  return (
    <>
      <Childern1 />
      <Childern2 />
    </>
  );
}
export default App;

devtools(浏览器查看)

安装Redux DevTools(在浏览器Redux插件中查看状态)

想看我们的状态管理我们就可以使用Redux DevTools,

使用需要在代码里面做一些小配置 devtools

如果引入为空方法

要使用devtools中间件,您需要单独安装zustand-devtools包。请执行以下步骤:

  1. 安装zustand-devtools包:在终端或命令提示符中,导航到您的项目目录,并运行以下命令安装包:

:

npm install zustand-devtools

语法如下:

import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { devtools } from "zustand/middleware";
const MyZustand = create(
  immer(
    devtools(
      (set) => ({
        count: 0,
        color: "red",
        increment: () =>
          set((state) => {
            state.count += 1;
          }),
        decrement: () =>
          set((state) => {
            state.count -= 1;
          }),
      }),
      { enabled: true, name: "MyZustand" }
    )
  )
);

export default MyZustand;

使用Redux DevTools ,我们需要从zustand里面导入一个方法devtools(是否检查,name:检查起名)

persist中间件(本地存储)

使用我们的中间件实现持久化存储,语法和devtools类似

导入我们中间件

import { devtools, persist } from "zustand/middleware";
const MyZustand = create(
  immer(
    devtools(
      persist(
        (set) => ({
          count: 0,
          color: "red",
          increment: () =>
            set((state) => {
              state.count += 1;
            }),
          decrement: () =>
            set((state) => {
              state.count -= 1;
            }),
        }),
        { enabled: true, name: "MyZustand" },//这是devtools
        { name: "Mystand" }//persist 持久化存储
      )
    )
  )
);

export default MyZustand;

中间件顺序

immer(
devtools(
		persist(回调,{name:'...'}) //实现在本地存储我们的状态对象
	)
)
partialize(仅存储指定属性)
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { devtools, persist } from "zustand/middleware";
const MyZustand = create(
  immer(
    devtools(
      persist(
        (set) => ({
          count: 0,
          color: "red",
          cat: 3,
          BigDog: 5,
          increment: () =>
            set((state) => {
              state.count += 1;
            }),
          decrement: () =>
            set((state) => {
              state.count -= 1;
            }),
        }),
        {
          name: "MyZustand",
          partialize: (state) => ({
            count: state.count,
            cat: state.cat,
            BigDog: state.BigDog,
          }),
        }
      )
    )
  )
);

export default MyZustand;

import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { createJSONStorage, devtools, persist } from "zustand/middleware";

const MyZustand = create(
  // 创建状态
  immer(
    // 使用 immer 简化状态更新的语法
    devtools(
      // 使用 devtools 可以在浏览器的 Redux 状态中查看我们的数据
      persist(
        // 实现本地化存储
        (set) => ({
          count: 0,
          color: "red",
          cat: 3,
          BigDog: 5,
          increment: () =>
            set((state) => {
              state.count += 1;
            }),
          decrement: () =>
            set((state) => {
              state.count -= 1;
            }),
        }),
        {
          name: "MyZustand",
          partialize: (state) =>
            Object.fromEntries(
              // 遍历数据进行筛选
              Object.entries(state).filter(([key]) => key !== "count")
            ),//存储位置||存储什么格式
          storage: () => createJSONStorage(() => localStorage), 
        }
      )
    )
  )
);

export default MyZustand;

清除 storage

Myzustand.persist.clearStorage

immer(中间件)

导入 immer 中间件

npm i immer

import React from "react";
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
const useMydata = create(
  //使用中间件
  immer((set, get) => ({
    count: 10,
    add: () => {
      set((value) => ({ count: value.count + 1 }));
    },
    less: () => {
      set((state) => ({ count: state.count - 1 }));
    },
    double: () => {
      set((state) => ({ count: state.count * 2 }));
    },
  }))
);
const App = () => {
  const { count, add, less, double } = useMydata();
  return (
    <>
      <p>{count}</p>
      <button onClick={add}>+1</button>
      <button onClick={less}>-1</button>
      <button onClick={double}>翻倍</button>
    </>
  );
};
export default App;

Router

什么是路由?

npm install react-router-dom
路由是指根据不同的 URL 地址,将用户导航到不同的页面或视图的过程。它决定了用户在浏览器中浏览网页时,页面的展示和切换方式。

在多页面应用中,每个页面都是独立的,点击链接或按钮会导致整个页面的重新加载,从而展示不同的内容。每次页面跳转都需要向服务器发送请求,加载新的页面。

而在单页面应用中,整个网站只有一个 HTML 页面,所有的内容都是通过 JavaScript 动态地在同一个页面上进行切换和更新,而不需要重新加载整个页面。单页面应用使用路由来管理不同的视图和页面状态,通过改变 URL 来切换不同的视图,同时避免了页面的刷新。

React Router 是一个用于在 React 应用中实现路由功能的库,它提供了一组组件和 API,使得在单页面应用中实现路由变得更加简单和灵活。通过使用 React Router,可以根据 URL 的变化,动态地渲染不同的组件和页面内容,实现单页面应用的路由功能。

1.多页面 就是我们点击页面发生跳转就是多页面

s2.单页面 在路径下切换浏览器未刷新

面试 单页面和多页面的区别

多页面就是多个html文件来回跳转,通过window.location.herf互相跳转

缺点:每个页面都要跳转,重新加载资源,性能会比较慢

好处:SEO(搜索引擎优化) 友好 ,适合c端(面对消费者)项目 ,隔离性好,每一个页面都是独立的项目

单页面:

好处:在html中进行路由跳转,实际上是通过js去控制的.代表性的如B端(自己不向外的),不考虑SEO浏览器搜索检索,

缺点:没SEO搜索检索

React-router和React-router-dom这个库区别?

react-router-dom(浏览器专用)Q内部实际上依赖react-router这个库的。

5种路由

BrowserRouter

​ (浏览器 路由)

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter, Route, Routes } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
const Bpp = () => {
  return <h1>Hallo Bpp</h1>;
};
root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/app" element={<App />} />
      <Route path="/Bpp" element={<Bpp />} />
    </Routes>
  </BrowserRouter>
);

要访问页面就3000/app

问题:BrowserRouter在使用的时候会遇到404的问题

HashRouter

​ (哈希路由)

哈希路由模式

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter, Route, Routes, HashRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
const Bpp = () => {
  return <h1>Hallo Bpp</h1>;
};
root.render(
  <HashRouter>
    <Routes>
      <Route path="/app" element={<App />} />
      <Route path="/Bpp" element={<Bpp />} />
    </Routes>
  </HashRouter>
);

外部替换使用 /#app就可以正常访问了

用的少很丑 ----------非必要不要使用这种方式 过时!!!

MemoryRouter

​ 内存形路由

​ 应用场景 :单测

NativeRouter

																				(安卓ios 路由)

StaicRouter

​ (静态路由)

面试题 2. 路由有几种

一共有五种

方法

Outlet

Outlet可以说是页面占位符匹配到那个就跳转那个

import React from "react";
import { Outlet } from "react-router-dom";
import "./Header.css";
const Header = () => {
  return (
    <header>
      header
      <Outlet />
    </header>
  );
};
export default Header;

useLoacation(获取当前地址的信息)

useParams来获取参数

useMatch 用来检查当前url是否匹配某个路由

路由跳转

useNavigate

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Header from "./layout/Header";
const root = ReactDOM.createRoot(document.getElementById("root"));
const Bpp = () => {
  return <h1>Hallo Bpp</h1>;
};
root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Header />}>
        <Route path="/app" element={<App />} />
        <Route path="/Bpp" element={<Bpp />} />
      </Route>
    </Routes>
  </BrowserRouter>
);



import React from "react";
import { Outlet, useNavigate } from "react-router-dom";
import "./Header.css";
const Header = () => {
  const navigator = useNavigate();
  return (
    <header>
      <h1>header</h1>
      <p>
        <button
          onClick={() => {
            navigator("/app");
          }}
        >
          App
        </button>
        <button
          onClick={() => {
            navigator("/bpp");
          }}
        >
          Bpp
        </button>
      </p>
      <Outlet />
    </header>
  );
};
export default Header;

Link标签
 ```
      <Link to="/App">跳转App</Link>
 ```
useLocation

可以查看到我们的元素的各种信息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  const Location = useLocation();
  console.log("Bpp", Location);

可以获取一些我们navigate路由过来的状态

import React from "react";
import { Outlet, useNavigate } from "react-router-dom";
import "./Header.css";
const Header = () => {
  const navigator = useNavigate();
  return (
    <header>
      <h1>header</h1>
      <p>
        <button
          onClick={() => {
            navigator("/app");
          }}
        >
          App
        </button>
        <button
          onClick={() => {
            navigator("/bpp", { state: { name: "Panda", form: "china" } });
          }}
        >
          Bpp
        </button>
        <button
          onClick={() => {
            navigator(-1);
          }}
        >
          返回
        </button>
      </p>
      <Outlet />
    </header>
  );
};
export default Header;

面试题 我们需要给页面跳转的页面添加一些数据应该怎么办

答案如上

跳转路由Navigate

Navigate 是用于编程式导航的,它通常在某个条件满足时使用

------------------                    静态路由        --------------------

动态路由

usePanmes
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import {
  BrowserRouter,
  Route,
  Routes,
  useLocation,
  useParams,
} from "react-router-dom";
import Header from "./layout/Header";
const root = ReactDOM.createRoot(document.getElementById("root"));
const Bpp = () => {
  const Location = useLocation();
  console.log("Bpp", Location);
  return <h1>Hallo Bpp</h1>;
};
const User = () => {
  const Params = useParams();
  return (
    <div>
      {Params.id}
      <p>User</p>
    </div>
  );
};
root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Header />}>
        <Route path="/app" element={<App />} />
        <Route path="/Bpp" element={<Bpp />} />
        <Route path="/User/:id" element={<User />} />
      </Route>
    </Routes>
  </BrowserRouter>
);

useMatch(匹配当前路由)

匹配当前路由

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建路由

createBrowserRouter 创建路由对象

import React from "react";
import ReactDOM from "react-dom/client";
import {
  createBrowserRouter, //创建
  RouterProvider, 
} from "react-router-dom";
import Root from "./router/root";
import "./index.css";
//创建路由
const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
  },
]);
ReactDOM.createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />//配置使用对象

);

路由失败

当我们路由失败我们可以设置一个自己的错误页组件

想设置在根路径里用errorElement

useRouteErrot() 既然报错就可以看报错信息

想用的方法如下: 13行

import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Root from "./router/root";
import "./index.css";
import App from "./module/App";
import Errorpage from "./Errorpage";
//创建路由
const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    errorElement: <Errorpage />, //在根路由下我们点击失败.写入errorElement里面就可以了
    children: [
      {
        path: "/app",
        element: <App />,
      },
    ],
  },
]);
ReactDOM.createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />
);

子路由写在chidern里面就行

我们要跳转就先需要用Oulat站位

跳转两种:

const navigate = useNavigate();

理解loader和action

权限路由-demo

0-1 Router

路由导航传参

searchParams 传参

  const [params] = useSearchParams();
	想获取id   let id = params.get("id");
其他数据同理

params(参数)

useParams()

{
    path: "/article/:id",
    element: <Article />,
  },
    
    
    //
    const idParams = useParams();
  let GetId = idParams.id;
  console.log(GetId);

使用这个一定要去用占位符

const Router = createBrowserRouter([
  {
    path: "/login",
    element: <Login />,
  },
  {
    path: "/article/:id/:name",
    element: <Article />,
  },
]);

export default Router;

嵌套路由

antD

antD-mobile主题定制

ΩΩΩΩ

axios

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失落在大海的人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值