> 技术文档 > react学习

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行内样式测试
);}export default App;
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 (
this is App
)}export default App

9.3跨层组件通信

步骤:

  1. 使用 createContext方法创建一个上下文对象Ctx

  2. 在顶层组件(App)中通过 Ctx.Provider 组件提供数据

  3. 在底层组件(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使用规则

  1. 只能在组件中或者其他自定义Hook函数中调用

  2. 只能在组件的顶层调用,不能嵌套在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.关于&&

    可以根据前面的条件是否成立而决定是否渲染后面的样式。

    考研终止。。。