《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} 严格模式 $的话是会执行 两次
答: 因为初始化会直接渲染一次
- 在组件首次渲染时,
useEffect
会执行一次,因为这是组件的初始化过程。 - 当你点击
"点击+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节点标签的,
- 保存可变状态:
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
useEffect
、useInsertionEffect
和 useLayoutEffect
这三个 React Hooks 都是用于处理副作用的,但它们之间有一些重要的区别:
- 执行时机:
useEffect
: 在浏览器完成页面渲染后执行,即在 DOM 更新之后。useInsertionEffect
: 在 DOM 更新之前执行,优先级高于useEffect
。useLayoutEffect
: 在 DOM 更新后,但在浏览器绘制之前执行,优先级高于useEffect
。
- 用途:
useEffect
: 常用于处理订阅、事件监听、数据获取等副作用。useInsertionEffect
: 适用于在 DOM 插入时修改样式的场景,如注入全局样式。useLayoutEffect
: 适用于同步测量和操作 DOM 的场景,如设置页面滚动位置。
- 性能影响:
useEffect
: 由于在浏览器绘制后执行,对性能影响较小。useInsertionEffect
: 由于在 DOM 更新之前执行,可能会造成一些视觉上的闪烁。useLayoutEffect
: 由于在浏览器绘制之前执行,可能会阻塞页面渲染,对性能影响较大。
- 错误处理:
useEffect
: 如果 effect 函数返回一个清理函数,React 会在组件卸载时调用它。useInsertionEffect
: 目前没有官方文档介绍如何处理错误和清理。useLayoutEffect
: 与useEffect
类似,可以返回一个清理函数。
总的来说,useEffect
是最常用的副作用钩子,适用于大多数需要处理副作用的场景。useInsertionEffect
主要用于样式注入,而 useLayoutEffect
则主要用于同步测量和操作 DOM 的场景,需要谨慎使用以避免阻塞页面渲染。
Redux
使用步骤:
- 定义一个 reducer 函数(根据当前想要做的修改返回一个新的状态)
- 使用createStore方法传入 reducer函数 生成一个store实例对象
- 使用store实例的 subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知)
- 使用store实例的dispatch方法提交action对象 触发数据变化(告诉reducer你想怎么改数据)
- 使用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
包。请执行以下步骤:
- 安装
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主题定制
ΩΩΩΩ