创建项目
在终端输入 npx create-react-app react-basic 切换到当前项目下,输入npm start
看到这个界面就表示创建成功啦!
认识目录
1.package.json
项目依赖的核心包:
react/react-dom最最核心
可执行命令:
start是启动的命令
build是打包的命令
2.src
index.js
//项目的入口,从这里开始运行
//react必要的两个核心包
import React from 'react';
import ReactDOM from 'react-dom/client';
//导入项目的根组件
import App from './App';
//吧App根组件渲染到id为root的DOM节点上
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
);
app.js
//项目的根组件
//app被引入到index.js ,被index的一些核心代码渲染到public/index.html里面叫做root到div上去
function App() {
return (
<div className="App">
this is app
</div>
);
}
export default App;
了解JSX
概念
JSX是javascript和XML(html)的缩写,表示在js代码中编写html模板结构,它是react中编写UI模板的方式。 优势:1.html的声明式子模板写法。2.js的可编程能力 JSX不是标准的js语法是js的扩展浏览器本身不能识别,需要通过解析工具做解析之后才能在浏览器中运行
JSX中使用JS表达式
在JSX中可以通过 大括号语法{}识别 JavaScript中的表达式,比如常见的变量、函数调用、方法调用等等
- 使用引号传递字符串
- 使用JavaScript变量
- 函数调用和方法调用
- 使用JavaScript对象
//项目的根组件
//app被引入到index.js ,被index的一些核心代码渲染到public/index.html里面叫做root到div上去
const name = "Katrina";
const getName = () => {
return "Jack";
};
function App() {
return (
<div className="App">
this is app
{/* 使用引号传递字符串 */}
{"halo!"}
{/* 识别js变量 */}
{name}
{/* 函数调用 */}
{getName()}
{/* 方法调用 */}
{new Date().getDate()}
{/* 使用js对象 */}
<div style={{ color: "red" }}>this is ...</div>
</div>
);
}
export default App;
JSX中实现列表渲染
语法: 在ISX中可以使用原生JS中的map方法遍历渲染列表
const list = [
{ id: 1, name: "Katrina" },
{ id: 2, name: "Chammy" },
{ id: 3, name: "Winnie" },
];
function App() {
return (
<div className="App">
<ul>{list.map(item => <li key={item.id}>{item.id}:{item.name}</li>)}</ul>
</div>
);
}
{/map 循环哪个结构 return结构/} {/* 注意事项:加上一个独一无二的key 字符串或者number id */} key的作用:React框架内部使用 提升更新性能的
JSX实现条件渲染
语法:在React中,可以通过逻辑与运算符&&、三元表达式(?:)实现基础的条件染
const isLog = false;
function App() {
return (
<div className="App">
{/* 逻辑与 */}
{isLog && <span>this is span</span>}
{isLog ? <span>jack</span> : <span>loading...</span>}
</div>
);
}
JSX中实现复杂条件渲染
需求:列表中需要根据文章状态适配三种情况,单图,三图,和无图三种模式解决方案:自定义函数 +if判断语句
const articleType = 1; //0 1 3
//定义核心函数
function getArticleTem() {
if (articleType === 0) {
return <div>我是无图文章</div>;
} else if (articleType === 1) {
return <div>我是单图文章</div>;
} else {
return <div>我是三图文章</div>;
}
}
function App() {
return (
<div className="App">
{/* 调用函数 */}
{getArticleTem()}
</div>
);
}
React基础事件绑定
语法:on+事件名称 ={事件处理程序},整体上遵循驼峰命名法
可以拿到事件参数e
const click = (e) => {
alert("halo啊!");
//事件参数e
console.log(e);
};
function App() {
return (
<div className="App">
<button onClick={click}></button>
</div>
);
}
传递自定义参数
语法:事件绑定的位置改造成箭头函数的写法,在执行实际处理函数的时候传递实参
const click = (name) => {
alert(`${name},halo啊!`);
};
function App() {
return (
<div className="App">
<button onClick={() => click("Katrina")}></button>
</div>
);
}
注意:不能直接写函数调用,这里事件绑定需要一个函数引用
既要又要~(同时传递事件对象和自定义参数)
语法:在事件绑定的位置传递事件实参e和自定义参数,click函数中声明形参,注意顺序对应
const click = (name, e) => {
alert(`${name},halo啊!`);
console.log(e);
};
function App() {
return (
<div className="App">
<button onClick={(e) => click("Katrina", e)}></button>
</div>
);
}
React组件基础使用
React中组件:首字母大写的函数(可以是箭头函数)
内部存放了组件的逻辑和视图U,渲染组件只需要把组件当成标签书写即可
//定义组件
function Button() {
// 业务逻辑组件逻辑
return <button>click me!!</button>;
}
function App() {
return (
<div className="App">
{/* 使用组件(渲染组件) */}
{/* 自闭标签 */}
<Button />
{/* 成对标签 */}
<Button></Button>
</div>
);
}
useState
基础使用
useState 是一个React Hook(函数),它允许我们向组件添加一个状态变量,从而控制影响组件的渲染结果
本质:和普通JS变量不同的是,状态变量一旦发生变化 组件的视图UI也会跟着变化(数据驱动视图)
- useState是一个函数,返回值是一个数组
- 数组中的第一个参数是状态变量,第二个参数是set函数(用来修改状态变量)
- useState的参数将作为count的初始值
import { useState } from "react";
function App() {
//useState实现一个计数器按钮
// count 状态变量
// setCount 修改状态变量的方法
const [count, setCount] = useState(0);
const clickBtn = () => {
//setCount作用:
// 1. 传入的新增修改count
// 2. 重新使用新的count渲染UI
setCount(count + 1);
};
return (
<div className="App">
<button onClick={clickBtn}>{count}</button>
</div>
);
}
修改状态的规则
在React中,状态被认为是只读的,我们应该始终替换它而不是修改它,直接修改状态不能引发视图更新
const [count, setCount] = useState(0);
const clickBtn = () => {
//直接修改 无法引发视图更新
count++;
};
传过来一个新值之后,这个方法会在内部尝试用这个新值替换旧值,引发视图更新
const [count, setCount] = useState(0);
const clickBtn = () => {
// 传过来一个新值之后,这个方法会在内部尝试用这个新值替换旧值,引发视图更新
setCount(count + 1);
};
复杂类型的修改——修改对象状态
规则:对于对象类型的状态变量,应该始终传给set方法一个全新的对象来进行修改
直接修改原对象,不引发视图变化
const [from, setFrom] = useState({
name: "jack",
});
const clickBtn = () => {
// 直接修改原对象,不引发视图变化
from.name = "Katrina";
};
传入全新对象:
import { useState } from "react";
function App() {
const [from, setFrom] = useState({
name: "jack",
});
const clickBtn = () => {
setFrom({
...from,
name: "Katrina",
});
};
return (
<div className="App">
<button onClick={clickBtn}>{from.name}</button>
</div>
);
}
基础样式控制
组件基础样式方案
React组件基础的样式控制有俩种方式
- 行内样式(不推荐)
<span style={{ color: "red", fontSize: "50px" }}>this is span</span>
//或
const style = { color: "red", fontSize: "50px" };
function App() {
return (
<div className="App">
{/* 行内样式控制 */}
<span style={style}>this is span</span>
</div>
);
}
- class类名控制 index.css中:
.foo {
color: "red";
font-size: "50px";
}
App.js中:
import "./index.css";
function App() {
return (
<div className="App">
<span className="foo">this is span</span>
</div>
);
}
案例(wei)
classnames优化类名控制(wei)
classnames是一个简单的JS库,可以非常方便的通过条件动态控制class类名的显示
现在的问题:字符串的拼接方式不够直观,也容易出错
受控表单绑定(类型vue的v-model)
概念:使用React组件的状态(useState)控制表单的状态
- 准备一个React状态值
- 通过value属性绑定状态,通过onChange属性绑定状态同步的函数 通过e拿到输入的最新数据
React中获取DOM(useRef)
在 React 组件中获取/操作 DOM,需要使用 useRef 钩子函数,分为两步:
- 使用useRef创建ref对象,并与JSX绑定 const inputRef = useRef(null)
- 在DOM可用时,通过inputRef.current拿到 DOM 对象 console.log(inputRef.current) 渲染完毕之后dom生成之后才可用
import { useRef } from "react";
function App() {
const inputRef = useRef(null);
const showDom = () => {
console.log(inputRef.current);
};
return (
<div>
<input type="text" ref={inputRef}></input>
<button onClick={showDom}>获取dom</button>
</div>
);
}
组件通信
父传子
基础实现
props说明
chilren说明
子传父
使用状态提升实现兄弟组件通信
使用context机制跨层传递数据
useEffect
概念理解和基础使用
useEfect是一个React Hook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作,比如发送AJAX请求,更改DOM等等
说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于“只由渲染引起的操作”
useEffect 的基础使用
需求:在组件渲染完毕之后,立刻从服务端获取频道列表数据并显示到页面中语法:
useEffect(() => {},[])
参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作参数2是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次
import { useEffect, useState } from "react";
const URL = "http://geek.itheima.net/v1_0/channels";
function App() {
//创建一个状态数据
const [list, setList] = useState([]);
useEffect(() => {
//额外的操作 获取...
async function getList() {
const res = await fetch(URL);
const jsonRes = await res.json();
console.log(jsonRes);
setList(jsonRes.data.channels);
}
getList();
}, []);
return (
<div>
{list.map((item) => (
<li>{item.name}</li>
))}
</div>
);
}
不同依赖项说明
useEffect副作用函致的执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现
- 没有依赖项 组件初始渲染 + 组件更新时执行
import { useEffect, useState } from "react";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
//1.没有依赖项 初始 + 组件更新
console.log("副作用函数执行");
});
return (
<div>
<button onClick={() => setCount(count + 1)}>+{count}</button>
</div>
);
}
2. 空数组依赖
只在初始渲染时执行一次
import { useEffect, useState } from "react";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
//2. 空数组依赖
console.log("副作用函数执行");
}, []);
return (
<div>
<button onClick={() => setCount(count + 1)}>+{count}</button>
</div>
);
}
- 添加特定依赖项 组件初始渲染*特性依魏项交化时执行 (跟第一个的区别就是,这个只会由count引发更新,而第一个没有依赖项,不管是count 还是 name,只要让数据重新发生渲染,就会不断执行)
import { useEffect, useState } from "react";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
//3. 传入特定依赖项目 初始 + 依赖项变化时执行
console.log("副作用函数执行");
}, [count]);
return (
<div>
<button onClick={() => setCount(count + 1)}>+{count}</button>
</div>
);
}
清除副作用
在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,我们想在组件卸载时把这个定时器再清理掉,这个过程就是清理副作用
//需求:在Son组件渲染时开启一个定时器,卸载时清除这个定时器
import { useEffect, useState } from "react";
function Son() {
//1. 渲染时开启一个定时器
useEffect(() => {
const timer = setInterval(() => {
console.log("定时器执行中");
}, 1000);
//在里面return一个清除定时器,不然组件卸载后还是会执行
return () => {
//清除副作用(组件卸载时)
clearInterval(timer);
};
}, []);
return <div>this is son</div>;
}
function App() {
const [show, setShow] = useState(true);
return (
<div>
{show && <Son />}
<button onClick={() => setShow(false)}></button>
</div>
);
}
export default App;
说明:清除副作用的函数最常见的执行时机是在组件卸载时自动执行
自定义Hook实现
//封装自定义hook通用思路 //1.声明一个以use打头的函数 // 2.在函数体内封装可复用的逻辑(只要是可复用的逻辑) // 3.把组件中用到的状态或者回调return出去(以对象或者数组) // 4.在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用
import { useState } from "react";
function useToggle() {
//可复用代码
const [value, setValue] = useState(true);
const toggle = () => setValue(!value);
//哪些状态和回调函数需要在其他组件中使用 return
return {
value,
toggle,
};
}
function App() {
//问题:布尔切换的逻辑 当前组件耦合在一起的 不方便复用
//解决思路: 自定义hook
// const [value, setValue] = useState(true);
// const toggle = () => setValue(!value);
const { value, toggle } = useToggle();
return (
<div>
{value && <div>this is div</div>}
<button onClick={toggle}>toggle</button>
</div>
);
}
ReactHooks使用规则说明
使用规则
- 只能在组件中或者其他自定义Hook函数中调用
- 只能在组件的顶层调用,不能嵌套在if、for、其他函数中
Redux
什么是Redux
Redux是React最常用的集中状态管理工具,类似于Vue中的Pinia(Vuex),可以独立于框架运行作用:通过集中管理的方式管理应用的状态
使用步骤:
- 定义一个 reducer 函数(根据当前想要做的修改返回一个新的状态)
- 使用createStore方法传入reducer函数 生成一个store实例对象
- 使用store实例的 subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知)
- 使用store实例的dispatch方法提交action对象 触发数据变化(告诉reducer你想怎么改数据)
- 使用store实例的 getState方法 获取最新的状态数据更新到视图中
Redux管理数据流程梳理
为了职责清晰,数据流向明确,Redux把整个数据修改的流程分成了三个核心概念,分别是:state、action和reducer
- state-一个对象 存放着我们管理的数据状态
- action-一个对象 用来描述你想怎么改数据
- reducer-一个函数 根据action的描述生成一个新的state
//还没运行起来:
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>
<!-- <script src="https://unpkg.com/redux@latest/dist/redux.js"></script> -->
<script src="/node_modules/redux/dist/cjs/redux.cjs"></script>
<script>
// import Redux from "/node_modules/redux/dist/redux";
//1.
//state:管理的数据初始状态
//action:对象type标记当前想要做什么样的修改
function reducer(state = { count: 0 }, action) {
//数据不可变:基于原始状态生成一个新的状态
if (action.type === "INCREMENT") {
return { count: state.count + 1 };
}
if (action.type === "DECREMENT") {
return { count: state.count - 1 };
}
return state;
}
//2. 使用reducer函数生成store实例
const store = Redux.createStore(reducer);
//3.通过store实例的subscribe订阅数据变化
//回调可以在每次state发生变化的时候自动执行
store.subscribe(() => {
console.log("store变化了", store.getState());
document.getElementById("count").innerText = store.getState().count;
});
//4. 通过store实例的dispatch函数提交action更改状态
const inBtn = document.getElementById("increment");
inBtn.addEventListener("click", () => {
//增
store.dispatch({
type: "INCREMENT",
});
});
const dBtn = document.getElementById("decrement");
dBtn.addEventListener("click", () => {
//减
store.dispatch({
type: "DECREMENT",
});
});
//5.通过store实例的getState方法获取最新状态更新到视图中
</script>
Redux与React
环境准备
- 使用 CRA 快速创建 React 项目 npx create-react-app react-redux
- 安装配套工具 npm i @reduxjs/toolkit react-redux
- 启动项目 npm run start
安装后创建目录
实现count
整体路径熟悉
使用React Toolkit创建counterStore
在module/counterStore:
import { createSlice } from "@reduxjs/toolkit";
const counterStore = createSlice({
name: "counter",
//初始化state
initialState: {
count: 0,
},
//修改状态的方法 同步方法 支持直接修改
reducers: {
inscrement(state) {
state.count++;
},
decrement(state) {
state.count--;
},
},
});
//解构出来actionCreater函数
const { inscrement, decrement } = counterStore.actions;
//获取reducer
const reducer = counterStore.reducer;
//以按需导出的方式导出actionCreater
export { inscrement, decrement };
//以默认导出的方式导出reducer
export default reducer;
在index.js引入:
import { configureStore } from "@reduxjs/toolkit";
//导入子模块reducer
import counterReducer from "./modules/counterStore";
const store = configureStore({
reducer: {
conter: counterReducer,
},
});
export default store;
为React注入store
react-redux负责把Redux和React连接起来,内置 Provider组件 通过 store 参数把创建好的store实例注入到应用中链接正式建立 在src/index.js中:
//在这里导入
import store from "./store";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
{/* 为React注入store */}
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
React组件使用store中的数据
在React组件中使用store中的数据,需要用到一个钩子函数-useSelector,它的作用是把store中的数据映射到组件中,使用样例如下:
在App.js中:
import { useSelector } from "react-redux";
function App() {
const { count } = useSelector((state) => state.counter);
return <div className="App">{count}</div>;
}
export default App;
React组件修改store中的数据
React组件中修改store中的数据需要借助另外一个hook函数-useDispatch,它的作用是生成提交action对象的dispatch函数,使用样例如下:
在App.js里面导入useDispatch,导入之前actionCreater里面的,,,,
import { useDispatch, useSelector } from "react-redux";
//导入actionCreater
import { inscrement, decrement } from "./store/modules/counterStore";
function App() {
const { count } = useSelector((state) => state.counter);
const dispatch = useDispatch();
return (
<div className="App">
<button onClick={() => dispatch(decrement())}>-</button>
{count}
<button onClick={() => dispatch(inscrement())}>+</button>
</div>
);
}
export default App;
总结
- 组件中使用哪个hook函数获取store中的数据: useSelector
- 组件中使用哪个hook函数获取dispatch方法: useDispatch
- 如何得到要提交action对象: 执行store模块中导出的actionCreater方法
提交aciton传参
提交action传参实现需求
在reducers的同步修改方法中添加action对象参数,在调用actionCreater的时候传递参数,参数会被传递到action对象payload属性上
在counterStore.js里面的reducer里加个addToNum(这时下面的导出也要记得写)
const counterStore = createSlice({
name: "counter",
//初始化state
initialState: {
count: 0,
},
//修改状态的方法 同步方法 支持直接修改
reducers: {
inscrement(state) {
state.count++;
},
decrement(state) {
state.count--;
},
addToNum(state, action) {
state.count = action.payload;
},
},
});
在App.js里再引入addToNum,跟之前不同的是,在调用时传入了参数,会自动传递到action.payload交给count。
//导入actionCreater
import { inscrement, decrement, addToNum } from "./store/modules/counterStore";
function App() {
const { count } = useSelector((state) => state.counter);
const dispatch = useDispatch();
return (
<div className="App">
<button onClick={() => dispatch(decrement())}>-</button>
{count}
<button onClick={() => dispatch(inscrement())}>+</button>
//这里传入参数:
<button onClick={() => dispatch(addToNum(10))}>add To 10</button>
<button onClick={() => dispatch(addToNum(20))}>add To 20</button>
</div>
);
}
export default App;
异步操作样板代码(wei)
- 创建store的写法保持不变,配置好同步修改状态的方法
- 单独封装一个函数,在函数内部return一个新函数,在新函数中 2.1 封装异步请求获取数据 2.2 调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交3.组件中dispatch的写法保持不变 14:48
路由
什么是前端路由:
一个path对应一个组件component,当我们在浏览器中访问一个path时,path对应的组件会被渲染
创建路由开发环境
1.创建项目并安装所有依赖 npx create-react-app react-router-pro 2.安装最新的 ReactRouter包npm i react-router-dom 3.启动项目 npm run start
快速开始
需求:创建一个可以切换登录页和文章页的路由系统
在src/index.js中:
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
//导入
import { createBrowserRouter, RouterProvider } from "react-router-dom";
//1. 创建router实例对象并且配置路由对应关系
const router = createBrowserRouter([
{
path: "/login",
element: <div>我是登录页</div>,
},
{
path: "/article",
element: <div>我是文章页</div>,
},
]);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
{/* 2.路由绑定 */}
<RouterProvider router={router}></RouterProvider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
抽象路由模块
创建src/page文件夹
在page里面创建子文件夹Article、Login,在它们的子文件index.js里面写组件 如src/page/Article/index.js:
const Article = () => {
return <div>我是登录页</div>;
};
export default Article;
创建src/router文件夹
在src/router/index.js里引入组件 配置好对path与组件对应关系,导出实例对象
import Login from "../page/Login";
import Article from "../page/Article";
import { createBrowserRouter } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/login",
element: <Login />,
},
{
path: "/article",
element: <Article />,
},
]);
export default router;
应用入口文件渲染
在src/index.js中:
//导入
import { RouterProvider } from "react-router-dom";
//1.导入路由router
import router from "./router";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
{/* 2.路由绑定 */}
<RouterProvider router={router}></RouterProvider>
</React.StrictMode>
);
路由导航
什么是路由导航
路由系统中的多个路由之间需要进行路由跳转,并且在跳转的同时有可能需要传递参数进行通信
声明式导航
声明式导航是指通过在模版中通过<Link/>组件描述出要跳转到哪里去,比如后台管理系统的左侧菜单通常使用这种方式进行
语法说明:通过给组件的to属性指定要跳转到路由path,组件会被渲染为浏览器支持的a链接,如果需要传参直接通过字符串拼接的方式拼接参数即可 eg:在src/page/Login/index.js中: (注意Link是首字母大写!)
import { Link } from "react-router-dom";
const Login = () => {
return (
<div>
我是登录页
<Link to="/article">跳转到文章页</Link>
</div>
);
};
export default Login;
编程式导航
通过useNavigate钩子得到导航方法,然后通过调用方法以命令式的形式进行路由跳转,比如想在登录请求完毕之后跳转就可以选择这种方式,更加灵活
语法说明:通过调用navigate方法传入地址path实现跳转
在src/page/Login/index.js中:
import { Link, useNavigate } from "react-router-dom";
const Login = () => {
const navigate = useNavigate();
return (
<div>
我是登录页
{/* 声明式的写法:(常见在菜单里面根据模板进行绑定) */}
<Link to="/article">跳转到文章页</Link>
{/* 命令式的写法:(通常会写到js逻辑代码里面,比如登录后要跳转) */}
<button onClick={() => navigate("/article")}>跳转到文章页</button>
</div>
);
};
export default Login;
路由导航传参
searchParmas传参
自己总结就是:
- 在要传的地址后面加?去传,不同参数用&隔开
- 在接收的地方导入useSearchParams,通过params拿到。
const [params] = useSearchParams();
const id = params.get("id");
login/index.js中:
<button onClick={() => navigate("/article?id=100&name=jack")}>searchParams
在Article/index.js中接收参数:
import { useSearchParams } from "react-router-dom";
const Article = () => {
const [params] = useSearchParams();
const id = params.get("id");
const name = params.get("name");
return (
<div>
我是文章页{id}-{name}
</div>
);
};
export default Article;
params传参
- 传参时不需要加问号,直接拼接(注意拼接后要回到路由的root的位置加上一个占位符)(要是传多个参数就再加几个相应的占位符)
- 接收的部分换了一个构造函数,变成了useParams,调用这个方法就可以得到一个对象,通过点语法拿到它里面的参数的值
在Login/index.js中:
<button onClick={() => navigate("/article/1001")}>Params传参</button>
在路由router/index.js中: (加占位符:id)
{
path: "/article/:id",
element: <Article />,
},
接收方Article/index.js:
// import { useSearchParams } from "react-router-dom";
import { useParams } from "react-router-dom";
const Article = () => {
// const [params] = useSearchParams();
const id = useParams().id;
// const id = params.get("id");
// const name = params.get("name");
return <div>我是文章页{id}</div>;
};
export default Article;
嵌套路由配置
在一级路由中又内嵌了其他路由,嵌套至一级路由内的路由又称作二级路由 实现步骤:
- 使用 children 属性配置路由嵌套关系
- 使用
<Outlet/>组件配置二级路由渲染位置
默认二级路由
场景和配置方式
当访问的是一级路由时,默认的二级路由组件可以得到渲染,只需要在二级路由的地址去掉path,设置index属性为ture。
设置为默认二级路由,一级路由访问的时候,它也能得到渲染
404路由
场景:当浏览器输入url的路径在整个路由配置中都找不到对应的path,为了用户体验,可以使用404兜底组件进行渲染
实现步骤:
- 准备一个NotFound组件
- 在路由表数组的末尾,以 * 号作为路由path配置路由
两种路由模式
......
useReducerngji配套gt攻击配套gtjingji配套gt攻击配套ao配套gt攻击配套gtjingji配套gt攻击配套gtji配套gt攻击配套gtjingji配套gt攻击配套ao配套gt攻击配套gtjingji配套gt攻击配套gtoi
useCallback
作用:
[04:38](https://www.bilibili.com/video/BV1ZB4y1Z7o8?p=137 "Day10-05.useCallback")