当前位置: 首页 > article >正文

yup 使用 3 - 利用 meta 实现表单字段与表格列的统一结构配置(适配 React Table)

yup 使用 3 - 利用 meta 实现表单字段与表格列的统一结构配置(适配 React Table)

Categories: Tools
Last edited time: May 11, 2025 7:45 PM
Status: Done
Tags: form validation, schema design, yup

本文介绍如何通过 Yup 的 meta() 字段,在 React 项目中实现表单字段与表格列的统一配置,适配多区域字段展示与结构共用需求。

前情提要:

  • yup 基础使用以及 jest 测试
    yup 的基础使用,这里不会过多涉及 yup 的使用,所以对 yup 0 了解的可以看看
  • yup 使用 2 - 获取默认值,循环依赖,超大数字验证,本地化
    和本篇内容关系不大,想要对 yup 更深入的了解一下的可以看看
  • [React] 如何用 Zustand 构建一个响应式 Enum Store?附 RTKQ 实战与 TS 架构落地
    本篇笔记的 base code 一部分是来自于这篇笔记,主要是下面拿值的时候方便一些

之前在写完 yup 这两个系列的时候,我其实就有在考虑用 yup 处理一个问题——结合 yup 的 meta 属性,然后合并管理表单/表格的定义逻辑,这样可以更好地集中处理现有项目中,关于每一个属性的定义及实现

在经过了一系列反复测试和修改后,算是拿出了一个还可以的解决方案,故此记录一下

问题 - 重复的定义

我做的 B2B 和 B2C 的很多项目都是重表单的项目,因此从很久以前我就已经在想怎么要处理表单和结构的问题,总体来说也是经历了这么几种情况:

  • 手写验证+自己抽离 UI 逻辑
    这个是最初开始接触 React 时,在写了几个项目需求的表单后,自己琢磨出来的一套,在当时的项目里可以使用的功能。因为当时项目的需求要求 bundle 要小,需要 minimal dependency,所以很多东西都必须自己实现,也就自己造了些轮子
    大体的逻辑是,每个 state 抽出来这样的逻辑:
    const form = [{type: "str",header: "Label for this field",key: "attributeName",validations: [minLength(3), maxLength(5)],},
    ];
    
    作为一个基础的实现,这样可以比较快的遍历整个 state,获取当前这个 field 的 type,attribute name,displayed name,完成表单的渲染,同时 validations 中也提供了一部分的验证,可以完成一些比较基础的,包括 min/max,nullable,isNotEmpty 之类的验证
    这个时候的实现其实已经可以看到一些工具的影子了,只不过当时确实没有想这么多就是了
  • 手写验证+使用工具生成表单
    这已经是不同的项目了,不过因为 tech debt,整体的项目处在一种又老又新的状态。老是因为确实不少的工具——比如说 redux form,有些年头了,虽然当时用起来比较舒服,不过对于日新月异的更新来说,还是稍显疲软。新则是因为当时确实也在从 class based components 转向 functional components,并且大量的资源放在了 React 方面的重写,对于之前存在工具和验证还是采取了保留的状态
    当然,我没有在这个项目停留很久,只是作为 supportive resource 帮助完成了 React 方面的 refactor 就跑路了,不过我开始慢慢意识到了,验证和结构的分离,似乎在很多项目里都存在,也是不少项目都在头疼的地方
  • 忽略验证+手写 structure
    这也是最近这些年在工作的项目,忽略验证的原因是因为所有的验证 backend 都有 cover,当时是没有考虑做前端的验证,也是属于 tech debt。之所以单独把这项目拎出来的原因,则是因为手写 structure 的部分——structure 做起来其实还是有点麻烦的,因为它需要同时支持 table 和 form 两个 level 的显示
    除此之外,这个项目因为要做 multi-region support,因此每个 region 都会有对应的 structure。这也就意味着,当一个页面逻辑比较复杂的时候,它的管理难度就会直线上升:
    • 对于同一个属性来说,它在 table/form 层的逻辑可能不一样
      如,在 table 层,它可能是 dropdown,但是在 form 层它可以打开一个 modal
      或者在 table 层要做最简渲染,因此 table 层不会显示该属性,但是在 form 层有足够的空间显示完整的数据,因此需要显示
    • 对于不同 region 来说,显示的 attribute 也不一样
      针对这种情况,我其实已经做了一部分的 refactor,将类似 手写验证+自己抽离 UI 逻辑 中的逻辑,从 array based 换成 object based,使用下来大体如下:
    const regionA = { attributeA, attributeB, attributeC };
    const regionB = { attributeA, attributeC };
    
    在 component 中,则通过获取属性名称的方法进行重写,这样解决了一部分的问题——属性可以统一管理,修改显示名之类的只需要修改 base object,不需要重复 cv 多个地方,在 react 组件中的重写也比较方便,比起版本 1 来说是一个有效的提升了
    但是本质上来说,重复的 declaration 还是没有办法解决,尤其是在看特定属性,在不同 region 中的表现——是否存在、是否可编辑——也非常的困难
    尤其在使用 yup 后,就会发现,schema 和 structure 出现了很大的重复,这也是我现在主要想解决的问题

以上是已经有 tech debt 中的项目会遇到的问题,如果没有的话,我个人建议还是优先考虑使用 react hook form 这种已经可以原生支持 yup/zod 等 schema validators 的工具。如果确实有一个 schema/structure 需要同时支持 form 和 table 的需求,再往下看

dummy data

这里放一下页面中要渲染的数据:

const demoStudentCourseRecords = [{studentName: "Alice Johnson",studentId: "S1001",courseLevel: "beginner",timeSlot: "morning",courseTaken: ["ml"],},{studentName: "Bob Smith",studentId: "S1002",courseLevel: "intermediate",timeSlot: "afternoon",courseTaken: ["db", "stats"],},{studentName: "Charlie Lee",studentId: "S1003",courseLevel: "advanced",timeSlot: "evening",courseTaken: ["ds"],},{studentName: "Diana Wang",studentId: "S1004",courseLevel: "beginner",timeSlot: "morning",courseTaken: ["stats"],},{studentName: "Ethan Brown",studentId: "S1005",courseLevel: "intermediate",timeSlot: "evening",courseTaken: ["ml", "ds", "stats"],},
];

这里所有的 courseTaken 能和具体的课程有显著的不同,其他的都是大小写的区别,这个是缩写和全称的区别。也就是说,完整渲染的表格上,如果不能显示完整的名称,那就证明下拉框的实现出现问题了

meta 基础定义

这里是一些基础的 meta data,验证部分暂时略过:

export function createStudentCourseRecordSchema(courseLevels: string[],timeSlots: string[],courseList: string[],courseLevelOptions: { id: string; label: string }[],timeSlotOptions: { id: string; label: string }[],courseOptions: { id: string; label: string }[]
): yup.AnySchema {return yup.object().shape({studentName: studentNameField,studentId: studentIdField,courseLevel: yup.string().required(`${FIELD_LABELS.courseLevel} is required`).oneOf(courseLevels, `Invalid ${FIELD_LABELS.courseLevel.toLowerCase()}`).meta({label: FIELD_LABELS.courseLevel,table: { header: FIELD_LABELS.courseLevel, accessorKey: "courseLevel" },type: "enum",options: courseLevelOptions,}),timeSlot: yup.string().required(`${FIELD_LABELS.timeSlot} is required`).oneOf(timeSlots, `Invalid ${FIELD_LABELS.timeSlot.toLowerCase()}`).meta({label: FIELD_LABELS.timeSlot,table: { header: FIELD_LABELS.timeSlot, accessorKey: "timeSlot" },type: "enum",options: timeSlotOptions,}),courseTaken: yup.array().of(yup.string().oneOf(courseList, `Invalid course taken`)).min(1,`At least one ${FIELD_LABELS.courseTaken.toLowerCase()} must be selected`).required(`${FIELD_LABELS.courseTaken} is required`).meta({label: FIELD_LABELS.courseTaken,table: { header: FIELD_LABELS.courseTaken, accessorKey: "courseTaken" },type: "multi-enum",options: courseOptions,}),});
}

这里注意一下 typetype: "enum"type: "multi-enum",分别对应的是单选和多选,这个在后面转化成 react table 时需要用到

这里也是一个比较初期的想法,因此在选择创建 createStudentCourseRecordSchema 的时候采取了传值的方法。现在的想法是,之后抽空回到 zustand 的实现,然后通过 pub/sub 的方法,在获取数据后自动 publish 相应的变化,然后让 yup 通过 subscribe 去自动更新

生成 table column

方法如下:

export function generateTableColumnsFromSchema(schema: yup.AnySchema
): ColumnDef<any, any>[] {console.log(schema);if (!schema) return [];const description = schema.describe() as ObjectSchemaDescription;if (description.type !== "object" || !description.fields) {throw new Error("Schema must be a Yup object schema.");}const fields = description.fields;const columns: ColumnDef<any, any>[] = [];for (const [_, fieldDesc] of Object.entries(fields)) {const meta = fieldDesc.meta as any;const { header, accessorKey } = meta.table;const type = meta?.type;const options = meta?.options;let cell;if (type === "enum" && options) {const optionMap = new Map(options.map((opt: any) => [opt.id, opt.label]));cell = ({ getValue }: { getValue: () => any }) => {const rawValue = getValue();return optionMap.get(rawValue) ?? rawValue;};} else if (type === "multi-enum" && options) {const optionMap = new Map(options.map((opt: any) => [opt.id, opt.label]));cell = ({ getValue }: { getValue: () => any }) => {const rawValues = getValue() as string[];if (!Array.isArray(rawValues)) return null;return rawValues.map((val) => optionMap.get(val) ?? val).join(", ");};} else {cell = ({ getValue }: { getValue: () => any }) => {const rawValue = getValue();return rawValue;};}columns.push({header,accessorKey,cell,});}return columns;
}

这里对于 cell 的处理就是依据不同的类型做的,具体是要通过 key 去获得 value。

渲染效果如下:

可以看到,所有的 course 都从缩写变成了完整的课程,这也可以证明数据获取和渲染都是没有问题的

拓展 - 动态支持

拓展部分会用到一个 package:sift,sift 实现了一种类似 mongodb 的语法,我在用的过程中发现,至少对大多数的项目来说,一个简单的 $or 用法就够了

schema 部分的拓展

代码如下:

// 更新一下 props 定义,如果要写多个 util 方法的话,可以让 AnySchema 拓展下面的 meta 部分
// 这样可以获取更好的支持
export type Region = "us" | "apac" | "eu";
export type Env = "dev" | "uat" | "prod";
export type FieldVisibility = "form" | "table" | "both";
export type Context = {env?: Env;region?: Region;fieldVisibility?: FieldVisibility;
}[];export interface IFieldMeta {label?: string;table?: {header?: string;accessorKey?: string;};type?: string;options?: { id: string; label: string }[];isAvailable?: boolean | Context;
}// ========================================export function createStudentCourseRecordSchema(courseLevels: string[],timeSlots: string[],courseList: string[],courseLevelOptions: { id: string; label: string }[],timeSlotOptions: { id: string; label: string }[],courseOptions: { id: string; label: string }[]
): yup.AnySchema {return yup.object().shape({studentName: studentNameField,studentId: studentIdField,courseLevel: yup.string().required(`${FIELD_LABELS.courseLevel} is required`).oneOf(courseLevels, `Invalid ${FIELD_LABELS.courseLevel.toLowerCase()}`).meta({label: FIELD_LABELS.courseLevel,table: { header: FIELD_LABELS.courseLevel, accessorKey: "courseLevel" },type: "enum",options: courseLevelOptions,isAvailable: [{ fieldVisibility: "form" }],}),timeSlot: yup.string().required(`${FIELD_LABELS.timeSlot} is required`).oneOf(timeSlots, `Invalid ${FIELD_LABELS.timeSlot.toLowerCase()}`).meta({label: FIELD_LABELS.timeSlot,table: { header: FIELD_LABELS.timeSlot, accessorKey: "timeSlot" },type: "enum",options: timeSlotOptions,isAvailable: [{ fieldVisibility: "both" }],}),courseTaken: yup.array().of(yup.string().oneOf(courseList, `Invalid course taken`)).min(1,`At least one ${FIELD_LABELS.courseTaken.toLowerCase()} must be selected`).required(`${FIELD_LABELS.courseTaken} is required`).meta({label: FIELD_LABELS.courseTaken,table: { header: FIELD_LABELS.courseTaken, accessorKey: "courseTaken" },type: "multi-enum",options: courseOptions,}),});
}

这里主要更新的就是类型,以及 courseLeveltimeSlotisAvailable 部分,这一部分需要包含所有的选项,让 sift 可以去做 filter → 具体用法在下面

Context 单独抽出来是因为,这里可以不止可以用在 isAvailable 里,同样的逻辑也可以放在 isVisible, isEditable 等其他可拓展的逻辑中

这样就可以做一个非常直观且 fine grain 的控制:

  • 当前属性在 form/table/region 中是否包含
  • 当前属性在 form/table/region 中是否可见
  • 当前属性在 form/table/region 中是否可变

如果没有设置的话,则是默认 true ,同时也支持通过 boolean 的方式强制重写,这个优先权会高于 context 的设置

util 部分的拓展

代码如下:

export function generateTableColumnsFromSchema(schema: yup.AnySchema
): ColumnDef<any, any>[] {console.log(schema);if (!schema) return [];const description = schema.describe() as ObjectSchemaDescription;if (description.type !== "object" || !description.fields) {throw new Error("Schema must be a Yup object schema.");}const fields = description.fields;const columns: ColumnDef<any, any>[] = [];for (const [_, fieldDesc] of Object.entries(fields)) {const meta = fieldDesc.meta as any;const { header, accessorKey } = meta.table;const type = meta?.type;const options = meta?.options;let cell;if (type === "enum") {const optionMap = new Map(options.map((opt: any) => [opt.id, opt.label]));cell = ({ getValue }: { getValue: () => any }) => {const rawValue = getValue();return optionMap.get(rawValue) ?? rawValue;};} else if (type === "multi-enum") {const optionMap = new Map(options.map((opt: any) => [opt.id, opt.label]));cell = ({ getValue }: { getValue: () => any }) => {const rawValues = getValue() as string[];if (!Array.isArray(rawValues)) return null;return rawValues.map((val) => optionMap.get(val) ?? val).join(", ");};} else {cell = ({ getValue }: { getValue: () => any }) => {const rawValue = getValue();return rawValue;};}let isColAvailable = true;// const currEnv = {//   region: 'us'// };const currEnv = {$or: [{ env: "dev" },{ region: "us" },{ fieldVisibility: "table" },{ fieldVisibility: "both" },],};if (meta?.isAvailable) {// 添加 typeof 的检查,保证 boolean 的优先权isColAvailable = meta.isAvailable.filter(sift(currEnv)).length > 0;}if (!isColAvailable) continue;columns.push({header,accessorKey,cell,});}return columns;
}

这里为了展示一下做的稍微简化了一些,本来的做法是写三个方法的:

  • 第一个是 generateTableColumnsFromSchema
  • 第二个是 generateFormFieldsFromSchema

然后二者通过 sift 过滤一下 isColAvailable ,获取一个 IFieldMeta[] 结构的 columns,传到最后集中处理逻辑的 util function 里

这里核心的修改逻辑是这个:

let isColAvailable = true;
// const currEnv = {
//   region: 'us'
// };
const currEnv = {$or: [{ env: "dev" },{ region: "us" },{ fieldVisibility: "table" },{ fieldVisibility: "both" },],
};
if (meta?.isAvailable) {// 添加 typeof 的检查,保证 boolean 的优先权isColAvailable = meta.isAvailable.filter(sift(currEnv)).length > 0;
}if (!isColAvailable) continue;

这一段代码可以实现对当前 attribute 的控制,并且,我会建议把这个 context 再分一下,分成 global context 和 local context。global context,如 region,env 这种,在特定环境下是不会变化的,这个就完全可以抽出来放到其他的 constant 或是 util 中,等到要用的时候再 extend 即可

最终渲染效果如下:

Course Level 成了一个 form only 的选项,因此在 table view 中被忽略了,而 Time Slot 则是因为会出现在 form 和 table 中得以被保留,可以正常的在页面中渲染

相关文章:

yup 使用 3 - 利用 meta 实现表单字段与表格列的统一结构配置(适配 React Table)

yup 使用 3 - 利用 meta 实现表单字段与表格列的统一结构配置&#xff08;适配 React Table&#xff09; Categories: Tools Last edited time: May 11, 2025 7:45 PM Status: Done Tags: form validation, schema design, yup 本文介绍如何通过 Yup 的 meta() 字段&#xff0…...

类初始化方法

一、类初始化方法 成员初始化列表 class Point {int x, y; public:Point(int a, int b) : x(a), y(b) {} };就地初始化&#xff08;C11&#xff09; 声明时初始化。 class Widget {int size 10; // 类内成员初始化vector<int> data{1,2,3}; };特殊情况&#xff1a;静…...

【OpenCV】imread函数的简单分析

目录 1.imread()1.1 imread()1.2 imread_()1.2.1 查找解码器&#xff08;findDecoder&#xff09;1.2.2 读取数据头&#xff08;JpegDecoder-->readHeader&#xff09;1.2.2.1 初始化错误信息&#xff08;jpeg_std_error&#xff09;1.2.2.2 创建jpeg解压缩对象&#xff08;…...

【Linux实践系列】:进程间通信:万字详解共享内存实现通信

&#x1f525; 本文专栏&#xff1a;Linux Linux实践项目 &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 人生就像一场马拉松&#xff0c;重要的不是起点&#xff0c;而是坚持到终点的勇气 ★★★ 本文前置知识&#xff1a; …...

【笔记】BCEWithLogitsLoss

工作原理 BCEWithLogitsLoss 是 PyTorch 中的一个损失函数&#xff0c;用于二分类问题。 它结合了 Sigmoid 激活函数和二元交叉熵&#xff08;Binary Cross Entropy, BCE&#xff09;损失在一个类中。 这不仅简化了代码&#xff0c;而且通过数值稳定性优化提高了模型训练的效…...

Oracle SYSTEM/UNDO表空间损坏的处理思路

Oracle SYSTEM/UNDO表空间损坏是比较棘手的故障&#xff0c;通常会导致数据库异常宕机进而无法打开数据库。数据库的打开故障处理起来相对比较麻烦&#xff0c;读者可以参考本书第5章进一步了解该类故障的处理过程。如果数据库没有备份&#xff0c;通常需要设置官方不推荐的隐含…...

为什么 cout<<“中文你好“ 能正常输出中文

一, 简答: 受python3字符串模型影响得出的下文C字符串模型结论 是错的&#xff01;C的字符串和python2的字符串模型类似&#xff0c;也就是普通的字符串是ASCII字符串和字节串两种语义&#xff0c;类似重载或多态&#xff0c;有时候解释为整数&#xff0c;有时候是字节串。Uni…...

Leetcode 3547. Maximum Sum of Edge Values in a Graph

Leetcode 3547. Maximum Sum of Edge Values in a Graph 1. 解题思路2. 代码实现 题目链接&#xff1a;3547. Maximum Sum of Edge Values in a Graph 1. 解题思路 这一题主要是在问题的分析上面。由题意易知&#xff0c;事实上给定的图必然只可能存在三种可能的结构&#x…...

关于Go语言的开发环境的搭建

1.Go开发环境的搭建 其实对于GO语言的这个开发环境的搭建的过程&#xff0c;类似于java的开发环境搭建&#xff0c;我们都是需要去安装这个开发工具包的&#xff0c;也就是俗称的这个SDK&#xff0c;他是对于我们的程序进行编译的&#xff0c;不然我们写的这个代码也是跑不起来…...

Flutter PIP 插件 ---- 为iOS 重构PipController, Demo界面,更好的体验

接上文 Flutter PIP 插件 ---- 新增PipActivity&#xff0c;Android 11以下支持自动进入PIP Mode 项目地址 PIP&#xff0c; pub.dev也已经同步发布 pip 0.0.3&#xff0c;你的加星和点赞&#xff0c;将是我继续改进最大的动力 在之前的界面设计中&#xff0c;还原动画等体验一…...

Redis 基本命令与操作全面解析:从入门到实战

前言 Redis 作为高性能内存数据库&#xff0c;其丰富的命令体系是发挥强大功能的基础。掌握 Redis 的基本命令&#xff0c;不仅能实现数据的高效读写&#xff0c;还能深入理解其内存模型与工作机制。本文将系统梳理 Redis 的核心命令&#xff0c;涵盖连接操作、键管理、数据类…...

数据库管理-第325期 ADG Failover后该做啥(20250513)

数据库管理325期 2025-05-13 数据库管理-第325期 ADG Failover后该做啥&#xff08;20250513&#xff09;1 故障处置2 恢复原主库3 其他操作总结 数据库管理-第325期 ADG Failover后该做啥&#xff08;20250513&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&a…...

SQLi-Labs 第21-24关

Less-21 http://127.0.0.1/sqli-labs/Less-21/ 1&#xff0c;抓个请求包看看 分析分析cookie被base64URL编码了&#xff0c;解码之后就是admin 2&#xff0c;那么这个网站的漏洞利用方式也是和Less-20关一样的&#xff0c;只是攻击语句要先base64编码&#xff0c;再URL编码&…...

Oracle — 数据管理

介绍 Oracle数据库作为全球领先的关系型数据库管理系统&#xff0c;其数据管理能力以高效性、安全性和智能化为核心。系统通过多维度技术实现海量数据的存储与实时处理&#xff0c;支持高并发事务操作与复杂分析查询&#xff0c;满足企业关键业务需求。在安全领域&#xff0c;O…...

在 Qt Creator 中为 QDockWidget 设置隐藏和显示按钮

在 Qt Creator 中为 QDockWidget 设置隐藏和显示按钮 是的&#xff0c;QDockWidget 内置了隐藏和显示的功能&#xff0c;可以通过以下几种方式实现&#xff1a; 1. 使用 QDockWidget 自带的关闭按钮 QDockWidget 默认带有一个关闭按钮&#xff0c;可以通过以下代码启用&…...

LS-NET-012-TCP的交互过程详解

LS-NET-012-TCP的交互过程详解 附加&#xff1a;TCP如何保障数据传输 TCP的交互过程详解 一、TCP协议核心交互流程 TCP协议通过三次握手建立连接、数据传输、四次挥手终止连接三大阶段实现可靠传输。整个过程通过序列号、确认应答、窗口控制等机制保障传输可靠性。 1.1 三次…...

每日算法刷题Day1 5.9:leetcode数组3道题,用时1h

1.LC寻找数组的中心索引(简单) 数组和字符串 - LeetBook - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 思想: 计算总和和左侧和&#xff0c;要让左侧和等于右侧和&#xff0c;即左侧和总和-左侧和-当前数字 代码 c代码: class Solution { public:i…...

解构认知边界:论万能方法的本体论批判与方法论重构——基于跨学科视阈的哲学-科学辩证

一、哲学维度的本体论批判 &#xff08;1&#xff09;理性主义的坍缩&#xff1a;从笛卡尔幻想到哥德尔陷阱 笛卡尔在《方法论》中构建的理性主义范式&#xff0c;企图通过"普遍怀疑-数学演绎"双重机制确立绝对方法体系。然而哥德尔不完备定理&#xff08;Gdel, 19…...

PVE WIN10直通无线网卡蓝牙

在 Proxmox VE (PVE) 中直通 Intel AC3165 无线网卡的 **蓝牙模块**&#xff08;通常属于 USB 设备&#xff0c;而非 PCIe 设备&#xff09;需要特殊处理&#xff0c;因为它的蓝牙部分通常通过 USB 连接&#xff0c;而 Wi-Fi 部分才是 PCIe 设备。以下是详细步骤&#xff1a; …...

第六节第二部分:抽象类的应用-模板方法设计模式

模板方法设计模式的写法 建议使用final关键字修饰模板方法 总结 代码&#xff1a; People(父类抽象类) package com.Abstract3; public abstract class People {/*设计模板方法设计模式* 1.定义一个模板方法出来*/public final void write(){System.out.println("\t\t\t…...

在另一个省发布抖音作品,IP属地会随之变化吗?

你是否曾有过这样的疑惑&#xff1a;出差旅游时在外地发布了一条抖音视频&#xff0c;评论区突然冒出“IP怎么显示xx省了&#xff1f;”的提问&#xff1f;随着各大社交平台上线“IP属地”功能&#xff0c;用户的地理位置标识成为公开信息&#xff0c;而属地显示的“灵敏性”也…...

卷积神经网络-从零开始构建一个卷积神经网络

目录 一、什么是卷积神经网络CNN 1.1、核心概念 1.2、卷积层 二、什么是卷积计算 2.1、卷积计算的例子: 2.2、点积 2.3、卷积与点积的关系 2.4、Padding(填充) 2.4.1、Padding的主要作用 1、控制输出特征图尺寸 2、保留边缘信息 3. 支持深层网络训练 2.4.2、Str…...

力扣-101.对称二叉树

题目描述 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 class Solution { public:bool check(TreeNode* p,TreeNode* q){if(!p&&!q)return true;if(!p&&q||!q&&p)return false;if(p->val!q->val)return false;return check(p…...

Tomcat和Nginx的主要区别

1、功能定位 Nginx&#xff1a;核心是高并发HTTP服务器和反向代理服务器&#xff0c;擅长处理静态资源&#xff08;如HTML、图片&#xff09;和负载均衡。Tomcat&#xff1a;是Java应用服务器&#xff0c;主要用于运行动态内容&#xff08;如JSP、Servlet&#xff09;&#xf…...

贪心算法:最小生成树

假设无向图为&#xff1a; A-B:1 A-C:3 B-C:1 B-D:4 C-D:1 C-E:5 D-E:6 一、使用Prim算法&#xff1a; public class Prim {//声明了两个静态常量&#xff0c;用于辅助 Prim 算法的实现private static final int V 5;//点数private static final int INF Integer.MA…...

uniapp-文件查找失败:‘@dcloudio/uni-ui/lib/uni-icons/uni-icons.vue‘

uniapp-文件查找失败&#xff1a;‘dcloudio/uni-ui/lib/uni-icons/uni-icons.vue’ 今天在HBuilderX中使用uniapp开发微信小程序时遇到了这个问题&#xff0c;就是找不到uni-ui组件 当时创建项目&#xff0c;选择了一个中间带的底部带选项卡模板&#xff0c;并没有选择内置u…...

Vue2.x 和 Vue3.x 对比-差异

Vue3的优点 diff算法的提升 vue2中的虚拟DOM是全量的对比&#xff0c;也就是不管是写死的还是动态节点都会一层层比较&#xff0c;浪费时间在静态节点上。 vue3新增静态标记&#xff08;patchflag &#xff09;&#xff0c;与之前虚拟节点对比&#xff0c;只对比带有patch fla…...

MacOS 用brew 安装、配置、启动Redis

MacOS 用brew 安装、配置、启动Redis 一、安装 brew install redis 二、启动 brew services start redis 三、用命令行检测 set name tom get name...

agentmain对业务的影响

前面一篇已经说了java agent技术主要有premain和agentmain两种形式&#xff0c;如果大部分业务已经在线上运行的话&#xff0c;不方便用premain的方式来实现&#xff0c;所以agentmain的方式是更加通用、灵活的 由于RASP是与用户业务运行在同一个jvm中的 &#xff0c;所以RASP…...

精益数据分析(56/126):创业阶段的划分与精益数据分析实践

精益数据分析&#xff08;56/126&#xff09;&#xff1a;创业阶段的划分与精益数据分析实践 在创业和数据分析的探索之旅中&#xff0c;理解创业阶段的划分以及与之对应的精益数据分析方法至关重要。今天&#xff0c;依旧怀揣着与大家共同进步的心态&#xff0c;深入研读《精…...