react笔记-04redux篇

news2024/10/7 18:23:12

redux和react-redux笔记,以及项目中如何使用,对redux的封装,让其使用类似于vuex一样方便。

一、redux

1. redux工作流程

在这里插入图片描述

流程:创建action => dispatch分发action => 交给store => reducer加工数据返回给store

2. redux的三个核心概念

2.1 action

动作对象,包含两个属性:

  • type:标识属性,值为string,唯一,必要属性
  • data:数据属性,值类型任意,可选属性
{ type: 'userInfo', data: { name: 'xiaotian', age: 20 } }

2.2 reducer

用于初始化状态、加工状态。加工时,根据旧的stateaction,产生新的state的纯函数。redux的reducer函数必须是一个纯函数。

纯函数:同样的输入,同样的输出

遵循:

  1. 不得改写参数数据
  2. 不会产生任何副作用,例如:网络请求,输入输出设备
  3. 不能调用Date.now()或者Math.random()等不纯的方法

例如:下面这个函数就不是纯函数

// 传入同样的参数num,每次返回的结果都不相同,所以不是纯函数。
function fn(num) {
    return Math.random() + num
}

错误写法:

let personList = []
export default function personReducer(preState = personList, action) {
    const { type, data } = action
    switch (type) {
        case 'AddPerson':
        		// 不能这样写,会导致personList被改写了,personReducer就不是一个纯函数了,会导致reducx不能识别到数据的改变。
            personList.unshift(data)
            return personList
        default:
            return preState
    }
}

正确写法:

let personList = []
export default function countReducer(preState = personList, action) {
    const { type, data } = action
    switch (type) {
        case 'AddPerson':
            return [data, ...personList]
        default:
            return preState
    }
}

2.3 store

用于存储数据,有如下方法:

  • getState(): 获取当前的state对象。

  • dispatch(action): 分发action,这是改变state的唯一途径。每个action是一个描述“发生了什么”的普通JavaScript对象。

  • subscribe(() => {}): 注册一个监听器,当state发生改变时,会调用该回调。

3. redux使用

3.1 安装redux

npm install redux

3.2 基本使用

  1. 新建redux目录,在redux目录下新建store.jsxxx.js
  2. store.js
// 引入legacy_createStore,专门用于创建redux中最核心的store对象
import { legacy_createStore } from 'redux'
// 引入为xxx组件服务的reducer
import countReducer from './count_reducer'

// 用于暴露一个store对象,整个应用只有一个store
export default legacy_createStore(countReducer)
  1. xxx.js(这里是count_reducer.js

创建一个为xxx组件服务的reducer,reducer的本质就是一个函数

countReducer有两个参数:preState(之前的状态)、action(动作对象)

reducer第一次调用时,是store自动触发的,传递的preState是undefined,action是@@REDUX/INITxxx

export default function countReducer(preState = 0, action) {
    // 从action对象中获取:type、data
    const { type, data } = action
    // 根据type决定如何加工数据
    switch (type) {
        case 'increment':
            return preState + data 
        case 'decrement':
            return preState - data
        default:
            return preState
    }
}
  1. getState:获取store中的state数据
store.getState()
  1. dispatchstore派发数据(更新数据)
store.dispatch({ type: 'increment', data: xxx })
  1. subscribe:监听store中的数据变化
store.subscribe(() => {
  	// 调用render,渲染页面
})

组件中使用:

componentDidMount() {
    store.subscribe(() => {
        this.setState({})
    })
}

全局index.js中使用:

import store from './redux/store'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
)

store.subscribe(() => {
  root.render(
    <React.StrictMode>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </React.StrictMode>
  )
})

3.3 异步action

  1. action分类:
  • 同步action:一般对象类型,{type: string, data: any}
  • 异步action:函数类型
  1. 异步action的使用:

组件中:

import React, { Component } from 'react'
import store from '../redux/store'
import { createIncrementAction, createAsyncIncrementAction } from '../redux/count_action'

export default class count extends Component {
    handleAsyncIncrement = () => {
        const { value } = this.selectNumber
        store.dispatch(createAsyncIncrementAction(value))
    }

    render() {
        return (
            <div>
                <h1>当前求和为: {store.getState()}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
                <button onClick={this.handleAsyncIncrement}>异步加</button>
            </div>
        )
    }
}

count_action.js中(用于创建action对象):

// 异步action:指action的值是函数,异步action中都会调用同步action去真正的操作数据
export const createAsyncIncrementAction = (data) => {
    return (dispatch) => {
      	// 由redux调用,传递参数dispatch
        setTimeout(() => {
            dispatch({ type: 'increment', data: data * 1 })
        }, 1000)
    }
}

⚠️注意:redux的action默认是不支持函数类型的,需要使用中间件redux-thunk

安装:

npm install redux-thunk

store.js中:

import { legacy_createStore, applyMiddleware } from 'redux'
// 引入redux-thunk,用于支持异步action
import { thunk } from 'redux-thunk'
import countReducer from './count_reducer'

export default legacy_createStore(countReducer, applyMiddleware(thunk))

这样就可以使用异步action了。

二、react-redux(类式组件中使用)

在这里插入图片描述

react-redux搭配redux使用步骤:

1. 目录结构

在这里插入图片描述

  • components: 存放UI组件
  • containers: 存放容器组件
  • redux: 存放redux仓库

2. react-redux的基本使用

2.1 redux

store.js: 创建store

import { legacy_createStore, applyMiddleware } from 'redux'
import count_reducer from './count_reducer'
import { thunk } from 'redux-thunk'

export default legacy_createStore(count_reducer, applyMiddleware(thunk))

count_reducer.js

export default function countReducer(preState = 0, action) {
    const { type, data } = action
    switch (type) {
        case 'ADD':
            return preState + data
        default:
            return preState
    }
}

2.2 containers(容器组件): containers/count.jsx

容器组件的store是靠父组件的props传递进去的(2.3的App.jsx),而不是自己import引入的

// 引入cont的ui组件
import CountUI from '../components/count'
// 引入connect用于连接ui组件和redux
import { connect } from 'react-redux'

// 1. mapStateToProps函数的返回的是一个对象
// 2. 返回对象中的key就作为传递给ui组件的props的key,value就作为传递给ui组件props的value
// 3. mapStateToProps函数的本质:传递状态,把状态传递给props
function mapStateToProps(state) {
    return {
        count: state
    }
}

// 1. mapDispatchToProps函数的返回的是一个对象
// 2. 返回对象中的key就作为传递给ui组件的props的key,value就作为传递给ui组件props的value
// 3. mapDispatchToProps函数的本质:传递操作状态的方法
function mapDispatchToProps(dispatch) {
    return {
        add: () => {
            console.log('add')
            dispatch({ type: 'ADD', data: 1 })
        }
    }
}

// 使用connect()()创建并暴露一CountUI的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)

2.3 App.jsx

⚠️注意:这里引入的Count组件是容器组件(2.2 创建的),而不是UI组件

通过props的方式,将store传入给容器组件,在mapStateToProps中就可以获取到statemapDispatchToProps中可获取到dispatch

import React, { Component } from 'react'
import Count from './containers/count'
import store from './redux/store'

export default class App extends Component {
  render() {
    return <>
      <div>
        <h1>App</h1>
        <Count store={store} />
      </div>
    </>
  }
}

2.4 components(UI组件): components/count.jsx

这个时候通过this.props即可获取redux的store仓库中数据和方法(在2.2容器组件定义的数据和方法)

import React, { Component } from 'react'

export default class count extends Component {
  handleAdd = () => {
    this.props.add()
  }

  render() {
    // console.log('ui组件props', this.props)
    return (
      <div>
        <p>num为: {this.props.count}</p>
        <button onClick={this.handleAdd}>+1</button>
      </div>
    )
  }
}

3. 优化

3.1 优化1:容器组件mapDispatchToProps的简写

mapDispatchToProps可以是函数,也可以是对象

value只要返回action,react-redux自动dispatch分发action

// 引入cont的ui组件
import CountUI from '../components/count'
// 引入connect用于连接ui组件和redux
import { connect } from 'react-redux'

export default connect(
    state => ({
        count: state
    }),
    // mapDispatchToProps的简写
    {
        add: () => ({ type: 'ADD', data: 1 })
    }
)(CountUI)

3.2 优化2:不用再手动监测redux数据的变化,react-redux自动监测

3.3 优化3:Provider使用

如果有多个容器组件,需要每个容器组件都传入store,就类似这样:

<Count store={store} />
<Demo store={store} />
<Text store={store} />

优化:可以通过Provider向每个容器组件都传入store,在入口文件中如下使用:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import { Provider } from 'react-redux'
import store from './redux/store.js'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
)

需要组件的地方直接使用即可:

<Count />

3.4 整合:UI组件+容器组件

import React, { Component } from 'react'
import { connect } from 'react-redux'

// ui组件
class count extends Component {
    handleAdd = () => {
        this.props.add()
    }

    render() {
        return (
            <div>
                <p>num为: {this.props.count}</p>
                <button onClick={this.handleAdd}>+1</button>
            </div>
        )
    }
}

// 容器组件
export default connect(
    state => ({
        count: state
    }),
    {
        add: () => ({ type: 'ADD', data: 1 })
    }
)(count)

4. 多个reducer的使用

store.js中:

import { legacy_createStore, applyMiddleware, combineReducers } from 'redux'
import count_reducer from './reducers/count'
import person_redercer from './reducers/person'
import { thunk } from 'redux-thunk'

// 汇总所有的reducer变成一个总的reducer
const allReducer = combineReducers({
  count: count_reducer,
  personList: person_redercer
})

export default legacy_createStore(allReducer, applyMiddleware(thunk))

容器组件中:

import React, { Component } from 'react'
import { connect } from 'react-redux'

class count extends Component {
    handleAdd = () => {
      	// 操作状态的方法不需要区分
        this.props.add()
    }

    render() {
        return (
            <div>
                <p>num为: {this.props.count}</p>
                <button onClick={this.handleAdd}>+1</button>
                <div>{this.props.personList.length}</div>
            </div>
        )
    }
}

export default connect(
    state => ({
      	// state.xxx:xxx是根据store.js中的allReducer中的key值决定的
        count: state.count,
        personList: state.personList
    }),
    {
        add: () => ({ type: 'ADD', data: 1 })
    }
)(count)

5. redux开发者工具的使用

5.1 下载浏览器插件

下载地址:https://chrome.zzzmh.cn/info/lmhkpmbekcpmknklioeibfkpmmfibljd

5.2 项目中安装

npm i redux-devtools-extension

5.3 在store.js中配置

import { legacy_createStore, applyMiddleware } from 'redux'
import count_reducer from './reducers/count'
import { thunk } from 'redux-thunk'
// 引入开发者工具
import { composeWithDevTools } from 'redux-devtools-extension'

export default legacy_createStore(count_reducer, composeWithDevTools(applyMiddleware(thunk)))

这样浏览器插件就会亮了。

三、react-redux(函数式组件使用)

1. 可以通过UI组件和容器组件结合的写法:

import { connect } from 'react-redux'

const CountUI = (props) => {
  return (
    <div>
      <h1>当前求和为:{props.count}</h1>
      <button onClick={props.add}>+1</button>
    </div>
  )
}

function mapStateToProps(state) {
    return {
        count: state
    }
}

function mapDispatchToProps(dispatch) {
    return {
        add: () => {
            console.log('add')
            dispatch({ type: 'ADD', data: 1 })
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(CountUI)

2. Hooks写法(推荐)

2.1 创建仓库

import { legacy_createStore } from 'redux'
import reducer from './reducer'

// window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 配置redux-devtools
const store = legacy_createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
export default store

2.2 创建reducer(初始化仓库数据,和修改仓库数据)

// 仓库数据
const defaultState = {
    num: 0
}

// 当调用dispatch的时候会触发函数
let reducer = (state = defaultState, action) => {
    // action:dispatch传递过来的对象

    //  对数据进行深拷贝
    let newState = JSON.parse(JSON.stringify(state))

    switch (action.type) {
        case 'ADD1':
            newState.num += 1
            break;
        case 'ADD2':
            newState.num -= action.num
            break;
        default:
            break;
    }

    return newState
}

export default reducer

2.3 在main.js中将store与项目关联

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import { Provider } from 'react-redux'
import store from './store/index.js'

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <App />
  </Provider>
)

2.4 在组件中获取仓库数据修改仓库数据(2.2关联)

  • 获取仓库数据:通过useSelector获取
  • 修改仓库数据:通过useDispatch获取
import { useSelector, useDispatch } from "react-redux"

export default () => {
  // 通过useSelector获取仓库数据,回调返回一个对象或数组
  const { num } = useSelector((state) => {
    return {
      num: state.num
    }
  })

  // 修改仓库数据
  const dispatch = useDispatch()
  const changeNum = () => {
    // dispatch({type: '字符串', val: 'val可以自定义,也可以不传'})
    dispatch({type: 'ADD1'})
  }

  const changeNum2 = () => {
    dispatch({type: 'ADD2', val: 2})
  }

  return (
    <div>
      <p>{num}</p>
      <button onClick={changeNum}>num1</button>
      <button onClick={changeNum2}>num2</button>
    </div>
  )
}

3. 对react-redux的优化

3.1 将state数据抽离

  1. 新建index.js用于存放数据和方法
// index.js 用于存放数据
// src/store/index.js 用于存放num模块数据
export default {
    state: {
        num: 0
    },
    actions: {
        ADD1(newState, action) {
            newState.num += 1
        },
        ADD2(newState, action) {
            newState.num += action.val
        }
    },
    // 名字统一管理
    actionsName: {
        add1: 'ADD1',
        add2: 'ADD2'
    }
}
  1. reducer.js
// 仓库数据
import handleStore from './index'
const defaultState = {
    // num: handleStore.state.num 多个属性这么写会很麻烦
    ...handleStore.state
}

// 当调用dispatch的时候会触发函数
let reducer = (state = defaultState, action) => {
    // action:dispatch传递过来的对象

    //  对数据进行深拷贝
    let newState = JSON.parse(JSON.stringify(state))

    switch (action.type) {
        case handleStore.actionsName.add1:
            // handleStore.actions.ADD1(newState, action)
            // 将add1抽离
            handleStore.actions[handleStore.actionsName.add1](newState, action)
            break;
        case handleStore.actionsName.add2:
            handleStore.actions[handleStore.actionsName.add2](newState, action)
            break;
        default:
            break;
    }

    return newState
}

export default reducer

页面的使用参考2.4

3.2 仓库模块化

仓库目录结构:

在这里插入图片描述

  • modules:用于对模块进行划分
    • 不同模块目录:
    • index.js:用于存放仓库数据和方法
    • reducer.js:创建不同模块的reducer
  • index.js:redux仓库

拆分优化过程:

  1. index.js进行reducer合并
import { legacy_createStore, combineReducers,  } from 'redux'
import num_reducer from './modules/num/reducer'
import arr_reducer from './modules/arr/reducer'

const reducers = combineReducers({
    num_module: num_reducer,
    arr_module: arr_reducer
})

const store = legacy_createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
export default store

页面上展示使用:

import { useSelector, useDispatch } from "react-redux"

export default () => {
  const { num } = useSelector((state) => {
    return {
      // 需要加上模块名称使用
      num: state.num_module.num
    }
  })

  const dispatch = useDispatch()
  const changeNum = () => {
    dispatch({ type: 'ADD1' })
  }

  const changeNum2 = () => {
    dispatch({ type: 'ADD2', val: 2 })
  }

  const { arr } = useSelector((state) => {
    return {
      // 需要加上模块名称使用
      arr: state.arr_module.arr
    }
  })

  const arrPush = () => {
    dispatch({ type: 'ARRPUSH', val: 40 })
  }

  return (
    <div>
      <p>{num}</p>
      <button onClick={changeNum}>num1</button>
      <button onClick={changeNum2}>num2</button>
      <hr />
      <p>{arr}</p>
      <button onClick={arrPush}>push</button>
    </div>
  )
}
  1. 对每个模块的reducer.js中的switch语句进行优化
// 仓库数据
import handleStore from './index'
const defaultState = {
    // num: handleStore.state.num 多个属性这么写会很麻烦
    ...handleStore.state
}

// 当调用dispatch的时候会触发函数
let reducer = (state = defaultState, action) => {
    // action:dispatch传递过来的对象
    
    let newState = JSON.parse(JSON.stringify(state))

    // 优化前:
    // switch (action.type) {
    //     case handleStore.actionsName.add1:
    //         // handleStore.actions.ADD1(newState, action)
    //         // 将add1抽离
    //         handleStore.actions[handleStore.actionsName.add1](newState, action)
    //         break;
    //     case handleStore.actionsName.add2:
    //         handleStore.actions[handleStore.actionsName.add2](newState, action)
    //         break;
    //     default:
    //         break;
    // }

    // 优化后
    for (const key in handleStore.actionsName) {
        if (action.type === handleStore.actionsName[key]) {
            handleStore.actions[action.type](newState, action)
          	break
        }
    }

    return newState
}

export default reducer
  1. 根据每个模块的index.js中的actions的名字自动生成actionsName,也可以将actionsName进行提取到单独文件统管理
const store = {
    state: {
        num: 0
    },
    actions: {
        ADD1(newState, action) {
            newState.num += 1
        },
        ADD2(newState, action) {
            newState.num += action.val
        }
    },
    // 名字统一管理(最好将key=value=actions的方法名)
    // actionsName: {
        // ADD1: 'ADD1',
        // ADD2: 'ADD2'
    // }
}

let actionsName = {}
for (const key in store.actions) {
    actionsName[key] = key
}
Object.assign(store, { actionsName })

export default store

效果及其如何使用:

  • 效果:做到了只有修改每个模块的index.js中的数据和方法,不去动reducer.js文件,使用起来跟``vuex`一样
  • 复制index.jsreducer.js,在index.jsstate中添加数据,actions添加方法即可;在store/index.js中引入不同模块的reducer

4. 异步action

4.1 跟之前的一样,需要安装redux-thunk

4.2 在store/index.js中配置

import { legacy_createStore, combineReducers, compose, applyMiddleware } from 'redux'
import { thunk } from 'redux-thunk'
import num_reducer from './modules/num/reducer'
import arr_reducer from './modules/arr/reducer'

const reducers = combineReducers({
    num_module: num_reducer,
    arr_module: arr_reducer
})

// 判断是否有__REDUX_DEVTOOLS_EXTENSION__COMPOSE__这个开发者工具模块
let composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION__COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION__COMPOSE__() : compose

const store = legacy_createStore(reducers, composeEnhancers(applyMiddleware(thunk)))
export default store

4.3 组件中使用

import { useSelector, useDispatch } from "react-redux"

export default () => {
  const { num } = useSelector((state) => {
    return {
      num: state.num_module.num
    }
  })

  const dispatch = useDispatch()

  const asyncChangeNum = () => {
    // 异步用法:dispatch中传入回调
    dispatch((disp) => {
      setTimeout(() => {
        disp({ type: 'add1', val: 1 })
      }, 1000)
    })
  }

  return (
    <div>
      <p>{num}</p>
      <button onClick={asyncChangeNum}>num异步+1</button>
    </div>
  )
}

4.4 优化:将组件内的方法抽取到每个模块的index.js

index.js

const store = {
    state: {
        num: 0
    },
    actions: {
        add1(newState, action) {
            newState.num += action.val
        }
    },
  	// 异步方法
    asyncActions: {
        asyncAdd1(dispathch) {
            setTimeout(() => {
                dispathch({ type: 'add1', val: 1 })
            }, 1000)
        }
    }
}

let actionsName = {}
for (const key in store.actions) {
    actionsName[key] = key
}
Object.assign(store, { actionsName })

export default store

组件内:

import { useSelector, useDispatch } from "react-redux"

export default () => {
  const { num } = useSelector((state) => {
    return {
      num: state.num_module.num
    }
  })

  const dispatch = useDispatch()

  const asyncChangeNum = () => {
    // 异步用法:dispatch中传入回调
    dispatch(调用asyncAdd1方法即可)
  }

  return (
    <div>
      <p>{num}</p>
      <button onClick={asyncChangeNum}>num异步+1</button>
    </div>
  )
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1859016.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 类对象

Python 类对象 经典迭代器 可迭代对象的定义&#xff1a; 使用内置的iter可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__方法&#xff0c;那么对象就是可迭代的。序列都可以迭代。实现了__getitem__方法&#xff0c;而且接受从0开始的索引&#xff0c;这种对象也…

2024年,AI的机会在行业大模型吗?

前言 随着科技的不断进步&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度改变着我们的世界。而在2024年&#xff0c;AI的机会无疑将更加聚焦于行业大模型。这些模型不仅代表着技术的巅峰&#xff0c;更是推动行业创新、提升效率、优化体验的关键力量。 行…

UE5 场景物体一键放入蓝图中

场景中&#xff0c;选择所有需要加入到蓝图的模型或物体。 点击 蓝图按钮&#xff0c;点击“将选项转换为蓝图” 在创建方法中&#xff0c;选择“子Actor”或着 “获取组件” 如果需要保持相对应的Actor的父子级别&#xff08;多层&#xff09;&#xff0c;那么选择“获取组件…

分布式事务msb

分布式事务使用场景 添加商品看库存够不够。库存扣减&#xff0c;扣完给订单服务一个响应&#xff0c;如果新加商品出问题了怎么回滚。 分布式事务概念 XA规范 XA规范&#xff1a;总之一句话&#xff1a; 就X/Open DTP 定义的 事务协调者与数据库之间的接口规范&#xff08;即…

2024 年计算机专业是否依旧稳坐就业市场的黄金宝座?

引言&#xff1a;选择的十字路口 随着 2024 年高考的结束&#xff0c;一股新的就业浪潮即将席卷全国 - 百万计的高三毕业生站在了人生的十字路口&#xff0c;面对着选专业这一重大决策。 计算机科学及相关领域&#xff0c;长久以来被誉为就业市场的“黄金门票”&#xff0c;吸…

代码随想录训练营第十六天 513找树左下角的值 112路径总和I 113路径总和II 106从中序和后序遍历序列构造二叉树

第一题&#xff1a; 原题链接&#xff1a;513. 找树左下角的值 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;用回溯的思想&#xff1a; 这题就是求最大深度&#xff0c;当遍历到第一个最大深度的时候&#xff0c;记录下的节点值就是最左边的元素。 参数和返回…

声波的种类

声波可以根据不同的特性进行分类&#xff0c;主要包括频率和传播方式两个方面&#xff1a; ### 按频率分类&#xff1a; 1. **次声波**&#xff1a;频率低于20Hz的机械波&#xff0c;这类波通常不能被人耳感知。 2. **可闻声波**&#xff1a;频率在20Hz至20kHz之间的机械波&am…

5. Revit API: Application

5. Revit API: Application 前言 上一篇中&#xff0c;讲到了UI篇的Ribbon&#xff08;界面&#xff09;&#xff0c;并提到要创建 RibbonPanel&#xff0c;需要使用UIControlledApplication.CreateRibbonPanel(..)方法&#xff0c;还在结尾说到要写“UI”开头的那些个类&…

Spring循环依赖问题——从源码画流程图

文章目录 关键代码相关知识为什么要使用二级缓存为什么要使用三级缓存只使用两个缓存的问题不能解决构造器循环依赖为什么多例bean不能解决循环依赖问题初始化后代理对象赋值给原始对象解决循环依赖SpringBoot开启循环依赖 循环依赖 在线流程图 关键代码 从缓存中查询getSingl…

鸿蒙开发系统基础能力:【@ohos.hiTraceChain (分布式跟踪)】

分布式跟踪 本模块提供了端侧业务流程调用链跟踪的打点能力&#xff0c;包括业务流程跟踪的启动、结束、信息埋点等能力。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import hi…

固特超声波清洗机怎么样?三大超声波清洗机美的、固特、希亦谁更好?

眼镜是我们日常生活中不可或缺的用具&#xff0c;但随着使用时间的增长&#xff0c;眼镜上的灰尘和污垢也会逐渐积累&#xff0c;传统的清洗方法往往难以彻底清洁。为了解决这一难题&#xff0c;超声波清洗机出现了&#xff01;它利用超声波振动原理&#xff0c;可以轻松、快速…

群辉NAS中文件下载的三种方案

目录 一、迅雷套件 1、添加套件来源 2、安装套件 3、手机安装迅雷 二、qBittorrent套件 1、添加套件来源 2、改手工安装 3、更新后的问题 4、最后放弃DSM6 (1)上传文件手工安装 (2)添加套件来源 5、解决登陆报错 6、添加tracker 7、修改下载默认位置 8、手机…

【十一】【QT开发应用】模拟腾讯会议登录界面设计UI

ui 加入会议的样式表 QPushButton { /* 前景色 */ color:#0054E6; /* 背景色 */ background-color:rgb(255,255,255); /* 边框风格 */ border-style:outset; /* 边框宽度 */ border-width:0.5px; /* 边框颜色 */ border-color:gray; /* 边框倒角 */ border-radius…

python自动化测试之DDT数据驱动的实现代码

时隔已久&#xff0c;再次冒烟&#xff0c;自动化测试工作仍在继续&#xff0c;自动化测试中的数据驱动技术尤为重要&#xff0c;不然咋去实现数据分离呢&#xff0c;对吧&#xff0c;这里就简单介绍下与传统unittest自动化测试框架匹配的DDT数据驱动技术。 话不多说&#xff…

神经网络的编程基础

神经网络的编程基础 二分类 二分类是机器学习中的一种基本分类问题&#xff0c;其中每个样本被划分为两个类别中的一个&#xff0c;即正类或负类。这种分类问题在现实生活中有广泛的应用&#xff0c;例如判定邮件是否为垃圾邮件、判别某个人是否患病等。二分类模型根据样本的特…

Python高压电容导电体和水文椭圆微分

&#x1f3af;要点 &#x1f3af;二维热传导二阶偏微分方程 | &#x1f3af;调和函数和几何图曲率 | &#x1f3af;解潮汐波动方程 | &#x1f3af;解静止基态旋转球体流体运动函数 | &#x1f3af;水文空间插值 | &#x1f3af;流体流动模拟求解器 | &#x1f3af;随机算法解…

光伏半导体的种类

光照射半导体材料时&#xff0c;其电导率发生变化的实质是光生载流子的产生。在半导体中&#xff0c;价带中的电子受到一定能量的光子激发后&#xff0c;可以跃迁到导带&#xff0c;形成自由电子和空穴对&#xff0c;即光生载流子。这些光生载流子会增加半导体的导电能力&#…

NepnepxCATCTF Pwn-Chao

文章目录 参考类型混淆异常处理的栈回退机制虚表和类的恢复假想的程序结构逆向工程场景步骤解析 idabug检查找虚表strupsc_str()alloca异常逆向maindisplayupdatecreate 新东西exp和思路 参考 https://www.cnblogs.com/winmt/articles/17018284.html 类型混淆 关于C中由虚函…

【乐吾乐2D可视化组态编辑器】图表动态显示

1. 添加数据 乐吾乐2D可视化组态编辑器地址&#xff1a;https://2d.le5le.com/ 图表动态展示是指一个图表图元的数据属性&#xff08;一般是dataY&#xff09;绑定多个变量&#xff0c;建立通信后数据动态变化展示。 官网默认Echarts图表拖拽到画布中是已经添加了图元的da…

【SpringCloud-Seata源码分析3】

文章目录 事务的提交客户端提交流程服务端提交流程客户端删除undo_log 事务回滚客户端事务回滚服务端回滚事务 事务的提交 前面两篇我们分析了seata的TC初始化和TM,RM初始化&#xff0c;并且事务准备阶段源码及业务Sql执行&#xff0c;下面我们分析事务的提交源码。 客户端提…