手写Redux(二):实现React-redux

news2024/11/25 22:51:42

在React中,组件和组件之间通过props传递数据的规范,极大地增强了组件之间的耦合性,而context类似全局变量一样,里面的数据能被随意接触就能被随意修改,每个组件都能够改context里面的内容会导致程序的运行不可预料。

Redux是一个独立专门用于做状态管理的JS库,帮助开发出行为稳定可预测的、易于测试的应用程序,通过react-redux,可集中式管理React应用中多个组件共享的状态。

1 前置知识,context和高阶组件

在实现自己的React-redux之前,需要先了解下以下2个东西:React的context和高阶组件。

1.1 React中的Context

在开发react项目时,我们会写很多的组件,同时组件之间嵌套组成一棵组件树,组件之间需要共享数据,如果通过props的方式传递数据的话,就需要将共享数据保存在这些组件的公共父节点组件的state,再通过props往下传递;如果还需要修改这些共享数据的话,需层层传递修改数据的回调函数,这简直就是地狱,完全无法维护。 为此,React的Context提供了一种在组件之间共享数据的方式,而不必显式地通过组件树的逐层传递props,其设计目的是为了共享那些对于一个组件树而言是“全局”的数据。 我们看一个具体示例,比如我们有这样一棵组件树,看如何通过Context实现组件树的数据共享:

import React,{Component} from 'react';
import ReactDOM from 'react-dom/client';
import PropTypes from 'prop-types';

class Index extends Component {// 如果你要给组件设置 context,那么 childContextTypes 是必写的// 它的作用其实 propsType 验证组件 props 参数的作用类似。不过它是验证 getChildContext 返回的对象。static childContextTypes = {themeColor: PropTypes.string}constructor () {super()this.state = { themeColor: 'red' }}// 设置 context 的过程,它返回的对象就是 contextgetChildContext () {return { themeColor: this.state.themeColor }}render () {return (<div><Header /><Content /></div>)}
}

class Header extends Component {render () {return (<div><h2>Header</h2><Title /></div>)}
}

class Content extends Component {render () {return (<div><h2>Content</h2></div>)}
}

class Title extends Component {// static contextTypes = {themeColor: PropTypes.string}render () {return (// 获取context中的数据<h1 style={{ color: this.context.themeColor }}>Title</h1>)}
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Index />
); 

Indexstate.themeColor放到某个地方,这个地方是每个Index的子组件都可以访问到的。当某个子组件需要的时候就直接去那个地方拿就好了,而不需要一层层地通过props来获取。不管组件树的层次有多深,任何一个组件都可以直接到这个公共的地方提取themeColor状态。 Context就是这么一个东西,某个组件只要往自己的Context里面放了某些状态,这个组件之下的所有子组件都直接访问这个状态而不需要通过中间组件的传递。一个组件的Context只有 它的子组件 能够访问,它的父组件是不能访问到的。

1.2 高阶组件

高阶组件在概念上很简单,但却非常常用、实用的东西,被大量 React.js 相关的第三方库频繁地使用,如果你能够灵活地使用高阶组件,可以让你代码更加优雅,复用性、灵活性更强。

高阶组件就是一个函数,传给它一个组件,它返回一个新的组件,这个新的组件会使用你传给它的组件作为子组件。

一个简单的高阶组件:

import React, { Component } from 'react'
export default (WrappedComponent) => {class NewComponent extends Component {// 可以做很多自定义逻辑render () {return <WrappedComponent />}}return NewComponent
} 

它就是简单的构建了一个新的组件类NewComponent,然后把传进入去的WrappedComponent渲染出来,这个例子看着好像没啥用,但是我们可以在render()之前做很多自定义逻辑,比如从远程获取数据,通过props传给子组件。

下面再给出个实用点的例子:

src/wrapWithRemoteData.js

import React, { Component } from 'react'

// 替代ajax
const getRemoteDate = key => {if (key==='user') return '张三';else if (key==='content') return 'Redux 是 JavaScript 状态容器,提供可预测化的状态管理。';
}

export default (WrappedComponent, key) => {class NewComponent extends Component {constructor () {super()this.state = { data: null }}componentWillMount () {let data = getRemoteDate(key)this.setState({ data })}render () {return <WrappedComponent data={this.state.data} />}}return NewComponent
} 

src/InputWithUser.js

import React,{Component} from "react";
import wrapWithRemoteData from './wrapWithRemoteData'
class InputWithUserName extends Component {render () {return <>user: <input value={this.props.data} /></>}
}
export default wrapWithRemoteData(InputWithUserName, 'user') 

src/textareaWithContent.js

import React,{Component} from "react";
import wrapWithRemoteData from './wrapWithRemoteData'
class TextareaWithContent extends Component {render () {return <textarea value={this.props.data} />}
}
export default wrapWithRemoteData(TextareaWithContent, 'content') 

可以看到,wrapWithRemoteData作为一个高阶组件,接收2个参数:

1.WrappedComponent: 被包装的子组件;
2.key: 数据key,根据该值获取远程数据,并通过props传给WrappedComponent

InputWithUser生成一个新的输入框,其内容在高级组件通过key='user'远程数据访问获取; textareaWithContent生成一个新的文本域,其内容在高级组件通过key='content'远程数据访问获取。

到这里,高阶组件的作用其实不言而喻,其实就是为了组件之间的代码复用。组件可能有着某些相同的逻辑,把这些逻辑抽离出来,放到高阶组件中进行复用。高阶组件内部的包装组件和被包装组件之间通过 props 传递数据。

2 使用createStore管理共享数据

了解完Context和高阶组件的概念后,我们使用上一篇文章【xxx】中的createStore结合React组件来实现自己的react-redux。 设计这样一棵组件树:

其中,Index组件中使用Context维护全局组件颜色的状态变量themeColorThemeSwitch中两个按钮控制所有组件的颜色。

使用create-react-app脚手架创建一个新的react项目my-redux,在src/下添加以下文件:

src/index.js

import React, { Component } from 'react'
import ReactDOM from 'react-dom/client';
import Header from './Header'
import Content from './Content'
import PropTypes from 'prop-types'
import './index.css'

function createStore(reducer) {let state = nullconst listeners = []const subscribe = (listener) => listeners.push(listener)const getState = () => stateconst dispatch = (action) => {state = reducer(state, action)listeners.forEach((listener) => listener())}dispatch({}) // 初始化 statereturn { getState, dispatch, subscribe }
}

// 负责主题颜色的 reducer
const themeReducer = (state, action) => {if (!state) return {themeColor: 'red'	// 状态名themeColor,初始值red}switch (action.type) {case 'CHANGE_COLOR':		// 只允许一种操作:修改themeColorreturn { ...state, themeColor: action.themeColor }default:return state}
}

// 创建store
const store = createStore(themeReducer)

class Index extends Component {// 给顶层父组件组件设置 context,childContextTypes 是必写的static childContextTypes = {store: PropTypes.object}// 把 store 设置到 context 中的过程,它返回的对象就是 contextgetChildContext () {return { store }}render () {return (<div><Header /><Content /></div>)}
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
	<Index />
); 

src/header.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Header extends Component {static contextTypes = {store: PropTypes.object}constructor () {super()this.state = { themeColor: '' }}// 首次渲染 & 注册渲染回调函数componentWillMount () {const { store } = this.contextthis._updateThemeColor()store.subscribe(() => this._updateThemeColor())}_updateThemeColor () {// 获取context中的storeconst { store } = this.contextconst state = store.getState()this.setState({ themeColor: state.themeColor })}render () {return (<h1 style={{ color: this.state.themeColor }}>Header</h1>)}
}
export default Header 

src/content.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ThemeSwitch from './ThemeSwitch'
class Content extends Component {static contextTypes = {store: PropTypes.object}constructor () {super()this.state = { themeColor: '' }}// 首次渲染 & 注册渲染回调函数componentWillMount () {const { store } = this.contextthis._updateThemeColor()store.subscribe(() => this._updateThemeColor())}_updateThemeColor () {// 获取context中的storeconst { store } = this.contextconst state = store.getState()this.setState({ themeColor: state.themeColor })}render () {return (<div><p style={{ color: this.state.themeColor }}>Content</p><ThemeSwitch /></div>)}
}
export default Content 

src/themeSwitch.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
class ThemeSwitch extends Component {static contextTypes = {store: PropTypes.object}constructor () {super()this.state = { themeColor: '' }}// 首次渲染 & 注册渲染回调函数componentWillMount () {const { store } = this.contextthis._updateThemeColor()store.subscribe(() => this._updateThemeColor())}_updateThemeColor () {// 获取context中的storeconst { store } = this.contextconst state = store.getState()this.setState({ themeColor: state.themeColor })}// dispatch action 去改变颜色handleSwitchColor (color) {const { store } = this.contextstore.dispatch({type: 'CHANGE_COLOR',themeColor: color})}// button组件绑定点击回调方法render () {return (<div><buttonstyle={{ color: this.state.themeColor }}onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button><buttonstyle={{ color: this.state.themeColor }}onClick={this.handleSwitchColor.bind(this, 'blue')}>Blue</button></div>)}
}
export default ThemeSwitch 

启动服务后,点击两种按钮就可以触发dispatch回调,修改store中的state,更改整体颜色:

1.在顶层父组件Index中,创建了一个store用来存取全局共享数据;
2.在顶层父组件Index中,创建了一个themeReducer用来初始化全局颜色状态数据,并定义只允许CHANGE_COLOR这个action来操作数据;
3.通过Context将该状态共享给Index所有的子组件;
4.子组件中在componentWillMount生命周期里获取状态数据,渲染组件颜色,并通过subscribe注册状态变化时重新渲染的回调方法。

3 使用高阶组件封装重复代码

3.1 代码复用性的问题

上面一节,我们将上篇文章中实现的createStore版本的Redux应用到了实际的组件树中,但是还存在两个问题:

1.有大量重复的逻辑:它们基本的逻辑都是,取出Context,取出里面的store,然后用里面的状态设置自己的状态,这些代码逻辑其实都是相同的。
2.对Context依赖性过强:这些组件都要依赖Context来取数据,使得这个组件复用性基本为零。想一下,如果别人需要用到里面的ThemeSwitch组件,但是他们的组件树并没有Context也没有store,他们没法用这个组件了。

对于第一个问题,根据上面高阶组件章节,可以把一些可复用的逻辑放在高阶组件当中,高阶组件包装的新组件和原来组件之间通过props传递信息,减少代码的重复程度。 第二个问题,可以通过纯函数的思想解决。到底什么样的组件才叫复用性强的组件?如果一个组件对外界的依赖过于强,那么这个组件的移植性会很差,就像这些严重依赖Context的组件一样。一个组件的渲染只依赖于外界传进去的props和自己的state,而并不依赖于其他的外界的任何数据,也就是说像纯函数一样,给它什么,它就吐出(渲染)什么出来,这种组件的复用性是最强的。

看下如何通过高阶组件、纯函数的思想解决这两个问题:

import React, { Component } from 'react'
import PropTypes from 'prop-types'
export connect = (WrappedComponent) => {class Connect extends Component {static contextTypes = {store: PropTypes.object}// TODO: 如何从 store 取数据?render () {return <WrappedComponent />}}return Connect
} 

connect函数接受一个组件WrappedComponent作为参数,把这个组件包含在一个新的组件Connect里面,Connect会去context里面取出store。现在要把store里面的数据取出来通过props传给WrappedComponent,这样做就解决前面提出的两个问题:

1.Context中的store注入到WrappedComponent中的逻辑由connect高阶组件统一完成;
2.WrappedComponent作为纯函数本身不依赖于其他的外界的任何数据,该组件的渲染只依赖于外界传进去的props和自己的state

3.2 mapStateToProps和mapDispatchToProps

解决了代码复用性的问题后,又出现了两个新的问题:

1.每个WrappedComponent需要的state是不同,不应该整个state传给WrappedComponent,而是需要做个转换,按需分配;
2.同理,不应该把dispatch的完整功能传给WrappedComponent,而是传递与这个WrappedComponent相关的操作;

用户可以自定义两个转换函数,对statedispatch做转换,如:

// 只传递 state.themeColor, state.themeName
const mapStateToProps = (state) => {return {themeColor: state.themeColor,themeName: state.themeName}
} 
// 只允许 CHANGE_COLOR 这个action
const mapDispatchToProps = (dispatch) => {return {onSwitchColor: (color) => {dispatch({ type: 'CHANGE_COLOR', themeColor: color })}}
} 

3.3 connect

综合3.1和3.2提出的问题,这一节提出我们的解决方案,实现这样一个方法connect

入参: 接收 mapStateToPropsmapDispatchToProps

出参: 生成一个高阶组件,这个高阶组件内部获取Context的store,将store中的statedispatch分别应用入参的mapStateToPropsmapDispatchToProps转换函数转换后,作为props传递给WrappedComponent

具体用法如下:

const higherOrderComponent = connect(mapStateToProps, mapDispatchToProps); // 返回一个高阶组件
const NewComponent = higherOrderComponent(WrappedComponent); // 高阶组件包裹子组件 

将上面两条语句合并成一条

const NewComponent = connect(mapStateToProps, mapDispatchToProps)(WrappedComponent) 

connect、WrappedComponent的关系如下图所示:

下面给出具体实现:

src/react-redux.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {class Connect extends Component {// 子组件获取context中数据时用于校验类型static contextTypes = {store: PropTypes.object}constructor () {super()this.state = {// 存放转换后的state、转换后的dispatch、组件本身接收的propsallProps: {}}}componentWillMount () {// 获取context中的storeconst { store } = this.contextthis._updateProps()// 注册状态数据变化时的回调store.subscribe(() => this._updateProps())}_updateProps () {const { store } = this.context// 转换statelet stateProps = mapStateToProps? mapStateToProps(store.getState(), this.props): {} // 防止 mapStateToProps 没有传入// 转换dispatchlet dispatchProps = mapDispatchToProps? mapDispatchToProps(store.dispatch, this.props): {} // 防止 mapDispatchToProps 没有传入// 存放转换后的state、转换后的dispatch、组件本身接收的propsthis.setState({allProps: {...stateProps,...dispatchProps,...this.props}})}render () {// 将转换后的state、转换后的dispatch、组件本身接收的props 作为props传给子组件return <WrappedComponent {...this.state.allProps} />}}return Connect
} 

以ThemeSwitch为例,看下如何使用connect进行改造:

src/ThemeSwitch.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from './react-redux'
class ThemeSwitch extends Component {// 类型检查static propTypes = {themeColor: PropTypes.string,onSwitchColor: PropTypes.func}// 按钮点击操作回调handleSwitchColor (color) {if (this.props.onSwitchColor) {// 调用转换后的 onSwitchColorthis.props.onSwitchColor(color)}}render () {return (<div><buttonstyle={{ color: this.props.themeColor }}onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button><buttonstyle={{ color: this.props.themeColor }}onClick={this.handleSwitchColor.bind(this, 'blue')}>Blue</button></div>)}
}

// 将state转换后通过props注入被包裹的组件
const mapStateToProps = (state) => {return {themeColor: state.themeColor}
}

// 将props转换后通过props注入被包裹的组件
const mapDispatchToProps = (dispatch) => {return {onSwitchColor: (color) => {dispatch({ type: 'CHANGE_COLOR', themeColor: color })}}
}
ThemeSwitch = connect(mapStateToProps, mapDispatchToProps)(ThemeSwitch)
export default ThemeSwitch 

src/Header.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from './react-redux'
class Header extends Component {static propTypes = {themeColor: PropTypes.string}render () {return (<h1 style={{ color: this.props.themeColor }}>Header</h1>)}
}
const mapStateToProps = (state) => {return {themeColor: state.themeColor}
}
Header = connect(mapStateToProps)(Header)
export default Header 

src/content.js

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ThemeSwitch from './ThemeSwitch'
import { connect } from './react-redux'
class Content extends Component {static propTypes = {themeColor: PropTypes.string}render () {return (<div><p style={{ color: this.props.themeColor }}>Content</p><ThemeSwitch /></div>)}
}
const mapStateToProps = (state) => {return {themeColor: state.themeColor}
}
Content = connect(mapStateToProps)(Content)
export default Content 

src/index.js

import React, { Component } from 'react'
import ReactDOM from 'react-dom/client';
import Header from './Header'
import Content from './Content'
import PropTypes from 'prop-types'
import './index.css'

function createStore (reducer) {let state = nullconst listeners = []const subscribe = (listener) => listeners.push(listener)const getState = () => stateconst dispatch = (action) => {state = reducer(state, action)listeners.forEach((listener) => listener())}dispatch({}) // 初始化 statereturn { getState, dispatch, subscribe }
}
const themeReducer = (state, action) => {if (!state) return {themeColor: 'red'}switch (action.type) {case 'CHANGE_COLOR':return { ...state, themeColor: action.themeColor }default:return state}
}
const store = createStore(themeReducer)

class Index extends Component {static childContextTypes = {store: PropTypes.object}getChildContext () {return { store }}render () {return (<div><Header /><Content /></div>)}
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Index />
); 

通过这一节的改造,与第2节中的基础实现相比,ThemeSwitchHeaderContent中的

1.获取context中的store
2.通过subscribe注册共享状态数据变化时回调;
3.直接调用dispatch修改共享状态数据;

这3类操作都被优化掉了,我们的组件变得更加干净,取而代之的是,通过props将这些状态、操作回调方法传给对应组件,这3类操作如下所示:

...

// 首次渲染 & 注册渲染回调函数
componentWillMount () {const { store } = this.contextthis._updateThemeColor()store.subscribe(() => this._updateThemeColor())
}
_updateThemeColor () {// 获取context中的storeconst { store } = this.contextconst state = store.getState()this.setState({ themeColor: state.themeColor })
}

... 
...

// dispatch action 去改变颜色
handleSwitchColor (color) {const { store } = this.contextstore.dispatch({type: 'CHANGE_COLOR',themeColor: color})
}

... 

4 Provider

通过上一节改造,ThemeSwitchHeaderContent已经变得非常干净了,还剩Index公共父组件中依然包含ContexcreateStore的代码逻辑,这一节讲解通过Provider优化Index父组件实现。

由于Index组件是所有组件的公共父组件,所以将Contex设置在Index中,这样他的所有子组件才能共享Context中的数据。我们可以额外构建一个组件来做这种脏活,然后让这个组件成为组件树的根节点,那么它的子组件都可以获取到Context了。

src/react-redux.js中新增如下代码

export class Provider extends Component {static propTypes = {store: PropTypes.object,children: PropTypes.any}static childContextTypes = {store: PropTypes.object}getChildContext () {return {store: this.props.store}}render () {return (<div>{this.props.children}</div>)}
} 

Provider做的事情也很简单,它就是一个容器组件,会把嵌套的内容原封不动作为自己的子组件渲染出来。它还会把外界传给它的props.store放到Context,这样子组件connect的时候都可以获取到。

可以用它来重构我们的 src/index.js:

class Index extends Component {// 删除 Index 里面所有关于 context 的代码// static childContextTypes = {// store: PropTypes.object// }// getChildContext () {// return { store }// }render () {return (<div><Header /><Content /></div>)}
}

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

这样我们就把所有关于Context的代码从Index组件里面删除了。

5 总结

这几节的成果就是 react-redux 这个文件里面的两个内容:connect 函数和 Provider 容器组件。理解了

1.为什么要 connect,
2.为什么要 mapStateToProps 和 mapDispatchToProps,
3.什么是 Provider,

这就是 react-redux 的基本内容,当然它是一个简易 react-redux,很多地方需要完善。

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

借助cubeMX实现STM32MP157A(-M4核)UART、按键中断、环境检测开关实验

main.c 可以添加一句打印提示 int main(void) {/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init(…

准备蓝桥杯的宝贝们,二分法基础篇(下)例题讲解

二分法例题第一题&#xff1a;搜索插入位置解法一&#xff08;左闭右闭&#xff09;解法二&#xff08;左闭右开&#xff09;解法三&#xff08;暴力求解&#xff09;第二题&#xff1a;在排序数组中查找元素的第一个和最后一个位置解法一&#xff08;左闭右闭&#xff09;第三…

​Base64编码知识详解 ​

在我们进行前端开发时&#xff0c;针对项目优化&#xff0c;常会提到一条&#xff1a;针对较小图片&#xff0c;合理使用Base64字符串替换内嵌&#xff0c;可以减少页面http请求。 并且还会特别强调下&#xff0c;必须是小图片&#xff0c;大小不要超过多少KB&#xff0c;等等。…

Flume监听多个文件目录,并根据文件名称不同,输出到kafka不同topic中

一、Flume监听多个文件目录 1. flume的环境搭建和基础配置参考 https://blog.csdn.net/qinqinde123/article/details/128130131 2. 修改配置文件flume-conf.properties #定义两个是数据源source1、source2 agent.sources source1 source2 agent.channels channel1 agent.…

B. Password(KMP)

Problem - 126B - Codeforces Asterix、Obelix和他们的临时伙伴Suffix和Prefix终于找到了和谐寺。然而&#xff0c;它的门被牢牢地锁住了&#xff0c;即使是Obelix也没能打开它们。 过了一会儿&#xff0c;他们发现在寺庙大门下面的一块岩石上刻着一个字符串。亚力认为那是打开…

realme手机配什么蓝牙耳机?realme蓝牙耳机推荐

蓝牙耳机作为人手必备的单品&#xff0c;不同厂商的产品更是多种多样&#xff0c;用户可以有更多的选择&#xff0c;选购蓝牙耳机的时候&#xff0c;除了看重佩戴舒适度、发声单元人们更加追求最新研发的技术。realme是为年轻人而来的科技潮牌。秉持“敢越级”品牌理念&#xf…

iOS MD5基础知识

MD5信息摘要算法&#xff08;英语&#xff1a;MD5 Message-Digest Algorithm&#xff09;&#xff0c;一种被广泛使用的密码散列函数&#xff0c;可以产生出一个128位&#xff08;16字节&#xff09;的散列值&#xff08;hash value&#xff09;&#xff0c;用于确保信息传输完…

实现了Spring的Aware接口的自定义类什么时候执行的?

在之前的内容中 Spring的Aware接口有什么用&#xff1f;_轻尘的博客-CSDN博客_aware接口的作用 了解到用户可以通过实现相应的Aware接口来获取spring框架提供的能力&#xff0c;俗称“攀亲戚” 以如下代码为例&#xff0c;自定义类MyAware实现了BeanFactroryAware&#xff0…

数据库、计算机网络,操作系统刷题笔记5

数据库、计算机网络&#xff0c;操作系统刷题笔记5 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&…

【MySQL基础】MySQL常用的图形化管理工具有那些?

目录 一、为什么要使用MySQL图形化管理工具 原因 / 目的 / 作用 二、什么是DOS窗口? 三、常见的MySQL图形化管理工具有那些&#xff1f; 四、 常见几个MySQL图形工具的介绍 Navicat SQLyog MySQL Workbench DataGrip 五、Navicat图形工具的安装与使用 第一步&#x…

python带你制作随机点名系统,超级简单

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 在某些难以抉择得时候&#xff0c;我们经常要用外力来帮助我们做出选择 比如&#xff0c;课堂随机点名或面对活动需要人上台表演时等等场景 这个时候&#xff0c;有一个随机点名系统就非常好啦&#xff0c;毕竟运气得事~ …

QT之 给控件添加右键菜单

一、效果预览 二、代码 cpp文件 //listView右键菜单 void MainWindow::Rightclicklistview() {//初始化一级菜单TotalRightclick new QMenu(this);AddDevice new QMenu(this);upDevice new QAction(this);DownDevice new QAction(this);Delete new QAction(this);EditDev…

压缩包里的文件名可以这样隐藏起来

我们知道&#xff0c;压缩后的文件如果有保密需要&#xff0c;可以给压缩包设置打开密码。 设置密码后&#xff0c;还是可以打开压缩包&#xff0c;也可以看到压缩包里面的文件名称&#xff0c;当你点击里面的文件&#xff0c;才会提示需要输入密码后才能打开文件。 如果希望加…

数据运算——逻辑运算

数据运算——逻辑运算一、逻辑运算1.通过例题掌握位模式层次上的逻辑运算2.位模式层次上的逻辑运算的应用1.**与运算使指定位复位**2.**或运算使指定位置位**3.**异或运算使指定位取反**二、移位运算1.逻辑移位2.循环移位3.算术移位算术右移算术左移举例1>.(算术右移)2>.…

KepServer EX6模拟仿真PlC数据以及点表的复制跟项目的迁移

一.模拟plc数据绑定标点 1.新建通道选择“Simulator” 右击 “连接性”》新建通道选择Simulator 填写通道名称&#xff08;自定义&#xff09; 然后一直默认设置点击下一页知道完成!!! 添加展示 2.给通道添加设备 右击通道》添加设备 设备名自定义 然后一直默认进行下一步…

[附源码]计算机毕业设计springboot人事管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

使用WPS生成二维码,手机扫码访问主机的资源

问题描述 如果我们想要使用二维码&#xff0c;包装一个链接&#xff0c;访问目标资源。 在淘宝上可以看到&#xff0c;一些网店提供制作二维码服务。其实我们自己也可以做。 原理是&#xff1a;我们把资源发送给商家&#xff0c;商家拿到后&#xff0c;将资源部署到服务器上…

ARM cortex-M4核中断实验 中断和串口

要求&#xff1a;按键触发时&#xff0c;LED灯状态取反&#xff0c;并且在串口工具打印一句话。 KEY1按键按下&#xff0c;LED1状态取反&#xff0c;串口工具打印key1 down!!!! GPIO模块&#xff1a; UART模块&#xff1a; 主函数&#xff1a; 实验现象&#xff1a…

2022世界杯漫谈与猜想,谁是你心目中的第一

文章目录0、 我与足球1、卡塔尔世界杯2、亚洲球队水平3、中国足球4、展望0、 我与足球 1、第一次意义上的踢足球还是初中&#xff0c;记得是五四青年节说全校搞一场足球比赛&#xff0c;我们班莫名其妙的组了一个队&#xff0c;然后在放学后提提足球&#xff0c;那时候规则都不…

JSON端口操作实例

JSON 端口可直接实现在 JSON 和 XML 之间进行转换。端口会自动检测输入文件是 JSON 还是 XML&#xff0c;然后将文件在两种格式间相互转换。 该端口较多的是运用在API接口调用集成方案的项目当中&#xff0c;我们以百思买项目为例&#xff0c;知行之桥将接收到的百思买的EDI报…