react基础+pc项目
-
技术栈为:React + Hook + React-router-v6 + Mobx + AntD
-
React 基础讲义: https://www.yuque.com/fechaichai/qeamqf/xbai87
-
ReactPc 项目讲义: https://www.yuque.com/fechaichai/tzzlh1
-
React 英文文档(https://reactjs.org/)
-
React 中文文档 (https://zh-hans.reactjs.org/)
-
React 新文档 (https://beta.reactjs.org/)(开发中....)
-
安装 npx create-react-app demo
-
JSX 条件渲染
-
可以使用三元运算符或者逻辑&&运算
-
原则: 模板中的逻辑尽量保持精简
-
复杂的多分支的逻辑收敛为一个函数 通过一个专门的函数来写分支逻辑,模板中只负责调用
JSX 注意事项
add
- class->className
- for -> htmlFor
- <></> 幽灵节点
- JSX 换行需要()包裹
表单组件
-
受控组件: 被 react 的状态控制的组件
-
input 框自己的状态被 React 组件状态控制
-
非受控组件
-
手动操作 dom 的方式获取文本的值
-
createRef
-
myRef.current.value
-
生成独一无二的 id 可以使用 uuid 包 yarn add uuid
import { v4 as uuid } from "uuid" uuid() // 得到一个独一无二的id
- 父传子 props 函数
- 子传父:子组件调用父组件传递过来的函数,并且把想要传递的数据当成函数的实参
兄弟组件通信
-
通过状态提升机制,利用共同的父组件实现兄弟通信
-
组件通信-Context 实现跨组件传递数据
-
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法
-
Provider 提供者 Consumer 消费者
-
实现步骤
-
1、创建 Context 对象,导出 Provider 和 Consumer 对象
-
const { Provider,Consumer } = createContext()
-
2、使用 Provider 包裹跟组件提供数据
<Provider value={message}> <!-- 根组件 --> </Provider>
-
3、需要用到数据的组件使用 Consumer 包裹获取数据
<Consumer> {value => /*基于context值进行渲染*/} </Consumer>
children 属性
- 表示该组件的子节点,只有组件内部有子节点,props 中就有该属性
- children 可以是
- 1、普通文本
- 2、普通标签元素
- 3、函数
- 4、JSX
props 类型校验
-
实现步骤
-
1、安装属性校验包:yarn add prop-types
-
2、导入 prop-types 包
-
3、使用组件名.propTypes = {} 给组件添加校验规则
import PropTypes from "prop-types"; const list = props => { const arr = props.colors const lis = arr.map((item,index) => <li key={index}>{item.name}</li>) return <ul>{lis}</ul> } list.propTypes = { colors: PropTypes.array }
props 校验-规则说明
-
四种常见结构
-
1、 常见类型:array、bool、func、number、object、string
-
2、React 元素类型:element
-
3、必填项:isRequired
-
4、特定的结构对象:shape({})
// 常见类型 optionalFunc: PropTypes.func // 必选 requiredFunc: PropTypes.func.isRequired // 特定结构的对象 optionalObjectWithShape: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number })
prop 默认值
-
1、函数组件 使用 defaultProps
// 设置默认值 xxx.defaultProps = { pageSize: 10 }
-
使用函数参数默认值(推荐)
-
注意:函数组件,新的 react 已经不再推荐 defaultProps 来添加默认值,而是推荐函数参数默认值来实现
-
区别:第一种在用的时候组件内部已经有了 pageSize 这个 prop 第二种只有传递的时候组件内部才有了 prop
function List({pageSize = 10}) { return( <div></div> ) }
-
2、类组件 defaultProps
xxx.defaultProps = { pageSize: 10 }
-
使用类静态属性声明
static defaultProps = { pageSize: 10 }
生命周期
- 只有类组件才有生命周期(类组件实例化 函数组件不需要实例化)
生命周期-挂载阶段
-
constructor ---> render ---> componentDidMount
-
1、钩子函数:constructor;触发时机: 创建组件时,最先执行,初始化的时候只执行一次;作用:1.初始化 state 2.创建 Ref 3.使用 bind 解决 this 指向问题等
-
2、钩子函数:render;触发时机:每次组件渲染都会触发;作用:渲染 UI(注意:不能再里面调用 setState())
-
3、钩子函数:componentDidMount ;触发时机: 组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次;作用:1.发送网络请求 2.DOM 操作
生命周期-更新阶段
-
render ---> conponentDidUpdate
-
1、钩子函数:render; 触发时机:每次组件渲染都会触发;作用: 渲染 UI(与挂载阶段是同一个 render)
-
2、钩子函数:componentDidUpdate;触发时机:组件更新后(DOM 渲染完毕);作用:DOM 操作,可以获取到更新后的 DOM 内容,不用直接调用 setState
生命周期-卸载阶段
-
componentWillUnmount
-
1、钩子函数:componentWillUnmount;触发时机:组件卸载(从页面中消失);作用:执行清理工作(比如:清理定时器等)
Hooks 概念理解
1、什么是 hooks
-
Hooks 的本质:一套能够使函数组件更强大,更灵活的“钩子”
-
React 的设计理念
UI = f(data)
-
useState
-
useEffect
-
自定义 hook
-
自定义一个 hook 函数,实现获取滚动距离的 y
import { useState } from "react" export function useWindowScroll() { const [y,setY] = useState(0); window.addEventListener('scroll',() => { const h = document.documentElement.scrollTop setY(h) }) return [y] } // 引入 import { useWindowScroll } from "./xxx.js" const [y] = useWindowScroll()
-
自定义 hook 函数,可以自动同步到本地 LocalStorage
-
message 可以通过自定义传入默认初始值
-
每次修改 message 数据的时候,都会自动往本地同步一份
import { useEffect, useState } from "react" export function useLocalStorage(key,defaultValue) { const [message,setMessage] = useState(defaultValue) // 每次只要message变化,就会自动同步到本地ls useEffect(() =>{ window.localStorage.setItem(key,message) },[message,key]) return [message,setMessage] } // 引用 import { useLocalStorage } from "xxx" const [message,setMessage] = useLocalStorage('hook-key','测试') setTimeout(() => { setMessage('cp) },5000)
-
useState -回调函数的参数
-
const [name,setName ] = useState(() => { // 编写计算逻辑 return '计算之后的初始值'})
-
useState(() = > {})
useEffect - 清理副作用
useEffect(() => {
console.log('副作用函数执行了')
// 副作用函数的执行时机为:在下一次副作用函数执行之前执行
return() => {
console.log('清理副作用的函数执行了')
// 在这里写清理副作用的代码
}
})
-
案例
function Test() { useEffect(() => { let timer = setInterval(() => { console.log('定时器执行了') },1000) return () => { // 清理的动作 clearInterval(timer) } },[]) }
-
发送请求
useEffect(() => { async = function fetchData() { const res = await axios.get('xxx') console.log(res) } fetchData(); },[])
useContext
-
实现步骤
-
1、使用 createContext 创建 Context 对象
-
2、在顶层组件通过 Provider 提供数据
-
3、在底层组件通过 useContext 函数获取数据
-
代码实现
import { createContext, useContext } from "react" const Context = createContext(); function Foo() { return <div>Foo<Bar/></div> } function Bar() { // 底层组件通过useContext函数获取数据 const name = useContext(Context); return <div>Bar {name}</div> } function App() { return ( // 顶层组件通过Provider提供数据 <Context.Provider value={'this is name'}> <div><Foo /></div> </Context.Provider> ) }
-
context 如果要传递的数据,只需要在整个应用初始化的时候传递一次就可以,就可以选择在当前文件(index.js)里做数据提供
- 如果 context 需要传递数据并且将来还需要在对数据做修改底层组件也需要,跟着一起变,需要写到 app.js
- 如果提供数据是静态不变-index.js 包裹
- 如果提供数据需要编号-app.js 可以发便更改数据
React Router V6
1、基础使用
- 安装 yarn add react-router-dom@6
-
app.jsx
// 引入必要的内置组件 import { BrowserRouter, Routes, Route, Link } from "react-router-dom" // 准备两个路由组件 const Home = () => <div>this is home</div> const Ablut = () => <div>this is about</div> // 进行路由配置 function App() { return( // BrowserRouter声明当前要用一个非hash模式的路由 <BrowserRouter> // Link指定跳转的组件,to用来配置路由地址 <Link to="/">首页</Link> <Link to="/about">关于</Link> // Routes路由出口 路由对应的组件会在这里进行渲染 <Routes> // Route指定路径和组件的对应关系,path代表路径 element代表组件成对出现path -> element <Route path="/" element={<Home />}></Route> // /about 可以省略/ /about =about <Route path="/about" element={<About />}></Route> </Routes> </BrowserRouter> ) }
2、核心组件说明
(1)、核心组件-BrowerRouter
- 作用:包裹整个应用,一个 React 应用只需要使用一次
- 两种常见 Router: HashRouter 和 BrowserRouter
- HashRouter: 使用 URL 的哈希值实现(http://localhose:3000/#/first)
- BrowserRouter: 使用 H5 的 history.pushState API 实现(http://localhose:3000/first)
(2)、核心组件-Link
-
作用: 用于指定导航链接,完成路由跳转
-
语法说明:组件通过 to 属性指定路由地址,最终会渲染为 a 链接元素
-
<Link to="/path" >页面一</Link>
(3)、核心组件- Routes
-
作用:提供一个路由出口,满足条件的路由组件会渲染到组件内部
<Routes> // 满足条件的路由组件会渲染到这里 <Route /> <Route /> </Routes>
(4)、核心组件- Route
- 作用:用于指定导航链接、完成路由匹配
- 语法说明:path 属性指定匹配的路径地址,element 属性指定要渲染的组件
<Route path="/about" element={<Ablut />}/>
- 说明: 当 url 路径为
/about
时,会渲染<About />
组件
3、编程式导航(跳转与参数)
(1)、编程式导航-跳转
-
作用:通过 js 编程的方式进行路由页面跳转,比如从登录页跳转到关于页
-
语法说明:
-
1、导入 useNavigate 钩子函数
-
2、执行钩子函数得到跳转函数
-
3、执行跳转函数完成跳转
-
注意: 如果在跳转时不想加历史记录,可以添加额外参数 replace 为 true
navigate('/about', { replace: true } )
import { useNavigate } from "react-router-dom" const Home = () => { //执行函数 const navigate = useNavigate(); return ( <div> Home <button onClick={() => navigate('/about')}>跳转关于页</button> </div> ) } export default Home
(2)、编程式导航-跳转携带参数
-
1、searchParams 传参
-
传参
-
navigate(''/about?id=1001)
-
取参
-
let [params] = useSearchParams();let id = params.get('id')
-
params 是一个对象,对象里有一个 get 的方法,用来获取对应的参数,把参数的名称作为 get 的实参传过来
-
2、params 传参
-
路由传参
-
navigate('/about/1001')
-
路由取参
-
let params = useParams();let id = params.id
4、嵌套路由
-
实现步骤
-
1、App.js 中定义嵌套路由声明
-
2、Layout 组件内部通过
指定二级路由出口 -
App.jsx
<Routes> <Route path="/" element={<Layout />}> <Route path="board" element={<Board />}> <Route path="article" element={<Article />}> </Route> </Routes>
-
Layout.jsx
import { Outlet } from "react-router-dom" const Layout = () => { return( <div> layout页面 // 二级路由的path等于一级path+二级path <Link to="/board">board<Link> <Link to="/article">article<Link> // 二级路由出口 <Outlet /> </div> ) } export default Layout;
默认二级路由
- 场景:应用首次渲染完毕就需要显示的二级路由
- 实现步骤
- 1、给默认二级路由标记 index 属性
- 把原本的路径 path 属性去掉
-
App.jsx
<Routes> <Route path="/" element={<Layout />}> <Route index element={<Board />}> <Route path="article" element={<Article />}> </Route> </Routes>
-
Layout.jsx
import { Outlet } from "react-router-dom" const Layout = () => { return( <div> layout页面 // 二级路由的path等于一级path+二级path <Link to="/">board<Link> <Link to="/article">article<Link> // 二级路由出口 <Outlet /> </div> ) } export default Layout;
5、404 页配置
-
场景:当 url 的路径在整个路由配置中都找不到对应的 path,使用 404 兜底组件进行渲染
-
1- 准备一个 NotFound 组件
-
NotFound.jsx
const NotFound = () => { return <div>this is NotFound</div> } export default NotFound
-
App.jsx
<BrowserRouter> <Routes> <Route path="/" element={<Layout />}> <Route index element={<Board />} /> <Route path="article" element={<Article />} /> </Route> <Route path="*" element={<NotFound />}></Route> </Routes> </BrowserRouter>
Mobx
- 一个可以和 React 良好配合的集中状态管理工具,mobx 和 react 的关系,相当于 vuex 和 vue
- 同类工具还有:
- redux
- dva
- recoil
Mobx 环境配置
- Mobx 来做响应式数据建模
- 1、使用 create-react-app 初始化 react 项目
npx create-react-app mobx-react-app
- 2、安装 mobx 和 mobx-react-lite
yarn add mobx mobx-react-lite
Mobx 第一个 store
1、初始化 mobx
-
初始化步骤
-
1、定义数据状态(state)
-
2、在构造器中实现数据响应式处理
-
3、定义 action 函数(修改数据)
-
4、实例化 store 并导出
-
counterStore.js
import { makeAutoObservable } from "mobx" class CounterStore { count = 0 // 定义数据 constructor() { makeAutoObservable(this) // 响应式处理 } // 定义修改数据的方法 addCount = () => { // 定义action函数 this.count++ } } // 实例化 const counterStore = new CounterStore(); export default counterStore
2、React 使用 store
- 实现步骤
- 1、导入 store 实例
- 2、使用 store 中的数据
- 3、修改 store 中的数据
- 4、让组件视图响应数据变化
-
App.js
// 导入counterStore import conuterStore from "./store" // 导入observer方法 import { observer } from "mobx-react-lite" function App() { return( <div> <button onClick={() => conuterStore.addCount()}>{conuterStore.count}</button> </div> ) } // 包裹组件让视图响应数据变化 export default observer(App)
3、computed 计算属性
- 实现步骤
- 1、声明一个存在的数据
- 2、定义 get 计算属性
- 3、在 makeAutoObservable 方法中标记
-
counterStore.js
import { computed, makeAutoObservable } from 'mobx' class CounterStore { list = [1, 2, 3, 4, 5, 6] constructor() { makeAutoObservable(this, { filterList: computed }) } // 修改原数组 changeList = () => { this.list.push(7, 8, 9) } // 定义计算属性 get filterList () { return this.list.filter(item => item > 4) } } const counter = new CounterStore() export default counter
-
App.js
// 导入counterStore import counterStore from './store' // 导入observer方法 import { observer } from 'mobx-react-lite' function App() { return ( <div className="App"> {/* 原数组 */} {JSON.stringify(counterStore.list)} {/* 计算属性 */} {JSON.stringify(counterStore.filterList)} <button onClick={() => counterStore.changeList()}>change list</button> </div> ) } // 包裹组件让视图响应数据变化 export default observer(App)
4、模块化
-
实现步骤
-
1、拆分模块 js 文件,每个模块中定义自己独立的 state/action
-
2、在 store/index.js 中导入拆分之后的模块,进行模块组合
-
3、利用 React 的 context 的机制导出统一的 useStore 方法,给业务组件使用
-
1、定义 task 模块
-
store/taskStore.js
import { makeAutoObservable } from "mobx" class TaskStore{ taskList = [] constructor() { makeAutoObservable(this) } addTask() { this.taskList.push('vue','react') } } const task = new TaskStore() export default tash
-
2、定义 counterStore
import { makeAutoObservable } from "mobx" class CounterStore{ count = 0; list = [1,2,3,4,5,6] constructor() { makeAutoObservable(this) } addCount = () => { this.count++ } changeList = () => { this.list.push(7,8,9) } get filterList () { return this.list.filter(item => item >4 ) } } const counter = new CounterStore(); export default counter
-
3、组合模块导出统一方法
-
store/index.js
import React from "react" import counter from "./counterStore" import task from "./taskStore" class RootStore{ constructor() { this.counterStore = counter this.taskStore = task } } const rootStore = new RootStore() const context = React.createContext(rootStore) const useStore = () => React.useContext(context) export { useStore }
-
4、组件使用模块中的数据
import { observer } from "mobx-react-lite" // 导入方法 import { useStore } from "./store" function App() { // 得到store const store = useStore(); return( <div> <button onClick={() => store.counterStore.addCount()}>{store.counterStore.count}</button> </div> ) } // 包裹组件让视图响应数据变化 export default observer(App)
Mobx 基础使用-总结
- 1、初始化 mobx 的过程是怎样的?
- 声明数据 ->响应式处理 ->定义 action 函数 ->实例化导出
- 2、mobx 如何配合 react,需要依赖什么包?
- mobx-react-lite 作为链接包,导出 observer 方法,包裹组件(只能和函数组件配合)
- 3、模块化解决了什么问题?
- 维护性问题
- 4、如何实现 mobx 的模块化?
- 按照功能拆分 store 模块,根模块中组合子模块,利用 context 机制依赖注入
react-uuid 创建一个独一无二的 id
- import uuid from "react-uuid"
Mobx 和 React 职责划分
Store
- 1、业务状态数据
- 2、业务状态操作逻辑
React
-
1、渲染业务数据
-
2、UI 临时状态维护
-
3、事件触发,调用 Mobx
-
富文本编辑器 react-quill
pc 端项目
1、项目技术
-
React 官方脚手架 create-react-app
-
react hooks
-
状态管理:mobx
-
UI 组件库:antd v4
-
ajax 请求库:axios
-
路由:react-router-dom 以及 history
-
富文本编辑器:react-quill
-
CSS 预编译器:sass
body { margin: 0; } #root { height: 100%; }
2、实现步骤
- 1、使用 create-react-app 生成项目 npx create-react-app react-demo-pc
- 2、进入跟目录 cd react-demo-pc
- 3、启动项目 yarn start
3、 使用 gitee 管理项目
- 1、在项目根目录打开终端,并初始化 git 仓库(如果已经有了 git 仓库,无需重复该步),命令:git init
- 2、添加项目内容到暂存区: git add .
- 3、提交项目内容到仓库区: git commit -m '项目初始化'
- 4、添加 remote 仓库地址: git remote add origin [gitee 仓库地址]
- 5、将项目内容推送到 gitee: git push origin master -u // -u 可不传
4、使用 scss 预处理器
- 1、安装解析 sass 的包: yarn add sass -D
- 创建全局样式文件: index.scss
5、配置基础路由
-
1、安装路由:yarn add react-router-dom
-
2、在 page 目录中创建两个文件夹: Login、Layout
-
3、分别在两个目录中创建 index.js 文件,并创建一个简单的组件后导出
-
4、在 App 组件中,导入路由组件以及两个页面的组件
-
5、配置 Login 和 Layout 的路由规则
-
page/Login/index.js
const Login = () => { return <div>login</div> } export default Login
-
pages/Layout/index.js
const Layout = () => { return <div>layout</div> } export default Layout
-
app.js
// 导入路由 import { BrowserRouter, Route, Routes } from 'react-router-dom' // 导入页面组件 import Login from './pages/Login' import Layout from './pages/Layout' // 配置路由规则 function App() { return ( <BrowserRouter> <div className="App"> <Routes> <Route path="/" element={<Layout/>}/> <Route path="/login" element={<Login/>}/> </Routes> </div> </BrowserRouter> ) } export default App
6、组件库 antd 使用
-
1、安装 antd 组件库: yarn add antd
-
2、全局导入 antd 组件库的样式
-
3、导入 Button 组件
-
4、在 Login 页面渲染 Button 组件进行测试
-
src/index.js
// 先导入 antd 样式文件 // https://github.com/ant-design/ant-design/issues/33327 import 'antd/dist/antd.min.css' // 再导入全局样式文件,防止样式覆盖! import './index.css'
-
pages/Login/index.js
import { Button } from 'antd' const Login = () => ( <div> <Button type="primary">Button</Button> </div> )
7、配置别名路径
- 自定义 CRA 的默认配置:https://ant.design/docs/react/use-with-create-react-app-cn#高级配置
- craco 配置文档:https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#configuration
-
CRA 将所有工程化配置,都隐藏在了 react-scripts 包中,所以项目中看不到任何配置信息
-
如果要修改 CRA 的默认配置,有以下几种方案:
-
a. 通过第三方库来修改,比如,@craco/craco (推荐)
-
b. 通过执行 yarn eject 命令,释放 react-scripts 中的所有配置到项目中
-
实现步骤
-
1、安装修改 CRA 配置的包:yarn add -D @craco/craco
-
2、在项目根目录中创建 craco 的配置文件:
craco.config.js
,并在配置文件中配置路径别名 -
3、修改
package.json
中的脚本命令 -
4、在代码中,就可以通过
@
来表示 src 目录的绝对路径 -
5、重启项目,让配置生效
-
craco.config.js
const path = require('path') module.exports = { // webpack 配置 webpack: { // 配置别名 alias: { // 约定:使用 @ 表示 src 文件所在路径 '@': path.resolve(__dirname, 'src') } } }
-
package.json
// 将 start/build/test 三个命令修改为 craco 方式 "scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "react-scripts eject" }
8、@别名路径提示
-
1、在项目根目录创建
jsconfig.json
配置文件 -
2、在配置文件中添加一下配置
-
jsconfig.json
{ "compilerOptions": { "baseUrl": "./", "paths": { "@/*": ["src/*"] } } }
-
vscode 会自动读取 jsconfig.json 中的配置,让 vscode 知道@就是 src 目录
9、路由鉴权实现
-
实现步骤
-
1、在 components 目录中,创建 AuthRoute/index.js 文件
-
2、判断是否登录
-
3、登录时,直接渲染相应页面组件
-
4、未登录时,重定向到登录页面
-
5、将需要鉴权的页面路由配置,替换为 AuthRoute 组件渲染
-
components/AuthRoute/index.js
// 1、判断 token 是否存在
// 2、如果存在 直接正常渲染
// 3、如果不存在 重定向到登录路由
// 高阶组件:把一个组件当初另外一个组件的参数传入 然后通过一定的判断 返回新的组件
import { getToken } from "@/utils"
import { Naviagate } from "react-router-dom"
function AuthRoute({children}) {
const isToken = getToken();
if(isToken) {
return <>{children}</>
}else {
return <Navigate to="/login" replace>
}
}
// <AuthComponent><Layout /></AuthComponent>
// 登录:<><Layout /></>
// 非登录: <Navigate to="/login" replace/>
export { AuthRoute }
-
src/app.js
import { Router, Routes,Route } from "react-router-dom" import { AuthRoute } from "@/components/AuthRoute" import Layout from "@/page/Layout" import Login from "@/pages/Login" function App() { return( <Router> <Routes> /*需要鉴权的路由*/ <Route path="/*" element={<AuthRoute><Layout /></AuthRoute>}/> /**不需要鉴权的路由/ <Route path="/login" element={<Login />} /> </Routes> </Router> ) }
-
如何在 react 获取 dom -> useRef
-
在什么地方获取 dom 节点 -> useEffect
// 获取频道列表 const [ channels,setChannels] = useState(); useEffect(() => { async function featchChannels() { const res = await http.get('/xxx') setChannels(res.data.channels) } featchChannels(); }.[])
-
如果异步请求函数需要依赖一些数据的变化而重新执行, 推荐把它写到内部
-
统一不抽离函数到外面 只有涉及到异步请求的函数 都放到 useEffect 内部
-
本质区别:写到外面每次组件更新都会重新进行函数初始化 这本身就是一次性性能消耗,
-
而写到 useEffect 中 只会在依赖项发生变化的时候 函数才会进行重新初始化
-
避免性能损失
// 文章列表管理 统一管理数据 将来修改给setList传对象 const [list, setList ] = useState({ list: [], // 文章列表 count: 0, // 文章数量 }) // 文章参数管理 const [ params, setParams ] = useState({ page: 1, per_page: 10 })
富文本编辑器
-
实现步骤
-
1、安装富文本编辑器:yarn add react-quill
-
2、导入富文本编辑器组件以及样式文件
-
3、渲染富文本编辑器组件
-
4、通过 Form 组件的 initialValues 为富文本编辑器设置初始值,否则会报错
-
5、调整富文本编辑器的样式
import React, { useState } from 'react'; import ReactQuill from 'react-quill'; import 'react-quill/dist/quill.snow.css'; function MyComponent() { const [value, setValue] = useState(''); return <ReactQuill theme="snow" value={value} onChange={setValue} />; }
暂存图片列表实现
问题描述
- 如果当前为三图模式,已经完成了上传,选择单图只显示一张,再切换到三图继续显示三张,该如何实现?
实现思路
- 在上传完毕之后通过 ref 存储所有图片,需要几张就显示几张,其实也就是把 ref 当仓库,用多少拿多少
实现步骤
-
1、通过 useRef 创建一个暂存仓库,在上传完毕图片的时候把图片列表存入
-
2、如果是单图模式,就从仓库里取第一张图,以数组的形式存入 fileList
-
3、如果是三图模式,就把仓库里所有的图片,以数组的形式存入 fileList
const Publish = () => { // 1、声明一个暂存仓库 const fileListRef = useRef([]) // 2、上传图片时,将所有图片存储到ref中 const onUploadChange = info => { fileListRef.current = imgUrls } // 3、切换图片类型 const changeType = e => { // 使用原始数据作为判断条件 const count = e.target.value setMaxCount(count) if(count === 1) { // 单图,只展示第一张 const firstImg = fileListRef.current[0] setFileList(!firstImg ? [] : [firstImg]) }else if(count === 3) { // 三图展示所有图片 setFileList(fileListRef.current) } } }
- 获取传参的 id
?id=xxx
- const [ params ] = useSearchParams();
- const id = params.get('id')
路由懒加载
使用步骤
-
1、在 App 组件中,导入 Suspense 组件
-
2、在路由 Router 内部,使用 Suspense 组件包裹组件内容
-
3、为 Suspense 组件提供 fallback 属性,指定 loading 占位内容
-
4、导入 lazy 函数,并修改为懒加载方式导入路由组件
import { Routes, Route } from 'react-router-dom' import { HistoryRouter, history } from './utils/history' import { AuthRoute } from './components/AuthRoute' // 导入必要组件 import { lazy, Suspense } from 'react' // 按需导入路由组件 const Login = lazy(() => import('./pages/Login')) const Layout = lazy(() => import('./pages/Layout')) const Home = lazy(() => import('./pages/Home')) const Article = lazy(() => import('./pages/Article')) const Publish = lazy(() => import('./pages/Publish')) function App () { return ( <HistoryRouter history={history}> <Suspense fallback={ <div style={{ textAlign: 'center', marginTop: 200 }} > loading... </div> } > <Routes> {/* 需要鉴权的路由 */} <Route path="/" element={ <AuthRoute> <Layout /> </AuthRoute> }> {/* 二级路由默认页面 */} <Route index element={<Home />} /> <Route path="article" element={<Article />} /> <Route path="publish" element={<Publish />} /> </Route> {/* 不需要鉴权的路由 */} <Route path='/login' element={<Login />} /> </Routes> </Suspense> </HistoryRouter> ) } export default App