React速通笔记
相关视频:
黑马程序员前端React18入门到实战视频教程,从react+hooks核心基础到企业级项目开发实战(B站评论、极客园项目等)及大厂面试全通关_哔哩哔哩_bilibili
一、React介绍
React由Meta公司开发,是一个用于 构建Web和原生交互界面的库
React的优势
相较于传统基于DOM开发的优势:
-
组件化的开发方式
-
不错的性能
相较于其它前端框架的优势:
-
丰富的生态
-
跨平台支持
React的市场情况:全球最流行,大厂必备
开发环境创建
使用vite安装:
npm create vite@latest my-app -- --template react
二、JSX基础
什么是JSX
概念:JSX是JavaScript和XMl(HTML)的缩写,表示在JS代码中编写HTML模版结构,它是React中构建UI的方式
const message = 'this is message'
function App(){return (<div><h1>this is title</h1>{message}</div>)
}
优势:
-
HTML的声明式模版写法
-
JavaScript的可编程能力
JSX的本质
JSX并不是标准的JS语法,它是 JS的语法扩展,浏览器本身不能识别,需要通过解析工具做解析之后才能在浏览器中使用
JSX高频场景-JS表达式
在JSX中可以通过
大括号语法{}
识别JavaScript中的表达式,比如常见的变量、函数调用、方法调用等等
-
使用引号传递字符串
-
使用JS变量
-
函数调用和方法调用
-
使用JavaScript对象
注意:if语句、switch语句、变量声明不属于表达式,不能出现在{}中
const message = 'this is message'
function getAge(){return 18
}
function App(){return (<div><h1>this is title</h1>{/* 字符串识别 */}{'this is str'}{/* 变量识别 */}{message}{/* 变量识别 */}{message}{/* 函数调用 渲染为函数的返回值 */}{getAge()}</div>)
}
JSX高频场景-列表渲染
在JSX中可以使用原生js种的
map方法
实现列表渲染
const list = [{id:1001, name:'Vue'},{id:1002, name: 'React'},{id:1003, name: 'Angular'}
]
function App(){return (<ul>{list.map(item=><li key={item.id}>{item}</li>)}</ul>)
}
JSX高频场景-条件渲染
在React中,可以通过逻辑与运算符&&、三元表达式(?:) 实现基础的条件渲染
const flag = true
const loading = falsefunction App(){return (<>{flag && <span>this is span</span>}{loading ? <span>loading...</span>:<span>this is span</span>}</>)
}
JSX高频场景-复杂条件渲染
需求:列表中需要根据文章的状态适配 解决方案:自定义函数 + 判断语句
const type = 1 // 0|1|3
function getArticleJSX(){if(type === 0){return <div>无图模式模版</div>}else if(type === 1){return <div>单图模式模版</div>}else(type === 3){return <div>三图模式模版</div>}
}
function App(){return (<>{ getArticleJSX() }</>)
}
React的事件绑定
基础实现
React中的事件绑定,通过语法
on + 事件名称 = { 事件处理程序 }
,整体上遵循驼峰命名法
function App(){const clickHandler = ()=>{console.log('button按钮点击了')}return (<button onClick={clickHandler}>click me</button>)
}
使用事件参数
在事件回调函数中设置形参e即可
function App(){const clickHandler = (e)=>{console.log('button按钮点击了', e)}return (<button onClick={clickHandler}>click me</button>)
}
传递自定义参数
语法:事件绑定的位置改造成箭头函数的写法,在执行clickHandler实际处理业务函数的时候传递实参
function App(){const clickHandler = (name)=>{console.log('button按钮点击了', name)}return (<button onClick={()=>clickHandler('jack')}>click me</button>)
}
注意:不能直接写函数调用,这里事件绑定需要一个函数引用
同时传递事件对象和自定义参数
语法:在事件绑定的位置传递事件实参e和自定义参数,clickHandler中声明形参,注意顺序对应
function App(){const clickHandler = (name,e)=>{console.log('button按钮点击了', name,e)}return (<button onClick={(e)=>clickHandler('jack',e)}>click me</button>)
}
React组件基础使用
组件是什么
概念:一个组件就是一个用户界面的一部分,它可以有自己的逻辑和外观,组件之间可以互相嵌套,也可以服用多次
组件基础使用
在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI, 渲染组件只需要把组件当成标签书写即可
// 1. 定义组件
function Button(){return <button>click me</button>
}
// 2. 使用组件
function App(){return (<div>{/* 自闭和 */}<Button/>{/* 成对标签 */}<Button></Button></div>)
}
组件状态管理-useState
基础使用
useState 是一个 React Hook(函数),它允许我们向组件添加一个
状态变量
, 从而控制影响组件的渲染结果 和普通JS变量不同的是,状态变量一旦发生变化组件的视图UI也会跟着变化(数据驱动视图)
function App(){const [ count, setCount ] = React.useState(0)return (<div><button onClick={()=>setCount(count+1)}>{ count }</button></div>)
}
状态的修改规则
在React中状态被认为是只读的,我们应该始终
替换它而不是修改它
, 直接修改状态不能引发视图更新
修改对象状态
对于对象类型的状态变量,应该始终给set方法一个
全新的对象
来进行修改
组件的基础样式处理
React组件基础的样式控制有俩种方式,行内样式和class类名控制
<div style={{ color:'red'}}>this is div</div>
.foo{color: red;
}
import './index.css'
function App(){return (<div><span className="foo">this is span</span></div>)
}
React表单控制
受控绑定
概念:使用React组件的状态(useState)控制表单的状态
function App(){const [value, setValue] = useState('')return (<input type="text" value={value} onChange={e => setValue(e.target.value)}/>)
}
非受控绑定
概念:通过获取DOM的方式获取表单的输入数据
function App(){const inputRef = useRef(null)
const onChange = ()=>{console.log(inputRef.current.value)}return (<input type="text" ref={inputRef}onChange={onChange}/>)
}
三、React组件通信
概念:组件通信就是
组件之间的数据传递
, 根据组件嵌套关系的不同,有不同的通信手段和方法
-
A-B 父子通信
-
B-C 兄弟通信
-
A-E 跨层通信
父子通信-父传子
基础实现
实现步骤
-
父组件传递数据 - 在子组件标签上绑定属性
-
子组件接收数据 - 子组件通过props参数接收数据
function Son(props){return <div>{ props.name }</div>
}
function App(){const name = 'this is app name'return (<div><Son name={name}/></div>)
}
props说明
props可以传递任意的合法数据,比如数字、字符串、布尔值、数组、对象、函数、JSX
props是只读对象 子组件只能读取props中的数据,不能直接进行修改, 父组件的数据只能由父组件修改
特殊的prop-chilren
场景:当我们把内容嵌套在组件的标签内部时,组件会自动在名为children的prop属性中接收该内容
父子通信-子传父
核心思路:在子组件中调用父组件中的函数并传递参数
function Son({ onGetMsg }){const sonMsg = 'this is son msg'return (<div>{/* 在子组件中执行父组件传递过来的函数 */}<button onClick={()=>onGetMsg(sonMsg)}>send</button></div>)
}
function App(){const getMsg = (msg)=>console.log(msg)return (<div>{/* 传递父组件中的函数到子组件 */}<Son onGetMsg={ getMsg }/></div>)
}
兄弟组件通信
实现思路: 借助
状态提升
机制,通过共同的父组件进行兄弟之间的数据传递
A组件先通过子传父的方式把数据传递给父组件App
App拿到数据之后通过父传子的方式再传递给B组件
// 1. 通过子传父 A -> App
// 2. 通过父传子 App -> B
import { useState } from "react"
function A ({ onGetAName }) {// Son组件中的数据const name = 'this is A name'return (<div>this is A compnent,<button onClick={() => onGetAName(name)}>send</button></div>)
}
function B ({ name }) {return (<div>this is B compnent,{name}</div>)
}
function App () {const [name, setName] = useState('')const getAName = (name) => {setName(name)}return (<div>this is App<A onGetAName={getAName} /><B name={name} /></div>)
}
export default App
跨层组件通信
实现步骤:
-
使用
createContext
方法创建一个上下文对象Ctx -
在顶层组件(App)中通过
Ctx.Provider
组件提供数据 -
在底层组件(B)中通过
useContext
钩子函数获取消费数据
// App -> A -> B
import { createContext, useContext } from "react"
// 1. createContext方法创建一个上下文对象
const MsgContext = createContext()
function A () {return (<div>this is A component<B /></div>)
}
function B () {// 3. 在底层组件 通过useContext钩子函数使用数据const msg = useContext(MsgContext)return (<div>this is B compnent,{msg}</div>)
}
function App () {const msg = 'this is app msg'return (<div>{/* 2. 在顶层组件 通过Provider组件提供数据 */}<MsgContext.Provider value={msg}>this is App<A /></MsgContext.Provider></div>)
}
export default App
React副作用管理-useEffect
useEffect是一个React Hook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作(副作用), 比 如发送AJAX请求,更改DOM等等
说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于“只由渲染引起的操作”
基础使用
需求:在组件渲染完毕之后,立刻从服务端获取平道列表数据并显示到页面中
说明:
-
参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作
-
参数2是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次
warning:接口地址:http://geek.itheima.net/v1_0/channels
useEffect依赖说明
useEffect副作用函数的执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现
依赖项 | 副作用功函数的执行时机 |
---|---|
没有依赖项 | 组件初始渲染 + 组件更新时执行 |
空数组依赖 | 只在初始渲染时执行一次 |
添加特定依赖项 | 组件初始渲染 + 依赖项变化时执行 |
清除副作用
概念:在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,我们想在组件卸载时把这个定时器再清理掉,这个过程就是清理副作用
说明:清除副作用的函数最常见的执行时机是在组件卸载时自动执行
import { useEffect, useState } from "react"function Son () {// 1. 渲染时开启一个定时器useEffect(() => {const timer = setInterval(() => {console.log('定时器执行中...')}, 1000)return () => {// 清除副作用(组件卸载时)clearInterval(timer)}}, [])return <div>this is son</div>
}function App () {// 通过条件渲染模拟组件卸载const [show, setShow] = useState(true)return (<div>{show && <Son />}<button onClick={() => setShow(false)}>卸载Son组件</button></div>)
}export default App
自定义Hook实现
概念:自定义Hook是以
use打头的函数
,通过自定义Hook函数可以用来实现逻辑的封装和复用
// 封装自定义Hook// 问题: 布尔切换的逻辑 当前组件耦合在一起的 不方便复用// 解决思路: 自定义hookimport { useState } from "react"function useToggle () {// 可复用的逻辑代码const [value, setValue] = useState(true)const toggle = () => setValue(!value)// 哪些状态和回调函数需要在其他组件中使用 returnreturn {value,toggle}
}// 封装自定义hook通用思路// 1. 声明一个以use打头的函数
// 2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
// 3. 把组件中用到的状态或者回调return出去(以对象或者数组)
// 4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用function App () {const { value, toggle } = useToggle()return (<div>{value && <div>this is div</div>}<button onClick={toggle}>toggle</button></div>)
}export default App
React Hooks使用规则
-
只能在组件中或者其他自定义Hook函数中调用
-
只能在组件的顶层调用,不能嵌套在if、for、其它的函数中
四、Redux介绍
Redux 是React最常用的集中状态管理工具,类似于Vue中的Pinia(Vuex),可以独立于框架运行 作用:通过集中管理的方式管理应用的状态
为什么要使用Redux?
-
独立于组件,无视组件之间的层级关系,简化通信问题
-
单项数据流清晰,易于定位bug
-
调试工具配套良好,方便调试
Redux快速体验
1. 实现计数器
需求:不和任何框架绑定,不使用任何构建工具,使用纯Redux实现计数器
使用步骤:
-
定义一个 reducer 函数 (根据当前想要做的修改返回一个新的状态)
-
使用createStore方法传入 reducer函数 生成一个store实例对象
-
使用store实例的 subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知)
-
使用store实例的 dispatch方法提交action对象 触发数据变化(告诉reducer你想怎么改数据)
-
使用store实例的 getState方法 获取最新的状态数据更新到视图中
代码实现:
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
<script>// 定义reducer函数 // 内部主要的工作是根据不同的action 返回不同的statefunction counterReducer (state = { count: 0 }, action) {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 }case 'DECREMENT':return { count: state.count - 1 }default:return state}}// 使用reducer函数生成store实例const store = Redux.createStore(counterReducer)
// 订阅数据变化store.subscribe(() => {console.log(store.getState())document.getElementById('count').innerText = store.getState().count
})// 增const inBtn = document.getElementById('increment')inBtn.addEventListener('click', () => {store.dispatch({type: 'INCREMENT'})})// 减const dBtn = document.getElementById('decrement')dBtn.addEventListener('click', () => {store.dispatch({type: 'DECREMENT'})})
</script>
2. Redux数据流架构
Redux的难点是理解它对于数据修改的规则, 下图动态展示了在整个数据的修改中,数据的流向
为了职责清晰,Redux代码被分为三个核心的概念,我们学redux,其实就是学这三个核心概念之间的配合,三个概念分别是:
-
state: 一个对象 存放着我们管理的数据
-
action: 一个对象 用来描述你想怎么改数据
-
reducer: 一个函数 根据action的描述更新state
Redux与React - 环境准备
Redux虽然是一个框架无关可以独立运行的插件,但是社区通常还是把它与React绑定在一起使用,以一个计数器案例体验一下Redux + React 的基础使用
1. 配套工具
在React中使用redux,官方要求安装俩个其他插件 - Redux Toolkit 和 react-redux
-
Redux Toolkit(RTK)- 官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式
-
react-redux - 用来 链接 Redux 和 React组件 的中间件
安装配套工具:
npm i @reduxjs/toolkit react-redux
2. store目录结构设计
-
通常集中状态管理的部分都会单独创建一个单独的
store
目录 -
应用通常会有很多个子store模块,所以创建一个
modules
目录,在内部编写业务分类的子store -
store中的入口文件 index.js 的作用是组合modules中所有的子模块,并导出store
Redux与React - 实现counter
1. 整体路径熟悉
2. 使用React Toolkit 创建 counterStore
import { createSlice } from '@reduxjs/toolkit'
const counterStore = createSlice({// 模块名称独一无二name: 'counter',// 初始数据initialState: {count: 1},// 修改数据的同步方法reducers: {increment (state) {state.count++},decrement(state){state.count--}}
})
// 结构出actionCreater
const { increment,decrement } = counter.actions
// 获取reducer函数
const counterReducer = counterStore.reducer
// 导出
export { increment, decrement }
export default counterReducer
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './modules/counterStore'
export default configureStore({reducer: {// 注册子模块counter: counterReducer}
})
3. 为React注入store
react-redux负责把Redux和React 链接 起来,内置 Provider组件 通过 store 参数把创建好的store实例注入到应用中,链接正式建立
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'
ReactDOM.createRoot(document.getElementById('root')).render(// 提供store数据<Provider store={store}><App /></Provider>
)
4. React组件使用store中的数据
在React组件中使用store中的数据,需要用到一个钩子函数 - useSelector,它的作用是把store中的数据映射到组件中,使用样例如下:
5. React组件修改store中的数据
React组件中修改store中的数据需要借助另外一个hook函数 - useDispatch,它的作用是生成提交action对象的dispatch函数,使用样例如下:
Redux与React - 提交action传参
需求:组件中有俩个按钮
add to 10
和add to 20
可以直接把count值修改到对应的数字,目标count值是在组件中传递过去的,需要在提交action的时候传递参数
实现方式:在reducers的同步修改方法中添加action对象参数,在调用actionCreater的时候传递参数,参数会被传递到action对象payload属性上
Redux与React - 异步action处理
需求理解
实现步骤
-
创建store的写法保持不变,配置好同步修改状态的方法
-
单独封装一个函数,在函数内部return一个新函数,在新函数中 2.1 封装异步请求获取数据 2.2 调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交
-
组件中dispatch的写法保持不变
代码实现
测试接口地址: http://geek.itheima.net/v1_0/channels
import { createSlice } from '@reduxjs/toolkit'
import axios from 'axios'
const channelStore = createSlice({name: 'channel',initialState: {channelList: []},reducers: {setChannelList (state, action) {state.channelList = action.payload}}
})
// 创建异步
const { setChannelList } = channelStore.actions
const url = 'http://geek.itheima.net/v1_0/channels'
// 封装一个函数 在函数中return一个新函数 在新函数中封装异步
// 得到数据之后通过dispatch函数 触发修改
const fetchChannelList = () => {return async (dispatch) => {const res = await axios.get(url)dispatch(setChannelList(res.data.data.channels))}
}
export { fetchChannelList }
const channelReducer = channelStore.reducer
export default channelReducer
import { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { fetchChannelList } from './store/channelStore'
function App () {// 使用数据const { channelList } = useSelector(state => state.channel)useEffect(() => {dispatch(fetchChannelList())}, [dispatch])
return (<div className="App"><ul>{channelList.map(task => <li key={task.id}>{task.name}</li>)}</ul></div>)
}
export default App
五、Zustand快速上手
store/index.js - 创建store
import { create } from 'zustand'
const useStore = create((set) => {return {count: 0,inc: () => {set(state => ({ count: state.count + 1 }))}}
})
export default useStore
app.js - 绑定组件
import useStore from './store/useCounterStore.js'
function App() {const { count, inc } = useStore()return <button onClick={inc}>{count}</button>
}
export default App
异步支持
对于异步操作的支持不需要特殊的操作,直接在函数中编写异步逻辑,最后把接口的数据放到set函数中返回即可
store/index.js - 创建store
import { create } from 'zustand'
const URL = 'http://geek.itheima.net/v1_0/channels'
const useStore = create((set) => {return {count: 0,ins: () => {return set(state => ({ count: state.count + 1 }))},channelList: [],fetchChannelList: async () => {const res = await fetch(URL)const jsonData = await res.json()set({channelList: jsonData.data.channels})}}
})
export default useStore
app.js - 绑定组件
import { useEffect } from 'react'
import useChannelStore from './store/channelStore'
function App() {const { channelList, fetchChannelList } = useChannelStore()useEffect(() => {fetchChannelList()}, [fetchChannelList])
return (<ul>{channelList.map((item) => (<li key={item.id}>{item.name}</li>))}</ul>)
}
export default App
切片模式
场景:当我们单个store比较大的时候,可以采用一种切片模式
进行模块拆分再组合
拆分并组合切片
import { create } from 'zustand'
// 创建counter相关切片
const createCounterStore = (set) => {return {count: 0,setCount: () => {set(state => ({ count: state.count + 1 }))}}
}
// 创建channel相关切片
const createChannelStore = (set) => {return {channelList: [],fetchGetList: async () => {const res = await fetch(URL)const jsonData = await res.json()set({ channelList: jsonData.data.channels })}}
}
// 组合切片
const useStore = create((...a) => ({...createCounterStore(...a),...createChannelStore(...a)
}))
组件使用
function App() {const {count, inc, channelList, fetchChannelList } = useStore()return (<><button onClick={inc}>{count}</button><ul>{channelList.map((item) => (<li key={item.id}>{item.name}</li>))}</ul></>)
}
export default App
六、路由快速上手
1. 什么是前端路由
一个路径 path 对应一个组件 component 当我们在浏览器中访问一个 path 的时候,path 对应的组件会在页面中进行渲染
安装最新的ReactRouter包:
npm i react-router-dom
2. 快速开始
import React from 'react'
import ReactDOM from 'react-dom/client'const router = createBrowserRouter([{path:'/login',element: <div>登录</div>},{path:'/article',element: <div>文章</div>}
])ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={router}/>
)
抽象路由模块
路由导航
1. 什么是路由导航
路由系统中的多个路由之间需要进行路由跳转,并且在跳转的同时有可能需要传递参数进行通信
2. 声明式导航
声明式导航是指通过在模版中通过
<Link/>
组件描述出要跳转到哪里去,比如后台管理系统的左侧菜单通常使用这种方式进行
语法说明:通过给组件的to属性指定要跳转到路由path,组件会被渲染为浏览器支持的a链接,如果需要传参直接通过字符串拼接的方式拼接参数即可
3. 编程式导航
编程式导航是指通过 useNavigate
钩子得到导航方法,然后通过调用方法以命令式的形式进行路由跳转,比如想在登录请求完毕之后跳转就可以选择这种方式,更加灵活
语法说明:通过调用navigate方法传入地址path实现跳转
导航传参
嵌套路由配置
1. 什么是嵌套路由
在一级路由中又内嵌了其他路由,这种关系就叫做嵌套路由,嵌套至一级路由内的路由又称作二级路由,例如:
2. 嵌套路由配置
实现步骤
使用
children
属性配置路由嵌套关系使用
<Outlet/>
组件配置二级路由渲染位置
3. 默认二级路由
当访问的是一级路由时,默认的二级路由组件可以得到渲染,只需要在二级路由的位置去掉path,设置index属性为true
4. 404路由配置
场景:当浏览器输入url的路径在整个路由配置中都找不到对应的 path,为了用户体验,可以使用 404 兜底组件进行渲染
实现步骤:
-
准备一个NotFound组件
-
在路由表数组的末尾,以*号作为路由path配置路由
5. 俩种路由模式
各个主流框架的路由常用的路由模式有俩种,history模式和hash模式, ReactRouter分别由 createBrowerRouter 和 createHashRouter 函数负责创建
路由模式 | url表现 | 底层原理 | 是否需要后端支持 |
---|---|---|---|
history | url/login | history对象 + pushState事件 | 需要 |
hash | url/#/login | 监听hashChange事件 | 不需要 |
七、React 内置 Hook
1. useReducer
基础使用
作用: 让 React 管理多个相对关联的状态数据
import { useReducer } from 'react'
// 1. 定义reducer函数,根据不同的action返回不同的新状态
function reducer(state, action) {switch (action.type) {case 'INC':return state + 1case 'DEC':return state - 1default:return state}
}
function App() {// 2. 使用useReducer分派actionconst [state, dispatch] = useReducer(reducer, 0)return (<>{/* 3. 调用dispatch函数传入action对象 触发reducer函数,分派action操作,使用新状态更新视图 */}<button onClick={() => dispatch({ type: 'DEC' })}>-</button>{state}<button onClick={() => dispatch({ type: 'INC' })}>+</button></>)
}
export default App
更新流程
分派action传参
做法:分派action时如果想要传递参数,需要在action对象中添加一个payload参数,放置状态参数
// 定义reducer
import { useReducer } from 'react'
// 1. 根据不同的action返回不同的新状态
function reducer(state, action) {console.log('reducer执行了')switch (action.type) {case 'INC':return state + 1case 'DEC':return state - 1case 'UPDATE':return state + action.payloaddefault:return state}
}
function App() {// 2. 使用useReducer分派actionconst [state, dispatch] = useReducer(reducer, 0)return (<>{/* 3. 调用dispatch函数传入action对象 触发reducer函数,分派action操作,使用新状态更新视图 */}<button onClick={() => dispatch({ type: 'DEC' })}>-</button>{state}<button onClick={() => dispatch({ type: 'INC' })}>+</button><button onClick={() => dispatch({ type: 'UPDATE', payload: 100 })}>update to 100</button></>)
}
export default App
2. useMemo
作用:它在每次重新渲染的时候能够缓存计算的结果。
看个场景
下面我们的本来的用意是想基于count的变化计算斐波那契数列之和,但是当我们修改num状态的时候,斐波那契求和函数也会被执行,显然是一种浪费
// useMemo
// 作用:在组件渲染时缓存计算的结果
import { useState } from 'react'
function factorialOf(n) {console.log('斐波那契函数执行了')return n <= 0 ? 1 : n * factorialOf(n - 1)
}
function App() {const [count, setCount] = useState(0)// 计算斐波那契之和const sumByCount = factorialOf(count)
const [num, setNum] = useState(0)
return (<>{sum}<button onClick={() => setCount(count + 1)}>+count:{count}</button><button onClick={() => setNum(num + 1)}>+num:{num}</button></>)
}
export default App
useMemo缓存计算结果
思路: 只有count发生变化时才重新进行计算
import { useMemo, useState } from 'react'
function fib (n) {console.log('计算函数执行了')if (n < 3) return 1return fib(n - 2) + fib(n - 1)
}
function App() {const [count, setCount] = useState(0)// 计算斐波那契之和// const sum = fib(count)// 通过useMemo缓存计算结果,只有count发生变化时才重新计算const sum = useMemo(() => {return fib(count)}, [count])
const [num, setNum] = useState(0)
return (<>{sum}<button onClick={() => setCount(count + 1)}>+count:{count}</button><button onClick={() => setNum(num + 1)}>+num:{num}</button></>)
}
export default App
3. React.memo
作用:允许组件在props没有改变的情况下跳过重新渲染
组件默认的渲染机制
默认机制:顶层组件发生重新渲染,这个组件树的子级组件都会被重新渲染
// memo
// 作用:允许组件在props没有改变的情况下跳过重新渲染
import { useState } from 'react'
function Son() {console.log('子组件被重新渲染了')return <div>this is son</div>
}
function App() {const [, forceUpdate] = useState()console.log('父组件重新渲染了')return (<><Son /><button onClick={() => forceUpdate(Math.random())}>update</button></>)
}
export default App
使用React.memo优化
机制:只有props发生变化时才重新渲染 下面的子组件通过 memo 进行包裹之后,返回一个新的组件MemoSon, 只有传给MemoSon的props参数发生变化时才会重新渲染
import React, { useState } from 'react'
const MemoSon = React.memo(function Son() {console.log('子组件被重新渲染了')return <div>this is span</div>
})
function App() {const [, forceUpdate] = useState()console.log('父组件重新渲染了')return (<><MemoSon /><button onClick={() => forceUpdate(Math.random())}>update</button></>)
}
export default App
props变化重新渲染
import React, { useState } from 'react'
const MemoSon = React.memo(function Son() {console.log('子组件被重新渲染了')return <div>this is span</div>
})
function App() {console.log('父组件重新渲染了')
const [count, setCount] = useState(0)return (<><MemoSon count={count} /><button onClick={() => setCount(count + 1)}>+{count}</button></>)
}
export default App
props的比较机制
对于props的比较,进行的是‘浅比较’,底层使用
Object.is
进行比较,针对于对象数据类型,只会对比俩次的引用是否相等,如果不相等就会重新渲染,React并不关心对象中的具体属性
import React, { useState } from 'react'
const MemoSon = React.memo(function Son() {console.log('子组件被重新渲染了')return <div>this is span</div>
})
function App() {// const [count, setCount] = useState(0)const [list, setList] = useState([1, 2, 3])return (<><MemoSon list={list} /><button onClick={() => setList([1, 2, 3])}>{JSON.stringify(list)}</button></>)
}
export default App
说明:虽然俩次的list状态都是 [1,2,3]
, 但是因为组件App俩次渲染生成了不同的对象引用list,所以传给MemoSon组件的props视为不同,子组件就会发生重新渲染
自定义比较函数
如果上一小节的例子,我们不想通过引用来比较,而是完全比较数组的成员是否完全一致,则可以通过自定义比较函数来实现
import React, { useState } from 'react'
// 自定义比较函数
function arePropsEqual(oldProps, newProps) {console.log(oldProps, newProps)return (oldProps.list.length === newProps.list.length &&oldProps.list.every((oldItem, index) => {const newItem = newProps.list[index]console.log(newItem, oldItem)return oldItem === newItem}))
}
const MemoSon = React.memo(function Son() {console.log('子组件被重新渲染了')return <div>this is span</div>
}, arePropsEqual)
function App() {console.log('父组件重新渲染了')const [list, setList] = useState([1, 2, 3])return (<><MemoSon list={list} /><button onClick={() => setList([1, 2, 3])}>内容一样{JSON.stringify(list)}</button><button onClick={() => setList([4, 5, 6])}>内容不一样{JSON.stringify(list)}</button></>)
}
export default App
4. useCallback
看个场景
上一小节我们说到,当给子组件传递一个引用类型
prop的时候,即使我们使用了memo
函数依旧无法阻止子组件的渲染,其实传递prop的时候,往往传递一个回调函数更为常见,比如实现子传父,此时如果想要避免子组件渲染,可以使用 useCallback
缓存回调函数
// useCallBack
import { memo, useState } from 'react'
const MemoSon = memo(function Son() {console.log('Son组件渲染了')return <div>this is son</div>
})
function App() {const [, forceUpate] = useState()console.log('父组件重新渲染了')const onGetSonMessage = (message) => {console.log(message)}
return (<div><MemoSon onGetSonMessage={onGetSonMessage} /><button onClick={() => forceUpate(Math.random())}>update</button></div>)
}
export default App
useCallback缓存函数
useCallback缓存之后的函数可以在组件渲染时保持引用稳定,也就是返回同一个引用
// useCallBack
import { memo, useCallback, useState } from 'react'
const MemoSon = memo(function Son() {console.log('Son组件渲染了')return <div>this is son</div>
})
function App() {const [, forceUpate] = useState()console.log('父组件重新渲染了')const onGetSonMessage = useCallback((message) => {console.log(message)}, [])
return (<div><MemoSon onGetSonMessage={onGetSonMessage} /><button onClick={() => forceUpate(Math.random())}>update</button></div>)
}
export default App
5. forwardRef(已废弃)
作用:允许组件使用ref将一个DOM节点暴露给父组件
import { forwardRef, useRef } from 'react'
const MyInput = forwardRef(function Input(props, ref) {return <input {...props} type="text" ref={ref} />
}, [])
function App() {const ref = useRef(null)
const focusHandle = () => {console.log(ref.current.focus())}
return (<div><MyInput ref={ref} /><button onClick={focusHandle}>focus</button></div>)
}
export default App
从 React 19 开始, ref 可作为 prop 使用 。在 React 18 及更早版本中,需要通过 forwardRef 来获取 ref 。
6. useImperativeHandle
作用:如果我们并不想暴露子组件中的DOM而是想暴露子组件内部的方法
import { forwardRef, useImperativeHandle, useRef } from 'react'
const MyInput = forwardRef(function Input(props, ref) {// 实现内部的聚焦逻辑const inputRef = useRef(null)const focus = () => inputRef.current.focus()
// 暴露子组件内部的聚焦方法useImperativeHandle(ref, () => {return {focus,}})
return <input {...props} ref={inputRef} type="text" />
})
function App() {const ref = useRef(null)
const focusHandle = () => ref.current.focus()
return (<div><MyInput ref={ref} /><button onClick={focusHandle}>focus</button></div>)
}
export default App
八、Hooks 与 TypeScript
useState
简单场景
简单场景下,可以使用TS的自动推断机制,不用特殊编写类型注解,运行良好
const [val, toggle] = React.useState(false)
// `val` 会被自动推断为布尔类型
// `toggle` 方法调用时只能传入布尔类型
复杂场景
复杂数据类型,useState支持通过泛型参数
指定初始参数类型以及setter函数的入参类型
type User = {name: stringage: number
}
const [user, setUser] = React.useState<User>({name: 'jack',age: 18
})
// 执行setUser
setUser(newUser)
// 这里newUser对象只能是User类型
没有具体默认值
实际开发时,有些时候useState的初始值可能为null或者undefined,按照泛型的写法是不能通过类型校验的,此时可以通过完整的类型联合null或者undefined类型即可
type User = {name: Stringage: Number
}
const [user, setUser] = React.useState<User>(null)
// 上面会类型错误,因为null并不能分配给User类型
const [user, setUser] = React.useState<User | null>(null)
// 上面既可以在初始值设置为null,同时满足setter函数setUser的参数可以是具体的User类型
useRef
在TypeScript的环境下,
useRef
函数返回一个只读
或者可变
的引用,只读的场景常见于获取真实dom,可变的场景,常见于缓存一些数据,不跟随组件渲染,下面分俩种情况说明
获取dom
获取DOM时,通过泛型参数指定具体的DOM元素类型即可
function Foo() {// 尽可能提供一个具体的dom type, 可以帮助我们在用dom属性时有更明确的提示// divRef的类型为 RefObject<HTMLDivElement>const inputRef = useRef<HTMLDivElement>(null)
useEffect(() => {inputRef.current.focus()})
return <div ref={inputRef}>etc</div>
}
// 如果你可以确保divRef.current 不是null,也可以在传入初始值的位置// 添加非null标记
const divRef = useRef<HTMLDivElement>(null!)
// 不再需要检查`divRef.current` 是否为null
doSomethingWith(divRef.current)
稳定引用存储器
当做为可变存储容器使用的时候,可以通过泛型参数
指定容器存入的数据类型, 在还为存入实际内容时通常把null作为初始值,所以依旧可以通过联合类型做指定
interface User {age: number
}
function App(){const timerRef = useRef<number | undefined>(undefined)const userRes = useRef<User | null> (null)useEffect(()=>{timerRef.current = window.setInterval(()=>{console.log('测试')},1000)return ()=>clearInterval(timerRef.current)})return <div> this is app</div>
}
九、Component 与 TypeScript
为Props添加类型
props作为React组件的参数入口,添加了类型之后可以限制参数输入以及在使用props有良好的类型提示
使用interface接口
interface Props {className: string
}
export const Button = (props:Props)=>{const { className } = propsreturn <button className={ className }>Test</button>
}
使用自定义类型Type
type Props = {className: string
}
export const Button = (props:Props)=>{const { className } = propsreturn <button className={ className }>Test</button>
}
为Props的chidren属性添加类型
children属性和props中其他的属性不同,它是React系统中内置的,其它属性我们可以自由控制其类型,children属性的类型最好由React内置的类型提供,兼容多种类型
type Props = {children: React.ReactNode
}
export const Button = (props: Props)=>{const { children } = propsreturn <button>{ children }</button>
}
说明:React.ReactNode是一个React内置的联合类型,包括 React.ReactElement
、string
、number
React.ReactFragment
、React.ReactPortal
、boolean
、 null
、undefined
为事件prop添加类型
// props + ts
type Props = {onGetMsg?: (msg: string) => void
}
function Son(props: Props) {const { onGetMsg } = propsconst clickHandler = () => {onGetMsg?.('this is msg')}return <button onClick={clickHandler}>sendMsg</button>
}
function App() {const getMsgHandler = (msg: string) => {console.log(msg)}return (<><Son onGetMsg={(msg) => console.log(msg)} /><Son onGetMsg={getMsgHandler} /></>)
}
export default App
为事件handle添加类型
为事件回调添加类型约束需要使用React内置的泛型函数来做,比如最常见的鼠标点击事件和表单输入事件:
function App(){const changeHandler: React.ChangeEventHandler<HTMLInputElement> = (e)=>{console.log(e.target.value)}const clickHandler: React.MouseEventHandler<HTMLButtonElement> = (e)=>{console.log(e.target)}
return (<><input type="text" onChange={ changeHandler }/><button onClick={ clickHandler }> click me!</button></>)
}
相关文章:
React速通笔记
相关视频: 黑马程序员前端React18入门到实战视频教程,从reacthooks核心基础到企业级项目开发实战(B站评论、极客园项目等)及大厂面试全通关_哔哩哔哩_bilibili 一、React介绍 React由Meta公司开发,是一个用于 构建W…...
深入蜂窝物联网:第二章 深度解读 NB-IoT:协议栈、部署与典型应用
1. NB-IoT 简介与应用场景 NB-IoT(Narrowband-IoT,窄带物联网)是3GPP在LTE演进基础上,为海量低速率IoT设备设计的无线接入技术。它仅占用 200 kHz 或 180 kHz(Guard-band)带宽,通过扩频和重复发射,提高了深度覆盖能力。 典型应用 远程抄表:电表、水表、气表等周期性少…...
处理对象集合,输出Map<String, Map<String, List<MyObject>>>格式数据,无序组合键处理方法
需求:对象有字段A,B,C,需要判断对象之间AB两个字段相同的对象 如: 对象1,Aaaa,Bbbb 对象2,Aaaa,Bbbb 对象3,Abbb,Baaa 对象4,Abbb,Baaa…...
基于SpringBoot的食物营养分析与推荐网站系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...
人智交互中的AI世代
人智交互中的AI世代 一、研究背景与意义 1.1 技术演进背景 人工智能技术自1956年达特茅斯会议提出概念以来,经历了多次技术迭代与产业周期。2020年后,以大语言模型(LLMs)和生成式AI(AIGC)为代表的突破性进…...
从Flask到智能体:装饰器模式在AI系统中的架构迁移实践
引言:框架设计中的模式复用 在人工智能系统开发领域,大型语言模型驱动的智能体(LLM Agent)正面临日益复杂的架构挑战。有趣的是,Web开发框架Flask的装饰器模式为此类系统的构建提供了极具启发性的解决方案。本文将深入…...
Docker的常用命令
docker的常用命令 今天来介绍docker的常用命令,非常的详细,请大家看起来! 获取镜像 如果只指定了镜像的名称,则默认会获取latest标签标记的镜像 命令格式:dokcer pull [Docker Registry地址]仓库名[:标签名] 获取c…...
在matlab中使用UAV123官方toolkits测试自己的数据集
一、前言 最近需要将自己的跟踪代码在自己拍摄的数据集上进行测试,这里我选择使用 UAV123 官方 toolkits 进行配置。首先需要搞清楚这部分代码是如何运行的,精度图和成功率图是如何绘制出来的,然后再将自己的数据集加进去进行测试。 二、UA…...
《淘宝 API 数据湖构建:实时商品详情入湖 + Apache Kafka 流式处理指南》
随着电商行业的蓬勃发展,淘宝作为头部电商平台,积累了海量的商品数据。构建淘宝 API 数据湖,将实时商品详情数据纳入其中,并借助 Apache Kafka 进行流式处理,能够为企业提供强大的数据支撑,助力精准营销、市…...
HTML5 WebSocket:实现高效实时通讯
一、引言 在当今的 Web 开发领域,实时通讯功能变得越来越重要。例如在线聊天、实时数据更新等场景都需要客户端与服务器之间能够进行高效的双向数据传输。HTML5 引入的 WebSocket 协议为我们提供了一种强大的解决方案,它在单个 TCP 连接上实现了全双工通讯,极大地改善了传统…...
数字人Live_Talking的搭建和使用
Live_Talking是一个实时交互流式数字人,可以实现音视频同步对话。今天咱们来试着部署一下项目。 先来看下本地环境 系统:Ubuntu 22.04 显卡:rtx 3060 cuda: Cuda 12.1 git上推荐cuda11.3,但是我用cuda12.2也搭建成功了。 1、…...
Coupang火箭计划深度攻略:eBay卖家突破韩国市场的三维数据作战模型
一、市场机遇与竞争格局解码 1.1 Coupang生态位分析 用户基数:2600万活跃买家(占韩国成年人口68%) 客单价表现:$82(较eBay韩国站高37%) 流量分布:移动端占比91%(需重点优化移动端详…...
Flask + ajax上传文件(四)--数据入库教程
本教程将详细介绍如何使用Flask后端和AJAX前端实现Excel/csv文件上传,并将数据导入数据库的功能。 一、系统架构概述 前端(HTML+JS) → AJAX请求 → Flask后端 → 数据库 二、环境准备 1. 安装必要库 pip install flask pandas sqlalchemy openpyxl2. 项目结构 data_imp…...
C++ 部署的性能优化方法
一、使用结构体提前存放常用变量 在编写前后处理函数时,通常会多次用到一些变量,比如模型输入 tensor 的 shape,count 等等,若在每个处理函数中都重复计算一次,会增加部署时的计算量。对于这种情况,可以考…...
并发设计模式实战系列(8):Active Object
🌟 大家好,我是摘星! 🌟 今天为大家带来的是并发设计模式实战系列,第8章Active Object,废话不多说直接开始~ 目录 一、核心原理深度拆解 1. 对象与执行解耦架构 2. 核心组件 二、生活化类比ÿ…...
jenkins容器提示磁盘空间过低
进入jenkins容器查看: sudo docker exec -it jenkins sh df -h查看磁盘占用情况: # df -h Filesystem Size Used Avail Use% Mounted on overlay 59G 56G 193M 100% / tmpfs 64M 0 64M 0% /dev shm…...
记一次pdf转Word的技术经历
一、发现问题 前几天在打开一个pdf文件时,遇到了一些问题,在Win10下使用WPS PDF、万兴PDF、Adobe Acrobat、Chrome浏览器打开都是正常显示的;但是在macOS 10.13中使用系统自带的预览程序和Chrome浏览器(由于macOS版本比较老了&am…...
【3分钟准备前端面试】Hybrid开发 谷歌浏览器调试安卓app
查看数据请求,页面dom结构和样式,日志打印输出,页面缓存等浏览器控制台素有功能,方便调试 检查元素,方便bug的定位 注:该文档是谷歌浏览器调试安卓apk内嵌网页 前提 app包需要是debug包,并且app的webview开启debug模式需要翻墙安卓手机打开开发者模式,开启usb调试调试…...
【二分查找】寻找峰值(medium)
6. 寻找峰值(medium) 题⽬描述:解法⼆(⼆分查找算法):算法思路:C 算法代码:Java 算法代码: 题⽬链接:162. 寻找峰值 题⽬描述: 峰值元素是指其值…...
这是一款好用的PDF工具!
用户习惯有时确实非常顽固,想要改变它可能需要漫长的时间。 比如PDF软件,我认为国产的福/昕、万/兴等软件都非常不错,它们贴合国人的使用习惯,操作起来非常顺手。但因为我习惯使用DC,所以在处理PDF文档时,…...
征程 6 逆向自证 hbm 与 bc 一致性
1.引言 在征程 6 算法工具链使用过程中,会存在算法侧与软件侧的交接,偶尔会遇到,需要自证清白的情况,例如: 算法侧反馈:bc 精度没问题,也参考了【征程 6】bc 与 hbm 一致性比对 文章ÿ…...
推荐一个微软官方开源浏览器自动化工具,可以用于UI自动化测试、爬虫等,具备.Net、Java、Python等多个版本!
推荐一个微软官方开源,且功能非常强大的浏览器自动化工具, 让我们很容易控制Chromium、Firefox 和 WebKit 内核的浏览器,实现跨浏览器的网页自动化操作。 01 项目简介 Playwright 一个开源浏览器自动化工具。 支持 Chromium、WebKit 和 Fir…...
深入理解链表:从基础操作到高频面试题解析
目录 一、链表基础概念 1.1 什么是链表? 1.2 链表核心特性 1.3 链表与数组对比 二、链表类型详解 2.1 单向链表 2.2 双向链表 2.3 循环链表 三、链表核心操作实现 3.1 插入操作 3.2 删除操作 四、链表高频面试题精讲 4.1 反转链表(LeetCode…...
【MCP Node.js SDK 全栈进阶指南】高级篇(1):MCP多服务器协作架构
随着业务规模的不断扩大和系统复杂度的提升,单一服务器架构往往无法满足高并发、高可用性和弹性扩展的需求。在MCP生态系统中,多服务器协作架构成为构建大规模应用的必然选择。本文将深入探讨MCP TypeScript-SDK在多服务器环境下的部署、协作和管理,以及如何构建高可用、高性…...
铭记之日(3)——4.28
铭记之日(3)——4.28 25.4.28,绝对是继20.12.19与24.6.26之后,又一个被钉在耻辱柱上的日子。 4.28本质上为12.19的严重恶劣版。 道德败坏、恶劣的大骗子终于在今日穿帮落马。 斯文面孔下,竟藏匿了如此罪恶幽暗混沌的内心。 24.10.20&…...
4月28日信息差全景:国际局势、科技突破与市场震荡一、国际政治与安全:俄乌冲突关键转折
一、国际政治与安全:俄乌冲突关键转折 1. 乌克兰反攻进展与情报差异 前线动态: 俄国防部称在顿涅茨克击退乌军三次进攻,摧毁12辆坦克;乌方则宣布在巴赫穆特南部推进2公里,双方战报存在显著差异。 信息差根源:战场信息管控导致西方媒体与俄媒报道截然不同。 国际援助: 美…...
Docker 容器虚拟化技术和自动化部署
Docker 容器虚拟化技术和自动化部署 一、Docker 核心组件1.1 Docker 引擎1.2 Docker 镜像1.3 Docker 容器1.4 Docker 仓库 二、Docker 环境安装清华镜像安装 三、Docker 基本操作3.1 镜像管理3.1.1 查看本地镜像 docker images3.1.2 添加镜像标签 docker tag3.1.3 查看镜像信息…...
人物5_My roommate
こんにちは Today, I will continue to share my life in JaPan. Everyone both know I couldn’t say JanPanese fluently【But I still learn this Language, I think it’s interesting for me{maybe it’s one exciting challenge, I want become a challenger that it li…...
OpenResty技术深度解析:原理、应用与生态对比-优雅草卓伊凡
OpenResty技术深度解析:原理、应用与生态对比-优雅草卓伊凡 一、OpenResty技术概述 1.1 OpenResty是什么? OpenResty是一个基于Nginx的高性能Web平台,它将标准的Nginx核心与一系列强大的第三方模块(主要是LuaJIT)捆绑在一起,形成了一个全功能的Web应用服务器。不同于传…...
深度学习任务评估指标
一、概念篇 混淆矩阵有何作用? 混淆矩阵(Confusion Matrix)是用于评估分类模型性能的工具,它展示了模型预测结果与实际标签之间的对比。混淆矩阵通常包括四个关键元素: True Positive (TP):模型正确预测为正类的数量。True Negative (TN):模型正确预测为负类的数量。F…...
Python-librosa库提取音频数据的MFCC特征
文章目录 MFCC特征代码分享 MFCC特征 MFCC(Mel-Frequency Cepstral Coefficients)是通过人耳对声音频率的感知方式对音频信号进行处理得到的特征,广泛用于语音识别和音频处理。 代码分享 import os import librosa import pywt import matpl…...
因特网和万维网
本文来源 :腾讯元宝 因特网(Internet)和万维网(World Wide Web,简称WWW)是紧密相关但完全不同的两个概念,它们的核心区别如下: 本质不同 因特网(Internet&#…...
道可云人工智能每日资讯|“人工智能科技体验展”在中国科学技术馆举行
道可云元宇宙每日简报(2025年4月28日)讯,今日元宇宙新鲜事有: 《2025年提升全民数字素养与技能工作要点》发布 近日,中央网信办、教育部、工业和信息化部、人力资源社会保障部联合印发《2025年提升全民数字素养与技能…...
Day8 鼠标控制与32位模式切换
文章目录 1. 例程harib05a(鼠标解读1)2. 例程harib05b(代码整理)3. 例程harib05c(鼠标解读2)4. 例程harib05d(移动鼠标指针)5. 通往32位模式之路 1. 例程harib05a(鼠标解…...
塔能科技:点亮节能之光,赋能工厂与城市
在能源形势日益严峻的当下,节能成为了各行各业的关键任务。工厂作为能耗大户,降低能耗迫在眉睫;市政照明作为城市运行的基本保障,也急需向绿色节能转型。塔能科技凭借其能源精准节能和定制开发的核心能力,为工厂节能和…...
UDP 报文结构与注意事项总结
目录 一、UDP报文结构简介 1. 源端口号(Source Port,16位): 2. 目的端口号(Destination Port,16位): 3. 长度(Length,16位): 4. 校…...
DBeaver CE 24.1.3 (Windows 64位) 详细安装教程
1. 下载安装包 dbeaver-ce-24.1.3-x86_64-setup.exe下载链接:https://pan.quark.cn/s/5a8dc9ad747f。 下载完成后,双击运行安装程序。 2. 运行安装向导 选择语言:安装程序启动后,选择安装语言(如英文或中文ÿ…...
Java多线程之线程控制
1、线程睡眠——sleep 如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread的sleep方法 注意如下几点问题 ①、sleep是静态方法,最好不要用Thread的实例对象调用它,因为它睡眠的始终是当前正在运…...
任意波形发生器——2路同步DA模拟量输出卡
定义 任意波形发生器(Arbitrary Waveform Generator, AWG)是一种电子测试仪器,能够通过数字信号处理(DSP)和数模转换(DAC)技术生成非周期性、可编程的任意形状电信号。与传统函数发生器仅支持…...
【Java】 使用 HTTP 响应状态码定义web系统返回码
系统状态码定义 public interface GlobalErrorCodeConstants {ErrorCode SUCCESS new ErrorCode(0, "成功");// 客户端错误段 ErrorCode BAD_REQUEST new ErrorCode(400, "请求参数不正确");ErrorCode UNAUTHORIZED new ErrorCode(401, "账号未登…...
测试反馈陷入死循环?5大策略拆解新旧Bug难题
新旧Bug堆叠,测试反馈陷入死循环,如果不及时解决此问题,往往容易导致项目延期、成本增加、团队效率降低,直接影响产品的市场竞争力 。因此需及时应对此问题,进而保障项目进度如期进行,提升软件质量…...
结合大语言模型的机械臂抓取操作学习
结合大语言模型的机械臂抓取操作学习(完整ppt和代码)无视频 代码能正常运行时不负责答疑! 电子产品,一经出售,概不退换 算法设计、毕业设计、期刊专利!感兴趣可以联系我。 🏆代码获取方式1: 私信…...
待验证---Oracle 19c 在 CentOS 7 上的快速安装部署指南
Oracle 19c 在 CentOS 7 上的快速安装部署指南 Oracle Database 19c 是一个功能强大的企业级数据库系统,下面我将为您提供在 CentOS 7 上快速安装部署 Oracle 19c 的详细步骤。 一、准备工作 1. 系统要求 CentOS 7 (64位)最小内存: 2GB (推荐 8GB 以上)最小磁盘…...
风力发电领域canopen转Profinet网关的应用
在风力发电领域,开疆canopen转Profinet网关KJ-PNG-205的应用案例通常涉及将风力涡轮机内部的CANopen网络与外部的Profinet工业以太网连接起来。这种转换网关允许风力发电场的控制系统通过Profinet协议收集和监控涡轮机的状态信息,同时发送控制命令。 风力…...
vr全景相机如何选择?
VR全景相机,作为虚拟现实技术的核心设备之一,能够拍摄360度全景照片和视频,使用户通过虚拟现实设备身临其境地体验拍摄场景。 这种技术的快速发展,得益于传感器、图像处理和计算机视觉技术的不断进步。 选择一台合适的VR全景相机…...
在 Conda 中,包的安装路径在电脑的哪里
在 Conda 中,包的安装路径取决于你的 Conda 安装方式 和 环境类型(base 或其他虚拟环境)。以下是不同情况下的详细说明: 📌 1. Conda 包的默认安装路径 Conda 将所有包存储在 pkgs 目录 下,并在各个环境中…...
phpstorm用php连接数据库报错
项目场景: phpstorm用php连接数据库 问题描述 用php使用mysql_connect 的时候报错了,没有这个函数 原因分析: php解释器问题,后来查资料得知mysql_connct只适用于php5.5以下解释器。一开始用的7,改成5.3以后还是报…...
今日行情明日机会——20250428
指数依然在震荡区间,等待方向选择~ 2025年4月28日涨停主要行业方向分析 一、核心主线方向 一季报增长(业绩驱动资金避险) • 涨停家数:10家(最强方向)。 • 代表标的: ◦ 珀莱雅(2…...
Spring Boot 3与JDK 8环境下的单元测试实践指南
一、引言 在Java后端开发中,单元测试是保障代码质量的核心手段。Spring Boot作为主流框架,其单元测试体系随着版本迭代不断优化。本文聚焦于JDK 8与Spring Boot 3的组合场景,深入解析单元测试的多种实现方式,对比不同测试策略的异…...
微分与积分(前言)
导数 导数是一个非常重要的概念,先来看一个引例:速度问题。历史上速度问题与倒数概念的形成有着密切的关系。 平均速度 v s t v\frac{s}{t} vts那么如何表示瞬时速度呢? 瞬时经过路程: Δ s s ( t 0 Δ t ) − s ( t 0 ) Δ…...