react学习
在一切开始之前,react启动命令是:
pnpm start
一、创建项目
1. 命令
npx create-react-app democd demopnpm start
2.清除文件,使得src文件夹中只剩下index.js和app.js文件
3.一些常用的包
axios:
pnpm i axios
dayjs:
pnpm i dayjs
classnames:
pnpm i classnames
lodash:
pnpm i lodash
json-server:
pnpm i json-server -D
@reduxjs/toolkit react-redux仓库的使用:
pnpm i @reduxjs/toolkit react-redux
react-router-dom关于路由的使用:
pnpm i react-router-dom
craco路径解析配置:
pnpm i -D @craco/craco
组件库
pnpm add antd-mobile
统一:用于代码复制:
pnpm i dayjs react-router-dom @reduxjs/toolkit react-redux classnames axios lodash json-server craco antd-mobile
二、JSX语法
1.{}
引号字符串、变量、函数调用、方法调用、对象
2.条件渲染
flag &&
三元表达式
函数return
三、react
1.事件绑定及传参
基础:
function App(){ const clickHandler = ()=>{ console.log(\'button按钮点击了\') } return ( )}
事件参数:
function App(){ const clickHandler = (e)=>{ console.log(\'button按钮点击了\', e) } return ( )}
自定义参数:
function App(){ const clickHandler = (name)=>{ console.log(\'button按钮点击了\', name) } return ( )}
自定义参数和事件参数都使用:
function App(){ const clickHandler = (name,e)=>{ console.log(\'button按钮点击了\', name,e) } return ( )}
2.组件化
// 1. 定义组件function Button(){ return }// 2. 使用组件function App(){ return ( {/* 自闭和 */} {/* 成对标签 */} )}
3.useState组件状态管理
import { useState } from \'react\'function App() {// useState()返回的结果是个数组 const [age, setage] = useState(0) return ( );}export default App;
4.样式表示
4.1行内样式
style属性内部是一个对象类型数据,因为是在html中包裹js,所以要加大括号。
4.1.1直接写对象
外层大括号是jsx语法,内层是对象类型数据的大括号。
function App() { return ( <div style={{ color: \'red\', fontSize: \'20px\' }}>style行内样式测试
4.1.2抽离出对象
const classObj = { color: \'red\', fontSize: \'40px\' }function App() { return ( style行内样式测试 );}export default App;
4.2class类名控制
className属性值是一个字符串
4.2.1基础用法
index.css
.test { color: aqua; font-size: 15px;}
index.js
注意用法:是className而不是class
import \'./index.css\'function App() { return ( 引入css文件,class类名控制 );}export default App;
4.2.2示例
解读:第一种方法className属性值是一个三元表达式,所以在大括号内部。第二种方法className属性值是一个模板字符串,所以在大括号内部,${}内部是一个判断语句,如果item.type === active成立,就返回\'active\'否则不返回。
return chooseActive(item.type)}>{item.text}return chooseActive(item.type)}>{ item.text}
4.2.3扩展用法——classnames
4.2.3.1安装
pnpm i classnames
4.2.3.2引入和使用
classNames函数返回值为字符串,有两个参数,第一个是字符串,表示静态类名,第二个是对象,属性名为动态类名,属性值为类名存在条件。
import classNames from \'classnames\'return chooseActive(item.type)}>{item.text}
5.lodash根据某个字段对对象进行排序
5.1安装
pnpm i lodash
5.2引入
import _ from \'lodash
5.3使用
orderBy(对谁进行排序, 按照谁来排, 顺序)
_.orderBy(commentList,\'like\',\'desc\')
6.表单控制,双向绑定
import { useState } from \'react\'function App() { const [value, setvalue] = useState(\'\') return ( {setvalue(e.target.value)}}> );}export default App;
7.获取DOM元素
通过inputRef.current获取到元素
function App(){ const inputRef = useRef(null) const onChange = ()=>{ console.log(inputRef.current.value) } return ( )}
8.两个包:uuid随机id,dayjs
装包->引入->使用
import { v4 as uuidv4 } from \'uuid\'import {dayjs} from \'dayjs\'uuidv4()//生成随机iddayjs(new Date()).format(\'MM-DD hh:mm\')//格式化时间

9.组件通信
9.1父子通信:父传子+子传父
上面代码中,等号左边是在子组件中接受的方式,右边是父组件中的形式。
可以传数据或函数。
import { useState } from \"react\"function Son({ msg,onsetmsg }) { const sonMsg = \'this is son msg\' return ( {/* 在子组件中执行父组件传递过来的函数 */} )}function App() { const [msg,setmsg]=useState(\'\') return ( {/* 这里可以通过添加属性的方式向子组件传递数据或函数 */} {/* 传递的数据是“父传子” */} {/* 传递的函数可以实现在子组件内部“子传父” */} )}export default App
9.2兄弟组件通信:状态提升
// 1. 通过子传父 A -> App// 2. 通过父传子 App -> Bimport { useState } from \"react\"function A ({ onGetAName }) { // Son组件中的数据 const name = \'this is A name\' return ( this is A compnent, )}function B ({ name }) { return ( this is B compnent, {name} )}function App () { const [name, setName] = useState(\'\') const getAName = (name) => { setName(name) } return ( )}export default App
9.3跨层组件通信
步骤:
-
使用 createContext
方法创建一个上下文对象Ctx
-
在顶层组件(App)中通过 Ctx.Provider
组件提供数据
-
在底层组件(B)中通过 useContext
钩子函数获取消费数据
// App -> A -> Bimport { createContext, useContext } from \"react\"// 1. createContext方法创建一个上下文对象const MsgContext = createContext()function A () { return ( this is A component )}function B () { // 3. 在底层组件 通过useContext钩子函数使用数据 const msg = useContext(MsgContext) return ( this is B compnent,{msg} )}function App () { const msg = \'this is app msg\' return ( )}export default App
10.useEffect()

参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作
参数2是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行
依赖项
副作用功函数的执行时机
没有依赖项
组件初始渲染 + 组件更新时执行
空数组依赖
只在初始渲染时执行一次
添加特定依赖项
组件初始渲染 + 依赖项变化时执行
清除副作用 :在useEffect()的第一个参数(也就是函数)的函数体中,添加一个return语句,return的是一个函数,函数体中执行的语句就是在清除副作用。通常是在组件卸载时执行。起到的作用例如关掉定时器等。
import { useEffect, useState } from \"react\"function Son () { // 1. 渲染时开启一个定时器 useEffect(() => { const timer = setInterval(() => { console.log(\'定时器执行中...\') }, 1000) return () => { // 清除副作用(组件卸载时) clearInterval(timer) } }, []) return this is son}function App () { // 通过条件渲染模拟组件卸载 const [show, setShow] = useState(true) return ( {show && } )}export default App
11.自定义Hook函数
抽离可复用的逻辑。
// 封装自定义Hook// 问题: 布尔切换的逻辑 当前组件耦合在一起的 不方便复用// 解决思路: 自定义hookimport { useState } from \"react\"function useToggle () { // 可复用的逻辑代码 const [value, setValue] = useState(true) const toggle = () => setValue(!value) // 哪些状态和回调函数需要在其他组件中使用 return return { value, toggle }}// 封装自定义hook通用思路// 1. 声明一个以use打头的函数// 2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)// 3. 把组件中用到的状态或者回调return出去(以对象或者数组)// 4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用function App () { const { value, toggle } = useToggle() return ( {value && this is div} )}export default App
12.React Hooks使用规则
-
只能在组件中或者其他自定义Hook函数中调用
-
只能在组件的顶层调用,不能嵌套在if、for、其它的函数中
13.json-server的使用(接口服务)
json-server是一个快速以.json文件作为数据源模拟接口服务的工具
13.1装包
pnpm i json-server -D
13.2准备json文件
13.3在package.json文件中添加命令
在scripts字段中添加
键值对形式,键可以随便起名(键用于执行命令,如键若为a,则执行命令为npm run a),值分为三部分,其中第二部分是json文件名,第三部分可以自定义端口
\"serve\":\"json-server db.json --port 3004\"
注意,如果数据json文件在项目根目录下的server文件夹中,名为data.json,那么配置应该是
\"serve\":\"json-server ./server/data.json --port 3004\"

13.4执行命令
npm run serve

14.axios的使用
装包
pnpm i axios
15.redux使用
15.1.装包
两个包
pnpm i @reduxjs/toolkit react-redux
15.2.使用
步骤:
写仓库模块(@reduxjs/toolkit)
组合模块
链接仓库和react
在react中使用仓库(react-redux)
15.2.1写仓库

store/modules/counterStore.js
import { createSlice } from \'@reduxjs/toolkit\'const counterStore=createSlice({ name: \'counter\', // 初始化数据 initialState: { count:1 }, // 修改数据的方法 reducers: { add(state) { state.count++ }, sub(state) { state.count-- }, // 传参使用 addTo10(state, action) { // 通过action.payload获取到参数 state.count=action.payload } }})const { add, sub, addTo10 } = counterStore.actionsconst counterReducer = counterStore.reducer// 按需导出修改数据的方法export { add, sub, addTo10 }// 默认导出reducer函数export default counterReducer
15.2.2组合模块
store/index.js
import { configureStore } from \"@reduxjs/toolkit\";import counterReducer from \"./modules/counterStore\";export default configureStore({ reducer: { counter:counterReducer }})
15.2.3链接仓库和react
index.js
import React from \'react\';import ReactDOM from \'react-dom/client\';import App from \'./App\';+import store from \'./store/index\'+import {Provider} from \'react-redux\'const root = ReactDOM.createRoot(document.getElementById(\'root\'));root.render(+ );
15.2.4使用仓库
import { useSelector, useDispatch } from \"react-redux\"import {sub,add} from \'./store/modules/counterStore\'function App() { const { count } = useSelector(state => state.counter) const dispatch = useDispatch() return ( {count} )}export default App
15.2.5关于异步请求
store/modules/counterStore.js
import { createSlice } from \'@reduxjs/toolkit\'import axios from \'axios\'const counterStore=createSlice({ name: \'counter\', // 初始化数据 initialState: { List:[] }, // 修改数据的方法 reducers: { setChannelList (state, action) { state.List = action.payload } }})const { setChannelList } = counterStore.actions// 封装异步请求const fetchChannelList = () => { return async (dispatch) => { const res = await axios.get(\'http://geek.itheima.net/v1_0/channels\') dispatch(setChannelList(res.data.data.channels)) }}const counterReducer = counterStore.reducer// 按需导出修改数据的方法export { fetchChannelList }// 默认导出reducer函数export default counterReducer
App.js
import { useSelector, useDispatch } from \"react-redux\"import { fetchChannelList } from \'./store/modules/counterStore\'function App() { const { List } = useSelector(state => state.counter) const dispatch = useDispatch() return ( {List.map((item) => { return {item.name} })} )}export default App
16.reactRouter路由使用
执行命令:
pnpm i react-router-dom
16.1配置
src/router/index.js
import Login from \'../page/login/index\'import Article from \'../page/article/index\'import { createBrowserRouter } from \'react-router-dom\'const router = createBrowserRouter([ { path:\'/login\', element: }, { path:\'/article\', element: }])export default router
src/index.js
import React from \'react\';+import ReactDOM from \'react-dom/client\';import App from \'./App\';+import router from \'./router/index\'+import { RouterProvider } from \'react-router-dom\';const root = ReactDOM.createRoot(document.getElementById(\'root\'));root.render( + // 这里本来是,改成下行+ );
16.2路由跳转
声明式导航:
import { Link } from \"react-router-dom\"const Login = () => { return ( 登录页 跳转至article页面 )}export default Login
编程式导航:
import { useNavigate } from \"react-router-dom\"const Login = () => { const navigate=useNavigate() return ( 登录页 )}export default Login
16.3传参
searchParams传参(类似于path传参):
import { useSearchParams } from \"react-router-dom\"const Article = () => { // 获取path传参 const [searchParams] = useSearchParams() const name=searchParams.get(\'name\') const job = searchParams.get(\'job\') return 文章页 searchParams:{name}-{job} }export default Article
params传参(类似于动态路由传参):
import { useParams } from \"react-router-dom\"const Article = () => { // 获取动态路由传参 const params = useParams() const hobby=params.hobby const age=params.age return 文章页 searchParams:{name}-{job} params:{hobby}-{age} }export default Article
16.4嵌套路由配置
router/index.js
import Login from \'../page/login/index\'import { createBrowserRouter } from \'react-router-dom\'import Login01 from \'../page/login01/index\'import Login02 from \'../page/login02/index\'const router = createBrowserRouter([ { path:\'/login\', element: , children: [ { // 注意这里的path前面不能加/,否则访问login/login01时报404错 path: \'login01\', element: }, { path: \'login02\', element: }, ] }])export default router
login/index.js
import { Link, Outlet } from \"react-router-dom\"const Login = () => { return ( 登录页 跳转至二级路由login01 跳转至二级路由login02 // 二级路由出口 )}export default Login
展示:
一级路由login:

二级路由login/login02:

默认和二级路由和404:
以上页面在展示的时候,当访问login这个一级路由时只会展示到一级,如果在配置时添加默认二级路由login01,则访问login时会跳转至login/login01
代码:
import Login from \'../page/login/index\'import { createBrowserRouter } from \'react-router-dom\'import Login01 from \'../page/login01/index\'import Login02 from \'../page/login02/index\'import NotFound from \'../page/notFound/index\'const router = createBrowserRouter([ { path:\'/login\', element: , children: [ { // 默认二级路由 index: true, element: }, { path: \'login02\', element: }, ] }, { path: \'*\', element: }])export default router
展示:

16.5两种路由模式
history和hash模式
区别:有无#
创建区别:history:createBrowserRouter:没有#
hash:createHashRouter:有#
17.路径配置
17.1路径解析配置(webpack)
目的:解析@为src
步骤:
安装插件craco
pnpm i -D @craco/craco
项目根目录下创建配置文件 craco.config.js
const path = require(\'path\')module.exports = { webpack: { alias: { \'@\': path.resolve(__dirname, \'src\') } }}
包文件中配置启动和打包命令
package.json中scripts:
\"scripts\": { \"start\": \"craco start\", \"build\": \"craco build\" },
17.2路径现象配置(vscode)
目的:VsCode 在输入 @/ 时,自动联想出来对应的 src/下的子级目录
步骤:
根目录下新增配置文件 - jsconfig.json
{ \"compilerOptions\":{ \"baseUrl\":\"./\", \"paths\":{ \"@/*\":[ \"src/*\" ] } }}
18.数据mock
用json-server
19.antD-mobile主题定制
装包
pnpm add antd-mobile
全局定制:
在src/theme.css文件中写样式,在src/index.js中导入
:root:root { --adm-color-primary: #a062d4;}
局部定制:
.purple-theme { --adm-color-primary: #a062d4;}
四、杂记
1.关于&&
可以根据前面的条件是否成立而决定是否渲染后面的样式。

考研终止。。。