SpringDoc枚举字段处理与SpringBoot接收枚举参数处理

本期内容

  1. 添加SpringDoc配置展示枚举字段,在文档页面中显示枚举值和对应的描述
  2. 添加SpringMVC配置使项目可以接收枚举值,根据枚举值找到对应的枚举

默认内容

先不做任何处理看一下直接使用枚举当做入参是什么效果。

  1. 定义一个枚举
package com.example.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 来源枚举
 *
 * @author vains
 */
@Getter
@AllArgsConstructor
public enum SourceEnum {

    /**
     * 1-web网站
     */
    WEB(1, "web网站"),

    /**
     * 2-APP应用
     */
    APP(2, "APP应用");

    /**
     * 来源代码
     */
    private final Integer value;

    /**
     * 来源名称
     */
    private final String source;

}

  1. 定义一个入参类
package com.example.model;

import com.example.enums.SourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

/**
 * 枚举属性类
 *
 * @author vains
 */
@Data
@Schema(title = "包含枚举属性的类")
public class EnumModel {

    @Schema(title = "名字")
    private String name;

    @Schema(title = "来源")
    private SourceEnum source;

}

  1. 定义一个接口,测试枚举入参的效果
package com.example.controller;

import com.example.enums.SourceEnum;
import com.example.model.EnumModel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 枚举接口
 *
 * @author vains
 */
@RestController
@RequestMapping("/enum")
@Tag(name = "枚举入参接口", description = "提供以枚举作为入参的接口,展示SpringDoc自定义配置效果")
public class EnumController {

    @GetMapping("/test01/{source}")
    @Operation(summary = "url参数枚举", description = "将枚举当做url参数")
    public SourceEnum test01(@PathVariable SourceEnum source) {
        return source;
    }

    @GetMapping("/test02")
    @Operation(summary = "查询参数枚举", description = "将枚举当做查询参数")
    public SourceEnum test02(SourceEnum source) {
        return source;
    }

    @PostMapping(value = "/test03")
    @Operation(summary = "参数类包含枚举", description = "将枚举当做参数类的属性")
    public EnumModel test03(@RequestBody EnumModel model) {
        return model;
    }

}

  1. 启动项目,查看接口文档显示效果

    单个枚举效果
    默认情况下接口显示效果
    作为参数属性显示效果
    枚举作为参数显示效果

文档中默认显示枚举可接收的值是定义的枚举名字(APP,WEB),但是在实际开发中前端会传入枚举对应的值/代码(1,2),根据代码映射到对应的枚举。

解决方案

单个处理方案

枚举入参

详细内容见文档

使用@Parameter注解(方法上/参数前)或者@Parameters注解来指定枚举参数可接受的值。如下所示

例1

@GetMapping("/test01/{source}")
@Parameter(name = "source", schema = @Schema(description = "来源枚举", type = "int32", allowableValues = {"1", "2"}))
@Operation(summary = "url参数枚举", description = "将枚举当做url参数")
public SourceEnum test01(@PathVariable SourceEnum source) {
    return source;
}

例2

@GetMapping("/test01/{source}")
@Operation(summary = "url参数枚举", description = "将枚举当做url参数")
public SourceEnum test01(@PathVariable
                         @Parameter(name = "source", schema =
                             @Schema(description = "来源枚举", type = "int32", allowableValues = {"1", "2"}))
                         SourceEnum source) {
    return source;
}

单独枚举入参显示效果

注解标注效果

枚举作为参数类属性

单独处理没有好的办法,像上边添加allowableValues属性只会在原有列表上添加,如下

添加allowableValues属性效果

全局统一处理方案

准备工作

  1. 定义一个统一枚举接口
package com.example.enums;

import com.fasterxml.jackson.annotation.JsonValue;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Objects;

/**
 * 通用枚举接口
 *
 * @param <V> 枚举值的类型
 * @param <E> 子枚举类型
 * @author vains
 */
public interface BasicEnum<V extends Serializable, E extends Enum<E>> {

    @JsonValue
    V getValue();

    /**
     * 根据子枚举和子枚举对应的入参值找到对应的枚举类型
     *
     * @param value 子枚举中对应的值
     * @param clazz 子枚举类型
     * @param <B>   {@link BasicEnum} 的子类类型
     * @param <V>   子枚举值的类型
     * @param <E>   子枚举的类型
     * @return 返回 {@link BasicEnum} 对应的子类实例
     */
    static <B extends BasicEnum<V, E>, V extends Serializable, E extends Enum<E>> B fromValue(V value, Class<B> clazz) {
        return Arrays.stream(clazz.getEnumConstants())
                .filter(e -> Objects.equals(e.getValue(), value))
                .findFirst().orElse(null);
    }

}

我这里为了通用性将枚举值的类型也设置为泛型类型了,如果不需要可以设置为具体的类型,比如StringInteger等,如果像我这样处理起来会稍微麻烦一些;另外我这里只提供了一个getValue的抽象方法,你也可以再提供一个getNamegetDescription等获取枚举描述字段值的抽象方法。

  1. 让项目中的枚举实现BasicEnum接口并重写getValue方法,如下
package com.example.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 来源枚举
 *
 * @author vains
 */
@Getter
@AllArgsConstructor
public enum SourceEnum implements BasicEnum<Integer, SourceEnum> {

    /**
     * 1-web网站
     */
    WEB(1, "web网站"),

    /**
     * 2-APP应用
     */
    APP(2, "APP应用");

    /**
     * 来源代码
     */
    private final Integer value;

    /**
     * 来源名称
     */
    private final String source;

}

  1. 定义一个基础自定义接口,提供一些对枚举的操作方法
package com.example.config.basic;

import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 基础自定义接口
 *
 * @author vains
 */
public interface BasicEnumCustomizer {

    /**
     * 获取枚举的所有值
     *
     * @param enumClazz 枚举的class
     * @return 枚举的所有值
     */
    default List<Object> getValues(Class<?> enumClazz) {
        return Arrays.stream(enumClazz.getEnumConstants())
                .filter(Objects::nonNull)
                .map(item -> {
                    // 收集values
                    Method getValue = ReflectionUtils.findMethod(item.getClass(), "getValue");
                    if (getValue != null) {
                        ReflectionUtils.makeAccessible(getValue);
                        return ReflectionUtils.invokeMethod(getValue, item);
                    }
                    return null;
                }).filter(Objects::nonNull).toList();
    }

    /**
     * 获取值和描述对应的描述信息,值和描述信息以“:”隔开
     *
     * @param enumClazz 枚举class
     * @return 描述信息
     */
    default String getDescription(Class<?> enumClazz) {
        List<Field> fieldList = Arrays.stream(enumClazz.getDeclaredFields())
                .filter(f -> !Modifier.isStatic(f.getModifiers()))
                // 排序
                .sorted(Comparator.comparing(Field::getName).reversed())
                .toList();
        fieldList.forEach(ReflectionUtils::makeAccessible);
        return Arrays.stream(enumClazz.getEnumConstants())
                .filter(Objects::nonNull)
                .map(item -> fieldList.stream()
                        .map(field -> ReflectionUtils.getField(field, item))
                        .map(String::valueOf)
                        .collect(Collectors.joining(" : ")))
                .collect(Collectors.joining("; "));
    }

    /**
     * 根据枚举值的类型获取对应的 {@link Schema} 类
     *  这么做是因为当SpringDoc获取不到属性的具体类型时会自动生成一个string类型的 {@link Schema} ,
     *  所以需要根据枚举值的类型获取不同的实例,例如 {@link io.swagger.v3.oas.models.media.IntegerSchema}、
     *  {@link io.swagger.v3.oas.models.media.StringSchema}
     *
     * @param type         枚举值的类型
     * @param sourceSchema 从属性中加载的 {@link Schema} 类
     * @return 获取枚举值类型对应的 {@link Schema} 类
     */
    @SuppressWarnings({"unchecked"})
    default Schema<Object> getSchemaByType(Type type, Schema<?> sourceSchema) {
        Schema<Object> schema;
        PrimitiveType item = PrimitiveType.fromType(type);

        if (item == null) {
            schema = new ObjectSchema();
        } else {
            schema = item.createProperty();
        }

        // 获取schema的type和format
        String schemaType = schema.getType();
        String format = schema.getFormat();
        // 复制原schema的其它属性
        BeanUtils.copyProperties(sourceSchema, schema);

        // 使用根据枚举值类型获取到的schema
        return schema.type(schemaType).format(format);
    }

}

全局自定义内容都是基于org.springdoc.core.customizers包下的一些Customizer接口,SpringDoc在扫描接口信息时会调用这些接口以实现加载使用者的自定义内容,所以这里提供一个基础的Customizer接口。

实现枚举参数自定义

定义一个ApiEnumParameterCustomizer类并实现ParameterCustomizer接口,实现对枚举入参的自定义,同时实现BasicEnumCustomizer接口使用工具方法。

package com.example.config.customizer;

import com.example.config.basic.BasicEnumCustomizer;
import com.example.enums.BasicEnum;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.springdoc.core.customizers.ParameterCustomizer;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;

/**
 * 枚举参数自定义配置
 *
 * @author vains
 */
@Component
public class ApiEnumParameterCustomizer implements ParameterCustomizer, BasicEnumCustomizer {

    @Override
    public Parameter customize(Parameter parameterModel, MethodParameter methodParameter) {
        Class<?> parameterType = methodParameter.getParameterType();

        // 枚举处理
        if (BasicEnum.class.isAssignableFrom(parameterType)) {

            parameterModel.setDescription(getDescription(parameterType));

            Schema<Object> schema = new Schema<>();
            schema.setEnum(getValues(parameterType));
            parameterModel.setSchema(schema);
        }

        return parameterModel;
    }
}

实现枚举属性的自定义

定义一个ApiEnumPropertyCustomizer类并实现PropertyCustomizer接口,实现对枚举属性的自定义,同时实现BasicEnumCustomizer接口使用工具方法。

package com.example.config.customizer;

import com.example.config.basic.BasicEnumCustomizer;
import com.example.enums.BasicEnum;
import com.fasterxml.jackson.databind.type.SimpleType;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.oas.models.media.Schema;
import org.springdoc.core.customizers.PropertyCustomizer;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 枚举属性自定义配置
 *
 * @author vains
 */
@Component
public class ApiEnumPropertyCustomizer implements PropertyCustomizer, BasicEnumCustomizer {

    @Override
    public Schema<?> customize(Schema property, AnnotatedType type) {
        // 检查实例并转换
        if (type.getType() instanceof SimpleType fieldType) {
            // 获取字段class
            Class<?> fieldClazz = fieldType.getRawClass();
            // 是否是枚举
            if (BasicEnum.class.isAssignableFrom(fieldClazz)) {
                // 获取父接口
                if (fieldClazz.getGenericInterfaces()[0] instanceof ParameterizedType parameterizedType) {

                    // 通过父接口获取泛型中枚举值的class类型
                    Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
                    Schema<Object> schema = getSchemaByType(actualTypeArgument, property);

                    // 重新设置字段的注释和默认值
                    schema.setEnum(this.getValues(fieldClazz));

                    // 获取字段注释
                    String description = this.getDescription(fieldClazz);

                    // 重置字段注释和标题为从枚举中提取的
                    if (ObjectUtils.isEmpty(property.getTitle())) {
                        schema.setTitle(description);
                    } else {
                        schema.setTitle(property.getTitle() + " (" + description + ")");
                    }
                    if (ObjectUtils.isEmpty(property.getDescription())) {
                        schema.setDescription(description);
                    } else {
                        schema.setDescription(property.getDescription() + " (" + description + ")");
                    }
                    return schema;
                }
            }
        }
        return property;
    }

}

如果读者不喜欢这样的效果可以自行修改枚举值、描述信息的显示效果

重启项目查看效果

接口1
/test01
接口2
/test02
接口3
/test03

SpringBoot接收枚举入参处理

不知道大家有没有注意到BasicEnum接口中的抽象方法getValue上有一个@JsonValue注解,这个注解会在进行Json序列化时会将该方法返回的值当做当前枚举的值,例如:1/2,如果不加该注解则序列化时会直接变为枚举的名字,例如: APP/WEB。

如果Restful接口入参中有@RequestBody注解则在——统一枚举的getValue方法上有@JsonValue注解的基础上,无需做任何处理,对于Json入参可以这样处理,但是对于POST表单参数或GET查询参数需要添加单独的处理。

定义一个EnumConverterFactory

根据枚举的class类型获取对应的converter,并在converter中直接将枚举值转为对应的枚举
,具体逻辑情况代码中的注释

package com.example.config.converter;

import com.example.enums.BasicEnum;
import com.fasterxml.jackson.databind.type.TypeFactory;
import lombok.NonNull;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.function.Function;

/**
 * 处理除 {@link org.springframework.web.bind.annotation.RequestBody } 注解标注之外是枚举的入参
 *
 * @param <V> 枚举值的类型
 * @param <E> 枚举的类型
 * @author vains
 */
@Component
public class EnumConverterFactory<V extends Serializable, E extends Enum<E>> implements ConverterFactory<String, BasicEnum<V, E>> {

    @NonNull
    @Override
    @SuppressWarnings("unchecked")
    public <T extends BasicEnum<V, E>> Converter<String, T> getConverter(Class<T> targetType) {
        // 获取父接口
        Type baseInterface = targetType.getGenericInterfaces()[0];
        if (baseInterface instanceof ParameterizedType parameterizedType
                && parameterizedType.getActualTypeArguments().length == 2) {
            // 获取具体的枚举类型
            Type targetActualTypeArgument = parameterizedType.getActualTypeArguments()[1];
            Class<?> targetAawArgument = TypeFactory.defaultInstance()
                    .constructType(targetActualTypeArgument).getRawClass();
            // 判断是否实现自通用枚举
            if (BasicEnum.class.isAssignableFrom(targetAawArgument)) {
                // 获取父接口的泛型类型
                Type valueArgument = parameterizedType.getActualTypeArguments()[0];
                // 获取值的class
                Class<V> valueRaw = (Class<V>) TypeFactory.defaultInstance()
                        .constructType(valueArgument).getRawClass();

                String valueOfMethod = "valueOf";
                // 转换入参的类型
                Method valueOf = ReflectionUtils.findMethod(valueRaw, valueOfMethod, String.class);
                if (valueOf != null) {
                    ReflectionUtils.makeAccessible(valueOf);
                }
                // 将String类型的值转为枚举值对应的类型
                Function<String, V> castValue =
                        // 获取不到转换方法时直接返回null
                        source -> {
                            if (valueRaw.isInstance(source)) {
                                // String类型直接强转
                                return valueRaw.cast(source);
                            }
                            // 其它包装类型使用valueOf转换
                            return valueOf == null ? null
                                    : (V) ReflectionUtils.invokeMethod(valueOf, valueRaw, source);
                        };
                return source -> BasicEnum.fromValue(castValue.apply(source), targetType);
            }
        }

        return source -> null;
    }

}

定义一个WebmvcConfig配置类,将EnumConverterFactory注册到添加到mvc配置中

package com.example.config;

import com.example.config.converter.EnumConverterFactory;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 添加自定义枚举转换配置
 *
 * @author vains
 */
@AllArgsConstructor
@Configuration(proxyBeanMethods = false)
public class WebmvcConfig implements WebMvcConfigurer {

    private final EnumConverterFactory<?, ?> enumConverterFactory;

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(enumConverterFactory);
    }
}

重启项目并打开在线文档进行测试

枚举入参示例

Gitee地址、Github地址

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

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

相关文章

便携式自动气象站让你随时随地掌握天气变化

WX-BXQX12 随着科技的发展&#xff0c;人们对气象信息的掌握越来越重要。无论是在日常生活中&#xff0c;还是在农业生产、旅游出行等领域&#xff0c;了解天气变化都显得至关重要。为了满足人们对气象信息的需求&#xff0c;一款名为“便携式自动气象站”的创新产品应运而生。…

第15届蓝桥STEMA测评真题剖析-2023年10月29日Scratch编程初中级组

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第160讲。 第15届蓝桥第2次STEMA测评&#xff0c;这是2023年10月29日举办的STEMA&#xff0c;比赛仍然采取线上形式。这…

Java 下载地址

1 地址&#xff1a; Java Downloads | Oracle 2 往下翻(Java 8 和 Java 11) 3 往下翻 (JRE 下载)(JRE 8 为例) 4 跳转到登录(登录账号才可以下载)

汽车托运汽车会产生公里数吗?

汽车托运&#xff0c;顾名思义就是把汽车放在板车上进行托运&#xff0c;既然是被托运&#xff0c;那为什么还会产生公里数呢?是被司机私用了吗?还是被当成租赁工具租借出去了呢? 其实不然&#xff0c;回到托运流程里&#xff0c;特别是大板车&#xff0c;我们的线路有很多需…

Vue 2使用element ui 表格不显示

直接修改package.json文件 把这两个依赖修改成对应的 删除node_modules 重新安装依赖 重启

安全框架springSecurity+Jwt+Vue-2(后端开发)

一、创建项目及配置 ①&#xff1a;创建新的项目及常用包 ②&#xff1a;引入依赖和配置 devtools&#xff1a;项目的热加载重启插件 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId&…

技术分享|电商数据接口|淘宝天猫京东商品API接口之数据同步

常见的数据同步/集成场景多发生于不同的存储系统、不同的存储格式&#xff0c;如从 mysql 同步数据至数仓、excel 或 csv 导入数据库中&#xff0c;但是众多数据同步解决方案很少涉及从 http 接口同步数据。 如淘宝、拼多多等电商平台&#xff0c;平台内部不同团队之间的数据打…

【开源】基于Vue.js的高校学院网站的设计和实现

项目编号&#xff1a; S 020 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S020&#xff0c;文末获取源码。} 项目编号&#xff1a;S020&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学院院系模块2.2 竞赛报名模块2.3 教…

webpack 创建typescript项目

【视频链接】尚硅谷TypeScript教程&#xff08;李立超老师TS新课&#xff09; 创建webpack 项目 IDE&#xff1a;webstorm 新建一个空的项目运行npm init初始化项目目录结构 1. 安装 webpack&#xff1a;构建工具webpack-cli&#xff1a; webpack的命令行工具typescript&am…

福州大学《嵌入式系统综合设计》实验三:多媒体开发基础编程

一、实验目的 本实验基于搭建好的开发环境和硬件环境&#xff0c;通过编写简单的通信实验&#xff0c;验证开发环境&#xff0c;掌握多媒体开发编程基础&#xff0c;包括SOCKET编程、多线程编程和线程同步知识。 二、实验内容 基于套接字、多线程、同步锁机制实现多媒体文件…

c++语言核心及进阶

核心编程 内存分区模型 根据c执行将内存划分为5个区域&#xff1a; 代码区&#xff0c;存放函数体的二进制&#xff0c;即CPU执行的机器指令&#xff0c;并且是只读的&#xff1b;常量区&#xff0c;存放常量&#xff0c;即程序运行期间不能被改变的量。全局区&#xff08;静…

汇编-PUSHFD和POPFD标志寄存器值压栈和出栈

PUSHFD指令将32位EFLAGS寄存器内容压入堆栈&#xff0c; 而POPFD指令则将栈顶单元内容弹出到EFLAGS寄存器 格式&#xff1a;

finalshell上传文件失败的解决方法

问题描述 最近使用虚拟机docker部署项目&#xff0c;使用的finalshell远程连接虚拟机&#xff0c;然后奇怪的发现无法往里面传输文件&#xff0c;各种传输方式&#xff08;手动or直接拉拽&#xff09;都不行&#xff0c;直接报错如下&#xff1a; 原因分析 一开始我以为是新…

kibana 7安装

手动安装 下载 wget https://artifacts.elastic.co/downloads/kibana/kibana-7.17.15-linux-x86_64.tar.gz 解压 mv kibana-7.17.15-linux-x86_64.tar.gz /usr/local tar -zxvf kibana-7.17.15-linux-x86_64.tar.gz chown -R es:es kibana-7.17.15-linux-x86_64修改配置 s…

【HarmonyOS】获取备案需要的公钥和MD5签名问题

【关键字】 HarmonyOS、公钥、MD5签名 【问题描述】 有cp反馈Harmony App如何获取备案需要的公钥和MD5签名。 【解决方案】 1、获取备案需要的公钥&#xff1a; 1&#xff09;用记事本打开签名对应.cer文件 2&#xff09;里面的内容粘贴到一个可以查看证书的网站上面&…

23设计模式详解「全23种」

✍️作者简介&#xff1a;码农小北&#xff08;专注于Android、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a; 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN &#x1f514;如果文章对您有一定的帮助请&#x1f449;关注✨、点赞&…

【idea】解决idea 执行maven build总下载 Downloading maven-metadata.xml文件

可以看到如下日志中打印了执行的命令行&#xff0c;其中包含 --update-snapshots&#xff0c;是强制更新的意思。 日志内容如下&#xff1a; D:\env\jdk1.8.0_261\bin\java.exe --update-snapshots -s D:\env\apache-maven-3.8.6\conf\settings.xml -Dmaven.repo.localD:\env\…

苹果Siri怎么打开?教你两招轻松唤醒!

苹果Siri助手是苹果公司开发的智能语音助手。作为智能语音助手&#xff0c;Siri可以理解用户的指令&#xff0c;并给出相应的回答或执行相应的操作&#xff0c;帮助大家完成各种任务&#xff0c;比如发送短信、查询天气、播放音乐、设置提醒等等。 然而&#xff0c;还有一些小…

【软件工程师从0到1】- 继承 (知识汇总)

前言 介绍&#xff1a;大家好啊&#xff0c;我是hitzaki辰。 社区&#xff1a;&#xff08;完全免费、欢迎加入&#xff09;日常打卡、学习交流、资源共享的知识星球。 自媒体&#xff1a;我会在b站/抖音更新视频讲解 或 一些纯技术外的分享&#xff0c;账号同名&#xff1a;hi…

React函数组件渲染两次

渲染两次是因为react默认开启了严格模式 React.StrictMode标签作用&#xff1a; 1、识别不安全的生命周期 2、关于使用过时字符串 ref API 的警告 3、关于使用废弃的 findDOMNode 方法的警告 4、检测意外的副作用 5、检测过时的 context API 注释掉React.StrictMode即为关闭严…