sql语句怎样避免因触发器中sql语句错误导致的主操作失败 sql语句触发器中错误致主操作失败的常见问题解决

要避免触发器内部sql错误导致主操作失败,最核心的策略是在触发器中实现错误捕获与处理机制,例如sql server使用try…catch、oracle使用exception块,通过捕获异常、记录日志并选择不重新抛出错误,使主操作得以继续提交,同时将错误信息保存至独立的日志表(oracle需使用自治事务确保日志持久化),并结合数据校验、避免复杂逻辑、处理多行操作等设计原则提升触发器健壮性,从而在保障主操作成功的同时保留故障排查能力。

sql语句怎样避免因触发器中sql语句错误导致的主操作失败 sql语句触发器中错误致主操作失败的常见问题解决

在SQL语句中,要避免因触发器内部的SQL错误导致主操作(比如INSERT、UPDATE、DELETE)失败,最核心的策略是在触发器内部实现严密的错误处理机制。这意味着你需要捕获并管理触发器代码中可能发生的异常,而不是让它们直接向上冒泡,进而回滚整个主事务。

解决方案

解决触发器内部错误导致主操作失败的问题,关键在于在触发器内部主动捕获并处理异常。这通常通过数据库提供的错误处理语法实现,例如SQL Server的

TRY...CATCH

块,或者Oracle PL/SQL的

EXCEPTION

块。

具体来说:

封装触发器逻辑:将触发器的核心业务逻辑包裹在一个错误处理块中。捕获异常:当错误发生时,错误处理块会拦截它。决定行为日志记录:将错误信息(如错误代码、错误消息、发生位置等)记录到专门的错误日志表中。这是至关重要的一步,即便你选择让主操作成功,也必须知道哪里出了问题。选择性回滚/继续:如果你希望主操作在触发器出错时仍然成功,那么在捕获到错误后,不要重新抛出异常(即不要使用

RAISEERROR

RAISE

)。仅仅记录错误,然后让触发器正常结束。如果你认为触发器中的错误是致命的,必须阻止主操作,那么在记录错误后,可以显式地重新抛出异常,强制主事务回滚。但请注意,这与你避免主操作失败的初衷相悖,所以要慎用。数据修正或默认值:在某些情况下,你甚至可以在捕获到错误时,尝试修正数据或使用默认值,以确保触发器能继续完成其任务,避免影响主操作。但这需要非常谨慎的设计。

通过这种方式,即使触发器内部的SQL语句有缺陷或遇到意外数据,主操作也能在一定程度上“幸免于难”,但前提是你已经妥善处理了错误,并了解了可能的数据不一致风险。

触发器中常见的SQL错误类型有哪些,以及它们如何影响主操作?

触发器内部的SQL语句,和任何其他SQL代码一样,可能遭遇多种错误。这些错误如果未经处理,往往会导致当前正在执行的主DML操作(INSERT、UPDATE、DELETE)连同整个事务一起被回滚。理解这些常见错误类型,是构建健壮触发器的第一步。

数据类型转换失败:这是非常普遍的错误。比如,你尝试将一个包含非数字字符的字符串插入到数字列中,或者将过长的字符串截断插入到固定长度的列中。触发器经常需要从

INSERTED

DELETED

伪表中读取数据,如果这些数据格式不符合预期,就会引发此类错误。例如,

CAST('ABC' AS INT)

就会失败。约束违反主键/唯一键冲突:触发器逻辑尝试插入或更新数据,但导致目标表中的主键或唯一键重复。外键约束违反:触发器尝试引用一个不存在于父表中的外键值,或者删除了被其他表引用的数据。CHECK约束违反:插入或更新的数据不满足列或表上定义的CHECK约束条件。NOT NULL约束违反:尝试插入NULL值到不允许为NULL的列。这些约束错误,无论是在主操作中发生还是在触发器中发生,都会导致事务回滚。算术错误:最典型的就是除以零错误。在计算字段值时,如果分母为零,就会抛出错误。对象不存在:触发器代码中引用了不存在的表、视图、列或函数。这通常在部署或修改后发生,但如果触发器逻辑是动态SQL,运行时也可能出现。死锁或锁超时:如果触发器逻辑需要访问或修改其他表,而这些表又被其他并发事务锁定,就可能导致死锁或锁超时。这会直接导致当前事务失败。无限递归:这是一个设计缺陷,而不是SQL语句本身的错误,但后果同样严重。例如,一个

AFTER INSERT

触发器又执行了

INSERT

操作到同一张表,而这个

INSERT

又触发了它自己,形成无限循环,最终导致事务栈溢出或资源耗尽。权限不足:触发器执行的操作(如插入到日志表)需要特定的权限,但触发器执行上下文没有这些权限。

这些错误一旦在触发器内部发生且未被捕获,数据库系统会默认将它们视为致命错误,并强制回滚包含主操作在内的整个事务。这意味着用户的操作会失败,数据不会被修改,但用户得到的错误信息可能只是“事务被终止”,而无法直接得知是触发器内部的问题。

如何在触发器内部实现健壮的错误处理和日志记录?

在触发器内部实现健壮的错误处理和日志记录是避免主操作失败的关键。不同的数据库系统有不同的实现方式,但核心思想都是一致的:捕获、记录、决定后续行为。

西语写作助手 西语写作助手

西语助手旗下的AI智能写作平台,支持西语语法纠错润色、论文批改写作

西语写作助手 0 查看详情 西语写作助手

SQL Server 示例:使用

TRY...CATCH

SQL Server提供了

TRY...CATCH

结构来处理T-SQL中的运行时错误。

CREATE TRIGGER trg_YourTable_AfterInsertUpdateON YourTableAFTER INSERT, UPDATEASBEGIN    SET NOCOUNT ON; -- 防止触发器对客户端发送多余的行数信息    BEGIN TRY        -- ---------------------------------------------------        -- 触发器核心业务逻辑        -- ---------------------------------------------------        -- 示例1: 模拟一个数据类型转换错误        -- DECLARE @invalid_num INT = CAST('abc' AS INT);        -- 示例2: 模拟一个除以零错误        -- DECLARE @result DECIMAL(10,2);        -- SET @result = 100 / 0;        -- 示例3: 正常业务逻辑,比如更新关联表或记录审计信息        INSERT INTO AuditLog (TableName, OperationType, ChangeDate, ChangedBy, OldValue, NewValue)        SELECT            'YourTable',            CASE WHEN EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted) THEN 'UPDATE'                 WHEN EXISTS (SELECT * FROM inserted) THEN 'INSERT'                 WHEN EXISTS (SELECT * FROM deleted) THEN 'DELETE'            END,            GETDATE(),            SUSER_SNAME(),            (SELECT OldCol FROM deleted), -- 假设OldCol是你要记录的旧值            (SELECT NewCol FROM inserted); -- 假设NewCol是你要记录的新值        -- 注意:在SQL Server的AFTER触发器中,inserted和deleted表可能包含多行        -- 上述单行SELECT示例在多行操作时会报错,实际应使用JOIN或循环处理        -- 正确的多行处理示例:        -- INSERT INTO AuditLog (TableName, OperationType, ChangeDate, ChangedBy, KeyValue, OldValue, NewValue)        -- SELECT        --     'YourTable',        --     'UPDATE',        --     GETDATE(),        --     SUSER_SNAME(),        --     i.ID, -- 假设ID是主键        --     d.SomeColumn,        --     i.SomeColumn        -- FROM inserted i        -- JOIN deleted d ON i.ID = d.ID;    END TRY    BEGIN CATCH        -- ---------------------------------------------------        -- 错误处理和日志记录部分        -- ---------------------------------------------------        INSERT INTO TriggerErrorLog (            ErrorNumber,            ErrorSeverity,            ErrorState,            ErrorProcedure,            ErrorLine,            ErrorMessage,            TriggerName,            ErrorTime,            AffectedRowsJson -- 可以存储inserted/deleted表的JSON表示        )        VALUES (            ERROR_NUMBER(),            ERROR_SEVERITY(),            ERROR_STATE(),            ERROR_PROCEDURE(),            ERROR_LINE(),            ERROR_MESSAGE(),            OBJECT_NAME(@@PROCID), -- 获取当前触发器的名称            GETDATE(),            (SELECT (SELECT * FROM inserted FOR JSON AUTO) AS inserted_data,                    (SELECT * FROM deleted FOR JSON AUTO) AS deleted_data FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)        );        -- 如果你想让主操作继续成功,则不要执行RAISERROR        -- 如果你希望主操作失败并回滚,则可以重新抛出错误:        -- RAISERROR ('触发器执行失败,详细信息已记录。', 16, 1);        -- 16是严重级别,1是状态。严重级别16表示一般错误,会终止事务。        -- 也可以使用THROW,THROW会保留原始错误信息:        -- THROW;    END CATCHEND;GO-- 创建错误日志表 (如果不存在)CREATE TABLE TriggerErrorLog (    LogID INT IDENTITY(1,1) PRIMARY KEY,    ErrorNumber INT,    ErrorSeverity INT,    ErrorState INT,    ErrorProcedure NVARCHAR(128),    ErrorLine INT,    ErrorMessage NVARCHAR(MAX),    TriggerName NVARCHAR(128),    ErrorTime DATETIME DEFAULT GETDATE(),    AffectedRowsJson NVARCHAR(MAX) -- 存储受影响行的JSON数据);GO

Oracle PL/SQL 示例:使用

EXCEPTION

在Oracle中,PL/SQL块使用

EXCEPTION

部分来处理运行时错误。

CREATE OR REPLACE TRIGGER trg_YourTable_AfterInsertUpdateAFTER INSERT OR UPDATE ON YourTableFOR EACH ROW -- 行级触发器,对每一行操作执行一次DECLARE    -- 声明变量    v_error_message VARCHAR2(4000);    PRAGMA AUTONOMOUS_TRANSACTION; -- 用于日志记录,使其不影响主事务的提交/回滚BEGIN    -- ---------------------------------------------------    -- 触发器核心业务逻辑    -- ---------------------------------------------------    -- 示例1: 模拟一个数据类型转换错误    -- DECLARE v_num NUMBER;    -- v_num := 'abc';    -- 示例2: 模拟一个除以零错误    -- DECLARE v_result NUMBER;    -- v_result := 100 / 0;    -- 示例3: 正常业务逻辑,比如记录审计信息    INSERT INTO AuditLog (table_name, operation_type, change_date, changed_by, old_value, new_value)    VALUES (        'YourTable',        CASE WHEN INSERTING THEN 'INSERT' WHEN UPDATING THEN 'UPDATE' ELSE 'DELETE' END,        SYSDATE,        USER,        :OLD.some_column, -- 访问旧值        :NEW.some_column   -- 访问新值    );EXCEPTION    WHEN OTHERS THEN -- 捕获所有其他未预期的异常        -- ---------------------------------------------------        -- 错误处理和日志记录部分        -- ---------------------------------------------------        v_error_message := SQLERRM; -- 获取错误消息        -- 将错误记录到日志表        INSERT INTO TriggerErrorLog (            error_code,            error_message,            trigger_name,            error_time,            affected_old_row_json, -- 存储旧行数据            affected_new_row_json  -- 存储新行数据        )        VALUES (            SQLCODE, -- 获取错误代码            v_error_message,            'TRG_YOURTABLE_AFTERINSERTUPDATE', -- 触发器名称            SYSDATE,            -- 可以将 :OLD 和 :NEW 行转换为JSON或XML存储,取决于你的需求            -- 例如:UTL_JSON.to_json_string(:OLD) 或自定义函数            NULL, -- 占位符            NULL  -- 占位符        );        COMMIT; -- 提交错误日志的事务,因为使用了AUTONOMOUS_TRANSACTION        -- 如果你想让主操作继续成功,则不要执行 RAISE;        -- 如果你希望主操作失败并回滚,则可以重新抛出错误:        -- RAISE;END;/-- 创建错误日志表 (如果不存在)CREATE TABLE TriggerErrorLog (    log_id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,    error_code NUMBER,    error_message VARCHAR2(4000),    trigger_name VARCHAR2(128),    error_time DATE DEFAULT SYSDATE,    affected_old_row_json CLOB,    affected_new_row_json CLOB);/

关键点和注意事项:

日志表的独立性:在Oracle中,如果希望错误日志在主事务回滚时仍然保留,你需要将日志插入操作放在一个自治事务(Autonomous Transaction)中(如上述Oracle示例中的

PRAGMA AUTONOMOUS_TRANSACTION

)。SQL Server的

TRY...CATCH

默认与主事务在同一会话中,但如果错误级别低于16,则不会回滚整个事务。对于严重的错误(级别16或更高),即使在

CATCH

块中,如果未重新抛出,主事务也可能回滚,或者你需要确保

CATCH

块本身没有引入新的致命错误。错误信息的完整性:记录尽可能多的错误上下文信息,包括错误编号、消息、触发器名称、发生行号(SQL Server)、以及最重要的是,导致错误的原始数据(

INSERTED

DELETED

伪表的内容或

:OLD

/

:NEW

行数据)。这对于后续的故障排除至关重要。决定是否重新抛出:这是最核心的决策。如果你不希望触发器失败导致主操作失败,那么在

CATCH

EXCEPTION

块中,完成日志记录后,不要再抛出任何异常。让触发器自然结束。如果触发器的逻辑是强制性的,必须成功,否则主操作没有意义,那么在记录日志后,你应该重新抛出错误。性能考量:错误处理和日志记录本身会带来性能开销。确保日志记录逻辑高效,避免在触发器中执行耗时的操作,尤其是对于高并发的表。

除了错误处理,还有哪些设计策略可以增强触发器的健壮性并减少失败风险?

除了在触发器内部实现精细的错误处理,一些设计和开发实践也能从根本上提升触发器的健壮性,减少其导致主操作失败的风险。

优先使用声明性约束而非触发器:这是数据库设计的一个黄金法则。如果业务规则可以通过

PRIMARY KEY

UNIQUE

FOREIGN KEY

CHECK

约束或

NOT NULL

属性来实现,那么就应该优先使用它们。这些声明性约束由数据库引擎原生支持,效率更高,更稳定,且错误处理机制也更明确(通常会直接抛出明确的约束违反错误)。触发器应该用于实现那些无法通过声明性约束表达的复杂业务逻辑、审计追踪或数据同步。保持触发器逻辑的简洁性:触发器应该尽量只做一件事,而且做得要简单高效。复杂的业务逻辑、大量的数据计算或跨多个表的复杂操作,更适合放在存储过程、函数或应用程序层中实现。这样可以降低触发器的复杂性,减少出错的可能性,也便于测试和维护。一个“胖”触发器更容易引入错误,也更难调试。充分考虑多行操作(Sets)的影响:在SQL Server中,

INSERTED

DELETED

伪表可能包含多行数据(例如,

INSERT INTO ... SELECT ...

UPDATE ... WHERE ...

)。触发器会为整个语句执行一次,而不是为每一行执行一次。因此,触发器代码必须能够正确处理多行数据,避免只考虑单行操作的逻辑。例如,使用

SUM()

AVG()

聚合函数,或者使用

JOIN

来处理

INSERTED

/

DELETED

表与目标表的关系。Oracle的

FOR EACH ROW

触发器虽然是行级的,但如果内部逻辑没有考虑到多行并发插入/更新/删除可能导致的锁冲突或数据一致性问题,仍然可能出问题。防御性编程数据校验:在触发器内部执行操作前,对

INSERTED

:NEW

中的数据进行额外的校验,确保数据符合预期格式和业务规则,例如检查日期格式、数字范围、字符串长度等。NULL值处理:对可能为NULL的列进行操作时,务必使用

IS NULL

以上就是sql语句怎样避免因触发器中sql语句错误导致的主操作失败 sql语句触发器中错误致主操作失败的常见问题解决的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/594403.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月10日 17:30:13
下一篇 2025年11月10日 17:30:57

相关推荐

  • Uniapp 中如何不拉伸不裁剪地展示图片?

    灵活展示图片:如何不拉伸不裁剪 在界面设计中,常常需要以原尺寸展示用户上传的图片。本文将介绍一种在 uniapp 框架中实现该功能的简单方法。 对于不同尺寸的图片,可以采用以下处理方式: 极端宽高比:撑满屏幕宽度或高度,再等比缩放居中。非极端宽高比:居中显示,若能撑满则撑满。 然而,如果需要不拉伸不…

    2025年12月24日
    400
  • 如何让小说网站控制台显示乱码,同时网页内容正常显示?

    如何在不影响用户界面的情况下实现控制台乱码? 当在小说网站上下载小说时,大家可能会遇到一个问题:网站上的文本在网页内正常显示,但是在控制台中却是乱码。如何实现此类操作,从而在不影响用户界面(UI)的情况下保持控制台乱码呢? 答案在于使用自定义字体。网站可以通过在服务器端配置自定义字体,并通过在客户端…

    2025年12月24日
    800
  • 如何在地图上轻松创建气泡信息框?

    地图上气泡信息框的巧妙生成 地图上气泡信息框是一种常用的交互功能,它简便易用,能够为用户提供额外信息。本文将探讨如何借助地图库的功能轻松创建这一功能。 利用地图库的原生功能 大多数地图库,如高德地图,都提供了现成的信息窗体和右键菜单功能。这些功能可以通过以下途径实现: 高德地图 JS API 参考文…

    2025年12月24日
    400
  • 如何使用 scroll-behavior 属性实现元素scrollLeft变化时的平滑动画?

    如何实现元素scrollleft变化时的平滑动画效果? 在许多网页应用中,滚动容器的水平滚动条(scrollleft)需要频繁使用。为了让滚动动作更加自然,你希望给scrollleft的变化添加动画效果。 解决方案:scroll-behavior 属性 要实现scrollleft变化时的平滑动画效果…

    2025年12月24日
    000
  • 如何为滚动元素添加平滑过渡,使滚动条滑动时更自然流畅?

    给滚动元素平滑过渡 如何在滚动条属性(scrollleft)发生改变时为元素添加平滑的过渡效果? 解决方案:scroll-behavior 属性 为滚动容器设置 scroll-behavior 属性可以实现平滑滚动。 html 代码: click the button to slide right!…

    2025年12月24日
    500
  • 如何选择元素个数不固定的指定类名子元素?

    灵活选择元素个数不固定的指定类名子元素 在网页布局中,有时需要选择特定类名的子元素,但这些元素的数量并不固定。例如,下面这段 html 代码中,activebar 和 item 元素的数量均不固定: *n *n 如果需要选择第一个 item元素,可以使用 css 选择器 :nth-child()。该…

    2025年12月24日
    200
  • 使用 SVG 如何实现自定义宽度、间距和半径的虚线边框?

    使用 svg 实现自定义虚线边框 如何实现一个具有自定义宽度、间距和半径的虚线边框是一个常见的前端开发问题。传统的解决方案通常涉及使用 border-image 引入切片图片,但是这种方法存在引入外部资源、性能低下的缺点。 为了避免上述问题,可以使用 svg(可缩放矢量图形)来创建纯代码实现。一种方…

    2025年12月24日
    100
  • 如何让“元素跟随文本高度,而不是撑高父容器?

    如何让 元素跟随文本高度,而不是撑高父容器 在页面布局中,经常遇到父容器高度被子元素撑开的问题。在图例所示的案例中,父容器被较高的图片撑开,而文本的高度没有被考虑。本问答将提供纯css解决方案,让图片跟随文本高度,确保父容器的高度不会被图片影响。 解决方法 为了解决这个问题,需要将图片从文档流中脱离…

    2025年12月24日
    000
  • 为什么 CSS mask 属性未请求指定图片?

    解决 css mask 属性未请求图片的问题 在使用 css mask 属性时,指定了图片地址,但网络面板显示未请求获取该图片,这可能是由于浏览器兼容性问题造成的。 问题 如下代码所示: 立即学习“前端免费学习笔记(深入)”; icon [data-icon=”cloud”] { –icon-cl…

    2025年12月24日
    200
  • 如何利用 CSS 选中激活标签并影响相邻元素的样式?

    如何利用 css 选中激活标签并影响相邻元素? 为了实现激活标签影响相邻元素的样式需求,可以通过 :has 选择器来实现。以下是如何具体操作: 对于激活标签相邻后的元素,可以在 css 中使用以下代码进行设置: li:has(+li.active) { border-radius: 0 0 10px…

    2025年12月24日
    100
  • 如何模拟Windows 10 设置界面中的鼠标悬浮放大效果?

    win10设置界面的鼠标移动显示周边的样式(探照灯效果)的实现方式 在windows设置界面的鼠标悬浮效果中,光标周围会显示一个放大区域。在前端开发中,可以通过多种方式实现类似的效果。 使用css 使用css的transform和box-shadow属性。通过将transform: scale(1.…

    2025年12月24日
    200
  • 为什么我的 Safari 自定义样式表在百度页面上失效了?

    为什么在 Safari 中自定义样式表未能正常工作? 在 Safari 的偏好设置中设置自定义样式表后,您对其进行测试却发现效果不同。在您自己的网页中,样式有效,而在百度页面中却失效。 造成这种情况的原因是,第一个访问的项目使用了文件协议,可以访问本地目录中的图片文件。而第二个访问的百度使用了 ht…

    2025年12月24日
    000
  • 如何用前端实现 Windows 10 设置界面的鼠标移动探照灯效果?

    如何在前端实现 Windows 10 设置界面中的鼠标移动探照灯效果 想要在前端开发中实现 Windows 10 设置界面中类似的鼠标移动探照灯效果,可以通过以下途径: CSS 解决方案 DEMO 1: Windows 10 网格悬停效果:https://codepen.io/tr4553r7/pe…

    2025年12月24日
    000
  • 使用CSS mask属性指定图片URL时,为什么浏览器无法加载图片?

    css mask属性未能加载图片的解决方法 使用css mask属性指定图片url时,如示例中所示: mask: url(“https://api.iconify.design/mdi:apple-icloud.svg”) center / contain no-repeat; 但是,在网络面板中却…

    2025年12月24日
    000
  • 如何用CSS Paint API为网页元素添加时尚的斑马线边框?

    为元素添加时尚的斑马线边框 在网页设计中,有时我们需要添加时尚的边框来提升元素的视觉效果。其中,斑马线边框是一种既醒目又别致的设计元素。 实现斜向斑马线边框 要实现斜向斑马线间隔圆环,我们可以使用css paint api。该api提供了强大的功能,可以让我们在元素上绘制复杂的图形。 立即学习“前端…

    2025年12月24日
    000
  • 图片如何不撑高父容器?

    如何让图片不撑高父容器? 当父容器包含不同高度的子元素时,父容器的高度通常会被最高元素撑开。如果你希望父容器的高度由文本内容撑开,避免图片对其产生影响,可以通过以下 css 解决方法: 绝对定位元素: .child-image { position: absolute; top: 0; left: …

    2025年12月24日
    000
  • CSS 帮助

    我正在尝试将文本附加到棕色框的左侧。我不能。我不知道代码有什么问题。请帮助我。 css .hero { position: relative; bottom: 80px; display: flex; justify-content: left; align-items: start; color:…

    2025年12月24日 好文分享
    200
  • 前端代码辅助工具:如何选择最可靠的AI工具?

    前端代码辅助工具:可靠性探讨 对于前端工程师来说,在HTML、CSS和JavaScript开发中借助AI工具是司空见惯的事情。然而,并非所有工具都能提供同等的可靠性。 个性化需求 关于哪个AI工具最可靠,这个问题没有一刀切的答案。每个人的使用习惯和项目需求各不相同。以下是一些影响选择的重要因素: 立…

    2025年12月24日
    300
  • 如何用 CSS Paint API 实现倾斜的斑马线间隔圆环?

    实现斑马线边框样式:探究 css paint api 本文将探究如何使用 css paint api 实现倾斜的斑马线间隔圆环。 问题: 给定一个有多个圆圈组成的斑马线图案,如何使用 css 实现倾斜的斑马线间隔圆环? 答案: 立即学习“前端免费学习笔记(深入)”; 使用 css paint api…

    2025年12月24日
    000
  • 如何使用CSS Paint API实现倾斜斑马线间隔圆环边框?

    css实现斑马线边框样式 想定制一个带有倾斜斑马线间隔圆环的边框?现在使用css paint api,定制任何样式都轻而易举。 css paint api 这是一个新的css特性,允许开发人员创建自定义形状和图案,其中包括斑马线样式。 立即学习“前端免费学习笔记(深入)”; 实现倾斜斑马线间隔圆环 …

    2025年12月24日
    100

发表回复

登录后才能评论
关注微信