当前位置: 首页 > news >正文

鸿蒙从零掌握核心:幸运数字生成器实战

一、引言:为什么选择这个例子?

在 HarmonyOS 应用开发的学习路径中,开发者面临的第一道坎往往不是复杂的业务逻辑,而是理解新框架的表达范式

"幸运数字生成器" 虽然功能简单——两个滑块设置范围、一个按钮抽取随机数、一行文字展示结果——但它恰好覆盖了 ArkTS 声明式UI的三大核心抽象

抽象层代码体现核心概念
状态管理@State修饰的三个变量响应式数据驱动UI
布局系统Column+Row嵌套弹性容器 + 线性布局
事件交互onClick/onChange闭包回调 + 状态变更

理解了这个例子,你就理解了 ArkTS 80% 的日常写法。剩下的 20%(自定义组件传参、@Link@Provide/@Consume、动画、自定义绘制)不过是在此基础上扩展。

本文的目标不是教你 "抄代码",而是帮你建立一张心智地图——当你在真实项目中遇到类似场景时,能清晰地知道:框架在这里做了什么,我写的每一行代码最终如何变成屏幕上的像素。


二、HarmonyOS 与 ArkTS:必要的历史上下文

2.1 从 Java 到 ArkTS:一次语言层面的范式切换

HarmonyOS 的第一个开发者预览版(2019年)支持的是Java + XML布局,写法类似 Android 原生开发。但从 HarmonyOS 3.0 开始,华为正式将ArkTS推为首选开发语言。

ArkTS 不是凭空创造的。它基于TypeScript的语法子集,保留了 TypeScript 的类型系统和结构化表达能力,同时:

  • 移除了 TypeScript 中不利于静态优化的特性(如any类型、装饰器参数表达式的动态性)
  • 增加了 UI 框架所需的装饰器语法(@Component@State@Entry等)
  • 约束了运行时行为,使编译器可以做更激进的 AOT(Ahead-of-Time)优化

这意味着:如果你写过 TypeScript,你对 ArkTS 的语法会有天然的亲切感;但如果你把 TypeScript 的那一套动态技巧带进来,编译器会报错。

2.2 声明式UI的行业趋势

ArkTS 采用的是声明式UI范式。与之同频的框架包括:

  • SwiftUI(Apple,2019)
  • Jetpack Compose(Google,2021)
  • Flutter(Google,2017,用 Dart 的声明式写法)
  • React Native(Meta,2015,JSX 声明式)

这些框架的核心共识是:UI 是状态的函数

typescript @Entry

**作用**:将紧随其后的 `@Component` 标记为页面的顶层入口组件。 **一个页面中只能有一个 `@Entry`**。这个限制不是任意的:它让框架知道哪个组件应该被路由系统加载。如果页面有多个入口,路由将产生歧义。 **内部机制**:`@Entry` 本质上是一个**编译期标记**。方舟编译器在编译阶段扫描所有带有 `@Entry` 的组件,为其生成页面级的生命周期管理代码。这意味着: - 页面的 `aboutToAppear()` / `aboutToDisappear()` 生命周期只在 `@Entry` 组件上生效 - 路由参数解析(`@Entry` 的 `params` 参数)只在这一个组件上可用 - 页面转场动画绑定的是 `@Entry` 组件而非内部子组件 ### 3.2 `@Component` 装饰器——组件的声明 ```typescript @Component struct LuckyNum {

组成部分

  • @Component:装饰器,告诉编译器这是一个UI组件
  • struct:ArkTS 使用结构体而不是类来定义组件。这是有意的设计选择:结构体是值类型,比引用类型(class)更可控、更可预测,有利于编译器做内存优化
  • LuckyNum:组件名,约定使用 PascalCase(大驼峰)

组件内部必须包含一个build()方法,这是组件的UI描述入口。

与 class 的区别:在 TypeScript 中,struct 和 class 的行为几乎一样。但在 ArkTS 中,struct 被做了限制——不能继承、不能实现接口、不能有计算属性(getter/setter)以外的非方法成员。这些限制让组件的行为更严格、更可预测,也为编译器的静态分析提供了便利。

3.3@State装饰器——响应式数据的起点

@State minNum: number = 1 @State maxNum: number = 50 @State lucky: number = 0

这是 ArkTS 最核心的概念之一。

@State做了什么?

  1. 声明数据为响应式:框架会监听这个变量的变化
  2. 建立依赖追踪:当build()方法中读取了某个@State变量,框架记录下 "此UI片段依赖该变量"
  3. 触发定向更新:当变量被赋新值时,框架只重绘依赖它的那部分UI,而不是重绘整个组件

背后的技术实现

每个@State变量在运行时对应一个状态节点。当build()执行时,框架开启一个依赖收集期——所有被读取的@State变量会自动注册到当前UI片段的依赖列表中。当变量的setter被触发,框架遍历该变量的依赖列表,标记对应的UI片段为 "脏(dirty)",在下一个帧循环中重绘。

为什么用@State而不是this.minNum = v直接赋值?

如果没有@Statethis.minNum = v只是一次普通的属性赋值,UI 不会感知到变化。@State相当于在赋值操作上插入了钩子(hook),触发后续的UI更新流程。

三个变量的作用域

变量类型初始值用途被谁修改
minNumnumber1随机数范围的下界最小值滑块
maxNumnumber50随机数范围的上界最大值滑块
luckynumber0抽出的幸运数字getLucky()方法

3.4getLucky()方法——纯逻辑抽取

getLucky() { this.lucky = Math.floor(Math.random() * (this.maxNum - this.minNum + 1)) + this.minNum }

这是一个纯函数风格的方法——它不接收参数,不返回结果,而是通过修改@State变量来间接驱动UI变化。

公式解析

Math.random() → [0, 1) 范围内的浮点数 this.maxNum - this.minNum + 1 → 范围内整数个数(包含两端) Math.floor(...) → 向下取整,得到 [0, 个数-1] 的整数 + this.minNum → 平移到 [minNum, maxNum]

例如:minNum=1, maxNum=50

Math.random() = 0.2736 × (50 - 1 + 1) = × 50 = 13.68 Math.floor = 13 + 1 = 14

为什么不用Math.round()

Math.round()会产生不均匀分布——范围两端的值概率只有中间值的一半。Math.floor()++1保证了每个整数的概率完全相等。

为什么这是一个方法而不是内联到 build 里?

  • 可复用:其他地方也可以调用getLucky()
  • 可测试:可以单独测试这个方法(如果抽离到纯逻辑类中)
  • 职责分离build()负责UI描述,方法负责业务逻辑

3.5build()方法——UI 描述的中心

build() { Column({ space: 30 }) { // ... } .width("100%") .height("100%") .padding(20) }

build()是每个@Component必须实现的方法。它返回一个组件树——由容器组件(Column、Row)和基础组件(Text、Slider、Button)组成的树状结构。

在 ArkTS 中,build()的写法是:

  • 顺序调用:在每个容器组件的大括号{}内,按顺序列出子组件
  • 链式调用:通过.属性().事件()的链式写法配置组件
  • 尾随闭包Column({ space: 30 }) { ... }中的{ ... }是尾随闭包,用于定义子组件

3.6 Column——垂直布局容器

Column({ space: 30 }) {

Column是 ArkUI 中最常用的布局容器之一,将其子组件从上到下垂直排列

参数space:子组件之间的间距,单位 vp(virtual pixel,虚拟像素)。30 vp 大约对应 15px 的物理像素(在 2x 屏幕上)。

Column的对齐方式

  • 水平对齐:通过.alignItems(HorizontalAlign.Start | Center | End)控制
  • 垂直对齐:通过.justifyContent(FlexAlign.Start | Center | End | SpaceBetween | SpaceAround | SpaceEvenly)控制

默认情况下,Column 的子组件沿水平方向居中对齐,垂直方向从顶部开始排列。

Column的尺寸行为

  • 如果不设宽高,Column 会包裹其子组件
  • 如果设置了.width("100%").height("100%"),Column 会撑满父容器
  • 子组件如果在主轴(垂直方向)上设置了权重(.layoutWeight(1)),会按比例分配剩余空间

3.7 Row——水平布局容器

Row() { Text("最小值:") Slider({ value: this.minNum, min: 1, max: 20 }) .width(120) .onChange(v => this.minNum = v) Text(`${this.minNum}`) }

Row是水平排列子组件的容器。在这个例子中,每一行包含三个部分:

  1. 标签文字Text("最小值:")——说明当前行的用途
  2. 滑块Slider——交互控件,让用户拖动调节数值
  3. 当前值显示Text(${this.minNum})——将@State变量嵌入字符串,实时显示

行的布局逻辑

默认情况下,Row的子组件在垂直方向上居中对齐。这意味着 "最小值:" 文字、滑块、数字三者会在垂直方向上自动对齐,不需要额外的边距调整——这是一个非常便利的默认行为。

3.8 Slider——滑块组件的深度解读

Slider({ value: this.minNum, min: 1, max: 20 }) .width(120) .onChange(v => this.minNum = v)

构造函数参数

参数类型含义
valuenumber当前值(双向绑定到@State变量)
minnumber最小值
maxnumber最大值
stepnumber(可选,默认 1)步长

当前代码没有设置step,默认步长为 1。如果设置step: 5,滑块只能停在 1、6、11、16 等值上。

链式调用.width(120)

在 ArkTS 中,组件配置通过链式方法调用完成。Slider(...)返回一个 Slider 实例,随后可以调用其属性方法。这等价于传统写法中的:

Slider slider = new Slider(); slider.setValue(this.minNum); slider.setMin(1); slider.setMax(20); slider.setWidth(120); slider.setOnChange((v) => { this.minNum = v; });

ArkTS 的链式语法更简洁、更声明式——你不需要关心配置的顺序,也不需要在不同的代码区域查找配置。

事件绑定.onChange(v => this.minNum = v)

onChange是 Slider 组件提供的事件回调,在滑块值变化时触发。回调参数v是滑块当前的值(number 类型)。

这里有一个关键的模式:事件回调直接更新@State变量。由于@State变量minNum的更新会触发UI重绘,滑块的位置和右侧的Text文字会自动同步更新——无需手动调用任何 "刷新" 方法。

为什么用箭头函数而不是普通函数?

箭头函数v => this.minNum = v自动捕获外层的this。如果使用普通函数function(v) { this.minNum = v }this会指向全局对象或 undefined(严格模式下),导致赋值失败。

3.9 Button——触发动作

Button("一键抽号").onClick(() => this.getLucky())

Button的构造函数接收一个字符串作为按钮文字。.onClick绑定点击事件。

这里有一个值得注意的设计决策:按钮的onClick直接调用getLucky()方法,而不是内联逻辑。这使得:

  • getLucky()可以被其他地方调用(例如:自动抽号定时器、手势触发)
  • 逻辑变更只需改一处
  • 后续如果要加历史记录,只需要在getLucky()中添加this.history.push(this.lucky)即可

3.10 Text——文本展示

Text("抽取幸运数字").fontSize(26)
Text(`${this.minNum}`)
Text(`你的幸运数字:${this.lucky}`).fontSize(40).fontColor("#e63946")

Text是基础文本组件,支持:

  • 模板字符串${this.lucky}形式嵌入变量
  • 字体大小.fontSize(26),单位 fp(font pixel,字体像素)
  • 字体颜色.fontColor("#e63946"),支持#RRGGBB#AARRGGBB格式

为什么标题用 26 而结果用 40?

视觉层次:标题 26 号字体已足够醒目,而结果数字用 40 号 + 红色创造视觉焦点——用户打开页面后视线会自然落在幸运数字上。这是一种没有动画的 "隐式引导"。

颜色#e63946的选择

这是一种饱和度较高的红色(接近珊瑚红),在白色背景上具有强烈的视觉冲击,适合用于 "结果展示" 或 "关键数据" 场景。如果你观察华为的官方应用,你会发现这种红色贯穿了整个设计语言。

3.11 容器样式配置

.width("100%") .height("100%") .padding(20)

这三行配置在Column上:

  • .width("100%").height("100%"):组件撑满可用空间
  • .padding(20):内边距,防止内容贴边

单位说明

ArkTS 中的尺寸单位默认为 vp(虚拟像素)。vp 是一个与设备无关的逻辑像素单位,在不同密度屏幕上自动缩放:

屏幕密度1 vp 对应的物理像素
1x (160 dpi)1 px
2x (320 dpi)2 px
3x (480 dpi)3 px

20 vp 在 2x 屏幕上就是 40 物理像素,在 3x 屏幕上就是 60 物理像素——保持了物理尺寸的一致性。


四、响应式状态管理的深入理解

4.1 单向数据流

ArkTS 的状态管理遵循单向数据流原则:

typescript @State config: { min: number, max: number } = { min: 1, max: 50 }

但 ArkTS 中 `@State` 对对象的变化检测是**浅比较**——修改 `config.min` 不会触发重绘。你需要: ```typescript this.config = { ...this.config, min: newVal }

这会导致max的 UI 片段也被不必要地重绘。因此,对于独立变化的状态,拆成多个@State是更优的实践。


五、从示例到生产:隐藏的问题与改进方案

5.1 边界条件漏洞

当前代码有一个隐藏的 bug:如果用户把最大值滑块滑到小于最小值怎么办?

minNum = 15(滑块范围 1~20) maxNum = 10(滑块范围 30~100,但用户先滑了最小值再滑最大值)

等等——两个滑块的范围是固定的:

  • 最小值滑块:1~20
  • 最大值滑块:30~100

这意味着minNum永远 ≤ 20,maxNum永远 ≥ 30,所以maxNum ≥ minNum + 10永远成立。这是通过 UI 约束而非逻辑校验来保证正确性。

但在更通用的场景中(例如两个滑块范围都是 1~100),就需要:

.onChange(v => { this.minNum = Math.min(v, this.maxNum - 1) })

5.2 用户体验改进

问题改进方案
页面加载时 lucky = 0,显示 "你的幸运数字:0" 会让人困惑初始值设为 null,用条件渲染显示占位文字
点击抽号没有反馈动画添加数字滚动动画或按钮缩放动画
滑块值超出文字显示区域使用.width(40)限制数字宽度,或Text组件设置textAlign
没有防抖快速拖动滑块时 onChange 高频触发,对性能不敏感的简单页面无影响,但复杂场景需要 debounce

5.3 可测试性

getLucky()方法修改了组件内部状态,这使得单元测试变得困难。更好的做法是将其抽离为纯函数:

// 在组件外 function getRandomInRange(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min } // 在组件内 onClick() { this.lucky = getRandomInRange(this.minNum, this.maxNum) }

纯函数getRandomInRange可以在不实例化组件的情况下进行测试。


六、ArkTS 与其他框架的深度对比

6.1 与 SwiftUI 对比

struct LuckyNum: View { @State private var minNum = 1 @State private var maxNum = 50 @State private var lucky = 0 var body: some View { VStack(spacing: 30) { Text("抽取幸运数字").font(.system(size: 26)) HStack { Text("最小值:") Slider(value: $minNum, in: 1...20) .frame(width: 120) Text("\(minNum)") } // ... 类似 } .padding(20) } }

差异

特性ArkTSSwiftUI
状态绑定@State+ 直接赋值@State+$双向绑定
布局嵌套Column { Row { ... } }VStack { HStack { ... } }
链式配置.属性().modifier()
事件绑定.onChange(v => ...).onChange(of:)+ 闭包

SwiftUI 的$minNum双向绑定语法更简洁,但隐式程度更高——开发者不需要显式写onChange。ArkTS 的显式 onChange 虽然多打几个字,但流程更透明。

6.2 与 Jetpack Compose 对比

@Composable fun LuckyNum() { var minNum by remember { mutableStateOf(1f) } var maxNum by remember { mutableStateOf(50f) } var lucky by remember { mutableStateOf(0) } Column( verticalArrangement = Arrangement.spacedBy(30.dp), modifier = Modifier.fillMaxSize().padding(20.dp) ) { Text("抽取幸运数字", fontSize = 26.sp) Row { Text("最小值:") Slider(value = minNum, onValueChange = { minNum = it }, valueRange = 1f..20f, modifier = Modifier.width(120.dp)) Text("${minNum.toInt()}") } // ... } }

差异

特性ArkTSCompose
组件定义struct+ 装饰器@Composable函数
状态声明@Stateremember { mutableStateOf() }
状态访问this.minNumminNum(by 委托)
布局修饰符.width(120)Modifier.width(120.dp)

Compose 使用纯函数(@Composable)而不是结构体来定义组件,这使其在组合性和复用性上有天然优势。ArkTS 的结构体方式在状态封装上更直观,但在高阶组合(HOC 模式)上不如 Compose 灵活。

6.3 与 Flutter 对比

class LuckyNum extends StatefulWidget { @override _LuckyNumState createState() => _LuckyNumState(); } class _LuckyNumState extends State<LuckyNum> { double minNum = 1; double maxNum = 50; int lucky = 0; @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.all(20), child: Column( children: [ Text("抽取幸运数字", style: TextStyle(fontSize: 26)), Row(children: [ Text("最小值:"), SizedBox( width: 120, child: Slider(value: minNum, min: 1, max: 20, onChanged: (v) => setState(() => minNum = v)), ), Text("${minNum.toInt()}"), ]), // ... ], ), ); } }

差异

Flutter 的状态管理需要显式调用setState()——这是它与 ArkTS 最核心的区别。ArkTS 的@State自动触发热更新,而 Flutter 需要开发者手动标记 "这里变了"。

Flutter 的StatefulWidget+State分离设计比 ArkTS 的单一 struct 更复杂,但在大型组件中提供了更清晰的生命周期管理。


七、性能分析:这段代码在底层发生了什么?

当用户拖动滑块到 15 时:

  1. 触摸事件:系统底层捕获触摸事件,传递给 Slider 组件
  2. 命中检测:框架确认触摸位置在 Slider 区域内
  3. 滑动计算:根据触摸位置计算新值(此例中为 15)
  4. onChange 回调:调用v => this.minNum = vthis.minNum = 15
  5. 状态标记@State minNum的 setter 检测到值从 10 变为 15,标记依赖minNum的UI片段为 dirty
  6. 脏节点收集:框架当前帧收集所有 dirty 节点(本例中:最小值滑块本身 + 最小值数字文字)
  7. 重新渲染:渲染引擎重新执行这些 dirty 节点的渲染逻辑
  8. 图层合成:新渲染的UI与未变化的UI合成最终帧
  9. 屏幕刷新:GPU 合成后的帧输出到屏幕

整个过程在 16ms(60fps)或 8ms(120fps)内完成。对于这个简单页面,实际耗时不超过 1ms。


八、扩展:如果继续完善这个应用

8.1 抽号历史

@State history: number[] = [] getLucky() { const num = Math.floor(Math.random() * (this.maxNum - this.minNum + 1)) + this.minNum this.lucky = num this.history.push(num) }

然后使用ForEach渲染历史列表:

if (this.history.length > 0) { Text("抽号历史").fontSize(20) ForEach(this.history, (item, index) => { Text(`第${index + 1}次:${item}`) }) }

8.2 动画效果

给幸运数字的展示加上动画:

Text(`你的幸运数字:${this.lucky}`) .fontSize(40) .fontColor("#e63946") .transition({ type: TransitionType.Insert, scale: { x: 0, y: 0 } }) .animation({ duration: 500, curve: Curve.EaseOut })

每次lucky变化时,数字会从 0 缩放到 1,产生 "弹入" 效果。

8.3 数据持久化

使用@StorageLink将状态同步到 AppStorage:

@StorageLink('luckyHistory') history: string = ''

这样即使应用重启,历史记录也能保留。

@Entry @Component struct LuckyNum { @State minNum: number = 1 @State maxNum: number = 50 @State lucky: number = 0 getLucky() { this.lucky = Math.floor(Math.random() * (this.maxNum - this.minNum + 1)) + this.minNum } build() { Column({ space: 30 }) { Text("抽取幸运数字").fontSize(26) Row() { Text("最小值:") Slider({ value: this.minNum, min: 1, max: 20 }) .width(120) .onChange(v => this.minNum = v) Text(`${this.minNum}`) } Row() { Text("最大值:") Slider({ value: this.maxNum, min: 30, max: 100 }) .width(120) .onChange(v => this.maxNum = v) Text(`${this.maxNum}`) } Button("一键抽号").onClick(() => this.getLucky()) Text(`你的幸运数字:${this.lucky}`).fontSize(40).fontColor("#e63946") } .width("100%") .height("100%") .padding(20) } }


九、常见面试问题

基于这段代码,面试官可能会问:

Q1:@State和普通变量的区别?

A:@State装饰的变量被框架监听,值变化时自动触发UI重绘;普通变量赋值不会引起UI更新。

Q2:如果不用@State,如何让这段代码正常工作?

A:可以使用@Prop(从父组件传递)或@Link(双向同步),但都不如@State适合管理组件内部状态。替代方案是使用LocalStorageAppStorage

Q3:ColumnRow可以互相嵌套吗?可以嵌套几层?

A:可以,没有深度限制。但建议不超过 3~5 层,过深的嵌套影响可读性和性能。出现深度嵌套时考虑提取子组件。

Q4:SlideronChange在拖动过程中会触发多少次?

A:取决于触摸事件的采样率,通常每帧一次(60fps 时每秒 60 次)。如果需要节流,可以在onChange内做 debounce 处理。


十、总结

让我们回顾一下这段 30 多行代码教会我们的东西:

10.1 核心知识清单

知识点掌握程度
@Entry/@Component装饰器理解其作用和限制
@State响应式状态理解其触发UI更新的机制
Column/Row布局理解主轴和交叉轴概念
Slider/Button/Text理解基础组件的用法
事件绑定(onClick / onChange)理解闭包与状态更新的关系
链式属性配置理解 ArkTS 的配置语法

10.2 超越代码的思维模型

  • 声明式思维:描述 "是什么" 而不是 "怎么变"
  • 状态驱动:UI 是状态的函数,不是操作序列的结果
  • 最小更新:框架只重绘变化的部分,无需开发者手动优化

10.3 下一步学习路径

  1. 掌握@Prop/@Link/@Provide/@Consume装饰器
  2. 学习ForEach/LazyForEach列表渲染
  3. 理解组件的生命周期(aboutToAppear/aboutToDisappear
  4. 掌握页面路由(router/Navigation
  5. 学习自定义绘制(Canvas/Shape
  6. 深入学习动画系统(animateTo/animation/transition
  7. 掌握状态管理进阶(LocalStorage/AppStorage/PersistentStorage
http://www.jsqmd.com/news/988459/

相关文章:

  • 2026淮安漏水维修攻略|一修匠修缮:厨卫 阳台 外墙 屋顶 地下室|靠谱防水门店 - 绿呼吸检测中心
  • 2026锦州漏水维修攻略|一修匠修缮:厨卫 阳台 外墙 屋顶 地下室|靠谱防水门店 - 绿呼吸检测中心
  • 2026马鞍山漏水维修攻略|一修匠修缮:厨卫 阳台 外墙 屋顶 地下室|靠谱防水门店 - 绿呼吸检测中心
  • MinIO创建存储桶与密钥对赋权操作指南
  • 温变粉厂家选购指南:如何选到靠谱正规的供应商 - 资讯快报
  • 2026贵阳漏水维修攻略|一修匠修缮:厨卫 阳台 外墙 屋顶 地下室|靠谱防水门店 - 绿呼吸检测中心
  • 性价比高的教务系统供应商
  • 华为:提供创新底座,推动AI从“概念”真正转化为“产业生产力”
  • 2026白银漏水维修攻略|一修匠修缮:厨卫 阳台 外墙 屋顶 地下室|靠谱防水门店 - 绿呼吸检测中心
  • AI Agent Harness Engineering 在物流与配送中的动态路径规划与优化
  • Java IO输入输出流精讲|流的作用、划分方式与使用场景梳理
  • 基于单片机的鱼缸监测与远程管理系统设计
  • 2026邯郸漏水维修攻略|一修匠修缮:厨卫 阳台 外墙 屋顶 地下室|靠谱防水门店 - 绿呼吸检测中心
  • 100 万科研者的共同选择背后:中国科研正在发生什么结构性变化?
  • 2026年 防静电塑料颗粒厂家推荐榜单:抗静电塑胶颗粒/导电工程塑胶颗粒/防静电改性颗粒源头工厂精选 - 品牌发掘
  • 2026年 无锡注册营业执照代办优选榜单:高效合规、一站式公司注册代办服务深度推荐 - 品牌发掘
  • 数据的加密与解密(22:22)
  • 【课程设计/毕业设计】基于Springboot的防诈骗管理系统小程序微信小程序校园反诈骗【附源码、数据库、万字文档】
  • 本地部署视频生成模型Wan2.2/LTX2.3及飞书应用开发可行性全案
  • 2026年口碑好的 青岛正规画室、美术培训学校排行:5家机构办学实力实测对比 - 起跑123
  • AI Agent Harness Engineering 作为企业家:自主发现并执行商业机会的潜力
  • 告别Keil MDK:用VSCode + ARM GCC + OpenOCD打造免费跨平台的STM32开发环境(Windows/Linux通用)
  • 测评|上海AI硬件企业做GEO应该怎么选服务商?靠谱GEO服务商推荐 - 极义GEO
  • 第 10 周:回归与二分类的“开山斧”
  • 第六十五天
  • 2026 济南防水补漏哪家靠谱?正规公司排名及避坑价格指南 - 苏易房屋修缮
  • 数据的加密与解密(22:35)
  • 2026年最新天津律师测评!全方位婚姻策略指导/证据收集/谈判支持/诉讼 - 资讯快报
  • 全国门店突破 800 家!方寸之美渠道服务双达标一线门窗品牌 25%+15% 评判标准 - 广东科技观察
  • 2026年单轨运输车厂家推荐排行榜:山地/果园/茶园/竹林/重载/爬坡及运饲料运矿石各场景车型专业解析 - 品牌发掘