不吹牛,完爆ant design的定位组件,floating-ui来也

news2024/9/20 9:24:44

前言

因为要写react定位组件(这不是标题党,就是完爆ant design的定位组件,你应该看到一半就会同意我的观点),如下图:

红框部分是用绝对定位放在按钮上面的,你们B端用的主流组件库都是这样实现的,它是很多组件的基础组件,比如下图:

下拉框组件

select组件

还有什么DataPicker,TreeSelect,Dropdown组件等等的下拉框都是以定位组件为基础的。

这个组件实现的复杂度在哪

上面提到,这不过就是一个绝对定位嘛(我们假设红框部分的的dom绝对定位是相较于body元素),我们拿最简单的情况来看,如下图,如何把红框部分渲染到按钮”更多“的下方呢?

我们可以计算更多按钮的getBoundingRect(),返回

const reference = {top: xx, // 按钮距离浏览器顶部的距离left: xx, // 按钮距离浏览器左边的距离width: xx, // 按钮的宽:没有paddingheight:xx,// 按钮的高:没有padding...等等其他属性
} 

所以红框部分左上角的坐标就轻易的计算出来了,上面的数据在reference对象上,所以借助reference的定位,我们计算红框部分的下拉框的定位是在哪

{position: 'absolute',top: reference.top + window.pageYOffset // 竖直方向滚动距离 + reference.heightleft: reference.left + window.pageXOffset // 横向滚动距离 
} 

为啥是上面这么计算呢,假如没有滚动条滚动,那么红框部分的绝对定位的top,是不是等于按钮的距离浏览器顶部的高度 + 本身的高度,这个没问题吧?

然后,如果滚动条滚动了的话,是不是要在上面top的基础上加上这段距离,就是红框部分在文档流绝对定位的top。

好了,到此为止,就是最基本的定位组件的逻辑了,我们接下来看复杂点!

复杂度1

还是拿上面的图的红色部分下拉框来说,下拉框一般是在下面,但是我可以定位到上边吧?左边,右边也没啥吧,再过分点,右上,左下?定位组件要处理对吧

复杂度2

假设,我们定位在下面,我想向左偏移8px,向下偏移3px咋办,你是不是应该有暴露一个口子

复杂度3

假设我定位在下面,那么我一直滚,马上就要滚动到看不见下拉框了,如下图

此时我想让定位在上面,能不能自动帮我处理?如下图:

复杂度4

是不是还有可能超出浏览器视口了,如下图:

我们想自动处理,遇到超出就自动变为下方样子:

复杂度5

此时我定位了一次,但是有可能滚动容器不是window,是另一个div,这个计算咋办?还有,是不是我滚动的时候,我要监听滚动事件,还要监听浏览器resize事件,因为我定位的值可能会变?为啥呢,我们上面复杂度3是不是自动帮我们在滚动的时候调整位置

所以你不监听滚动事件你咋知道要调整位置了?

还有很多细枝末节,比如浏览器兼容性等等。。。。

国内组件库怎么实现这个功能

目前阿里的ant design和字节的arco design都是自己实现的,我们拿arco来看(ant内部叫rc-trriger组件,arco叫trriger组件),面向过程的代码,看的我头皮发麻。。。我截个图:

上面的代码属于把我们提到的复杂度全部揉在了一起。

floating-ui为啥代码质量比ant高

它是以中间件的形式去处理的,思路是什么呢?它假设最开始有一个 computePosition函数,我们假设上面提到的复杂度都没有,也就是不考虑的前提下,我们怎么计算定位组件的坐标,也就是我们最前面的图里说的,红色框部分绝对定位的的的top值和left值:

API如下:

computePosition(要挂载的dom节点,下拉框组件,参数...) 

然后我们刚才提到的复杂度,它分别用中间件的形式去处理,比如复杂度2,是想定位之后还有点偏移量,floating-ui咋做的呢

import {computePosition, offset} from '@floating-ui/dom';

// referenceEl: 要挂载的dom节点
// floatingEl:下拉框组件(或者说想要挂载到上面referenceEl的dom元素)
computePosition(referenceEl, floatingEl, {middleware: [offset(10)],
}); 

如上,offset就是一个中间件,offset(10),就是向左偏移10px

好了,如果想处理复杂度3呢,我们用另一个中间件

import {computePosition, flip} from '@floating-ui/dom';
 
computePosition(referenceEl, floatingEl, {middleware: [flip()],
}); 

这样就自动处理了,是不是很简单啊

其实所有这些复杂度的解决方案,在floating-ui里都是以中间件的形式去处理的,还可以传多个中间件解决多个问题。

中间件的形式好在哪

那么我们就可以自定义很多中间件了,也就是你的组件不仅仅提供了很多功能,解决了很多常用的问题,你还允许用户写代码去拓展,试问,现在哪个组件库的代码是这么写的?没有吧?

代码中间件原理

我们先看看floating-ui的computePosition API是怎么实现的,它是floating-ui的核心方法,是串联所有中间件的基础。

下一篇写完整的源码(很晦涩,估计也没几个人看,所以这期就不写了),理解起来说实话,你不熟悉原生dom的话有点困难,比如说为啥这个库要用window.pageYoffset而不是document.body.scrollTop去获取浏览器html元素的滚动距离,因为document.body.scrollTop固定为0,取不到。。。

核心思路讲解:我们还是拿下图做类比

let {x, y} = 求出红色框里的下拉框绝对定位的x坐标和y坐标

// 记录原始placementlet statefulPlacement = placement;// 所有中间件导出的值都挂载到下面的对象上let middlewareData: MiddlewareData = {};// 数据经过middleware的处理// middleware是一个数组,存放所有中间件,就是我们上面说的处理每一个复杂度的对象for (let i = 0; i < middleware.length; i++) { // name是中间件的名字,fn是处理复杂度的逻辑const {name, fn} = middleware[i];   // 通过把最前面计算的x,y经过fn的处理,得到了新的x,y的值 // data是指返回的数据,想让后面的中间件也能访问到的数据/** * 每个middleware需要返回 * x 新的x坐标 * y 新的y坐标 * data  * reset */const {x: nextX,y: nextY,data,reset,} = await fn({/** * 每个middleware收到的参数 * x 目前的x坐标 * y 目前的y坐标 * initialPlacement 最初传入的placement * placement  * middlewareData middleware返回的额外数据 */x,y,initialPlacement: placement,placement: statefulPlacement,strategy,middlewareData,rects,platform,elements: {reference, floating},});x = nextX ?? x;y = nextY ?? y;// 每次处理后的数据想要让后面的中间件访问,就需要挂载到middlewareData对象// 这个对象非常好啊,用name隔离了作用域middlewareData = {...middlewareData,[name]: {...middlewareData[name],...data,},}; rest的处理逻辑。。。省略,不是很重要 

最后return出被处理完的x,y坐标,或者自动帮我们监听滚动事件和resize事件,然后拿着x,y就可以赋在css的绝对定位的top和left上,实现了定位。

每次处理后的数据想要让后面的中间件访问,就需要挂载到middlewareData对象,这个对象非常好啊,用name隔离了作用域,这就是比koa这个框架处理的高明之处,koa里的ctx对象就像一个垃圾桶,什么属性都往上面挂载,挂载太多了,你也不知道是哪个中间件挂载的

所以floating-ui的处理思路给我打开了新的思路!nice!!!

中间件如何写

源码再开一篇文章写,这里看看就好,不用过多去关系代码

export const offset = (value: Options = 0): Middleware => ({name: 'offset', // 中间件名字options: value,// 传给中间件的值async fn(middlewareArguments) { // 中间件处理函数const {x, y} = middlewareArguments;const diffCoords = await convertValueToCoords(middlewareArguments, value);return {x: x + diffCoords.x,y: y + diffCoords.y,data: diffCoords,};},
}); 

本文结束,所以如果市面上的组件库的每个组件都是这个形式暴露给用户,就是提供插件式的自定义的中间件,那么整个组件库的拓展性可以说碾压市面上国内所有的react的组件库了

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

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

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

相关文章

Python自动化小技巧14——自动批量发送邮件(带各种附件)

案例背景 我的博客下面评论都是各种要数据的......一个一个发其实很浪费时间的&#xff0c;每次输入评论者的邮箱&#xff0c;然后打开数据所在的文件夹&#xff0c;上传&#xff0c;填写标题正文&#xff0c;发送....... 一模一样的流程&#xff0c;所以这种重复性的劳动肯定…

Linux下 git 上传与删除 的基本指令

git的概述克隆仓库使用 git 上传文件删除 git 中的文件git的概述 Git 是一个免费并开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 在使用 git 应确保Linux系统中已安装有git 命令&#xff1a;git --version 作用&#xff1a;查看 git 是否…

Spring Boot 单元测试

文章目录1. 单元测试是什么2. 单元测试的优点3. 进行 Spring Boot 单元测试3.1 确认项目中已经内置了测试框架3.2 生成单元测试的类3.3 添加 SpringBootTest 注解3.4 添加单元测试的业务代码3.5 注解 Transactional4. 断言1. 单元测试是什么 单元测试&#xff0c;是指对软件中…

微信小程序022同学会学生会活动经费系统

同学会小程序采用B/S结构、java开发语言、以及Mysql数据库等技术。系统主要分为管理员端和用户端两部分&#xff0c;管理员管理主要功能包括&#xff1a;首页、个人中心、用户管理、共享账本管理、我的账本管理、经费信息管理、经费支出管理、活动信息、管理员管理、留言板管理…

分享怎么做公众号预约_美容院预约小程序开发制作功能介绍

小程序的功能首先是为美人有约客户提供更快选购、预约服务的线上工具&#xff0c;解决顾客对商品详情、线上购买、线上预约查看等各种服务需求。一、美容美发预约下单小程序主要功能有&#xff1a;首页&#xff1a;搜索、banner、金刚区、瓷片区、项目列表预约&#xff1a;单次…

Java技术栈,从入门到放弃,废了废了

Java技术路线应用框架后端Spring家族SpringIoCAOPSpring MVCSpring Boot自动配置、开箱即用整合Web整合数据库&#xff08;事务问题&#xff09;整合权限ShiroSpring Security整合中间件缓存MQRPC框架NIO框架服务器软件应用服务器TomcatJettyUndertowWeb服务器Nginx中间件缓存R…

Deathstalker的核心武器——Janicab新变种

01 概述 DeathStalker是一个专门针对金融机构和律师事务所进行攻击的组织&#xff0c;而Janicab是其所使用的比较古老的武器。 Janicab 首次在2013年被发现&#xff0c;它是能够运行在MacOS和Windows操作系统上的恶意软件。其中&#xff0c;Windows版本基于VBscript的植入作为…

【第27天】SQL进阶-查询优化- performance_schema系列实战三:锁问题排查(表级锁)(SQL 小虚竹)

回城传送–》《32天SQL筑基》 文章目录零、前言一、什么是表级锁二、什么时候适合加表级锁三、实战演练3.1 数据准备&#xff08;如果已有数据可跳过此操作&#xff09;3.2 开启第一个会话&#xff0c;执行显式加表级锁3.3 开启第二个会话&#xff0c;对该表执行update更新3.4 …

Vue 3.0 应用组件实例

#创建一个应用实例 每个 Vue 应用都是通过用 createApp 函数创建一个新的应用实例开始的&#xff1a; const app Vue.createApp({ /* 选项 */ }) 该应用实例是用来在应用中注册“全局”组件的。我们将在后面的指南中详细讨论&#xff0c;简单的例子&#xff1a; const app V…

jsp学生管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp 学生管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开 发&#xff0c;数据库为Mysql&#xff0c;使用ja…

【Docker】基础使用

目录 一、Docker简介 二、Docker核心概念 三、Docker安装 四、Docker常用操作 1. 镜像操作 2. 容器操作 3. 安装MySQL 一、Docker简介 Docker 是一个开源的应用容器引擎&#xff0c;基于Go 语言并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖…

基于Springboot搭建java项目(二十二)——过滤器、监听器和拦截器的使用

过滤器、监听器和拦截器的使用 一、过滤器、监听器和拦截器总览 过滤器&#xff08;Filter&#xff09;监听器&#xff08;Listener&#xff09;拦截器&#xff08;Interceptor&#xff09;关注点web请求系统级别参数、对象Action&#xff08;部分web请求&#xff09;如何实现…

20230201在AIO-3568J开发板在原厂Android11下增加右键返回

20230201在AIO-3568J开发板在原厂Android11下增加右键返回 2023/2/1 8:37 百度搜索&#xff1a;RK3568 右键返回 Z:\rk3568_Android11.0_ap6257s\frameworks\native\services\inputflinger\reader\mapper\accumulator\CursorButtonAccumulator.cpp uint32_t CursorButtonAccumu…

【学习OpenCV4】如何学习OpenCV

OpenCV是应用非常广泛的开源视觉处理库&#xff0c;在图像处理、计算机视觉和自动驾驶中有着非常重要的作用。 废话不多说&#xff0c;我就来讲讲OpenCV的使用和学习需要怎么做吧&#xff0c;大家觉得有道理的可以参考一下。 我理解的学习和使用的重点在四个方面&#xff1a; …

PTA L1-019 谁先倒(详解)

前言&#xff1a;本期是关于谁先倒的详解&#xff0c;内容包括四大模块&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读&#xff0c;今天你c了吗&#xff1f; 题目&#xff1a; 划拳是古老中国酒文化的一个有趣的组成部分。酒桌上两人划拳的方…

多模态搜索的未来:超越关键字和向量的混合搜索!

二十年前&#xff0c;“混合”一词仅在植物学和化学领域使用。如今&#xff0c;“混合”这个概念在搜索领域一片繁荣&#xff0c;许多搜索系统都在推出基于 AI 技术的混合搜索方案。但是&#xff0c;“混合搜索”是真的具有应用价值&#xff0c;还只是流行的一阵风呢&#xff1…

深度学习:Self-Attention与Multi-heads Attention详解

深度学习&#xff1a;Self-Attention与Multi-heads Attention详解IntroductionSelf - AttentionMulti-Head AttentionPosition- EncodingIntroduction Transformer 最初是由 Ashish Vaswani等人提出的一种用以完成机器翻译的 Seq2Seq 学习任务的全新网络结构&#xff0c;它完全…

nginx学习笔记9(小滴课堂)

业界主流高可用方案Linux虚拟服务器 LVS讲解 这部分因为需要多台虚拟机&#xff0c;所以我先不实操。 Keepalived核心配置讲解 echo "" > keepalived.conf是清空文件中的内容。 从机的优先级可以写的比master的优先级低一些。 准备NginxLvsKeepAlive相关软件环境 …

什么是最大子数组问题?

本文首发自「慕课网」&#xff0c;想了解更多IT干货内容&#xff0c;程序员圈内热闻&#xff0c;欢迎关注&#xff01; 作者| 慕课网精英讲师 JdreamZhang 最大子数组&#xff08;Max Subarray&#xff09;问题&#xff0c;是计算机科学与技术领域中一种常见的算法问题&#…

CVE-2022-26135 Atlassian Jira Mobile Plugin SSRF漏洞分析

漏洞描述 6月29日&#xff0c;Atlassian官方发布安全公告&#xff0c;在Atlassian Jira 多款产品中存在服务端请求伪造漏洞(SSRF)&#xff0c;经过身份验证的远程攻击者可通过向Jira Core REST API发送特制请求&#xff0c;从而伪造服务端发起请求&#xff0c;从而导致敏感信息…