尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Scala使用方式详解:从环境搭建到数据处理脚本实践
很多人拿到一份 Scala 教程翻到 1.1.4 这一节标题写着“Scala 的使用方式”心里多少会嘀咕一门语言还能不会用吗照着写不就行了。但真到了项目里你会发现 Scala 的写法差别特别大。有人拿它当“语法更花的 Java”有人把它写成纯函数式风格还有人只为了跑通一个数据处理脚本。这三种用法都能跑但可维护性、排查难度、团队协作体验完全是三个世界。我最早接触 Scala是因为一个分布式计算组件的二次开发任务后来又在多个数据类项目里写过完整的处理链路带过几位从 Java 转过来的同事。比较深的一个体会是Scala 真正难的不是语法本身而是思维方式的切换——从“命令式”切到“表达式驱动”从“可变状态”切到“不可变数据流”从“类型强转”切到“模式匹配与类型推导”。这篇文章就围绕“Scala 的使用方式”展开从环境搭建、语法要点、到实际脚本开发梳理一条能落地的路径。想用 Scala 写数据处理脚本的人准备转函数式风格的 Java 工程师或者只是想了解 JVM 生态多一门顺手语言的开发者都可以照着走一遍。1. 先想清楚 Scala 的定位它不只是“更好的 Java”很多人学 Scala 之前都听说过一句话Scala 融合了面向对象和函数式编程。这句话没错但真正理解它的人不多。面向对象部分Scala 有 class、object、trait几乎可以和 Java 的无缝概念对应上函数式部分又有不可变集合、高阶函数、模式匹配、Option 这些平时 Java 里用不到的东西。两种风格可以在同一个文件里共存也可以按团队偏好往某一侧倾斜。用个不一定严谨但好懂的类比Java 的做法像中餐大火爆炒每个动作都得写清楚——先切菜、再热油、然后下锅每一步都不能省Scala 的做法更像把食材丢进料理机你声明“我想要什么结果”剩下的交给语言和库的编排。1.1 Scala 到底混搭了哪两套东西从运行角度说Scala 跑在 JVM 上编译后产生字节码所以 Java 工程师的很多经验可以直接迁移同一套 JVM 参数、同一套监控工具、同一套内存模型。Scala 代码也可以直接调用海量的 Java 库依赖管理也用 Maven 中央仓库那套体系。但语言特性上Scala 做的事情比 Java 激进得多。最典型的一点是类型系统。Java 里很多运行期才能发现的类型错误Scala 里编译期就能拦住。比如把字符串和数字相加Java 可能到运行期才报错Scala 在编译器就直接拒绝。再比如模式匹配Java 的 switch 只能做简单等值判断Scala 的 match 可以匹配类型、匹配集合结构、匹配多个条件组合还要求分支必须完备漏一种情况编译时会有警告。还有一个容易忽略的点Scala 的 object单例对象承担了 Java 里 static 方法加全局状态的角色trait 则解决了 Java 多继承的难题。所以从 Java 切过来的工程师面对 Scala 的第一阶段通常是兴奋的——终于可以少写一半模板代码了。1.2 什么场景真正离不开 ScalaScala 的知名度在语言圈里不算顶尖但它在几个特定场景下几乎是绕不开的。一是大数据生态。很多主流分布式计算、流处理的底层库都是用 Scala 写的如果你要改源码、做定制、排查框架内部问题看不懂 Scala 会很被动。二是数据链路脚本。在 Java 后端环境里用 Scala 写 ETL、清洗、统计这类脚本长度通常只有 Java 版本的三分之一到一半而且链式集合操作读起来非常接近业务描述的语感。三是复杂领域建模。sealed trait 加 case class 加模式匹配对状态明确、分类清晰的业务模型特别友好比如订单状态机、事件溯源、规则引擎。四是异步与并发。Future 的组合在 Scala 里是原生能力配合集合操作可以写出比 Java 回调嵌套清晰得多的代码。我之前经手过一个日志系统分析任务需要同时处理几十种格式的日志先过滤、再聚合、最后输出报表。Java 版本写了大概 600 行后来用 Scala 重写核心逻辑不到 200 行。倒不是说行数少一定代表好但它确实反映了表达能力上的差距。同样的逻辑Java 需要写很多中间变量、循环、判断分支Scala 通过 map、flatMap、filter 和模式匹配能把“过程”压缩成“描述”。2. 开发环境与工具链先把脚手架搭起来讨论用法之前环境得先跑通。Scala 的开发工具链比 Java 稍复杂一点核心是三样JDK、构建工具、IDE。JDK 没什么悬念Scala 跑在 JVM 上安装常规 JDK 即可。构建工具这里推荐 sbt它是 Scala 官方主推的构建工具虽然学习曲线比 Maven 陡但增量编译和交互式开发体验是别的工具暂时追不上的。IDE 方面用你熟悉的集成开发环境装 Scala 插件即可社区版也能满足日常使用。2.1 版本选择不要一上来就追最新版本这块我建议稳妥优先。Scala 有两个大版本线2.13 和 3.x。如果你的项目没有历史包袱用 2.13 是比较保险的选择因为第三方库的支持最全踩坑最少。Scala 3 语法更现代但有些老库只有 2.13 的二进制版引入时需要额外的兼容桥接对新手来说容易卡住最好等到团队里有 Scala 经验比较多的成员再考虑。JDK 版本同样别追太新。Scala 2.13 在 JDK 8 和 JDK 11 上运行最稳JDK 17 之后的大多数场景也可以但个别老依赖会碰到模块化相关的反射权限问题。我的习惯是用 LTS 版本并把编译目标设为 JDK 8 的字节码这样部署环境的兼容性最好。2.2 用 sbt 建一个最小的工程sbt 工程的目录结构固定如下my-scala-project/ ├── build.sbt ├── project/ │ └── build.properties └── src/ ├── main/ │ └── scala/ │ └── Hello.scala └── test/ └── scala/build.sbt 是这个项目的核心配置文件最小版本只要三行name : scala-demo version : 0.1.0 scalaVersion : 2.13.15name 和 version 是项目标识scalaVersion 决定编译器版本这三个字段是 sbt 的硬性要求。如果你要加依赖写法是这样libraryDependencies org.scala-lang.modules %% scala-xml % 2.2.0这个%%是 sbt 特有的语法意思是根据当前 scalaVersion 自动选择对应二进制版本号发布的库。比如 scalaVersion 是 2.13它会去找scala-xml_2.13这个 artifact。不写%%而写死具体版本名的话升级 Scala 版本时很容易遇到依赖不兼容的问题。Hello.scala 里面写一个最简单的入口object Hello extends App { println(Hello, Scala) }可以和 Java 对比一下Java 要写 public class、public static void main、String[] argsScala 用 object 加 extends App 就完成了同样的事情。object 定义了一个单例对象extends App 让这个对象可以直接运行。在 sbt 里输入run或者命令行执行sbt run都能看到输出。2.3 先从 REPL 开始找感觉在 IDE 里建工程之前我建议先在 REPL 里玩半小时。所谓 REPL就是交互式解释环境输入一行表达式立刻返回结果不需要创建文件、编译、运行这一整套流程。启动方式很简单在项目目录执行sbt console或者安装 Scala 编译器后直接在命令行输入scala。进去之后可以试试这一行val nums (1 to 10).filter(_ % 2 0).map(_ * 2)这行代码的意思是取 1 到 10 的范围挑出偶数再乘以 2。结果是一个 Vector(4, 8, 12, 16, 20)。在 Java 里这行逻辑大概需要写一个 for 循环、一个 if 判断、一个临时列表、两次 add 操作五六行起步。Scala 用一行写完了而且整个过程没有出现一个显式的循环变量。REPL 对学习的价值在于你每试一个语法点都能立刻看到类型和值。比如你输入val a Hello它会返回a: String Hello类型推断结果直接显示出来。写错类型、写错符号也马上能在交互环境里发现比一次次编译省时间得多。3. 核心语法要点从 Java 思维切换到 Scala 思维语法细节查文档就能看到我不打算复述一遍官方教程只挑几个决定了 Scala “使用方式”的关键点讲讲它们背后的逻辑和常见的坑。3.1 val 和 var把可变状态收起来Scala 声明变量有两类关键字val 定义的值不可重新赋值var 定义的可以重新赋值。很多新手觉得 val 就是 Java 的 final这么理解没错但 Scala 社区的默认习惯是把 val 当成首选用 var 反而要说明理由。为什么强调不可变两个原因。第一不可变数据天然线程安全多个线程同时读一个 val不存在数据竞争问题第二不可变值让代码更容易推理。调试时你看到一个 val就能确定它在整个生命周期里不会变排查范围立刻缩小到它的初始化表达式。如果到处都是 var那么任何值都可能在任何位置被修改出 bug 时你需要检查所有修改点。工程实践里的折中方案是函数内部可以用 var 做一些临时累加跨线程共享的数据尽量用不可变结构。还有一个常见的替代套路就是用 foldLeft 代替 var 累加。比如统计列表求和命令式写法需要声明一个 var total 0然后循环累加Scala 的写法是List(1,2,3).foldLeft(0)(_ _)没有可变状态结果一样。3.2 集合操作三板斧map、filter、foldLeftScala 集合库是最值得花时间熟悉的部分它在“用很少的代码表达业务逻辑”这个目标上做到了很高的水平。三个最常用的操作map对每个元素做同样的变换结果长度不变。filter按条件筛选元素结果长度可能变短。flatMap先 map 再压平一层常用于处理嵌套结构。另外还有一个 foldLeft它从初始值开始用二元函数不断累积把整个集合折叠成一个值。举个例子假设有订单列表需要给有折扣的订单计算折后总价case class Order(id: String, amount: Double, discount: Double) val orders List( Order(A001, 120.0, 0.8), Order(A002, 200.0, 1.0), Order(A003, 150.0, 0.5) ) val total orders .filter(_.discount 1.0) .map(o o.amount * o.discount) .foldLeft(0.0)(_ _)filter 只留下折扣小于 1.0 的订单map 把每条订单映射成折后金额foldLeft 以 0.0 为起点把所有金额加起来。整个过程没有临时变量、没有循环、没有中间集合的变异。这就是所谓“表达式驱动”的写法——你用一串描述性的操作拼出结果而不是用命令一步步告诉机器怎么做。flatMap 有一点需要特别留意它经常用来处理“可能没有值”的场景。比如一个方法返回 Option你想对结果继续做变换又不想写 if else 判空就可以用 flatMap。这个能力在后面讲 Option 时会自然用到。3.3 case class 和模式匹配替掉一坨 if elseJava 17 也有了 record 类但 Scala 的 case class 走得比 record 更远。定义一个 case class编译器自动帮你生成 equals、hashCode、toString、copy而且这些方法都是值语义非常适合数据建模。举例一个事件系统有登录、购买、退出三种事件sealed trait Event case class Login(userId: String) extends Event case class Purchase(userId: String, amount: Double) extends Event case class Logout(userId: String) extends Event def handle(event: Event): String event match { case Login(uid) s用户 $uid 登录 case Purchase(uid, amount) s用户 $uid 消费 $amount case Logout(uid) s用户 $uid 退出 }sealed trait 表示这个特性的所有实现都必须在当前文件中编译器因此知道 Event 的全部子类型。模式匹配时如果漏写了一种 case编译期就会给出警告这在 Java 里只能靠运行期测试撞出来。而 match 的每个分支可以同时解构字段拿到 userId、amount 这类数据不需要像 Java 那样先 instanceof、再强转、再取字段。模式匹配招人喜欢还有一个原因它能把复杂的判断拆成平铺的分支而不是一堆嵌套 if。代码读起来像一张表条件列在前面结果列在后边维护性是嵌套分支完全比不了的。3.4 Option让“可能没有值”不再靠 nullnull 是 Java 历史上最著名的设计失误之一。Scala 的解决方案是 Option 类型它本质上是一个容器要么装着值Some要么一无所有None。你不再需要担心“调用方法返回 null”这种隐式风险因为类型系统直接告诉你结果可能为空。举一个真实场景从配置 Map 里读取端口号如果没有配置就用默认值 8080。val config Map(host - 192.168.1.10, port - 8080) val port config.get(port).map(_.toInt).getOrElse(8080)config.get 返回的是 Option[String]map 把里面的字符串转成 IntgetOrElse 在 None 时返回 8080。整条链没有任何 if 判空。而且注意这里 map 对 Option 和集合的作用完全一致如果 Option 是 Some就变换内部值如果是 None就保持 None。这种统一性让集合操作的经验能直接迁移过来。多个可能为空的值要组合使用最舒服的方式是用 for 推导式val conn for { host - config.get(host) port - config.get(port) } yield s$host:$port如果 config 里缺了 host 或 port 任何一个结果就直接是 None不会继续执行后续步骤。这在 Java 里要写两层 null 检查Scala 一行搞定。3.5 隐式转换方便但别当饭吃隐式转换是 Scala 早年很出名的一个特性后来社区的态度慢慢转向克制。它做的事情是编译器在发现类型不匹配时自动寻找一个隐式方法把类型转成目标类型。典型用法是给已有类型扩展方法。implicit class StringWordCount(val s: String) extends AnyVal { def wordCount: Int if (s.trim.isEmpty) 0 else s.trim.split(\\s).length } hello world.wordCount // 结果为 2运行到hello world.wordCount时编译器发现 String 没有 wordCount 方法于是查找从 String 到 StringWordCount 的隐式转换自动包装后调用。这是很好的扩展机制但代价是代码里看不到这次转换——你在编辑器里看到的明明是 String它的方法来自哪里必须靠隐式查找规则去追踪而这个过程对人是不可见的。所以我的建议是隐式转换可以用但一定要限制在单文件或非常明确的作用域内不要把一个隐式转换放在一个容易被到处导入的公共包里。团队里新人查“这个方法哪来的”查了一下午这种体验我相信谁都不想在项目里重演。4. 实操记录写一个日志统计脚本并逐步解读语法看了不少接接地气用一个直接能跑的日志统计脚本来感受 Scala 的实际使用方式。这个脚本的需求很简单读取一个文本日志文件统计 INFO、WARN、ERROR、DEBUG 各级别数量然后输出最近 10 条错误日志。4.1 任务拆解与设计思路这个任务看起来很基础但有个工程化的难点日志文件可能很大不能一次性把整个文件读进内存。所以我设计成逐行读取、增量统计。每一行先提取日志级别然后写入一个可变 Map 做计数累计错误行单独记录保留最近 10 条。为什么用可变 Map 而不是每次新建 Map因为增量更新语义天然适合可变结构Scala 的不可变 Map 在每次更新时都会创建新对象虽然内部有结构共享但写起来要额外写赋值逻辑不如可变 Map 直接。在纯函数式风格里其实也有解法但那个路线在这里会让代码复杂度明显上升合理使用可变状态也是一种工程判断。4.2 完整代码与运行方式import scala.io.Source object LogAnalyzer extends App { val path if (args.length 0) args(0) else app.log val source Source.fromFile(path) val pattern raw\[(INFO|WARN|ERROR|DEBUG)\].r val counts scala.collection.mutable.Map[String, Int]() .withDefaultValue(0) var errors List[String]() try { for (line - source.getLines()) { pattern.findFirstMatchIn(line) match { case Some(m) val level m.group(1) counts(level) 1 if (level ERROR) { errors line :: errors } case None } } println(各级别日志数量:) counts.toMap.foreach { case (level, cnt) println(s$level: $cnt) } println(最近 10 条错误日志:) errors.take(10).reverse.foreach(println) } finally { source.close() } }在 sbt 项目里把这段代码放到src/main/scala/LogAnalyzer.scala然后执行sbt run即可。如果你有日志文件也可以直接运行时带上文件路径sbt run /path/to/app.log。4.3 代码逐段拆解Source.fromFile返回一个 BufferedSource这是 Scala 对文件读取的封装。getLines()返回一个迭代器每调用一次取出一行不会把整个文件一次性装进内存。对大文件来说这是“活着读完文件”和“把机器内存撑爆”的分界线。正则模式串那里我用了raw前缀原始字符串可以避免写反斜杠转义直接按字面量编译正则。findFirstMatchIn(line)返回 Option[Match]这一行没有匹配级别的模式时返回 None匹配到就返回 Some刚好用模式匹配来处理。withDefaultValue(0)这一行很关键。它让 Map 在访问不存在的 key 时返回 0而不是抛出异常。只有这样counts(level) 1才能直接自增如果没有默认值第一次遇到某个级别时counts(level)会抛出 NoSuchElementException。这是 Scala Map 一个容易漏掉的细节。错误日志的记录方式用的是 List 的头插法。line :: errors表示把新行放到列表头部这操作是 O(1) 的如果反过来往尾部追加在不可变 List 上是 O(n)日志一多会变慢。最后take(10)取头 10 个此时它们是倒序的用reverse转回正序再打印。4.4 换个更函数的写法但要知道代价同样的统计功能用纯集合链可以写得更短val lines source.getLines().toList val counts lines .flatMap(line pattern.findFirstMatchIn(line).map(_.group(1))) .groupBy(identity) .view .mapValues(_.size) .toMap这个版本用 flatMap 把“匹配到的级别”提取出来groupBy 按级别分组mapValues 统计每组大小。逻辑更优雅但一个关键区别是toList会把所有行都加载进内存如果日志是几十 GB这条链直接就不成立。所以在实际任务里短不一定最好。你要根据数据规模决定策略。小文件可以直接用集合链大文件就得回到逐行迭代思路。这也是 Scala 使用方式里的一个核心权衡函数式写法往往更简洁但你必须时刻留意数据操作是否引入了额外的内存或性能开销。4.5 工程化补充可观测性和退出码真实项目里这类脚本通常不只是人肉跑一下。如果要做定时调度建议在输出统计后按条件设置退出码比如存在 ERROR 日志就返回非零方便任务调度系统感知失败。Scala 里可以这样实现if (counts.getOrElse(ERROR, 0) 0) { sys.exit(1) } else { sys.exit(0) }另一个建议是用更定制的格式化输出替代 println比如输出成 JSON 或 TSV方便下游消费。脚本类任务容易被低估但它同样是代码维护性、可观测性、退出约定都应该按项目标准来。5. 常见问题与排查技巧讲了这么多最后把我在实际使用中遇到的高频问题汇总一下。这些问题在官方文档里大多有解但没人帮你串起来的时候每一步都可能卡半天。5.1 编译慢、越用越慢sbt 的问题Scala 编译速度比 Java 慢是客观事实但很多时候慢的其实是构建配置。sbt 首次启动要下载大量依赖慢是正常的但如果你每改一次 build.sbt 就 reload 一次等于反复触发依赖解析浪费很多时间。比较好的习惯是启动一个 sbt 交互式 shell让它常驻改完代码直接在 shell 里执行run或test利用增量编译。增量编译通常只重编改动的部分大项目里体验接近秒级。如果你确定依赖已经下载完毕也可以加-Dsbt.offlinetrue参数让 sbt 不再检查远程仓库启动速度会快不少。5.2 None 相关的运行期异常Option 最大的坑在于get方法在 None 上调用会抛 NoSuchElementException报错信息还很委婉直接说 java.util.NoSuchElementException: None.get第一次遇到的新手可能完全反应不过来。规避方法很简单生产代码里不要直接调用.get。默认使用getOrElse提供兜底值或者用fold把空值分支和非空分支统一处理或者干脆用模式匹配把 Some 和 None 的情况都写清楚。这个原则不仅对 Option 适用对其他类似的包装类型同样成立只要方法可能返回“没有值”就不要假设它一定有值。5.3 隐式转换“找不到”或“疯狂生效”隐式转换有两个方向的坑。一个是编译器找不到你要用的隐式常见原因是 implicit 定义不在当前作用域或者没有通过 import 引入另一个是隐式转换被外部模块意外引入导致你想调自己的方法时编译器默默调用了别人的转换行为完全不符合预期。排查这类问题优先确认三件事隐式定义在哪个对象里是否被 import是否被继承到当前类路径如果代码里没有明确线索可以在编译器报错信息里看 implicit search 的详细日志。但更根本的办法是控制用量——隐式转换作为局部扩展工具而不是跨模块的全局约定。5.4 Scala 版本与依赖兼容性问题2.12、2.13、3.x 三个版本线在源码层面大部分兼容但编译产物不兼容依赖库也会区分_2.12、_2.13、_3等后缀。升级 Scala 版本前先检查所有依赖是否发布了对应版本号的构件。使用 sbt 的%%语法能自动匹配但如果你在配置文件里手写了完整的 artifact升级时很可能漏改。还有一个常见误区有人会在 build.sbt 里手动指定 scala-library 的版本这通常没必要scalaVersion 字段已经决定了编译和运行使用的版本。手动覆盖容易造成编译器版本和运行库版本不一致出现各种奇怪的二进制兼容问题。5.5 快速排查对照表现象可能原因排查方向编译很慢增量编译没有生效 / 频繁 reload常驻 sbt shell集中修改 build.sbt隐式转换不生效implicit 不在作用域检查 import 和定义位置None.get 异常对 Option 直接调用 get改用 getOrElse / fold / 模式匹配报错信息难以理解链式调用过长类型推断复杂拆成多个有名字的中间值逐个验证依赖版本冲突传递依赖引入不同版本的 Scala artifact用 sbt 的 dependencyTree 查看依赖树排查 Scala 问题时我自己的习惯是“先把链路拆开”。编译器报错如果非常抽象通常意味着某一环的类型和预期不符这时候硬读错误信息没用正确的做法是把长链拆成多行每一步在 REPL 里单独跑一遍很快就能定位到是哪一环出了问题。最后分享一点经验Scala 的使用方式从“会写几段语法”到“能把一个数据处理任务稳定跑在线上”中间其实只隔着一个东西工程习惯。我见过不少新手一上来就追求最短写法一个表达式串了十几个操作符出了 bug 根本没法断反而那些老老实实定义了数据类、用模式匹配替代 if 链、把所有隐式转换控制在极小范围的项目维护起来特别舒服。所以我给你的建议是先别急着啃各种函数式理论拿 Scala 去写一些小工具、做几次真实的数据清洗任务感受一下不可变集合和高阶函数带来的变化再回头补齐概念。用着用着你就会发现所谓 Scala 的使用方式其实就是你愿意在多大程度上让代码去描述“要什么”而不是每一行都在说“怎么做”。
RELATED

相关推荐

Java旅游汽车租赁管理系统毕设:从数据库设计到并发调度全解析

Java旅游汽车租赁管理系统毕设:从数据库设计到并发调度全解析

做Java毕设的人不少,但选题能同时兼顾“工作量适中”和“业务有深度”的真不多。旅游汽车租赁管理系统这个题目,我前前后后带过几个学生做过,也帮忙救过几个半路写崩的,对它算是有比较完整的认识。它表面上就是围绕车辆、订单、客…

📅 2026/10/15 23:36:17
美的老式空调RS232私有协议调试与现代接入指南

美的老式空调RS232私有协议调试与现代接入指南

简介:本资源是艾默生网络能源有限公司编制的《美的空调协议调试手册》,专为暖通自控工程师、楼宇智能化系统集成人员及Modbus协议开发人员设计,解决KF-120LW/K2SRY型号美的空调通过RS485/RS232实现远程监控与协议级调试的实际问题。手册涵盖非…

📅 2026/10/15 23:36:17
SpringBoot+Vue构建专业选择系统:从架构设计到智能推荐实现

SpringBoot+Vue构建专业选择系统:从架构设计到智能推荐实现

1. 项目概述最近有同学问到我一个很有意思的项目——基于SpringBootVue的专业选择系统,标识符qt53nlt7。乍一看名字平平无奇,但仔细拆解之后你会发现,这类系统在教育信息化领域其实是刚需中的刚需。简单说,这就是一个帮助学生从海…

📅 2026/10/15 23:36:17
MORE NEWS

更多资讯

📰

Snowy 前端组件 XnTreeSelect:懒加载树选择器与编辑回显完整实战指南

后端前端认证鉴权应用安全 【免费下载链接】Snowy 💖国内首个国密前后分离快速开发平台💖《免费商用》,基于开源技术栈精心打造,融合Vue3AntDesignVue4Vite5SpringBoot3MpHuToolSa-Token。平台内置国密加解密功能,保障…

📰

媒体爬虫实战指南:五分钟跑通社交数据

媒体爬虫实战指南:五分钟跑通社交数据 【免费下载链接】MediaCrawler 小红书笔记 | 评论爬虫、抖音视频 | 评论爬虫、快手视频 | 评论爬虫、B 站视频 | 评论爬虫、微博帖子 | 评论爬虫、百度贴吧帖子 | 百度贴吧评论回复爬虫 | 知…

📰

Fontsource 字体实战:用 Flow Circular 为线框原型与早期设计稿注入逼真的占位内容

前端CLI开发工具 【免费下载链接】fontsource Self-host Open Source fonts in neatly bundled NPM packages. 项目地址: https://gitcode.com/gh_mirrors/fo/fontsource 点击查看 免费下载 导读 Flow Circular 是设计师 Dan Ross 创作的 Flow 字体家族的一员&…

📰

Faster R-CNN老代码调试指南:从环境配置到mAP评估全流程踩坑实录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

LoopBack 4 Model API Builder 扩展点:用 Model 类自动生成 Repository 与 Controller 的插件契约

后端微服务 【免费下载链接】loopback-next LoopBack makes it easy to build modern API applications that require complex integrations. 项目地址: https://gitcode.com/gh_mirrors/lo/loopback-next 点击查看 免费下载 loopback/model-api-builder 是 LoopBa…

📰

Snowy 中 XnPageSelect 分页下拉选择器:基于 Ant Design Vue Select 的懒加载搜索封装实战

后端前端认证鉴权应用安全 【免费下载链接】Snowy 💖国内首个国密前后分离快速开发平台💖《免费商用》,基于开源技术栈精心打造,融合Vue3AntDesignVue4Vite5SpringBoot3MpHuToolSa-Token。平台内置国密加解密功能,保障…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬