react基础+pc项目

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)
  • 作用: 用于指定导航链接,完成路由跳转

  • 语法说明:组件通过 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 将所有工程化配置,都隐藏在了 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
    
posted @ 2022-11-22 17:07  不完美的完美  阅读(135)  评论(0编辑  收藏  举报