React快速入门(四)ReactHooks

2024-03-12 11:12

本文主要是介绍React快速入门(四)ReactHooks,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • ReactHooks
    • useState
    • useRef
    • useEffect
    • useLayoutEffect
    • useInsertionEffect
    • useReducer
    • useSyncExternalStore
    • useContext
    • useMemo
    • useCallback
    • useTransition
    • useDeferredValue
    • useDebugValue
    • useId
    • 自定义Hook


ReactHooks

  • Hook是React 16.8的新增特性,它可以让我们在不编写class的情况下使用state以及其他的React特性(比如生命周期)。
  • class组件相对于函数式组件的优势:
    • class组件可以定义自己的state,用来保存组件自己内部的状态
      • 函数式组件不可以,因为函数每次调用都会产生新的临时变量
    • class组件有自己的生命周期,我们可以在对应的生命周期中完成自己的逻辑
      • 在componentDidMount中发送网络请求,并且该生命周期函数只会执行一次
      • 函数式组件在之前,如果在函数中发送网络请求,意味着每次重新渲染都会重新发送一网络请求
    • class组件可以在状态改变时只会重新执行render函数以及我们希望重新调用的生命周期函数componentDidUpdate等
      • 函数式组件在重新渲染时,整个函数都会被执行,似乎没有什么地方可以只让它们调用一次
  • class组件存在的问题
    • 复杂组件难以理解,逻辑冗余,代码复杂度高
    • class难以理解,需要关注this的指向问题
    • 组件复用比较难,需要通过高阶组件来实现复用

Hook的出现

  • 可以让我们在不编写class的情况下使用state以及其他的React特性;我们可以由此延伸出非常多的用法,来解决class组件存在的问题
  • Hook的使用场景:
    • Hook的出现基本可以代替我们之前所有使用class组件的地方
    • 但是如果是一个旧的项目,你并不需要直接将所有的代码重构为Hooks,因为它完全向下兼容,你可以渐进式的来使用它
    • Hook只能在函数组件中使用,不能在类组件,或者函数组件之外的地方使用
  • Hook是:完全可选的、100%向后兼容的、当前可用
  • 可以参考:「React 进阶」 React 全部 Hooks 使用大全 (包含 React v18 版本 ) - 掘金 (juejin.cn)

useState

  • useState会帮助我们定义一个state变量,useState是一种新方法,它与class里面的this.state提供的功能完全相同。
    • 一般来说,在函数退出后变量就会”消失”,而state 中的变量会被React保留。
    • useState接受唯一一个参数,在第一次组件被调用时使用来作为初始化值。(如果没有传递参数,那么初始化值为undefined)。
    • useState的返回值是一个数组,我们可以通过数组的解构,来完成赋值会非常方便。
// 直接给定初始值
const [状态名, set函数] = useState(初始值)
// 函数返回值的形式
const [value, setValue] = useState(() => 初始值)
  • 例如:
//类式组件写法:
import React, { PureComponent } from 'react'export class CounterClass extends PureComponent {constructor(props) {super(props)this.state = {counter: 0}}increment() {this.setState({counter: this.state.counter + 1})}decrement() {this.setState({counter: this.state.counter - 1})}render() {const { counter } = this.statereturn (<div><h2>当前计数: {counter}</h2><button onClick={e => this.increment()}>+1</button><button onClick={e => this.decrement()}>-1</button></div>)}
}export default CounterClass
//函数式组件hook写法://只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
//只能在React的函数组件中调用Hook。不要在其他JavaScript函数中调用 。
import { memo, useState } from "react";function CounterHook(props) {
// useState是一个hook,参数:初始化值,不设置则为undefined;返回值:数组,包含两个元素,[当前值的状态,设置状态值的函数]const [counter, setCounter] = useState(0)return (<div><h2>当前计数: {counter}</h2><button onClick={e => setCounter(counter+1)}>+1</button><button onClick={e => setCounter(counter-1)}>-1</button></div>)
}export default memo(CounterHook)
  • 注意:当函数式组件被重新执行时,不会重复调用 useState() 给数据赋初值,而是会复用上次的 state 值。
// 以函数的形式为状态赋初始值
import React, { useState } from 'react'const App: React.FC = () => {const [date, setDate] = useState(() => {const dt = new Date()return { year: dt.getFullYear(), month: dt.getMonth() + 1, day: dt.getDate() }})return (<div><h1>今日信息:</h1><p>年份:{date.year}年</p><p>月份:{date.month}月</p><p>日期:{date.day}日</p></div>)
}export default App
  • 注意:以函数的形式为状态赋初始值时,只有组件首次被渲染才会执行 fn 函数;当组件被更新时,会以更新前的值作为状态的初始值,赋初始值的函数不会执行。
  • 注意事项
    1. 如果要更新对象类型的值,并触发组件的重新渲染,则必须使用展开运算符或**Object.assign()**生成一个新对象,用新对象覆盖旧对象,才能正常触发组件的重新渲染。
    2. 当连续多次以相同的操作更新状态值时,React 内部会对传递过来的新值进行比较,如果值相同,则会屏蔽后续的更新行为,从而防止组件频繁渲染的问题。这虽然提高了性能,但也带来了一个使用误区。为了解决这个问题,我们可以使用函数的方式给状态赋新值。当函数执行时才通过函数的形参,拿到当前的状态值,并基于它返回新的状态值。
    3. 在函数组件中,我们可以通过 useState 来模拟 forceUpdate 的强制刷新操作。因为只要 useState 的状态发生了变化,就会触发函数组件的重新渲染,从而达到强制刷新的目的。

useRef

  • useRef返回一个ref对象,返回的ref对象在组件的整个生命周期保持不变。
  • 用途:获取DOM元素或子组件的实例对象,存储渲染周期之间共享的数据,这个对象在整个生命周期中可以保存不变
import React, { memo, useRef } from 'react'const App = memo(() => {const titleRef = useRef()const inputRef = useRef()function showTitleDom() {console.log(titleRef.current)inputRef.current.focus()}return (<div><h2 ref={titleRef}>Hello World</h2><input type="text" ref={inputRef} /><button onClick={showTitleDom}>查看title的dom</button></div>)
})export default App
  • 注意事项:

    1. 组件rerender时useRef不会被重复初始化
    2. ref.current变化时不会造成组件的rerender
    3. ref.current 值的变化不会造成组件的 rerender,而且 React 也不会跟踪 ref.current 的变化,因此 ref.current 不可以作为其它 hooks(useMemo、useCallback、useEffect 等) 的依赖项。
    4. ref 的作用是获取实例,但由于函数组件不存在实例,因此无法通过 ref 获取函数组件的实例引用React.forwardRef 会创建一个 React 组件,这个组件能够将其接收到的 ref 属性转发到自己的组件树。
    // 被包装的函数式组件,第一个参数是 props,第二个参数是转发过来的 ref
    const Child = React.forwardRef((props, ref) => {// 省略子组件内部的具体实现
    })
    
    1. 通过uselmperativeHandle可以值暴露固定(forwardRef会将子组件的DOM直接暴露给了父元素)
    useImperativeHandle(通过forwardRef接收到的父组件的ref对象, () => 自定义ref对象, [依赖项数组(可选)])
    
    • 关于第三个参数(依赖项):
      1. 空数组:只在子组件首次被渲染时,执行 useImperativeHandle 中的 fn 回调,从而把 return 的对象作为父组件接收到的 ref。
      2. 依赖项数组:子组件首次被渲染时,依赖项改变时,会执行 useImperativeHandle 中的 fn 回调,从而让父组件通过 ref 能拿到依赖项的新值。
      3. 省略依赖项数组(省略第三个参数):此时,组件内任何 state 的变化,都会导致 useImperativeHandle 中的回调的重新执行。

useEffect

  • Effect Hook可以让你来完成一些类似于class中生命周期的功能;事实上,类似于网络请求、手动更新DOM、一些事件的监听,都是React更新DOM的一些副作用(Side Effects) ;所以对于完成这些功能的Hook被称之为Effect Hook
  • useEffect要求我们传入一个回调函数,在React执行完更新DOM操作之后,就会回调这个函数;默认情况下,无论是第一次渲染之后,还是每次更新之后,都会执行这个回调函数
  • class组件的编写过程中,某些副作用的代码,需要在componentWillUnmount中清除。useEffect传入的回调函数A本身可以有一个返回值,这个返回值是另外一个回调函数B。这是effect可选的清除机制,每个effect都可以返回一个清除函数,可以将添加和移除订阅的逻辑放在一起。React会在组件更新和卸载的时候执行清除操作。
  • useEffect有两个参数:
    • 参数一:执行的回调函数;
    • 参数二:依赖项,该useEffect在哪些state发生变化时,才重新执行;(受谁的影响)
      • 省略依赖项数组,每次更新渲染完毕之后都会执行
      • 指定依赖项数组,每次渲染完毕之后,判断依赖项是否变化,再决定是否执行
      • 指定空数组,仅在首次渲染完毕之后,执行唯一的一次
  • 基础语法:
useEffect(() => { /* 依赖项变化时,要触发的回调函数 */ }, [依赖项])
  • 例如:
// 类组件写法
import React, { PureComponent } from 'react'export class App extends PureComponent {constructor() {super()this.state = {counter: 100}}componentDidMount() {document.title = this.state.counter}componentDidUpdate() {console.log('-------')document.title = this.state.counter;}componentWillUnmount() {}render() {const { counter } = this.statereturn (<div><h2>计数: {counter}</h2><button onClick={e => this.setState({ counter: counter+1 })}>+1</button></div>)}
}export default App
// 函数式组件写法
import React, { memo } from 'react'
import { useState, useEffect } from 'react'const App = memo(() => {const [count, setCount] = useState(200)useEffect(() => {// 当前传入的回调函数会在组件被渲染完成后, 自动执行// 网络请求/DOM操作(修改标题)/事件监听document.title = count})return (<div><h2>当前计数: {count}</h2><button onClick={e => setCount(count+1)}>+1</button></div>)
})export default App
  • 清理副作用:如果当前组件中使用了定时器或绑定了事件监听程序,可以在返回的函数中清除定时器或解绑监听程序。
// 组件卸载或依赖项发生变化时执行,react会判断是否存在return函数,如果存在先执行return再执行useEffect函数
useEffect(() => {// 1. 执行副作用操作// 2. 返回一个清理副作用的函数return () => { /* 在这里执行自己的清理操作 */ }
}, [依赖项])
  • 注意事项:
    1. 不要在 useEffect 中改变依赖项的值,会造成死循环。
    2. 依赖项的值不应该是对象,会判断对象的引用;可以使用对象的具体属性或者使用展开运算符或**Object.assign()**生成一个新对象
    3. 多个不同功能的副作用尽量分开声明,不要写到一个 useEffect 中。

useLayoutEffect

  • useEffect会在渲染的内容更新到DOM上执行,异步执行,不会阻塞DOM的更新
  • useLayoutEffect会在渲染的内容更新到DOM上之执行,同步执行,会阻塞DOM的更新
  • 如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect
  • 注意:React 保证了 useLayoutEffect 中的代码以及其中任何计划的状态更新都会在浏览器重新绘制屏幕之前得到处理。

useInsertionEffect

  • 本质上 useInsertionEffect 主要是解决 CSS-in-JS 在渲染中注入样式的性能问题。除非你正在使用 CSS-in-JS 库并且需要注入样式,否则你应该使用 useEffect或者 useLayoutEffect
export default function Index(){React.useInsertionEffect(()=>{/* 动态创建 style 标签插入到 head 中 */const style = document.createElement('style')style.innerHTML = `.css-in-js{color: red;font-size: 20px;}`document.head.appendChild(style)},[])return <div className="css-in-js" > hello , useInsertionEffect </div>
}

useReducer

  • useReducer仅仅是useState的一种替代方案:
    • 在某些场景下,如果state的处理逻辑比较复杂,我们可以通过useReducer来对其进行拆分
    • 或者这次修改的state需要依赖之前的state时,也可以使用;useReducer只是useState的一种替代品,并不能替代Redux
    • 更好的描述“状态”,让代码逻辑更清晰,代码行为更易预测
  • 基础语法:
/*
1. reducer 是一个函数,类似于 (prevState, action) => newState。形参 prevState 表示旧状态,形参 action 表示本次的行为,返回值 newState 表示处理完毕后的新状态。
2. initState 表示初始状态,也就是默认值。
3. initAction 是进行状态初始化时候的处理函数,它是可选的,如果提供了 initAction 函数,则会把 initState 传递给 initAction 函数进行处理,initAction 的返回值会被当做初始状态。
4. 返回值 state 是状态值。dispatch 是更新 state 的方法,让他接收 action 作为参数,useReducer 只需要调用 dispatch(action) 方法传入的 action 即可更新 state。
*/
const [state, dispatch] = useReducer(reducer, initState, initAction?)

使用 Immer 编写更简洁的 reducer 更新逻辑

  1. 安装immer相关的依赖包:
npm install immer use-immer -S
  1. use-immer 中导入 useImmerReducer 函数,并替换掉 React 官方的 useReducer 函数的调用:
// 1. 导入 useImmerReducer
import { useImmerReducer } from 'use-immer'// 父组件
export const Father: React.FC = () => {// 2. 把 useReducer() 的调用替换成 useImmerReducer()const [state, dispatch] = useImmerReducer(reducer, defaultState, initAction)
}
  1. 修改 reducer 函数中的业务逻辑,case 代码块中不再需要 return 不可变的新对象了,只需要在 prevState 上进行修改即可。Immer 内部会复制并返回新对象,因此降低了用户的心智负担。

useSyncExternalStore

  • useSyncExternalStore 是一个订阅外部 store 的 React Hook。 能够让 React 组件在 concurrent(同时调度) 模式下安全地有效地读取外接数据源,在组件渲染过程中能够检测到变化,并且在数据源发生变化的时候,能够调度更新。当读取到外部状态发生了变化,会触发一个强制更新,来保证结果的一致性。
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
/*
参数一:subscribe:订阅函数,接收一个单独的 callback 参数并把它订阅到 store 上。当 store 发生改变,它应当调用被提供的 callback。这会导致组件重新渲染。subscribe 函数会返回清除订阅的函数。参数二:getSnapshot:一个函数,返回组件需要的 store 中的数据快照。在 store 不变的情况下,重复调用 getSnapshot 必须返回同一个值。如果 store 改变,并且返回值也不同了(用 Object.is 比较),React 就会重新渲染组件。参数三:可选 getServerSnapshot:hydration模式下的getSnapshot
*/

useContext

  • Context Hook允许我们通过Hook来直接获取某个Context的值;当组件上层最近的<MyContext.Provider>更新时,该Hook会触发重新渲染,并使用最新传递给MyContext provider的context value值。
  • 使用 props 层层传递数据的维护性太差了,我们可以使用 React.createContext() + useContext() 轻松实现多层组件的数据传递。
import React, { memo, useContext } from 'react'
import { UserContext, ThemeContext } from "./context"const App = memo(() => {// 使用Contextconst user = useContext(UserContext)const theme = useContext(ThemeContext)return (<div><h2>User: {user.name}-{user.level}</h2><h2 style={{color: theme.color, fontSize: theme.size}}>Theme</h2></div>)
})export default App

非侵入的方式使用Context

  • 为了保证父组件中代码的单一性,也为了提高 Provider 的通用性,我们可以考虑把 Context.Provider 封装到独立的 Wrapper 函数式组件中
// 声明 TS 类型
type ContextType = { count: number; setCount: React.Dispatch<React.SetStateAction<number>> }
// 创建 Context 对象
const AppContext = React.createContext<ContextType>({} as ContextType)// 定义独立的 Wrapper 组件,被 Wrapper 嵌套的子组件会被 Provider 注入数据
export const AppContextWrapper: React.FC<React.PropsWithChildren> = (props) => {// 1. 定义要共享的数据const [count, setCount] = useState(0)// 2. 使用 AppContext.Provider 向下共享数据return <AppContext.Provider value={{ count, setCount }}>{props.children}</AppContext.Provider>
}
  • 定义好 Wrapper 组件后,我们可以在 App.tsx 中导入并使用 WrapperLevelA 组件,代码如下:
import React from 'react'
import { AppContextWrapper, LevelA } from '@/components/use_context/base.tsx'const App: React.FC = () => {return (<AppContextWrapper><!-- AppContextWrapper 中嵌套使用了 LevelA 组件,形成了父子关系 --><!-- LevelA 组件会被当做 children 渲染到 Wrapper 预留的插槽中 --><LevelA /></AppContextWrapper>)
}export default App

useMemo

  • 当父组件被重新渲染的时候,也会触发子组件的重新渲染,这样就多出了无意义的性能开销。如果子组件的状态没有发生变化,则子组件是不需要被重新渲染的。在 React Hooks 中,我们可以使用 React.memo 来解决上述的问题,从而达到提高性能的目的。
const 组件 = React.memo(函数式组件)
  • useMemo会返回一个函数的memoized (记忆的)值;在依赖不变的情况下,多次定义的时候,返回的值是相同的
  • useCallback 和 useMemo都是react可用于性能优化的内置hooks。两者的区别在于:useCallback缓存的是一个函数,而useMemo缓存的是计算结果。
// useCallback
// 第一个参数是一个回调函数,useCallback会缓存这个函数,返回缓存的回调函数
// 第二个参数是依赖项,只有当依赖项改变时,才会重新创建这个函数
const memorizedCallback = useCallback(()=>{doSomething(a,b);
},[a,b])// useMemo
// 第一个参数是一个函数,useMemo会缓存函数运行返回的值,返回缓存的值
// 第二个参数是依赖项,只有当依赖改变时,才会重新计算这个值
const memorizedValue = useMemo(()=>computeValue(a,b),[a,b])

useCallback

  • useCallback 会返回一个 memorized 回调函数供组件使用,从而防止组件每次 rerender 时反复创建相同的函数,能够节省内存开销,提高性能。
const countRef = useRef()
countRef.current = count
const increment = useCallback(function foo() {console.log("increment")setCount(countRef.current + 1)
}, [])

useTransition

  • 返回一个状态值表示过渡任务的等待状态,以及一个启动该过渡任务的函数。
  • useTransition 可以将一个更新转为低优先级更新,使其可以被打断不阻塞 UI 对用户操作的响应,能够提高用户的使用体验。它常用于优化视图切换时的用户体验。
const [isPending, startTransition] = useTransition();
// 参数:调用 useTransition 时不需要传递任何参数/*
返回值(数组):
- isPending 布尔值:是否存在待处理的 transition,如果值为 true,说明页面上存在待渲染的部分,可以给用户展示一个加载的提示
- startTransition 函数:调用此函数,可以把状态的更新标记为低优先级的,不阻塞 UI 对用户操作的响应
*/
  • 注意事项:
    1. 传递给 startTransition 的函数必须是同步的。React 会立即执行此函数,并将在其执行期间发生的所有状态更新标记为 transition。异步状态更新不会被标记为 transition。
    2. 标记为 transition 的状态更新将被其他状态更新打断。例如在 transition 中更新图表组件,并在图表组件仍在重新渲染时继续在输入框中输入,React 将首先处理输入框的更新,之后再重新启动对图表组件的渲染工作。
    3. transition 更新不能用于控制文本输入。

useDeferredValue

  • ** useDeferredValue 接受一个值,并返回该值的新副本,该副本将推迟到更紧急地更新之后。**useDeferredValue的作用是一样的效果可以让我们的更新延迟。
  • useTransition 是把 startTransition 内部的更新任务变成了过渡任务transtion;而 useDeferredValue 是把原值通过过渡任务得到新的值,这个值作为延时状态。 也就是说一个是处理一段逻辑,另一个是生产一个新的状态。
  • 有些场景不能使用 useTransition 进行性能优化,因为 useTransition 会把状态更新标记为低优先级被标记为 transition 的状态更新将被其他状态更新打断。因此在高频率输入时,会导致中间的输入状态丢失的问题。
// 根据 kw 得到延迟的 kw
const deferredKw = useDeferredValue(kw);
// 子组件配合React.memo实现中间状态的缓存
  • useDeferredValue 的返回值为一个延迟版的状态
    1. 在组件首次渲染期间,返回值将与传入的值相同
    2. 在组件更新期间,React 将首先使用旧值重新渲染 UI 结构,这能够跳过某些复杂组件的 rerender,从而提高渲染效率。随后,React 将使用新值更新 deferredValue,并在后台使用新值重新渲染是一个低优先级的更新。这也意味着,如果在后台使用新值更新时 value 再次改变,它将打断那次更新。

useDebugValue

  • useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。这个hooks目的就是检查自定义hooks。在生产环境中,useDebugValue 不会产生任何效果,因为它只用于调试目的。
import { useDebugValue } from 'react';function useOnlineStatus() {// ...useDebugValue(isOnline ? 'Online' : 'Offline');// ...
}// useDebugValue(value, format?)
// 参数一:value--值,可以是任何类型
// 参数二:可选的format--格式化函数,不指定的直接显示value

useId

  • useld是一个用于生成横跨服务端和客户端的稳定的唯一ID的同时避免 hydration 不匹配的hook。
import { useId } from 'react';function PasswordField() {const passwordHintId = useId();// ...
}
  • useld是用于react的同构应用开发的,前端的SPA页面并不需要使用它

    • useld可以保证应用程序在客户端和服务器端生成唯一的ID,这样可以有效的避免通过一些手段生成的id不一致,造成hydration mismatch
  • 在进行SSR时,我们的页面会呈现为HTML。但仅HTML不足以使页面具有交互性。例如,浏览器端JavaScript为零的页面不能是交互式的(没有JavaScript事件处理程序来响应用户操作,例如单击按钮)。为了使我们的页面具有交互性,除了在Node.js中将页面呈现为HTML之外,我们的UI框架(Vue/React…)还在浏览器中加载和呈现页面。(它创建页面的内部表示,然后将内部表示映射到我们在Node.js中呈现的HTML的DOM元素。)
    这个过程称为hydration。


自定义Hook

  • 自定义Hook本质上只是一种函数代码逻辑的抽取,严格意义上来说,它本身并不算React的特性。
  • 举例::
// 使用本地存储
import { useEffect } from "react"
import { useState } from "react"function useLocalStorage(key) {// 1.从localStorage中获取数据, 并且数据数据创建组件的stateconst [data, setData] = useState(() => {const item = localStorage.getItem(key)if (!item) return ""return JSON.parse(item)})// 2.监听data改变, 一旦发生改变就存储data最新值useEffect(() => {localStorage.setItem(key, JSON.stringify(data))}, [data])// 3.将data/setData的操作返回给组件, 让组件可以使用和修改值return [data, setData]
}export default useLocalStorage
// 获取当前鼠标位置
import { useEffect, useState } from "react";export const useMousePosition = (delay: number = 0) => {const [position, setPosition] = useState({ x: 0, y: 0 });useEffect(() => {let timer: null | NodeJS.Timeout = null;const mouseMoveHandler = (e: MouseEvent) => {if (timer !== null) return;timer = setTimeout(() => {setPosition({ x: e.clientX, y: e.clientY });timer = null;}, delay);};window.addEventListener("mousemove", mouseMoveHandler);return () => window.removeEventListener("mousemove", mouseMoveHandler);}, []);return position;
};

这篇关于React快速入门(四)ReactHooks的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/801081

相关文章

CSS3 布局样式及其应用举例

《CSS3布局样式及其应用举例》CSS3的布局特性为前端开发者提供了无限可能,无论是Flexbox的一维布局还是Grid的二维布局,它们都能够帮助开发者以更清晰、简洁的方式实现复杂的网页布局,本文给... 目录深入探讨 css3 布局样式及其应用引言一、CSS布局的历史与发展1.1 早期布局的局限性1.2

使用animation.css库快速实现CSS3旋转动画效果

《使用animation.css库快速实现CSS3旋转动画效果》随着Web技术的不断发展,动画效果已经成为了网页设计中不可或缺的一部分,本文将深入探讨animation.css的工作原理,如何使用以及... 目录1. css3动画技术简介2. animation.css库介绍2.1 animation.cs

CSS引入方式和选择符的讲解和运用小结

《CSS引入方式和选择符的讲解和运用小结》CSS即层叠样式表,是一种用于描述网页文档(如HTML或XML)外观和格式的样式表语言,它主要用于将网页内容的呈现(外观)和结构(内容)分离,从而实现... 目录一、前言二、css 是什么三、CSS 引入方式1、行内样式2、内部样式表3、链入外部样式表四、CSS 选

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

SpringBoot快速搭建TCP服务端和客户端全过程

《SpringBoot快速搭建TCP服务端和客户端全过程》:本文主要介绍SpringBoot快速搭建TCP服务端和客户端全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录TCPServerTCPClient总结由于工作需要,研究了SpringBoot搭建TCP通信的过程

Nginx部署React项目时重定向循环问题的解决方案

《Nginx部署React项目时重定向循环问题的解决方案》Nginx在处理React项目请求时出现重定向循环,通常是由于`try_files`配置错误或`root`路径配置不当导致的,本文给大家详细介... 目录问题原因1. try_files 配置错误2. root 路径错误解决方法1. 检查 try_f

在React聊天应用中实现图片上传功能

《在React聊天应用中实现图片上传功能》在现代聊天应用中,除了文字和表情,图片分享也是一个重要的功能,本文将详细介绍如何在基于React的聊天应用中实现图片上传和预览功能,感兴趣的小伙伴跟着小编一起... 目录技术栈实现步骤1. 消息组件改造2. 图片预览组件3. 聊天输入组件改造功能特点使用说明注意事项

一文详解如何在Vue3中封装API请求

《一文详解如何在Vue3中封装API请求》在现代前端开发中,API请求是不可避免的一部分,尤其是与后端交互时,下面我们来看看如何在Vue3项目中封装API请求,让你在实现功能时更加高效吧... 目录为什么要封装API请求1. vue 3项目结构2. 安装axIOS3. 创建API封装模块4. 封装API请求

全解析CSS Grid 的 auto-fill 和 auto-fit 内容自适应

《全解析CSSGrid的auto-fill和auto-fit内容自适应》:本文主要介绍了全解析CSSGrid的auto-fill和auto-fit内容自适应的相关资料,详细内容请阅读本文,希望能对你有所帮助... css  Grid 的 auto-fill 和 auto-fit/* 父元素 */.gri

POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能

《POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能》ApachePOI是一个流行的Java库,用于处理MicrosoftOffice格式文件,提供丰富API来创建、读取和修改O... 目录前言:Apache POIEasyPoiEasyExcel一、EasyExcel1.1、核心特性