一、事件绑定
<button onClick = ()=>handleClick()> click me </button>
二、组件是什么
- 可以相互嵌套、可以多次复用。
- 在React 中,一个组件就是首字母大写的函数,渲染只需要把组件当成标签书写即可:
// 定义组件
function Button() {
return <button> click me! </button>
}
// 或者
const Button = ()=> {
return <button> click me! </button>
}
// 使用组件
function App() {
return(
<div className=”app”>
<Button/>
// 或者
<Button></Button>
</div>
)
}
三、useState 基础使用
useState是一个React Hook(函数),它允许我们向组件添加一个状态变量,从而控制影响组件的渲染结果。
const [count,setCount] = useState(0);
- useState 是一个函数,返回值是一个数组;
- 数组中的第一个参数是状态变量,第二个参数是set函数用来修改状态变量;
- useState的参数将作为count的初始值;
// 示例代码:
import { useState } from ‘react’;
function App() {
// 调用useState 添加一个状态变量
const [ count, setCount] = useState;
// 点击回调
const handleClick = ()=> {
setCount(count+1); // 用传入的新值修改count
}
return(
<div>
<button onClick={handleClick}>{count}</button>
</div>
)
}
export default App
修改状态的规则:
- 状态不可变
在React中,状态认为是可读的,应该始终替换它而不是修改它,直接修改状态不能引发视图更新。
直接修改:count++ 正确做法: setCount(count+1)
- 修改对象状态
规则:对于对象类型的状态变量,应该始终传给set方法一个全新的对象来进行修改。
const [form,setForm] = useState({
name:’jack’
})
const handleChangeName = ()=>{
setForm({
...from,
name:’john’,
})
}
四、组件基础样式
- 行内样式(不推荐)
<div style={color:’red’}> this is div </div>
- class类名控制
import ‘./index.css ’;
<div className = “foo”> this is div </div>
- classnames优化类名控制
classnames是一个简单的JS库,可以方便通过条件动态控制class类名的显示:如下
className={classNames(‘nav-item ’,{active:type===item.type}});
五、受控表单绑定
概念:使用react组件的状态(useState)控制表单的状态
- 准备一个react状态值
const [value,setValue] = useState(‘’);
- 通过value属性绑定状态,通过onChange属性绑定状态同步的函数
<input type=”text”value=[value] onChange=(e)=>setValue(e.target.value)};
六、React 中获取DOM元素
在react组件中获取/操作dom,需要使用useRef 钩子函数,分为两步:
- 使用useRef 创建ref对象,并与JSX绑定
import { useRef } from “react”;
const inputRef = useRef(null);
<input type=”text” ref={inputRef} />
- 在dom可用时(渲染完成),通过inputRef.current拿到dom对象
console.log(inputRef.current);
七、组件通信
概念:组件通信就是数组之间的数据传递
父传子:实现步骤
- 父组件传递数据-在子组件标签上绑定属性
- 子组件接收数据-子组件通过props参数接收数据
// 示例代码:子组件
function Son(props){
console.log(props); // {name:’父组件传递的数据’}
return <div>this is son,{props.name}</div>
}
// 父组件
function App(){
const name = ‘this is app name’;
return (
<div>
<Son name={name} />
</div>
)
}
export default App
注意:props可传递任意的数据,子组件只能读取props的数据,不能直接进行修改,父组件的数据只能父组件修改。
特性的props children 场景:当我们把内容嵌套到子组件标签中时,父组件会自动在名为children 的props中接收该内容。
<Son>
<span>this is span</span>
</Son>
// 控制台:
props
children:<span/>
New entry: ””
子传父:实现步骤
核心思路:在子组件中调用父组件的函数并传递参数*
// 示例代码:父组件
function App(){
const getMsg = (msg)=>{
console.log(msg)
};
return (
<div>
<Son onGetMsg={getMsg } />
</div>
)
}
// 子组件
function Son({onGetMsg}){
const sonMsg = “this is son msg”;
return (
<div>
<button onClick={()=>onGetMsg(sonMsg )}> send </button >
</div>
)
}
使用状态提升实现兄弟组件通信 实现思路:借助“状态提升”机制,通过父组件进行兄弟组件的数据传递。
A、B 是兄弟关系,C是父组件: A -> C -> B 或者 B -> C -> A
使用Context机制跨层级组件通信
App(顶层组件) -A(中间组件) - B(底层组件)
实现步骤:App - > B
1. 使用createContext方法创建一个上下文对象Ctx;
2. 在顶层组件(App)中通过Ctx.Provider组件提供数据;
在底层组件(B)中通过useContext钩子函数获取数据;
八、useEffect的概念理解
useEffect是一个React Hook函数,用于在React 组件中创建不是由事件引起而是由渲染本身引起的操作,比如发送AJAX请求,更改ODM等等。
说明:上面组件没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于“只由渲染引起的操作”。
语法: useEffect(()=>{ },[]);
参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作。
参数2是一个数组(可选参),在数组放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次。
useEffect - 依赖项参数说明
根据传入的依赖项不同, 会有不同的执行表现:
1. 没有依赖项:组件初始渲染+组件更新时执行
2. 空数组依赖:只有初始渲染时执行一次
3. 添加特定依赖项:组件初始渲染+特性依赖项变化时执行
useEffect - 清除副作用
在useEffect中编写的由渲染本身引起的对接组件外的操作,社区也经常把它叫做副作用操作, 比如在useEffect中开启了一个定时器,在组件卸载时把这个定时器清除掉,这个过程就是清理副作用。
// 示例代码:
useEffect(()=>{
// 实现副作用操作逻辑
const timer = setInterval(()=>{
Console.log(‘定时器执行中...’);
},1000)
return()=>{
// 清除副作用
clearInterval(timer);
}
},[]);
说明:清除副作用的函数最常见的执行时机是在组件卸载时自动执行
九、自定义Hook函数
概念:自定义Hook是以use打头的函数,通过自定义Hook函数可以用来实现逻辑的封装和复用。
封装自定义Hook通用思路:
- 声明一个以use打头的函数
- 在函数内封装可复用的逻辑(只要是可复用的逻辑)
- 把组件中用到的状态或者回调return出去
- 在那个组件中要用到这个函数,就执行这个函数,解构出来状态和回调进行使用
// 示例代码:
import {useState} from ‘react’;
// 封装函数
function useToggle(){
// 可复用的逻辑代码
const {value,setValue}= useState(true);
const toggle=()=>setValue(!value);
// 把要使用的状态和回调函数return 出去
return {
value,toggle
}
}
// 使用封装的函数
function App(){
const {value,toggle}= useToggle();
return (
<div>
{value && <div>this is div </div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
export default App
React Hooks使用规则****
- 只能在组件中或者其他自定义Hook函数中调用
- 只能在组件的顶层调用,不能嵌套在if、for、其他函数中
十、什么是Redux
Redux是React最常用的集中状态管理工具,类似于Vue中的pinia(Vuex),可以独立于框架运行。 Redux快速体验:不和任何框架绑定,不使用任何构建工具,使用纯Redux实现计数器。如: - 0 +
使用步骤:
1. 定义一个reducer函数(根据当前想要做的修改返回一个新的状态);
function reducer(state={count:0},action) { }
2. 使用createStore方法传入reducer函数 生成一个store实例对象;
const store = Redux.createStore(reducer);
3. 使用store实例的subscribe方法订阅数据变化(数据变化,可以得到通知);
store.subscribe(()=>{
console.log( ‘ state数据变化了 ’ ,store.getState());
})
4. 使用store实例的dispatch方法提交action对象触发数据变化(告诉 reducer你想怎么改数据);
store.dispatch({ type: ’ ADD ’ })
5. 使用store实例的getState方法获取最新的状态更新到视图中;
store.getState()
Redux管理数据流程梳理:
Redux把整个数据修改的流程分成了三个核心概念,分别是:state、action和reducer。
- state- 一个对象,存放着我们管理数据的状态;
- action- 一个对象,来描述你想怎么改数据;
- reducer- 一个函数,根据action的描述生成一个新的state;
十一、Redux和React-环境准备
在React中使用Redux,官方要求安装两个其他插件-Redux Toolkit和react-redux
-
Redux Toolkit(RTK)-官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式;
-
简化store的配置方式
-
内置immer支持可变式状态修改
-
内置thunk更好的异步创建
-
-
react-redux - 用来链接Redux和React组件的中间件
Redux -> 获取状态 -> React组件
React组件 -> 更新状态 -> Redux
配置基础环境
1.使用CRA快速创建React项目
npx create-react-app react-redux
2.安装配置工具
npm i @reduxjs/toolkit react-redux
3.启动项目
npm run start
十二、Redux与React-实现counter
store目录结构设计
- 通常集中状态管理的部分都会创建一个单独的‘store’目录;
- 应用通常会分多个store子模块 , 所以创建一个‘modules’目录,在内部编写业务分类的子store;
- store中的入口文件index.js的作用是组合modules’中所有的子模块,并导出store;
1. 首先创建一个‘modules’目录子模块
// 新增 ./modules/counterStore.js文件 - 示例代码:
import { createSlice }from ‘@Reduxjs/toolkit ’;
const counterStore = createSlice ({
name: ‘counter’,
// 数据状态 state
initialState:{
count: []
}
}),
reducers: {
// 同步修改方法
addToNum(state, action) {
state.count = action.payload
}
}
// 解构actionCreater函数
const { addToNum } = counterStore.actions
// 编写异步
const getCount = ()=>{
return async(dispatch)=>{
// 异步请求
const res = await axios.get(‘http/localhost:8888/ka’);
// 触发同步reducer
dispatch(setCounterList(addToNum(res.data)));
}
}
// 导出reducer
const reducer = counterStore.reducer
export default reducer
2. 使用Redux Toolkit创建counter
// store中的**入口文件index.js - 示例代码:
import { configureStore }from ‘@Reduxjs/toolkit ’;
import counterReducer from ‘./modules/counterStore’;
// 创建根store组合子模块
const store = configureStore({
reducer:{
counter: counterReducer
}
})
export default store
- 为React组件注入store
// react-redux负责把Redux和React组件链接起来,内置Provider组件,通过store参数把创建好的store实例注入到应用中,链接正式建立。
// index.js文件-示例代码:
import store from ‘./store ’;
import { Provider } from ‘react-redux’;
const root = ReactDOM.createRoot(document.getElementById(‘root’))
root.render(
<Provider store={store}>
<App />
</Provider>
)
4. React组件使用store中的数据
// 在React组件使用store中的数据,需要用到一个钩子函数 - useSelector,它的作用是把store中的数据映射到组件中,使用示例如下:
import { useSelector } from ‘react-redux’;
const { count } = useSelector(state=>state.counter);
5. React组件修改store中的数据
// React组件修改store中的数据需要借助另外一个**hook函数- useDispatch**,**它的作用是生成提交action对象的dispatch函数**,使用样例如下:
import { useDispatch } from ‘react-redux’;
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
十三、Redux与React-提交action传参
在reducers的同步修改方法中添加action对象参数,在调用actionCreater的时候传参,参数会被传递到action对象payload属性上。
- 在 ./store/modules/counterStore文件添加:
// 修改状态的方法,同步方法 支持直接修改
reducers:{
addToNum(state,action) {
State.count = action.payload;
}
}
export { addToNum };
- 在App.js文件:
import { addToNum }from ‘./store/modules/counterStore’;
<button onClick={()=>dispatch (addToNum(10))}> add to 10 </button>
十四、Redux与React-异步状态操作
- 创建store的写法保持不变,配置好同步修改状态的方法
- 单独封装一个函数,在函数内部return一个新函数,在新函数中
- 封装异步请求获取数据
- 调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch 提交
- 组件中dispatch 的写法保持不变
十五、Redux调试 - devtools
浏览器右上角三个点 -> 更多工具 -> 扩展程序 -> 安装Redux Devtools 3.0.19 -> 重启浏览器
十六、ReactRouter - 快速开始
1. 创建路由开发环境
使用路由我们还是采用CRA创建项目的方式进行基础环境配置
1.1创建React项目并安装所有依赖
npx create-react-app react-redux
npm i
1.2安装最新的ReactRouter包
npm i react-router-dom
1.3启动项目
npm run start
2. 配置基础路由步骤:
安装路由包 **react-router-dom**
准备两个**基础路由组件Layout和Login**
在router/index.js文件中引入组件进行路由配置,**导出router实例******
在**入口文件中渲染<RouterProvider />** ,传入router实例
3. ReactRouter - 抽象路由模块
// router/index.js文件下代码示例:
import Layout from ‘../page/Layout’
import Login from ‘../page/Login’
import { createBrowserRouter } from ‘react-router-dom’;
const router = createBrowserRouter([
{
path:‘/’,
element:<Layout />
},
{
path:‘/login’,
element:<Login />
},
])
export default router
// 入口文件index.js
import React from ‘react’
import ReactDOM from ‘react-dom/client’
import ‘./index.scss’
import { RouterProvider} from ‘react-router-dom’;
import router from ‘./router ’;
const root = ReactDOM.createRoot(document.getElementById(‘root’));
root.render(
<RouterProvider router ={router} />
)
4. 什么是路由导航
路由系统中多个路由之间需要进行路由跳转,并且在跳转的同时有可能需要传递参数进行通信。
4.1声明式导航: 通过在模板中 组件描述出要跳转的地方
<Link to=”/article”>文章</Link>
语法说明:通过组件的to属性指定要跳转到路由path,组件被渲染为浏览器支持的a链接,如果需要传参直接通过字符串拼接的方式拼接参数即可。
4.2编程式导航:通过‘ useNavigate ’钩子得到导航的方法,然后通过调用方法以命令式的形式进行路由跳转。
import { useNavigate } from ‘react-router-dom’;
const Login = ()=>{
const navigate = useNavigate();
return(
<div>登录页</div>
<button onClick={()=>navigate(‘/article’)}>跳转文章页</button>
)
}
export default Login
语法说明:通过调用navigate方法传入地址path实现跳转
4.3路由导航传参: 以编程式导航为例
import { useSearchParams, useParams } from ‘react-router-dom’;
searchParams传参:
传递参数:navigate(‘/article?id=100&name=jack’);
接收传参:const [params] = useSearchParams();
let id = params.get(‘id’); // 100
params传参:
对应路由配置需修改为path:‘/actilcle/:id/:name’,
传递参数:navigate(‘/article/id=1001/name=jack’’);
接收传参:const params = useParams();
let id = params.id; let name= params.name;
5. 嵌套路由
在一级路由又内嵌其他路由,这种关系就叫嵌套路由。
实现步骤:
1. 使用children属性匹配路由嵌套关系
2. 使用‘’组件配置二级路由渲染位置
// 代码示例: Layout 组件
import { useNavigate , Outlet } from ‘react-router-dom’;
const Layout = ()=>{
const navigate = useNavigate();
return(
<div>我是Layout </div>
// 二级路由出口
<Outlet />
<button onClick={()=>navigate(‘/login’)}>跳转登录页</button>
<button onClick={()=>navigate(‘/article’)}>跳转文章页</button>
)
}
export default Layout
// 路由配置
{
path:‘/’,
element:‘Layout’
children:[
{
path:‘/login’,
element:</Login>
},
{
path:‘/actilcle’,
element:</Actilcle>
}
]
}
3. ReactRouter - 默认二级路由
当我们访问一级路由时,默认的二级路由组件看可以得到渲染,只需要二级路由的位置去掉path,设置index属性为true
// 代码示例:
{
path:‘/’,
element:‘Layout’
children:[
{
index: true,
element:</Login>
},
{
path:‘/actilcle’,
element:</Actilcle>
}
]
}
4. ReactRouter - 404路由配置
场景:当浏览器输入url的路径在整个路由配置中找不到对应的path,为了用户体验,可以使用404兜底组件进行渲染。
404路由 - 实现步骤:
1. 准备一个NotFound组件
2. 在路由数组的末尾,以*号作为路由path配置路由
// 代码示例: NotFound组件
const NotFound = ()=>{
// 自定义模板
return <div> this is notFound </div>
}
Export default NotFound
// 路由配置
{
path:’*’,
element:’NotFound’
}
6. ReactRouter - 两种路由模式
history模式和hash模式,ReactRouter分别由createBrowserRouter和createHashRouter函数负责创建。
history :
url表现:url/login 底层原理:history对象+pushState事件 需要后端支持
hash :
url表现:url/#/login 底层原理:监听hashChange事件 不需要后端支持
十七、项目中配置别名路径 @:
1. 路径解析配置(webpack),把 @/ 解析为src/ (借助craco插件)
import Layout from ‘@/pages/Layout’;
配置步骤:
- 安装craco插件 npm i -D @craco/craco
- 项目根目录下创建配置文件craco.config
- 在craco.config配置文件中添加路径解析配置,如下:
const path = require(‘path’);
module.exports = {
webpack:{
alias:{
‘@’: path.resolve(_dirname,‘src’)
}
}
}
- 包文件中配置启动和打包命令:package.json
‘start’: ‘craco start’
‘build’: ‘craco build’
2. 路径联想配置(VScode) ,VScode在输入@/时,自动联想出对应的src/下的子级目录(需要在项目根目录中新增一个jsconfig.json文件)
配置步骤:
- 根目录下新增配置文件 - jsconfig.json
- 添加路径提示配置,代码如下:
{
“compilerOptions”:{
“baseUrl”: “/”,
“paths”: {
“@/*”:[
“src/*”
]
}
}
}
十八、json-server实现模拟数据Mock
json-server工具,它是一个node包,可以在不到30秒获得零编码的完整的Mock服务。
实现步骤:
- 项目中安装json-server工具:npm i -D json-server
- 准备一个json文件(在根目录新增server/data.json文件)
- 添加启动命令: “server”: “json-server ./server/data.json --port 8888”
- 访问接口进行测试: npm run server
十九、Ant Design Mobile 主题定制
定制方案****
1. 全局定制:整个应用范围内的组件都生效
// 实现方式:
:root:root {
--adm-color-primary:#a062d4
}
- 局部定制:只在某些元素内部的组件生效
// 实现方式:
.purple {
--adm-color-primary:#a062d4
}
二十、Ant Design 组件库
Ant Design 是由蚂蚁金服出品的社区使用最广的React PC端组件库,内置了常用的现成组件,可以帮助我们快速开发PC管理后台项目。
// 安装Ant Design到项目
npm install antd --save
//引入antd组件:
import { Button } from ‘antd’;
//使用:
<Button type= ‘text’>按钮<Button>
二十一、按照业务规范整理项目目录(重点是SRC目录)
- 文件夹 作用
- apis 接口
- assets 静态资源
- components 通用组件
- pages 页面级组件
- router 路由router
- store Redux状态
- utils 工具函数
二十二、使用gitee管理项目
目的:为了记录每次阶段性的功能,采用git管理我们的项目,方便复习
实现步骤:
- 在gitee上初始化一个空项目仓库
- 把远程仓库和本地仓库关联
- 提交代码到远程仓库
二十三、封装request请求模块
在整个项目中会发送很多网络请求,使用axios第三方库统一封装,方便统一管理和复用
- 几乎所有的接口都是一样的接口域名
- 几乎所有的接口都需要设置一样的超时时间
- 几乎所有的接口都需要做Token权限处理 等等
axios 统一封装 -> axios实例 -> 接口1 = 接口2 = 接口3
// 在utils文件夹下新建一个request.js文件,代码示例:
// 根域名配置
// 超时时间
// 请求拦截器 / 相应拦截器
import axios from ‘axios’;
const baseUrl = process.env.VUE_APP_ENV
const request = axios.create({
baseUrl,
Timeout: 5000
})
// **添加请求拦截器**
// 在请求发送之前,做拦截插入一些自定义配置[参数的处理]
request.interceptors.request.use((config)=>{
return config
},(error)=>{
return Promise.reject(error)
}
)
// **添加相应拦截器**
// 在响应返回客户端之前做拦截,重点处理返回的数据
request.interceptors.response.use((response)=>{
// 2xx 范围内的状态码都会触发该函数
// 对响应的数据处理
return response.data
},(error)=>{
return Promise.reject(error)
}
)
export { request }
// 然后在utils文件夹下index.js文件**统一中转**
// 统一中转工具模块函数
// 如其他地方使用:import { request } from‘@/utils’
import { request } from‘./request’
export {
request
}
登录-使用Redux管理token
token作为一个用户的标识数据,需要在很多个模块共享,Redux可以方便的解决状态共享问题。
Login组件体检 -> action -> Redux(Token管理)