redux_旧版本

reduxjs/toolkit(RTK)是 Redux 官方团队推出的一个工具集,旨在简化 Redux 的使用和配置。它于 2019 年 10 月 正式发布,此文章记录一下redux的旧版本如何使用,以及引入等等。
在这里插入图片描述

在这里插入图片描述
文件目录如下:
在这里插入图片描述

步骤

  1. 安装依赖:

    npm install redux react-redux redux-thunk
    
  2. 设置 Redux Store:
    创建 store.js 文件并配置 createStore。
    使用 combineReducers 合并多个 reducer。
    使用 applyMiddleware 添加 thunk 中间件。

  3. 创建 Actions:
    定义 action 类型和 action 创建函数。

  4. 创建 Reducers:
    根据 action 类型更新 state。

  5. 连接 React 组件到 Redux Store:
    使用 Provider 组件将 store 提供给整个应用。
    使用 connect 函数将组件连接到 Redux store,并映射 state 和 dispatch 方法到组件的 props。

  6. 使用 Redux DevTools:
    配置 Redux DevTools 以方便调试。

1. 安装必要的依赖

安装 redux、react-redux 和 redux-thunk(用于处理异步操作)。

npx create-react-app redux-test
cd redux-test
npm install redux react-redux redux-thunk

2. 设置 Redux Store

创建一个 store.js 文件来配置 Redux store
applyMiddleware 用于在 Redux store 中应用中间件,中间件在 Redux 的 action 被分发到 reducer 之前拦截这些 action,可以扩展 Redux 的功能,实现诸如异步操作(使用 redux-thunk 或 redux-saga)、路由控制、日志记录等。

applyMiddleware工作原理
1.Action 分发:
当一个 action 被分发时,它首先会被传递给中间件。
2.中间件链:
中间件按照应用的顺序形成一个链,每个中间件可以处理 action 并决定是否继续传递给下一个中间件或 reducer。
3.处理逻辑:
每个中间件可以执行自定义逻辑,例如记录日志、处理异步操作等。
4.传递 Action:
最终,action 会被传递给 reducer 进行状态更新

src/redux/store.js

/**
* 该文件专门用于暴露一个store对象,整个应用只有一个store对象
* */
// 引入createStore,专门用于创建redux中最为核心的 store
import { createStore,applyMiddleware } from 'redux'
// 引入总的Reducers
import Reducers from './reducers/index'// 引入redux-thunk插件,用于支持异步action
// redux-thunk 允许你返回一个函数而不是一个普通的 action 对象。 ==>体现在action的返回值上,主要处理异步action
import { thunk } from 'redux-thunk'
// 引入redux-devtools-extension插件,用于支持redux调试工具
import { composeWithDevTools } from "redux-devtools-extension"
// 暴露store
export default createStore(Reducers,composeWithDevTools(applyMiddleware(thunk)))

src/redux/reducers/index.js

combineReducers 汇总所有的reducer变为一个总的reducer

/*** 该文件用于汇总所有的reducer为一个总的reducer**/
// 引入combineReducers
import { combineReducers } from "redux";
// 引入为Count组件服务的reducer
import count from "./count";
// 引入Person组件服务的的reducer
import persons from "./person";
// 汇总所有的reducer变成一个总的reducer
export default combineReducers({count,persons
})
// export default 全局暴露的对象,可以自定义暴露对象的名称

3. 创建 Actions

src/common/common.js

// 该模块仅用于定义常量,其他模块导入该常量即可
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'
export const ADD_INFO = 'personInfo'

src/redux/actions/count.js

import {INCREMENT,DECREMENT} from "../../common/common";
// import store from "./store";
/*** 该文件专门为Count组件生成action对象*
**/
/**
* 同步action:action的值为Object类型的一般对象
* */
export const increment = (data)=>({type: INCREMENT, data})
export const decrement = (data)=>({type: DECREMENT, data})
/*** 异步action:action的值为函数类型,异步action中可以调用同步action,也可以自己写异步代码**/
export const incrementAsync = (data,time)=>{// createStore(countReducer,applyMiddleware(thunk))// applyMiddleware中可以传入多个中间件,中间件是一个函数,该函数的参数是store,所以此处无需传入storereturn (dispatch)=>{// console.log(dispatch);setTimeout(()=>{// store.dispatch(incrementAction(data))dispatch(increment(data))},time)}
}

src/redux/actions/person.js

import { ADD_INFO } from "../../common/common";
export const addPerson = (personObj)=>({type:ADD_INFO,personObj})

4. 创建 Reducers

创建 countReducer.js 和 personReducer.js 文件来处理 actions 并更新 state。
src/redux/reducers/count.js

/*** 1.该文件适用于创建一个为Count组件服务的redux模块中的reducer,reducer的本质是一个函数* 2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)* **/
import {INCREMENT,DECREMENT} from "../../common/common";let initialState = 0; // 数据初始化
export default function countReducer(preState =initialState, action) {// 从action对象中获取:type,datalet { type, data } = action// console.log(preState,action); // 初次获取0,"@@redux/INITx.1.n.3.n.9"// 根据type决定如何加工数据switch (type){case INCREMENT:// console.log(preState);return preState + data*1;case DECREMENT:return preState - data*1;default:return preState;// 初始化数据·}
}

src/redux/reducers/person.js

Redux 的 reducer 必须是纯函数,这意味着它不能修改传入的参数(即 preState),而是必须返回一个新的状态对象。

纯函数的定义:

  • 不能改变传递过来的参数的原始值
  • 只能通过返回值返回结果
  • 状态不可变性
// 数据参数
import { ADD_INFO } from "../../common/common"
const initPersonInfo =[] // 初始化列表数据
const personReducer = (preState=initPersonInfo,action)=>{let {type,personObj} = action // 此处解构switch (type){case ADD_INFO: // 若是添加数据// preState.unshift(personObj) // 修改的原数组,导致preState参数被改写了,       // personReducer就不是纯函数;且preState的指向地址没有发生变化,所以不会引起界面更新// return preState // vue中也是浅拷贝,但是vue中会进行深拷贝,所以界面更新// react 不会引起界面更新,因为指向地址并没有发生变化;--浅拷贝--修改引用指针return [personObj,...preState] // 浅拷贝,此时preState指向新的地址,界面更新 // 界面的更新比较的是两个对象的存储位置,浅比较default:return preState}
}
// 纯函数的定义:1.不能改变 传递过来的参数 的原始值 2.只能通过返回值返回结果
// 不管调用多少次,都只会返回一个结果
export  default personReducer
// export default 语句的语法不允许直接在 export default 后面使用 const 或 let 声明变量。
// export default 语句可以直接导出一个表达式或一个函数,但不能直接导出一个带有 const 或 let 声明的变量。

export default 语句可以直接导出一个表达式或一个函数,但不能直接导出一个带有 const 或 let 声明的变量。

5. 连接 React 组件到 Redux Store

src/App.jsx

import React, {Component} from 'react';
// 引入容器组件
import Count from './container/Count/index'
import Person from './container/Person/index'
// 引入store;传递给容器组件
class App extends Component {render() {return (<div><Count/><Person/></div>);}
}
export default App;

src/index.js

// 引入核心库
import React from 'react';
// 创建根节点
import { createRoot } from 'react-dom/client';
import store from "./redux/store";
// 使用Provider组件, 将store传递给APP组件,全局状态管理
import { Provider } from "react-redux"
// 引入文件
import App from './App';
// 创建容器
const container = document.getElementById('root'); // 外壳
const root = createRoot(container); // 创建根节点
root.render(/* 此处需要Provider包裹App,让App所有的后代容器组件都能接收到store */<Provider store={store}><App /></Provider>
);

src/container/Count/index.jsx
connect 作用:

  1. 连接组件到 Redux Store:
    connect 函数将 React 组件与 Redux store 连接起来,使得组件能够访问 store 中的状态和分发 actions。
  2. 传递状态到组件:
    通过 mapStateToProps 函数,将 Redux store 中的状态映射到组件的 props。
  3. 传递 dispatch 方法到组件:
    通过 mapDispatchToProps 函数,将 dispatch 方法映射到组件的 props,使得组件能够分发 actions。
  4. 优化渲染性能:
    connect 会自动处理组件的订阅和取消订阅,确保组件只在相关状态变化时重新渲染。

mapStateToProps

  1. mapStateToProps函数 返回的是一个对象;
  2. 返回的对象中的key就作为UI组件props的key,value就作为传递UI组件props的value
  3. mapStateToProps用于传递状态

mapStateToDispatch

  1. mapStateToDispatch函数 返回的是一个对象;
  2. 返回的对象中的key就作为UI组件props的key,value就作为传递UI组件props的value
  3. mapStateToDispatch用于传递操作状态的方法
// connect()(CountUI) ==> 容器container
// 使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
import React,{Component} from "react";
/*** 引入react-redux中connect函数,连接 UI组件和redyx(store)**/
import { connect } from "react-redux";
// store通过Provider传递引入
// 引入action
import {increment,decrement,incrementAsync} from "../../redux/actions/count"
// 容器通过 store 获取状态数据 ,传递给UI组件,UI组件通过props获取
class Count extends Component{incrementNum = ()=>{// 函数let { value } = this.selectNumthis.props.increment(value)}decrementNum = ()=>{// 函数let { value } = this.selectNumthis.props.decrement(value)}// 奇数时加incrementOddNum = ()=>{// 函数let { value } = this.selectNumlet  count  = this.props.countif(count % 2 !== 0){this.props.increment(value)}}incrementAsync = ()=>{// 函数let { value } = this.selectNum // 字符串形式需要转换,否则默认会字符串拼接this.props.incrementAsync(value,500)}render(){return(<div><h2>我是Count组件</h2><h1>求和的数值:{this.props.count}</h1><select ref={(c) => {this.selectNum = c}}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button onClick={this.incrementNum}>+</button><button onClick={this.decrementNum}>-</button><button onClick={this.incrementOddNum}>求和为基数时再加+</button><button onClick={this.incrementAsync}>异步再加+</button></div>)}
}export default connect(state=>{// Uncaught Error: Objects are not valid as a React child (found: object with keys {countNum}). If you meant to render a collection of children, use an array instead.return { count:state.count} // 映射到UI组件的props,返回的数值必须是一个对象且需要取准确的对象,store 统一管理数据},{increment, decrement, incrementAsync}
)(Count)

src/container/Person/index.jsx

import React, {Component} from 'react';
import {addPerson} from '../../redux/actions/person'
import {connect} from 'react-redux'
// 使用nanoid 生成唯一id
import {nanoid} from "nanoid";
class Person extends Component {// 在类组件中,方法默认不会自动绑定 this。// 在构造函数中手动绑定 this// constructor(props) {//     super(props);//     this.addUserInfo = this.addUserInfo.bind(this); // 手动绑定this// }// addUserInfo  (){//     console.log(this.nameNode.value,this.ageNode.value,6665)// }// 使用箭头函数来自动绑定 this。addUserInfo = ()=>{let name = this.nameNode.valuelet age = this.ageNode.valuelet id = nanoid()this.props.addPerson({id,name,age}) // 传递参数this.nameNode.value = ''this.ageNode.value = ''console.log(this.props)}render() {return (<div><h2>我是person组件</h2><input ref={c=>this.nameNode = c} type="text" placeholder="请输入姓名" /><input ref={c=>this.ageNode = c} type="text" placeholder="请输入年龄" /><button onClick={this.addUserInfo}>添加个人信息</button><ul>{ // 需要花括号遍历数组this.props.persons.map((item)=>{return <li key={item.id}>id:{nanoid()}-姓名:{item.name} - 年龄:{item.age}</li>})}</ul></div>);}
}
export default connect(state=>({ persons:state.persons}),{addPerson})(Person)

6. 使用 Redux DevTools

参考链接:https://blog.csdn.net/pikaqiu_komorebi/article/details/145908046

自用

同步action,是指action的值为Object类型的一般对象。

异步action,是指action的值为函数 =⇒主要是因为函数能开启异步任务。

const a = b⇒({data:b})

求和案例_redux精简版
  1. 去除Count组件自身的状态

  2. src下建
    -redux ( -store.js -count_reducer.js )

  3. store.js:
    1)reducer本质就是一个函数,接受:preState,action,返回加工后的状态
    2)reducer有两个作用:初始化状态,加工状态
    3)reducer被第一次调用时,是store自动触发的,传递prestate是undefined

  4. count_reducer.js

     1)reducer本质就是一个函数,接受:preState,action,返回加工后的状态2)reducer有两个作用:初始化状态,加工状态3)reducer被第一次调用时,是store自动触发的,传递prestate是undefined
    
  5. 在index.js中检测store中状态的改变,一旦发生改变重新渲染

// 监听redux中状态的改变,如redux的状态发生了改变,重新渲染App组件store.subscribe(()=>{root.render(<App />); // 渲染应用})

备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,需要自己去写

求和案例_redux异步action 版
  1. 明确:延迟的动作不想交给组件的自身,交给action ⇒ 异步动作

  2. 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回(非必须)

  3. 具体编码:

    1)yarn add redux-thunk,并配置在store中;使用redux的中间件 applyMiddleware ,用于添加中间件redux-thunk功能。2)创建action 的函数不再返回一般对象,而是一个函数,该函数中写异步任务3)异步任务有结果后,分发一个同步的action 去真正操作数据
    
  4. 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果再去分发同步action

求和案例_react-redux基本使用
  1. 明确两个概念

    1)UI组件:不能使用任何redux的api,只负责页面的呈现、交互等2)容器组件:负责和redux通信,并将结果交给UI组件
    
  2. 如何创建一个容器组件 — — — 靠react-redux的connect函数

    connect( mapStateToProps,mapDispatchToProps )( UI组件 )- mapStateToProps:映射状态,返回值是一个对象- mapStateToProps:映射操作状态的方法,返回值是一个对象UI组件通过props获取数据和方法;容器组件通过mapStateToProps,mapDispatchToProps传递
    
  3. 备注1:容器组件中的store是靠APP的props传进去的,而不是在容器中直接引入

  4. 备注2:mapDispatchToProps,也可以是一个对象,redux-redux内部调用分发action

  5. 备注3:容器组件能够自动检测store数据的变化,可去除store.subscribe(()⇒{})方法

  6. Provider能够给APP中所有的容器传递store,无需手动对容器组件传递store对象

  7. 合并文件,将容器组件和UI组件放到一起

求和案例_react-redux优化
  1. 容器组件和UI组件混成一个文件
  2. 无需手动给容器组件传递store,在index.js中给包裹一个即可。
// 引入核心库
import React from 'react';
// 创建根节点
import { createRoot } from 'react-dom/client';
import store from "./redux/store";
import { Provider } from "react-redux"
// 引入文件
import App from './App';
// 创建容器
const container = document.getElementById('root'); // 外壳
const root = createRoot(container); // 创建根节点root.render(<Provider store={store}><App /></Provider>
); 
  1. 使用了react-redux后不用自己监测redux中状态的改变,容器组件可自动监测
  2. mapStateToDispatch也可以简单的写成一个action对象,redux-redux可内部调用分发action
  3. 一个组件要和redux “打交道” 要经过哪几步
    1)定义好UI组件——不暴露
    2)引入connect生成容器组件,并暴露,写法如下
    3)在UI组件中通过this.props.xxxx读取状态和操作方法
connect(state⇒({key:value}), // 映射状态;value数值要根据总的Reducers取值(key:xxxAction) // 映射操作状态的方法 
)(UI组件)
求和案例_react-redux数据共享版
  1. 定义一个Person组件,和Count组件通过redux共享数据;
  2. 为Person组件编写:reducer、action、配置common常量
  3. 重点:Person的Reducer和Count的Reducer要使用combineReducers进行合并,合并后的总状态是一个对象
// 引入createStore,专门用于创建redux中最为核心的 store
import { createStore,applyMiddleware,combineReducers } from 'redux'
// 引入为Count组件服务的reducer
import countReducer from "./reducers/count"
import personReducer from "./reducers/person"
// 引入redux-thunk插件,用于支持异步action
import { thunk } from 'redux-thunk'
// combineReducers汇总所有的reducer变为一个总的reducer
const allReducers = combineReducers({countNum:countReducer,addPerson:personReducer
})
// 暴露store
export default createStore(allReducers,applyMiddleware(thunk))
  1. 交给store的是总reducer,最后注意在组件中取出状态的时候,取到位
求和案例_react-redux最终版
  1. 所有变量名称尽量触发对象的简写形式
  2. reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer

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

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

相关文章

MySQL:SQL优化实际案例解析(持续更新)

文章目录 一、MySQL&#xff1a;SQL优化1、时间格式化问题&#xff08;字符串&#xff09;2、in/inner join的问题 一、MySQL&#xff1a;SQL优化 1、时间格式化问题&#xff08;字符串&#xff09; -- 优化前 SELECT * FROM test_table WHERE date_format( begin_time, %Y-%…

【含文档+PPT+源码】基于Python的美食数据的设计与实现

项目介绍 本课程演示的是一款基于Python的美食数据分析系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 带你从零开始部署运行本套系统 该项目附带的源码…

vue调整表格样式之深度修改

举例&#xff1a; <div class"grid-item"><h3>日数据</h3><el-table :data"dailyData" v-loading"loading"><el-table-column label"销售姓名" align"center" prop"salesName" />…

【Go每日一练】统计字符出现的次数

&#x1f47b;创作者&#xff1a;丶重明 &#x1f47b;创作时间&#xff1a;2025年3月9日 &#x1f47b;擅长领域&#xff1a;运维 目录 1.&#x1f636;‍&#x1f32b;️题目&#xff1a;统计字符出现的次数2.&#x1f636;‍&#x1f32b;️代码中可用的资源3.&#x1f636;…

uniapp在APP平台(Android/iOS)选择非媒体文件

TOC 背景 在我们APP开发过程中&#xff0c;经常会有这样一个需求场景&#xff1a;从手机中选择文件然后进行上传&#xff0c;这些文件主要分为两类&#xff0c;媒体文件和非媒体文件。而媒体文件选择在APP平台我们可以使用uni.chooseImage和uni.chooseVideo这两个API来实现。…

【eNSP实战】配置交换机端口安全

拓扑图 目的&#xff1a;让交换机端口与主机mac绑定&#xff0c;防止私接主机。 主机PC配置不展示&#xff0c;按照图中配置即可。 开始配置之前&#xff0c;使用PC1 ping 一遍PC2、PC3、PC4、PC5&#xff0c;让交换机mac地址表刷新一下记录。 LSW1查看mac地址表 LSW1配置端…

卡尔曼滤波算法从理论到实践:在STM32中的嵌入式实现

摘要&#xff1a;卡尔曼滤波&#xff08;Kalman Filter&#xff09;是传感器数据融合领域的经典算法&#xff0c;在姿态解算、导航定位等嵌入式场景中广泛应用。本文将从公式推导、代码实现、参数调试三个维度深入解析卡尔曼滤波&#xff0c;并给出基于STM32硬件的完整工程案例…

Redis----大key、热key解决方案、脑裂问题

文章中相关知识点在往期已经更新过了&#xff0c;如果有友友不理解可翻看往期内容 出现脑裂问题怎么保证集群还是高可用的 什么是脑裂问题 脑裂说的就是当我们的主节点没有挂&#xff0c;但是因为网络延迟较大&#xff0c;然后和主节点相连的哨兵通信较差&#xff0c;之后主…

python总结(3)

创建自定义类 终于要创建自定义类了!下面是一个简单的示例: class Person:def set_name(self, name):self.name namedef get_name(self):return self.namedef greet(self):print("Hello, world! Im {}.".format(self.name))这个示例包含三个方法定义&#xff0c;它…

word毕业论文“et al.”替换为“等”——宏

Sub 中文参考文献改等()中文参考文献改等 宏Selection.Find.ClearFormattingSelection.Find.Replacement.ClearFormattingWith Selection.Find.Text "([一-龥], )et al.".Replacement.Text "\1等.".Forward True.Wrap wdFindContinue.Format False.Ma…

网络编程-----服务器(多路复用IO 和 TCP并发模型)

一、单循环服务器模型 1. 核心特征 while(1){newfd accept();recv();close(newfd);}2. 典型应用场景 HTTP短连接服务&#xff08;早期Apache&#xff09;CGI快速处理简单测试服务器 3. 综合代码 #include <stdio.h> #include <sys/types.h> /* See NO…

typora高亮方案+鼠标侧键一键改色

引言 在typora里面有一个自定义的高亮, <mark></mark>>但是单一颜色就太难看了, 我使用人工智能, 搜索全网艺术家, 汇集了几种好看的格式,并且方便大家侧键一键 调用, 是不是太方便啦 ! 示例 午夜模式 春意盎然 深海蓝调 石墨文档 秋日暖阳 蜜桃宣言 使用方法 …

swift -(5) 汇编分析结构体、类的内存布局

一、结构体 在 Swift 标准库中&#xff0c;绝大多数的公开类型都是结构体&#xff0c;而枚举和类只占很小一部分 比如Bool、 Int、 Double、 String、 Array、 Dictionary等常见类型都是结构体 ① struct Date { ② var year: Int ③ var month: Int ④ …

从零开始学机器学习——初探分类器

首先给大家介绍一个很好用的学习地址:https://cloudstudio.net/columns 在上一节中,我们已经完成了对所有经过均衡和清洗的菜品数据的准备工作。接下来,我们将考虑使用多种算法,也就是不同的分类器,来构建模型。我们将利用这个数据集和各种分类器,基于一组特定的配料,预…

黑色RGB是什么

>好的&#xff0c;用户现在问的是“黑色rgb是什么”。我需要先理解用户的问题。之前用户在使用MATLAB调整网格线颜色时&#xff0c;可能看到了默认颜色是黑色&#xff0c;或者之前我提到过默认颜色是[0.15 0.15 0.15]&#xff0c;而用户可能现在想知道黑色的RGB值具体是什么…

做到哪一步才算精通SQL

做到哪一步才算精通SQL-Structured Query Language 数据定义语言 DDL for StructCREATE&#xff1a;用来创建数据库、表、索引等对象ALTER&#xff1a;用来修改已存在的数据库对象DROP&#xff1a;用来删除整个数据库或者数据库中的表TRUNCATE&#xff1a;用来删除表中所有的行…

《深度解析DeepSeek-M8:量子经典融合,重塑计算能效格局》

在科技飞速发展的今天&#xff0c;量子计算与经典算法的融合成为了前沿领域的焦点。DeepSeek-M8的“量子神经网络混合架构”&#xff0c;宛如一把钥匙&#xff0c;开启了经典算法与量子计算协同推理的全新大门&#xff0c;为诸多复杂问题的解决提供了前所未有的思路。 量子计算…

解决电脑问题(2)——主板问题

当电脑主板出现问题时&#xff0c;可以尝试以下解决方法&#xff1a; 外观检查与清洁 检查硬件连接&#xff1a;仔细查看主板上的各种硬件连接&#xff0c;包括 CPU、内存、显卡、硬盘、电源等的连接线是否松动或损坏。确保所有插头都牢固地插入相应的插槽中&#xff0c;如有松…

Java 大视界 -- Java 大数据在智能家居能源管理与节能优化中的应用(120)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

【网络】TCP常考知识点详解

TCP报文结构 TCP报文由**首部&#xff08;Header&#xff09;和数据&#xff08;Data&#xff09;**两部分组成。首部包括固定部分&#xff08;20字节&#xff09;和可选选项&#xff08;最多40字节&#xff09;&#xff0c;总长度最大为60字节。 1. 首部固定部分 源端口&…