黑马React18: Redux

黑马React: Redux

Date: November 19, 2023
Sum: Redux基础、Redux工具、调试、美团案例




Redux介绍

Redux 是React最常用的集中状态管理工具,类似于Vue中的Pinia(Vuex),可以独立于框架运行

作用:通过集中管理的方式管理应用的状态

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/1.png

为什么要使用Redux?

  1. 独立于组件,无视组件之间的层级关系,简化通信问题
  2. 单项数据流清晰,易于定位bug
  3. 调试工具配套良好,方便调试



Redux快速体验

1. 实现计数器

需求:不和任何框架绑定,不使用任何构建工具,使用纯Redux实现计数器

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/2.png

使用步骤

  1. 定义一个 reducer 函数 (根据当前想要做的修改返回一个新的状态)
  2. 使用createStore方法传入 reducer函数 生成一个store实例对象
  3. 使用store实例的 subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知)
  4. 使用store实例的 dispatch方法提交action对象 触发数据变化(告诉reducer你想怎么改数据)
  5. 使用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 返回不同的state
  function 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的难点是理解它对于数据修改的规则, 下图动态展示了在整个数据的修改中,数据的流向

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/3.png

为了职责清晰,Redux代码被分为三个核心的概念,我们学redux,其实就是学这三个核心概念之间的配合,三个概念分别是:

  1. state: 一个对象 存放着我们管理的数据
  2. action: 一个对象 用来描述你想怎么改数据
  3. reducer: 一个函数 根据action的描述更新state



Redux与React - 环境准备

Redux虽然是一个框架无关可以独立运行的插件,但是社区通常还是把它与React绑定在一起使用,以一个计数器案例体验一下Redux + React 的基础使用



1. 配套工具

在React中使用redux,官方要求安装俩个其他插件 - Redux Toolkit 和 react-redux

  1. Redux Toolkit(RTK)- 官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式
  2. react-redux - 用来 链接 Redux 和 React组件 的中间件

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/4.png

2. 配置基础环境

  1. 使用 CRA 快速创建 React 项目
npx create-react-app react-redux
  1. 安装配套工具
npm i @reduxjs/toolkit  react-redux
  1. 启动项目
npm run start


3. store目录结构设计

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/5.png

  1. 通常集中状态管理的部分都会单独创建一个单独的 store 目录
  2. 应用通常会有很多个子store模块,所以创建一个 modules 目录,在内部编写业务分类的子store
  3. store中的入口文件 index.js 的作用是组合modules中所有的子模块,并导出store



Redux与React - 实现counter

1. 整体路径熟悉

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/6.png



2. 使用 React Toolkit 创建 counterStore

modules/counterStore.js

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 } = counterStore.actions

// 获取reducer函数
const counterReducer = counterStore.reducer

// 导出
export { increment, decrement }
export default counterReducer

store/index.js

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实例注入到应用中,链接正式建立

src/index.js

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中的数据 - useSelector

在React组件中使用store中的数据,需要用到一个钩子函数 - useSelector,它的作用是把store中的数据映射到组件中,使用样例如下:

useSelector作用:把store中的数据映射到组件中

注:上面是组件counterStore,下面是store/index.js. 即根组件

注:上面是组件counterStore,下面是store/index.js. 即根组件

src/App.js

import { useDispatch ,useSelector } from "react-redux"
import { increment, decrement, addToNum } from "./store/modules/counterStore1"

function App() {
  const { count } = useSelector(state => state.counter )
  const dispatch = useDispatch()
  return (
    <div className="App">
      <button onClick={() => dispatch(decrement())}>-</button>
      { count }
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(addToNum)}>add to 10</button>
      <button onClick={() => dispatch(addToNum)}>add to 20</button>
    </div>
  )
}

export default App


✅5. React组件修改store中的数据 - dispatch

React组件中修改store中的数据需要借助另外一个hook函数 - useDispatch,它的作用是生成提交action对象的dispatch函数

dispatch作用:在组件中提交action对象,从而修改store中数据

使用样例如下:

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/8.png



✅Redux与React - 提交action传参

需求:组件中有俩个按钮2

add to 10

add to 20

可以直接把count值修改到对应的数字,目标count值是在组件中传递过去的,需要在提交action的时候传递参数

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/9.png

实现方式:利用action传参实现

1-在reducers的同步修改方法中添加action对象参数

在调用actionCreater的时候传递参数,参数会被传递到action对象payload属性上

理解:这边直接理解 addToNum 接受的参数会被传递到 action.payload 即可

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/10.png

总结:

  1. 组件中使用哪个hook函数获取store中的数据?
    useSelector
  2. 组件中使用哪个hook函数获取dispatch方法?
    useDispatch
  3. 如何得到要提交action对象?
    执行store模块中导出的actionCreater方法



Redux与React - 异步action处理

需求理解

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/11.png

实现步骤

  1. 创建store的写法保持不变,配置好同步修改状态的方法

  2. 单独封装一个函数,在函数内部return一个新函数,在新函数中

    2.1 封装异步请求获取数据

    2.2 调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交

  3. 组件中dispatch的写法保持不变

Untitled

代码实现 > 测试接口地址: http://geek.itheima.net/v1_0/channels

modules/channelStore.js

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

src/App.js

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


Redux-React链路过程

Untitled




Redux调试 - devtools

Redux官方提供了针对于Redux的调试工具,支持实时state信息展示,action提交信息查看等

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/12.png

工具处理:

Untitled




美团小案例

1. 案例演示

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/13.png

基本开发思路:

使用 RTK(Redux Toolkit)来管理应用状态, 组件负责 数据渲染 和 dispatch action



2. 准备并熟悉环境

  1. 克隆项目到本地(内置了基础静态组件和模版)
git clone http://git.itcast.cn/heimaqianduan/redux-meituan.git
  1. 安装所有依赖
npm i
  1. 启动mock服务(内置了json-server)
npm run serve
  1. 启动前端服务
npm run start


3. 分类和商品列表渲染

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/14.png

1- 编写store逻辑

// 编写store
import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const foodsStore = createSlice({
  name: 'foods',
  initialState: {
    // 商品列表
    foodsList: []
  },
  reducers: {
    // 更改商品列表
    setFoodsList (state, action) {
      state.foodsList = action.payload
    }
  }
})

// 异步获取部分
const { setFoodsList } = foodsStore.actions
const fetchFoodsList = () => {
  return async (dispatch) => {
    // 编写异步逻辑
    const res = await axios.get('http://localhost:3004/takeaway')
    // 调用dispatch函数提交action
    dispatch(setFoodsList(res.data))
  }
}

export { fetchFoodsList }

const reducer = foodsStore.reducer

export default reducer

2- 组件使用store数据

// 省略部分代码
import { useDispatch, useSelector } from 'react-redux'
import { fetchFoodsList } from './store/modules/takeaway'
import { useEffect } from 'react'

const App = () => {
  // 触发action执行
  // 1. useDispatch -> dispatch 2. actionCreater导入进来 3.useEffect
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(fetchFoodsList())
  }, [dispatch])

  return (
    <div className="home">
      {/* 导航 */}
      <NavBar />

      {/* 内容 */}
      <div className="content-wrap">
        <div className="content">
          <Menu />
          <div className="list-content">
            <div className="goods-list">
              {/* 外卖商品列表 */}
              {foodsList.map(item => {
                return (
                  <FoodsCategory
                    key={item.tag}
                    // 列表标题
                    name={item.name}
                    // 列表商品
                    foods={item.foods}
                  />
                )
              })}
            </div>
          </div>
        </div>
      </div>
      {/* 购物车 */}
      <Cart />
    </div>
  )
}

export default App


4. 点击分类激活交互实现

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/15.png

1- 编写store逻辑

// 编写store

import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const foodsStore = createSlice({
  name: 'foods',
  initialState: {
    // 菜单激活下标值
    activeIndex: 0
  },
  reducers: {
    // 更改activeIndex
    changeActiveIndex (state, action) {
      state.activeIndex = action.payload
    }
  }
})

// 导出
const { changeActiveIndex } = foodsStore.actions

export { changeActiveIndex }

const reducer = foodsStore.reducer

export default reducer

2- 编写组件逻辑

const Menu = () => {
  const { foodsList, activeIndex } = useSelector(state => state.foods)
  const dispatch = useDispatch()
  const menus = foodsList.map(item => ({ tag: item.tag, name: item.name }))
  return (
    <nav className="list-menu">
      {/* 添加active类名会变成激活状态 */}
      {menus.map((item, index) => {
      return (
        <div
          // 提交action切换激活index
          onClick={() => dispatch(changeActiveIndex(index))}
          key={item.tag}
          // 动态控制active显示
          className={classNames(
            'list-menu-item',
            activeIndex === index && 'active'
          )}
          >
          {item.name}
        </div>
      )
    })}
    </nav>
  )
}


5. 商品列表切换显示

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/16.png

image.png

 <div className="list-content">
  <div className="goods-list">
    {/* 外卖商品列表 */}
    {foodsList.map((item, index) => {
      return (
        activeIndex === index && <FoodsCategory
          key={item.tag}
          // 列表标题
          name={item.name}
          // 列表商品
          foods={item.foods}
        />
      )
    })}
  </div>
</div>


6. 添加购物车实现

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/17.png

1- 编写store逻辑

// 编写store

import { createSlice } from "@reduxjs/toolkit"
import axios from "axios"

const foodsStore = createSlice({
  name: 'foods',
  reducers: {
    // 添加购物车
    addCart (state, action) {
      // 是否添加过?以action.payload.id去cartList中匹配 匹配到了 添加过
      const item = state.cartList.find(item => item.id === action.payload.id)
      if (item) {
        item.count++
      } else {
        state.cartList.push(action.payload)
      }
    }
  }
})

// 导出actionCreater
const { addCart } = foodsStore.actions

export { addCart }

const reducer = foodsStore.reducer

export default reducer

关键:

a.判断商品是否添加过

// 是否添加过?以action.payload.id去cartList中匹配 匹配到了 添加过
const item = state.cartList.find(item => item.id === action.payload.id)

2- 编写组件逻辑

<div className="goods-count">
  {/* 添加商品 */}
  <span
    className="plus"
    onClick={() => dispatch(addCart({
    id,
    picture,
    name,
    unit,
    description,
    food_tag_list,
    month_saled,
    like_ratio_desc,
    price,
    tag,
    count
  }))}></span>
</div>

Bug修复:

修复count增加问题



7. 统计区域实现

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/18.png

image.png

实现思路

  1. 基于store中的cartList的length渲染数量
  2. 基于store中的cartList累加price * count
  3. 购物车cartList的length不为零则高亮
// 计算总价
const totalPrice = cartList.reduce((a, c) => a + c.price * c.count, 0)

{/* fill 添加fill类名购物车高亮*/}
{/* 购物车数量 */}
<div onClick={onShow} className={classNames('icon', cartList.length > 0 && 'fill')}>
  {cartList.length > 0 && <div className="cartCornerMark">{cartList.length}</div>}
</div>

拓展:

react中不存在computed计算属性,因为每次数值的变化都会引起组件的重新渲染



8. 购物车列表功能实现

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/19.png

image.png

1-控制列表渲染

const Cart = () => {
  return (
    <div className="cartContainer">
      {/* 添加visible类名 div会显示出来 */}
      <div className={classNames('cartPanel', 'visible')}>
        {/* 购物车列表 */}
        <div className="scrollArea">
          {cartList.map(item => {
            return (
              <div className="cartItem" key={item.id}>
                <img className="shopPic" src={item.picture} alt="" />
                <div className="main">
                  <div className="skuInfo">
                    <div className="name">{item.name}</div>
                  </div>
                  <div className="payableAmount">
                    <span className="yuan">¥</span>
                    <span className="price">{item.price}</span>
                  </div>
                </div>
                <div className="skuBtnWrapper btnGroup">
                  {/* 数量组件 */}
                  <Count
                    count={item.count}
                  />
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  )
}

export default Cart

2- 购物车增减逻辑实现

// count增
increCount (state, action) {
  // 关键点:找到当前要修改谁的count id
  const item = state.cartList.find(item => item.id === action.payload.id)
  item.count++
},
// count减
decreCount (state, action) {
  // 关键点:找到当前要修改谁的count id
  const item = state.cartList.find(item => item.id === action.payload.id)
  if (item.count === 0) {
    return
  }
  item.count--
}
<div className="skuBtnWrapper btnGroup">
  {/* 数量组件 */}
  <Count
    count={item.count}
    onPlus={() => dispatch(increCount({ id: item.id }))}
    onMinus={() => dispatch(decreCount({ id: item.id }))}
    />
</div>

3-清空购物车实现

// 清除购物车clearCart (state) {  state.cartList = []}
<div className="header">
  <span className="text">购物车</span>
  <span
    className="clearCart"
    onClick={() => dispatch(clearCart())}>
    清空购物车
  </span>
</div>


9. 控制购物车显示和隐藏

https://nathan-blog-img.oss-cn-nanjing.aliyuncs.com/blog_img/20.png

image.png

// 控制购物车打开关闭的状态
const [visible, setVisible] = useState(false)

const onShow = () => {
  if (cartList.length > 0) {
    setVisible(true)
  }
}

{/* 遮罩层 添加visible类名可以显示出来 */}
<div
    className={
     classNames('cartOverlay', visible && 'visible')
  }
    onClick={() => setVisible(false)}
/>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue生成二维码并进行二维码图片下载

1、安包 npm install vue-qr --save2、引入 // vue2.0 import VueQr from vue-qr // vue3.0 import VueQr from vue-qr/src/packages/vue-qr.vue new Vue({components: {VueQr} })<!-- 设备二维码 对话框 270px--><el-dialog title"点位二维码" :visible.…

【Java 进阶篇】Redis:打开缓存之门

介绍 Redis&#xff08;Remote Dictionary Server&#xff09;是一个高性能的键值对存储系统&#xff0c;被广泛用作缓存、消息中间件和数据库。它以其快速的读写能力、支持多种数据结构和丰富的功能而闻名。在这篇博客中&#xff0c;我们将深入了解Redis的概念、安装以及基本…

阿里云 ACK 新升级,打造智算时代的现代化应用平台

云布道师 今天&#xff0c;能想到的或是想不到的领域&#xff0c;对容器和 Kubernetes 的需求都居高不减&#xff0c;使这项技术正在真正走向无处不在。 在 2023 云栖大会上&#xff0c;阿里云云原生产品线容器服务负责人易立关于容器服务 ACK 在本届亚运会上应用的介绍&#…

系列七、ThreadLocal为什么会导致内存泄漏

一、ThreadLocal为什么会导致内存泄露 1.1、ThreadLocalMap的基本结构 ThreadLocalMap是ThreadLocal的内部类&#xff0c;没有实现Map接口&#xff0c;用独立的方式实现了Map的功能&#xff0c;其内部的Entry也是独立实现的。源码如下&#xff1a; 1.2、ThreadLocal引用示意图…

ChinaSoft 论坛巡礼 | 高可信嵌入式软件智能化开发与测试论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

MS9708/MS9710/MS9714高速、低功耗数模转换器,可替代ADI的

产品简述 MS9708/MS9710/MS9714 是一个 8-Bit/10-Bit/14-Bit 高速、低功耗 D/A 转换器。当采样速率达到 125MSPS 时&#xff0c; MS9708/MS9710/MS9714 也能提供优越的 AC 和 DC 性能。 MS9708/MS9710/MS9714 的正常工作电压范围为 2.7V 到 5.5V &#xff0c;…

每日一题 2304. 网格中的最小路径代价(中等,动态规划)

由于他每一行的每一个值都可以到下一行的所有节点&#xff0c;且路径的代价没有什么相关性&#xff0c;所以只能用 O(mn2) 的动态规划求解 class Solution:def minPathCost(self, grid: List[List[int]], moveCost: List[List[int]]) -> int:m, n len(grid), len(grid[0])…

Attention is All You Need:Transformer各模块详解

Transformer encoder-decoder架构 Encoder&#xff1a;将输入序列转换为一个连续向量空间中的表示。Encoder通常是一个循环神经网络&#xff08;RNN&#xff09;或者卷积神经网络&#xff08;CNN&#xff09;&#xff0c;通过对输入序列中的每个元素进行编码&#xff0c;得到…

51单片机按键控制LED灯亮灭的N个玩法

51单片机按键控制LED灯亮灭的N个玩法 1.概述 这篇文章介绍按键的使用&#xff0c;以及通过控制LED灯的小实验&#xff0c;发现按键中存在的问题&#xff0c;然后思考并解决这些问题。达到熟练使用按键控制元器件。 2.搭建硬件环境 1.硬件准备 名称型号数量单片机STC12C205…

3d标签云实现过程(tagcloud.js)同步原生和 vue

写在前面 本来是没有准备写这个知识点&#xff0c;但是下载这个 js 的时候发现很多都是要钱或者是积分的&#xff0c;我就不明白了一个开源了这么久的 js 怎么还有人拿来挣钱的&#xff0c;同时还有一些只有原生 html 的例子&#xff0c;但是现在都是 框架主导的一些项目&#…

Find My音箱|苹果Find My技术与音箱结合,智能防丢,全球定位

音箱市场规模正在不断扩大。随着人们生活品质的提高&#xff0c;对音乐体验的需求也在不断升级。消费者对于蓝牙音箱的需求&#xff0c;已经从单纯的音质扩展到了功能、设计和价格等多个方面。随着移动化、即时化的视听娱乐需求的增长&#xff0c;蓝牙音箱性能、质量、外观设计…

嵌入式主板购买需要考虑哪些内容?

众所周知&#xff0c;各种先进电子器件和计算机处理技术在我国自动化工业生产中的应用&#xff0c;极大地提高了发展的效率和发展水平。而嵌入式主板以其多元化的设计特点在我国工业系统的控制中表现得越来越明显&#xff0c;消费者在选择这种嵌入式主板时必须注意以下几点考虑…

2023 年 亚太赛 APMCM 国际大学生数学建模挑战赛 |数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 cs数模团队在亚太赛 APMCM前为大家提供了许多资料的内容呀&…

数学建模之拟合及其代码

发现新天地&#xff0c;欢迎访问Cr不是铬的个人网站 引言 与插值问题不同&#xff0c;在拟合问题中不需要曲线一定经过给定的点。拟合问题的目标是寻求一个函数&#xff08;曲线&#xff09;&#xff0c;使得该曲线在某种准则下与所有的数据点最为接近&#xff0c;即曲线拟合…

JSP:Javabean

起初&#xff0c;JavaBean的目的是为了将可以重复使用的代码进行打包&#xff0c;在传统的应用中&#xff0c;JavaBean主要用于实现一些可视化界面&#xff0c;如一个窗体、按钮、文本框等&#xff0c;这样的JavaBean称之可视化的JavaBean。 随着技术的不断发展与项目的需求&am…

【前端】vue中合并表格行

做平台功能时&#xff0c;遇到一个需求是需要将表格某列有相同值时进行合并展示&#xff0c;比如 1、通过在Element中得知需要在表格中增加span-method方法 <el-table:data"tableData":span-method"cellMerge"borderstyle"width: 100%; margin-to…

Linux操作系统使用及C高级编程-D14共用体和枚举

共用体 不同数据类型可以使用共同的存储区域。和结构体的本质区别是&#xff1a;使用内存方式&#xff0c;其中共用体是使用同一块内存 1、 选择最大成员的大小 2、要能够整除成员大小&#xff0c;下图中un1中char s[7]是7个字节&#xff0c;但要遵循能整除int&#xff0c;所以…

「Docker」如何在苹果电脑上构建简单的Go云原生程序「MacOS」

介绍 使用Docker开发Golang云原生应用程序&#xff0c;使用Golang服务和Redis服务 注&#xff1a;写得很详细 为方便我的朋友可以看懂 环境部署 确保已经安装Go、docker等基础配置 官网下载链接直达&#xff1a;Docker官网下载 Go官网下载 操作步骤 第一步 创建一个…

scrapy框架流程

1、Scrapy从Spider子类中提取start_url,然后构造为request请求对象 2、将request请求对象传递给爬虫中间件 3、将request请求对象传递给Scrapy引擎&#xff08;核心代码&#xff09; 4、将request请求对象传递给调度器&#xff08;它负责对多个request安排&#xff0c;好比交…