React Hooks 学习笔记

举报
楚楚冻人玥玥仙女 发表于 2021/11/19 02:31:26 2021/11/19
【摘要】 React Hooks 学习笔记 文章出处: 拉 勾 大前端 高薪训练营 1. React Hooks 介绍 1.1 React Hooks 是用来做什么的 对函数型组件进...

React Hooks 学习笔记

文章出处: 拉 勾 大前端 高薪训练营

1. React Hooks 介绍

1.1 React Hooks 是用来做什么的

对函数型组件进行增强,让函数型组件可以存储状态,可以拥有处理副作用的能力,让开发者在不使用类组件的情况下,实现相同的功能。

1.2 类组件的不足(Hooks 要解决的问题)

  1. 缺少逻辑复用的机制

为了复用逻辑增加无实际渲染效果的组件,增加了组件层级,显示十分臃肿,增加了调试的难度以及运行效率的降低

  1. 类组件经常会变得很复杂难以维护

将一组相干的业务逻辑拆分到了多个生命周期函数中,在一个生命周期函数内,存在多个不相干的业务逻辑

  1. 类成员方法不能保证 this 指向的正确性

2. React Hooks 使用

Hooks 意为钩子, React Hooks 就是一堆钩子函数, React 通过这些钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能

2.1 useState()

用于为函数组件引入状态

import {useState} from 'react'

function App() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <span>{count}</span>
      <button onClick={()=>setCount(count+1)}> + 1</button>
    </div>
  );
}

export default App;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  1. 接受唯一的参数即状态初始值,初始值可以是任意数据类型。
const [count, setCount] = useState(0)
const [person, setPerson] = useState({ name: '张三', age: 20 })

  
 
  • 1
  • 2
  1. 返回值为数组,数组中存储值和更改状态值的方法,方法名称约定以 set 开头,后面加上状态名称。
const [count, setCount] = useState(0)

  
 
  • 1
  1. 方法可以被调用多次,用以保存不同状态值
<button onClick={()=>setCount(count+1)}> + 1</button>
<button onClick={()=>setPerson({name: '李四', age: 30})}>setPerson</button>
<button onClick={()=>setPerson({...person, name: '李四'})}>setPerson(只改变一个属性,其他属性不变)</button>

  
 
  • 1
  • 2
  • 3
  1. 参数可以是一个函数,函数返回什么,初始状态值就是什么,函数只会被调用一次,在初始值是动态值的情况。
// 当初始值是动态值
// 这样写每次渲染都会执行
const propsCount = props.count || 0
// const [count, setCount] = useState(propsCount)

// 应该这样写
const [count, setCount] = useState(() => {
  return props.count || 0 // 只有第一次执行的时候才会执行
})

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  1. 设置状态值方法的参数可以是一个值也可以是一个函数,设置状态值方法的方法本身是异步的
    如果代码依赖状态值,那要写在回调函数中:
function handleCount () {
  setCount((count) => {
    const newCount = count + 1
    document.title = newCount
    return newCount
  })
}

<button onClick={handleCount}> + 1</button>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.2 useReducer()

useReducer 是另一种让函数组件保存状态的方式,可以将 dispatch 传给子组件使用

import { useReducer } from "react";

export default function App () {

  function reducer (state, action) {
    switch (action.type) {
      case 'increment':
        return state + 1
      case 'decrement':
        return state - 1
      default:
        return state;
    }
  }

  const [count, dispatch] = useReducer(reducer, 0)
  return (
    <div>
      <button onClick={() => dispatch({type: 'decrement'})}>-1</button>
      <span>{count}</span>
      <button onClick={() => dispatch({type: 'increment'})}>+1</button>
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2.3 useContext()

在跨组件层级获取数据时简化获取数据的代码

import { createContext, useContext } from "react";

const countContext = createContext()

export default function App () {

  return (
    <countContext.Provider value={1000}>
      <Foo />
    </countContext.Provider>
  )
}
function Foo () {
  const value = useContext(countContext)
  return (
    <div>I am Foo {value}</div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2.4 useEffect()

让函数型组件拥有处理副作用的能力,类似生命周期函数

  1. useEffect 执行机制

可以把 useEffect 看做 componentDidMount, componentDidUpdate 和 componentWillUnmount 这三个函数的组合

useEffect(() => {}) => componentDidMount, componentDidUpdate
useEffect(() => {}, []) => componentDidMount
useEffect(() => () => {}) => componentDidUpdate, componentWillUnmount
useEffect(() => () => {}, []) => componentWillUnmount

import { useEffect, useState } from "react";
import ReactDOM from 'react-dom'

export default function App () {

  const [count, setCount] = useState(0)

  // 组件挂载完成之后执行,组件数据更新之后执行
  // useEffect(() => {
  //   console.log('123')
  // })

  // 组件挂载完成之后执行
  // useEffect(() => {
  //   console.log('456')
  // }, [])

  useEffect(() => {
    return () => {
      console.log('组件被卸载了')
    }
  })

  return (
    <div>
    <span>{count}</span>
    <button onClick={() => setCount(count+1)}>+1</button>
    <button onClick={() => ReactDOM.unmountComponentAtNode(document.getElementById('root'))}>卸载组件</button>
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  1. useEffect 使用方法

(1). 为 window 对象添加滚动事件
(2). 设置定时器让 count 数值每隔一秒增加 1

import { useEffect, useState } from "react";
import ReactDOM from 'react-dom'

export default function App () {

  const [count, setCount] = useState(0)
  function onScroll () {
    console.log('页面滚动了')
  }

  useEffect(() => {
    window.addEventListener('scroll', onScroll)
    return () => {
      window.removeEventListener('scroll', onScroll)
    }
  }, [])

  useEffect(() => {
    const timerId = setInterval(() => {
      setCount(count => {
        const newCount = count + 1
        document.title = newCount
        return newCount
      })
    }, 1000);
    return () => {
      clearTimeout(timerId)
    }
  }, [])

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => { ReactDOM.unmountComponentAtNode(document.getElementById('root')) }} >卸载组件</button>
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  1. useEffect 解决的问题
    (1). 按照用途将代码进行分类(将一组相同的业务逻辑归置到了同一个副作用函数中)
    (2). 简化重复代码,是组件内部代码更加清晰

  2. 只有指定数据发生变化时触发 effect

import { useEffect, useState } from "react";

export default function App () {

  const [count, setCount] = useState(0)
  const [person, setPerson] = useState({name: '张三'})
  
  useEffect(() => {
    // person 的变化不会触发 useEffect , 因为第二个数组参数中只监听了 count
    document.title = count
    console.log(111)
  }, [count])

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}> + 1</button>
      <button onClick={() => setPerson({name: '李四'})}>setPerson</button>
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  1. useEffect 钩子函数结合异步函数
    useEffect 中的参数函数不能是异步函数,因为 useEffect 函数要返回清理资源的函数,如果是异步函数就变成了返回 Promise. 可以写成自执行函数的形式:
  useEffect(() => {
    (async () => {
      await axios.get()
    })()
  }, [])

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

2.5 useMemo()

useMemo 的行为类似 Vue 中的计算属性,可以检测某个值的变化,根据变化只计算新值。
useMemo 会缓存计算结果,如果检测子没有发生变化,及时组建重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算。

import { useState, useMemo } from "react";

export default function App () {

  const [count, setCount] = useState(0)
  const [bool, setBool] = useState(true)
  const result = useMemo(() => {
    console.log('111') // 只有 count 改变才会重新执行这个回调函数
    return count * 2
  }, [count])

  return (
    <div>
      <span>{count} {result}</span>
      <span>{bool ? '真' : '假'}</span>
      <button onClick={() => setCount(count + 1)}> + 1</button>
      <button onClick={() => setBool(!bool)}>setBool</button>
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2.6 memo 方法

性能优化,如果本组件中的数据没有发生变化,阻止组件更新,类似类组件中的 PureComponent 和 shouldComponentUpdate

import { memo } from 'react'

const Foo = memo(function Foo () {
  return <div>I am Foo</div>
})

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

2.7 useCallback()

性能优化,缓存函数,使用组件重新渲染时得到相同的函数实例。否则每次父组件渲染,函数变量的实例都会变化,导致里层组件被重新渲染

import { useState, memo, useCallback } from "react";



const Foo = memo(function Foo (props) {
  console.log('Foo 重新渲染了')
  return <div>
    <span>I am Foo</span>
    <button onClick={props.resetCount}>resetCount</button>
  </div>
})


export default function App () {

  const [count, setCount] = useState(0)
  const resetCount = useCallback(() => {
    setCount(0)
  }, [setCount])

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}> + 1</button>
      <Foo resetCount={resetCount} />
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

2.8 useRef()

2.8.1 获取DOM元素对象
import { useRef } from "react";

export default function App () {
  const box = useRef()
  return (
    <div ref={box}>
      <button onClick={() => console.log(box)}> DIV </button>
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
2.8.2 保存数据(跨组件周期)

即使组件重新渲染,保存的数据仍然还在,保存的数据被更改不会触发组件重新渲染。

import { useRef, useState, useEffect} from "react";

export default function App () {
  const [count, setCount] = useState(0)
  let timeId = useRef() // 夸组件生命周期
  useEffect(() => {
    // 使用这个 ref 的 current 属性存储数据
    timeId.current = setInterval(() => {
      setCount(count => count + 1)
    }, 1000);
  }, [])

  const stopCount = () => {
    console.log(timeId.current)
    clearInterval(timeId.current)
  }

  const box = useRef()
  return (
    <div ref={box}>
      <span>{count}</span>
      <button onClick={() => stopCount()}> 停止 </button>
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

3. 自定义 Hook (为了在组件之间形成逻辑共享)

自定义 Hook 是标准的封装和共享逻辑的方式
自定义 Hook 是一个函数,其名称以 use 开头
自定义 Hook 其实就是逻辑和内置 Hook 的组合

如何使用自定义 Hook:

import { useState, useEffect} from "react";
import axios from "axios";

function useGetPost () {
  const [post, setPost] = useState({})
  useEffect(() => {
    axios.get('https://jsonplaceholder.typicode.com/posts/1')
    .then((response) => {
      setPost(response.data)
    })
  }, [])
  return [post, setPost]
}

export default function App () {
  const [post, setPost] = useGetPost()
  return (
    <div>
      <h1>{post.title}</h1>
      <div>{post.body}</div>
    </div>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

封装公共逻辑:

import { useState} from "react";

function useUpdateInput (initialState) {
  const [value, setValue] = useState(initialState)
  return {
    value,
    onChange: e => setValue(e.target.value)
  }
}
export default function App () {
  const usernameInput = useUpdateInput('')
  const passwordInput = useUpdateInput('')

  const submitForm = event => {
    event.preventDefault();
    console.log(usernameInput.value)
    console.log(passwordInput.value)
  }
  return (
    <form onSubmit={submitForm}>
      <input type="text" name="username" {...usernameInput} />
      <input type="password" name="password" {...passwordInput} />
      <input type="submit" />
    </form>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

4. React 路由 Hooks

4.1 react-router-dom 路由提供的钩子函数

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from "react-router-dom";
import App from './App';

ReactDOM.render(
  <Router>
    <App />
  </Router>,
  document.getElementById('root')
);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

App.js

import { Link, Route } from "react-router-dom";
import Home from './pages/Home'
import List from './pages/List'

export default function App () {
  
  return (
    <>
      <div>
        <Link to="/home/zhangsan">首页</Link>
        <Link to="/list">列表页</Link>
      </div>
      <div>
        <Route path="/home/:name" component={Home} />
        <Route path="/list" component={List} />
      </div>
    </>
  )
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Home.js

import { useHistory, useLocation, useRouteMatch, useParams } from "react-router-dom";

export default function Home(props) {
  console.log(props)
  console.log(useHistory())
  console.log(useLocation())
  console.log(useRouteMatch())
  console.log(useParams())
  return  <div>
    <h1>Home works</h1>
  </div>;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出结果:

{history: {…}, location: {…}, match: {…}, staticContext: undefined}
{length: 7, action: “PUSH”, location: {…}, createHref: ƒ, push: ƒ, …}
{pathname: “/home/zhangsan”, search: “”, hash: “”, state: undefined, key: “o6w5y3”}
{path: “/home/:name”, url: “/home/zhangsan”, isExact: true, params: {…}}
{name: “zhangsan”}

List.js

export default function List(props) {
  console.log(props)
  return  <div>
    <h1>List works</h1>
  </div>;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

5. useState 钩子函数的实现原理

使用数组 state 存储状态,用数组 setters 存储 setState方法,利用闭包管理,将 下标 stateIndex 缓存在闭包中,创建 对应的 setState.

// import { useState } from 'react'
import ReactDOM from 'react-dom'
let state = []
let setters = []
let stateIndex = 0

function createSetter (index) {
  return function (newState) {
    state[index] = newState
    render()
  }
}

function useState (initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  setters.push(createSetter(stateIndex))
  let value = state[stateIndex]
  let setter = setters[stateIndex]
  stateIndex ++
  return [value, setter]
}

function render () {
  stateIndex = 0
  ReactDOM.render(
    <App/>,
    document.getElementById('root')
  )
}

export default function App () {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('张三')
  return <div>
    <span>{count}</span>
    <button onClick={() => setCount(count + 1)}> + 1</button>
    <span>{name}</span>
    <button onClick={() => setName('李四')}> 李四</button>
  </div>
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

6. useEffect 钩子函数的实现原理

// import { useState } from 'react'
import ReactDOM from 'react-dom'
let state = []
let setters = []
let stateIndex = 0

function createSetter (index) {
  return function (newState) {
    state[index] = newState
    render()
  }
}

function useState (initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  setters.push(createSetter(stateIndex))
  let value = state[stateIndex]
  let setter = setters[stateIndex]
  stateIndex ++
  return [value, setter]
}

function render () {
  stateIndex = 0
  effectIndex = 0
  ReactDOM.render(
    <App/>,
    document.getElementById('root')
  )
}

// 上一次的依赖值
let prevDepsAry = []
let effectIndex = 0

/**
 * useEffect
 * @param {function} callback 回调函数
 * @param {Array} depsAry 依赖数组
 * @returns {function} 清理函数
 */
function useEffect (callback, depsAry) {
  if (Object.prototype.toString.call(callback) !== '[object Function]') throw new Error('useEffect 第一个参数必须是一个函数')
  if (typeof depsAry === 'undefined') {
    // 没有传递
    callback()
  } else {
    // 判断 depsAry 是不是数组
    if (Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error('useEffect 第二个参数必须是一个数组')
    // 获取上一次的状态值
    let prevDeps = prevDepsAry[effectIndex]
    // 将当前的依赖值和上一次的依赖值作对比,如果有变化,调用 callback
    let hasChanged = prevDeps ? !depsAry.every((dep, index) => dep === prevDeps[index]) : true
      // 判断值是否有变化
    if (hasChanged) {
      callback()
    }
    // 同步依赖值
    prevDepsAry[effectIndex++] = depsAry
  }
}

export default function App () {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('张三')

  useEffect(() => {
    console.log('Hello')
  }, [count])

  useEffect(() => {
    console.log('World')
  }, [name])

  // 测试不传监听数据的情况
  useEffect(() => {
    console.log('xxx')
  }, [])

  return <div>
    <span>{count}</span>
    <button onClick={() => setCount(count + 1)}> + 1</button>
    <span>{name}</span>
    <button onClick={() => setName('李四')}> 李四</button>
  </div>
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

7. useReducer 钩子函数的实现原理

// import { useState } from 'react'
// import { useReducer } from 'react'
import ReactDOM from 'react-dom'
let state = []
let setters = []
let stateIndex = 0

function createSetter (index) {
  return function (newState) {
    state[index] = newState
    render()
  }
}

function useState (initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  setters.push(createSetter(stateIndex))
  let value = state[stateIndex]
  let setter = setters[stateIndex]
  stateIndex ++
  return [value, setter]
}

function render () {
  stateIndex = 0
  effectIndex = 0
  ReactDOM.render(
    <App/>,
    document.getElementById('root')
  )
}

// 上一次的依赖值
let prevDepsAry = []
let effectIndex = 0

/**
 * useEffect
 * @param {function} callback 回调函数
 * @param {Array} depsAry 依赖数组
 * @returns {function} 清理函数
 */
function useEffect (callback, depsAry) {
  if (Object.prototype.toString.call(callback) !== '[object Function]') throw new Error('useEffect 第一个参数必须是一个函数')
  if (typeof depsAry === 'undefined') {
    // 没有传递
    callback()
  } else {
    // 判断 depsAry 是不是数组
    if (Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error('useEffect 第二个参数必须是一个数组')
    // 获取上一次的状态值
    let prevDeps = prevDepsAry[effectIndex]
    // 将当前的依赖值和上一次的依赖值作对比,如果有变化,调用 callback
    let hasChanged = prevDeps ? !depsAry.every((dep, index) => dep === prevDeps[index]) : true
      // 判断值是否有变化
    if (hasChanged) {
      callback()
    }
    // 同步依赖值
    prevDepsAry[effectIndex++] = depsAry
  }
}

function useReducer (reducer, initialState) {
  const [state, setState] = useState(initialState)
  function dispatch (action) {
    const newState = reducer(state, action)
    setState(newState)
  }
  return [state, dispatch]
}

export default function App () {

  function reducer(state, action) {
    switch (action.type) {
      case 'increment':
        return state + 1
      case 'decrement':
        return state - 1
      default:
        return state
    }
  }

  const [cnt, dispatch] = useReducer(reducer, 0)

  return <div>
    <div>
      <button onClick={() => dispatch({type: 'increment'})}> + 1</button>
      <span>{cnt}</span>
      <button onClick={() => dispatch({type: 'decrement'})}> - 1</button>
    </div>
  </div>
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

文章来源: blog.csdn.net,作者:爱玲姐姐,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jal517486222/article/details/112257091

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。