分类: 未分类

  • 2025 年,前端开发者不可不知的前端新特性

    在寻找最灵活的前端工作流程和工具包时,我们很容易忘记如今一些基础的网络技术已经变得多么强大。本文将带您领略新的前端特性及其功能。组件特定样式、基于子元素的父元素样式、相对颜色——网络平台正经历激动人心的时刻,许多过去需要JavaScript实现的功能,如今只需一行简单的HTML和CSS即可完成。

    2025年了,现在是时候重新审视一些现代浏览器广泛支持的新技术了。让我们深入探索它们如何简化您的日常工作,并帮助您构建现代UI组件。

    目录

    • 锚点定位
    • 自动字段大小调整
    • 容器查询
    • <dialog>元素
    • 独占式手风琴
    • :focus-visible 伪类
    • :has 伪类
    • hidden=until-found属性
    • 高清晰度颜色
    • <select>中的<hr>元素
    • inputmode属性
    • min(), max(), clamp()函数
    • 相对颜色
    • 响应式视频
    • 滚动行为
    • 滚动捕捉
    • text-wrap: balance属性
    • :user-valid 和:user-invalid 伪类
    • 视图过渡API

    CSS容器查询和样式查询

    组件特定样式?对于任何开发者来说,这听起来像是一个梦想,正慢慢变为现实。感谢容器查询,我们现在可以查询组件所在容器的宽度和样式。

    样式查询为我们提供了CSS中样式的更多逻辑控制,这目前仅适用于CSS自定义属性值,但已经有一些实际用例显示出样式查询的优势:当您有一个具有多个变体的可重用组件,或者当您无法控制所有样式但需要在某些情况下应用更改时,它们特别有用。

    如果您想深入了解容器样式查询的可能性以及我们未来可能期待的功能,可以参考以下文章学习:
    developer.chrome.com/docs/css-ui…

    不再有排版孤行和寡行

    通过应用text-wrap: balance属性,浏览器将自动计算单词数并将它们平均分配到两行——非常适合页面标题、卡片标题、工具提示、模态框和FAQ等。

    在处理大段文本(如段落)时,您可能需要查看text-wrap: pretty以防止最后一行出现孤行。

    参考文章:
    blog.csdn.net/qq_37247349…

    表单的自动字段大小调整

    为输入字段找到合适的大小通常涉及大量猜测——或使用JavaScript——来计算字符数并随着用户输入文本增加字段的高度或宽度。CSS field-sizing旨在改变这一点。通过field-sizing,我们可以自动增长输入和文本区域,也可以自动缩小短选择菜单,使表单始终完美适应内容大小。我们只需要一行CSS即可实现。

    以下文章总结了您需要了解的有关field-sizing的所有内容,详细探讨了field-sizing如何影响不同的<form>元素。为了防止您的输入字段变得太小或太大,插入一些额外的样式以保持它们的形状也是一个好主意。同时分享了一个您可以立即复制粘贴的代码片段。

    developer.chrome.com/docs/css-ui…

    使隐藏内容可搜索

    正如文章<developer.chrome.com/docs/css-ui… 中介绍,在他的使折叠内容可搜索指南中解释的那样,您可以用hidden=until-found属性替换隐藏部分的样式。如果您的页面还有另一个状态需要与您的部分是否显示保持同步,他建议添加一个beforematch事件监听器。它将在浏览器显示hidden=until-found元素之前触发。>

    <select>菜单中样式化组

    如果您想进一步细化,请务必查看<optgroup>。HTML元素允许您通过为每个组添加子标题来在<select>元素内分组选项。

    简化可滚动容器的捕捉

    以下该文章(中译),逐步引导您设置带有scroll snap的容器。您可以使用它来创建图像库、头像列表或其他组件,在这些组件中,您希望用户滚动并捕捉内容,无论是水平还是垂直。 juejin.cn/post/684490…

    工具提示和弹出框的锚点定位

    文章(<developer.chrome.com/blog/anchor… 在CSS锚点定位API介绍中详细总结了锚点定位的工作原理。深入探讨了锚点定位背后的机制,如何与一个和多个锚点绑定,以及如何根据锚点的大小调整和定位锚点定位元素。浏览器支持仍然有限,因此您可能希望在使用API时采取一些预防措施。>

    使用OKLCH和OKLAB的高清晰度颜色

    OKLCH和OKLAB都基于人类感知,可以指定人眼可见的任何颜色。虽然OKLAB最适合丰富的渐变,但OKLCH非常适合设计系统中的调色板。OKLCH/OKLAB颜色在Chrome、Edge、Safari、Firefox和Opera中得到完全支持。Figma尚不支持它们。 参考文章: developer.mozilla.org/zh-CN/docs/…

    CSS中的相对颜色

    为了派生和计算新颜色,我们可以使用from关键字用于颜色函数(color()、hsl()、oklch()等)来修改输入颜色的值。Adam Argyle文章分享了一些代码片段,展示了这在实践中的样子,或查看规范以获取更多详细信息。 smashing-freiburg-2024.netlify.app/24-relative…

    使用视图过渡API实现平滑过渡

    在许多用例中,平滑的视觉过渡可以使用户体验更加引人入胜。例如,当产品列表页面上的缩略图过渡到产品详情页面上的全尺寸图像时,或者当您有一个固定的导航栏在页面之间导航时保持原位。视图过渡API帮助我们创建网站不同视图之间的无缝视觉过渡。

    视图过渡不仅可以在单个文档上触发,还可以在两个不同文档之间触发。两者都依赖于相同的原则:浏览器拍摄旧状态和新状态的快照,在抑制渲染的同时更新DOM,并通过CSS动画驱动过渡。唯一的区别在于如何触发它们,正如Bramus Van Damme在他的视图过渡API指南中解释的那样。这是单页应用程序的一个很好的替代方案,单页应用程序通常依赖于繁重的JavaScript框架。 developer.chrome.com/docs/web-pl…

    独占式手风琴

    要构建独占式手风琴,我们需要向<details>元素添加name属性。当使用此属性时,所有具有相同name值的<details>元素形成一个语义组,并表现为独占式手风琴。Bramus Van Damme详细总结了它的工作原理。
    developer.chrome.com/docs/css-ui…

    实时和延迟验证

    通过:user-valid:user-invalid ,我们现在有一个本地CSS解决方案,可以自动处理所有这些。与:valid :invalid 相反,:user-valid:user-invalid 伪类仅在用户更改输入后提供有关错误的反馈。:user-valid:user-invalid 适用于inputselecttextarea控件。
    web.dev/articles/us…

    平滑滚动行为

    当将scroll-behavior值设置为smooth时,滚动框将以平滑的方式滚动,使用用户代理定义的缓动函数在用户代理定义的时间段内滚动。当然,您也可以使用scroll-behavior: auto,滚动框将立即滚动。

    使焦点可见

    :focus-visible 在元素匹配:focus 伪类且用户代理通过启发式方法确定应在元素上显示焦点时应用。好奇它在实践中如何工作?MDN Web Docs强调了:focus :focus-visible 之间的区别,您需要考虑的可访问性方面,以及如何为不支持:focus-visible 的旧浏览器版本提供回退。

    developer.mozilla.org/zh-CN/docs/…

    基于子元素的父元素样式

    :has 不仅限于父子关系或直接兄弟关系。相反,它让我们可以根据完全不同的容器中任何其他元素的属性或状态样式化一个元素。正如文章所示,它可以用作一种全局事件监听器——例如,在模态框打开时禁用页面滚动,或创建无JavaScript的暗模式切换。

    在类型和间距之间插值

    为了让您立即在项目中使用这些函数,这里有篇综合指南(<ishadeed.com/article/css… 解释了您需要了解的有关min()、max()和clamp()的所有内容,包括实际示例和用例,以及您可能遇到的所有混淆点。>

    如果您正在寻找一种快速简便的方法来创建流体比例,Utopia的流体类型比例计算器(<utopia.fyi/type/calcul… 可以满足您的需求。您只需要定义最小和最大视口宽度以及比例步数,计算器将为您提供比例的响应式预览和CSS代码片段。>

    可靠的对话框和弹出框

    虽然模态对话框中断与页面的交互,但非模态对话框允许在对话框打开时与页面交互。Adam Argyle发布了一些代码片段(smashing-freiburg-2024.netlify.app/27-dialog/)…`如何开箱即用地阻止弹出窗口和非阻塞菜单的弹出框。

    响应式HTML视频和音频

    这篇文章(<scottjehl.com/posts/using… 总结了响应式HTML视频——甚至音频——的工作原理,编写标记时需要考虑的事项,以及可以与HTML视频结合使用的其他类型的媒体查询。>

    移动设备上的正确虚拟键盘

    为了显示正确的键盘布局,我们可以使用inputmode(<css-tricks.com/everything-… 它指示浏览器显示哪个键盘,并支持数字、电话、十进制、电子邮件、URL和搜索键盘的值。为了进一步改善用户体验,我们可以添加enterkeyhint属性:它调整Enter键上的文本。如果未使用enterkeyhint,用户代理可能会使用inputmode属性的上下文信息。>

    总结

    当我们开始在项目中采用所有这些闪亮的新前端功能时,网络平台当然在不断演变——已经有一些令人兴奋的事情在酝酿中!例如,我们即将获得砌体布局、完全可定制的<selectmenu>下拉菜单以及用于调整字体以完美对齐网格的文本框修剪。向所有不懈努力推动网络发展的人们致敬!👏

  • 瞧瞧别人家的判空,那叫一个优雅!

    一、传统判空的血泪史

    某互联网金融平台因费用计算层级的空指针异常,导致凌晨产生9800笔错误交易。

    DEBUG日志显示问题出现在如下代码段:

    csharp体验AI代码助手代码解读复制代码// 错误示例
    BigDecimal amount = user.getWallet().getBalance().add(new BigDecimal("100"));
    

    此类链式调用若中间环节出现null值,必定导致NPE。

    初级阶段开发者通常写出多层嵌套式判断:

    scss体验AI代码助手代码解读复制代码if(user != null){
        Wallet wallet = user.getWallet();
        if(wallet != null){
            BigDecimal balance = wallet.getBalance();
            if(balance != null){
                // 实际业务逻辑
            }
        }
    }
    

    这种写法既不优雅又影响代码可读性。

    那么,我们该如何优化呢?

    最近准备面试的小伙伴,可以看一下这个宝藏网站:www.susan.net.cn,里面:面试八股文、面试真题、工作内推什么都有

    二、Java 8+时代的判空革命

    Java8之后,新增了Optional类,它是用来专门判空的。

    能够帮你写出更加优雅的代码。

    1. Optional黄金三板斧

    css体验AI代码助手代码解读复制代码// 重构后的链式调用
    BigDecimal result = Optional.ofNullable(user)
        .map(User::getWallet)
        .map(Wallet::getBalance)
        .map(balance -> balance.add(new BigDecimal("100")))
        .orElse(BigDecimal.ZERO);
    

    高级用法:条件过滤

    scss体验AI代码助手代码解读复制代码Optional.ofNullable(user)
        .filter(u -> u.getVipLevel() > 3)
        .ifPresent(u -> sendCoupon(u)); // VIP用户发券
    

    2. Optional抛出业务异常

    scss体验AI代码助手代码解读复制代码BigDecimal balance = Optional.ofNullable(user)
        .map(User::getWallet)
        .map(Wallet::getBalance)
        .orElseThrow(() -> new BusinessException("用户钱包数据异常"));
    

    3. 封装通用工具类

    typescript体验AI代码助手代码解读复制代码public class NullSafe {
        
        // 安全获取对象属性
        public static <T, R> R get(T target, Function<T, R> mapper, R defaultValue) {
            return target != null ? mapper.apply(target) : defaultValue;
        }
        
        // 链式安全操作
        public static <T> T execute(T root, Consumer<T> consumer) {
            if (root != null) {
                consumer.accept(root);
            }
            return root;
        }
    }
    
    // 使用示例
    NullSafe.execute(user, u -> {
        u.getWallet().charge(new BigDecimal("50"));
        logger.info("用户{}已充值", u.getId());
    });
    

    三、现代化框架的判空银弹

    4. Spring实战技巧

    Spring中自带了一些好用的工具类,比如:CollectionUtils、StringUtils等,可以非常有效的进行判空。

    具体代码如下:

    scss体验AI代码助手代码解读复制代码// 集合判空工具
    List<Order> orders = getPendingOrders();
    if (CollectionUtils.isEmpty(orders)) {
        return Result.error("无待处理订单");
    }
    
    // 字符串检查
    String input = request.getParam("token");
    if (StringUtils.hasText(input)) {
        validateToken(input); 
    }
    

    5. Lombok保驾护航

    我们在日常开发中的entity对象,一般会使用Lombok框架中的注解,来实现getter/setter方法。

    其实,这个框架中也提供了@NonNull等判空的注解。

    比如:

    less体验AI代码助手代码解读复制代码@Getter
    @Setter
    public class User {
        @NonNull // 编译时生成null检查代码
        private String name;
        
        private Wallet wallet;
    }
    
    // 使用构造时自动判空
    User user = new User(@NonNull "张三", wallet);
    

    四、工程级解决方案

    6. 空对象模式

    typescript体验AI代码助手代码解读复制代码public interface Notification {
        void send(String message);
    }
    
    // 真实实现
    public class EmailNotification implements Notification {
        @Override
        public void send(String message) {
            // 发送邮件逻辑
        }
    }
    
    // 空对象实现
    public class NullNotification implements Notification {
        @Override
        public void send(String message) {
            // 默认处理
        }
    }
    
    // 使用示例
    Notification notifier = getNotifier();
    notifier.send("系统提醒"); // 无需判空
    

    7. Guava的Optional增强

    其实Guava工具包中,给我们提供了Optional增强的功能。

    比如:

    ini体验AI代码助手代码解读复制代码import com.google.common.base.Optional;
    
    // 创建携带缺省值的Optional
    Optional<User> userOpt = Optional.fromNullable(user).or(defaultUser);
    
    // 链式操作配合Function
    Optional<BigDecimal> amount = userOpt.transform(u -> u.getWallet())
                                        .transform(w -> w.getBalance());
    

    Guava工具包中的Optional类已经封装好了,我们可以直接使用。

    五、防御式编程进阶

    8. Assert断言式拦截

    其实有些Assert断言类中,已经做好了判空的工作,参数为空则会抛出异常。

    这样我们就可以直接调用这个断言类。

    例如下面的ValidateUtils类中的requireNonNull方法,由于它内容已经判空了,因此,在其他地方调用requireNonNull方法时,如果为空,则会直接抛异常。

    我们在业务代码中,直接调用requireNonNull即可,不用写额外的判空逻辑。

    例如:

    typescript体验AI代码助手代码解读复制代码public class ValidateUtils {
        public static <T> T requireNonNull(T obj, String message) {
            if (obj == null) {
                throw new ServiceException(message);
            }
            return obj;
        }
    }
    
    // 使用姿势
    User currentUser = ValidateUtils.requireNonNull(
        userDao.findById(userId), 
        "用户不存在-ID:" + userId
    );
    

    最近就业形势比较困难,为了感谢各位小伙伴对苏三一直以来的支持,我特地创建了一些工作内推群, 看看能不能帮助到大家。

    你可以在群里发布招聘信息,也可以内推工作,也可以在群里投递简历找工作,也可以在群里交流面试或者工作的话题。

    添加苏三的私人微信:li_su223,备注:掘金+所在城市,即可加入。

    9. 全局AOP拦截

    我们在一些特殊的业务场景种,可以通过自定义注解 + 全局AOP拦截器的方式,来实现实体或者字段的判空。

    例如:

    less体验AI代码助手代码解读复制代码@Aspect
    @Component
    public class NullCheckAspect {
        
        @Around("@annotation(com.xxx.NullCheck)")
        public Object checkNull(ProceedingJoinPoint joinPoint) throws Throwable {
            Object[] args = joinPoint.getArgs();
            for (Object arg : args) {
                if (arg == null) {
                    throw new IllegalArgumentException("参数不可为空");
                }
            }
            return joinPoint.proceed();
        }
    }
    
    // 注解使用
    public void updateUser(@NullCheck User user) {
        // 方法实现
    }
    

    六、实战场景对比分析

    场景1:深层次对象取值

    scss体验AI代码助手代码解读复制代码// 旧代码(4层嵌套判断)
    if (order != null) {
        User user = order.getUser();
        if (user != null) {
            Address address = user.getAddress();
            if (address != null) {
                String city = address.getCity();
                // 使用city
            }
        }
    }
    
    // 重构后(流畅链式)
    String city = Optional.ofNullable(order)
        .map(Order::getUser)
        .map(User::getAddress)
        .map(Address::getCity)
        .orElse("未知城市");
    

    场景2:批量数据处理

    scss体验AI代码助手代码解读复制代码List<User> users = userService.listUsers();
    
    // 传统写法(显式迭代判断)
    List<String> names = new ArrayList<>();
    for (User user : users) {
        if (user != null && user.getName() != null) {
            names.add(user.getName());
        }
    }
    
    // Stream优化版
    List<String> nameList = users.stream()
        .filter(Objects::nonNull)
        .map(User::getName)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
    

    七、性能与安全的平衡艺术

    上面介绍的这些方案都可以使用,但除了代码的可读性之外,我们还需要考虑一下性能因素。

    下面列出了上面的几种在CPU消耗、内存只用和代码可读性的对比:

    方案CPU消耗内存占用代码可读性适用场景
    多层if嵌套★☆☆☆☆简单层级调用
    Java Optional★★★★☆中等复杂度业务流
    空对象模式★★★★★高频调用的基础服务
    AOP全局拦截★★★☆☆接口参数非空验证

    黄金法则

    • Web层入口强制参数校验
    • Service层使用Optional链式处理
    • 核心领域模型采用空对象模式

    八、扩展技术

    除了,上面介绍的常规判空之外,下面再给大家介绍两种扩展的技术。

    Kotlin的空安全设计

    虽然Java开发者无法直接使用,但可借鉴其设计哲学:

    ini体验AI代码助手代码解读复制代码val city = order?.user?.address?.city ?: "default"
    

    JDK 14新特性预览

    less体验AI代码助手代码解读复制代码// 模式匹配语法尝鲜
    if (user instanceof User u && u.getName() != null) {
        System.out.println(u.getName().toUpperCase());
    }
    

    总之,优雅判空不仅是代码之美,更是生产安全底线。