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

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

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

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

store/modules/counterStore.js
javascript
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.actions
const counterReducer = counterStore.reducer
// 按需导出修改数据的方法
export { add, sub, addTo10 }
// 默认导出reducer函数
export default counterReducer
15.2.2组合模块
store/index.js
javascript
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./modules/counterStore";
export default configureStore({
reducer: {
counter:counterReducer
}
})
15.2.3链接仓库和react
index.js
javascript
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(
+ <Provider store={store}><App /></Provider>
);
15.2.4使用仓库
javascript
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 (
<div className="app">
<button onClick={() => { dispatch(sub()) }}>-</button>
{count}
<button onClick={dispatch(add())}>+</button>
<!--传参使用-->
<button onClick={()=>dispatch(addTo10())}>加到10</button>
</div>
)
}
export default App
15.2.5关于异步请求
store/modules/counterStore.js
javascript
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
java
import { useSelector, useDispatch } from "react-redux"
import { fetchChannelList } from './store/modules/counterStore'
function App() {
const { List } = useSelector(state => state.counter)
const dispatch = useDispatch()
return (
<div className="app">
<button onClick={() => dispatch(fetchChannelList())}>异步请求更新仓库</button>
{List.map((item) => {
return <li>{item.name}</li>
})}
</div>
)
}
export default App
16.reactRouter路由使用
执行命令:
javascript
pnpm i react-router-dom
16.1配置
src/router/index.js
javascript
import Login from '../page/login/index'
import Article from '../page/article/index'
import { createBrowserRouter } from 'react-router-dom'
const router = createBrowserRouter([
{
path:'/login',
element: <Login></Login>
},
{
path:'/article',
element: <Article></Article>
}
])
export default router
src/index.js
javascript
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(
<React.StrictMode>
+ // 这里本来是<App />,改成下行
+ <RouterProvider router={router}><App /></RouterProvider>
</React.StrictMode>
);
16.2路由跳转
声明式导航:
javascript
import { Link } from "react-router-dom"
const Login = () => {
return (<div>
登录页
<Link to='/article'>跳转至article页面</Link>
</div>)
}
export default Login
编程式导航:
javascript
import { useNavigate } from "react-router-dom"
const Login = () => {
const navigate=useNavigate()
return (<div>
登录页
<button onClick={()=>navigate('/article')}>跳转至article页面</button>
</div>)
}
export default Login
16.3传参
searchParams传参(类似于path传参):
javascript
import { useSearchParams } from "react-router-dom"
const Article = () => {
// 获取path传参
const [searchParams] = useSearchParams()
const name=searchParams.get('name')
const job = searchParams.get('job')
return <div>文章页
<div>searchParams:{name}-{job}</div>
</div>
}
export default Article
params传参(类似于动态路由传参):
javascript
import { useParams } from "react-router-dom"
const Article = () => {
// 获取动态路由传参
const params = useParams()
const hobby=params.hobby
const age=params.age
return <div>文章页
<div>searchParams:{name}-{job}</div>
<div>params:{hobby}-{age}</div>
</div>
}
export default Article
16.4嵌套路由配置
router/index.js
javascript
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: <Login></Login>,
children: [
{
// 注意这里的path前面不能加/,否则访问login/login01时报404错
path: 'login01',
element:<Login01></Login01>
},
{
path: 'login02',
element:<Login02></Login02>
},
]
}
])
export default router
login/index.js
javascript
import { Link, Outlet } from "react-router-dom"
const Login = () => {
return (<div>
登录页
<Link to="/login/login01">跳转至二级路由login01</Link>
<Link to="/login/login02">跳转至二级路由login02</Link>
// 二级路由出口
<Outlet />
</div>)
}
export default Login
展示:
一级路由login:

二级路由login/login02:

默认和二级路由和404:
以上页面在展示的时候,当访问login这个一级路由时只会展示到一级,如果在配置时添加默认二级路由login01,则访问login时会跳转至login/login01
代码:
javascript
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: <Login></Login>,
children: [
{
// 默认二级路由
index: true,
element:<Login01></Login01>
},
{
path: 'login02',
element:<Login02></Login02>
},
]
},
{
path: '*',
element:<NotFound></NotFound>
}
])
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
javascript
const path = require('path')
module.exports = {
webpack: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
}
包文件中配置启动和打包命令
package.json中scripts:
javascript
"scripts": {
"start": "craco start",
"build": "craco build"
},
17.2路径现象配置(vscode)
目的:VsCode 在输入 @/ 时,自动联想出来对应的 src/下的子级目录
步骤:
根目录下新增配置文件 - jsconfig.json
javascript
{
"compilerOptions":{
"baseUrl":"./",
"paths":{
"@/*":[
"src/*"
]
}
}
}
18.数据mock
用json-server
19.antD-mobile主题定制
装包
pnpm add antd-mobile
全局定制:
在src/theme.css文件中写样式,在src/index.js中导入
css
:root:root {
--adm-color-primary: #a062d4;
}
局部定制:
html
<div className='purple-theme'>
<Button color='primary'>Purple</Button>
</div>
css
.purple-theme {
--adm-color-primary: #a062d4;
}
四、杂记
1.关于&&
可以根据前面的条件是否成立而决定是否渲染后面的样式。

考研终止。。。