news 2026/9/8 9:09:45

Compose ConstraintLayout实战:告别嵌套布局

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Compose ConstraintLayout实战:告别嵌套布局

如果你正在用 Jetpack Compose 做安卓开发,大概率会遇到这样一个场景:界面层级越来越深,ColumnRowRow再套Box,最后嵌套了五六层,改一个间距要翻半天代码,预览也卡得不行。这时候很多人会想,要是 Compose 里也有类似传统 View 系统里ConstraintLayout那样的约束布局就好了。

事实上,Compose 官方确实提供了ConstraintLayout,而且它不是简单把老布局搬运过来,而是用一套声明式 DSL 重新实现了约束逻辑。这篇文章会从“什么时候真的需要它”讲起,带你搞懂它的核心概念、写一个完整示例,再把最容易踩的坑和使用建议一并梳理清楚。

先说结论:在 Compose 中,ConstraintLayout 不是默认首选,但在复杂页面、自定义层级、动态对齐和性能敏感场景下,它依然是不可替代的布局工具。

1. 这篇文章真正要解决的问题

很多人刚学 Jetpack Compose 时,容易被“声明式 UI 不用再写布局文件”这句话误导,觉得布局问题已经被ColumnRowBox三个组件解决了。实际写项目后才发现,界面一旦复杂起来,线性布局嵌套的代价非常高。

先说性能。Compose 的布局阶段需要测量和放置每个子组件。嵌套层级越深,测量次数就越多。尤其是当你用ColumnRow,再用Row包多个Box去模拟“相对定位”时,Compose 需要一层层递归测量,才能计算出最终位置。在列表项这种高频 recomposition 的场景里,这种写法很容易造成掉帧。

再说代码可维护性。嵌套布局的缩进会让人崩溃,尤其是你发现自己为了把一个Text放到父容器底部,不得不在外面再包一层Box的时候。这里真正容易踩坑的地方是:很多新手以为 Compose 里没有 ConstraintLayout,于是拼命用BoxModifier.align来模拟约束,结果代码读起来非常痛苦,别人接手时根本不知道你想表达什么布局意图。

ConstraintLayout 解决的就是这三件事:

  • 用同级约束替代多层嵌套,降低测量深度。
  • 用明确的constrainAs描述组件相对位置,代码即布局图。
  • 提供 guideline、barrier、chain 等高级能力,适配复杂设计稿。

这篇文章适合以下读者:

  • 已经掌握ColumnRowBox基础用法,想进阶布局能力的安卓开发者。
  • 正在从传统 View 体系迁移到 Compose,但被嵌套布局困扰的开发者。
  • 想优化列表项布局性能,或者想让自己写的 Compose 代码更具可读性的开发者。

读完你会得到:一套完整可运行的 ConstraintLayout 示例、对核心 API 的理解、以及实际项目中如何选型布局方案的建议。

2. 约束布局的核心概念与适用场景

2.1 什么是约束布局

在传统 View 系统中,ConstraintLayout是一个允许你用“约束”来定义子视图之间相对位置的布局容器。所谓约束,可以理解为一条“规则”:A 的左边对齐 B 的右边,C 的底部对齐父容器的底部,D 的宽度跟随 A 和 B 的边界。

Compose 中的ConstraintLayout设计目标一致,但实现方式完全不同。传统 View 通过 XML 属性来描述约束,Compose 则通过 Kotlin DSL 在代码中创建约束。

一个最简单的约束关系是这样的:

// 伪代码,演示约束关系 ConstraintLayout { val title = createRef() val content = createRef() Text( text = "标题", modifier = Modifier.constrainAs(title) { top.linkTo(parent.top, margin = 16.dp) start.linkTo(parent.start, margin = 16.dp) } ) Text( text = "内容区域", modifier = Modifier.constrainAs(content) { top.linkTo(title.bottom, margin = 8.dp) start.linkTo(parent.start, margin = 16.dp) } ) }

这里有几个需要解释的概念:

  • createRef():为每个子组件创建一个“约束引用”,它并不代表组件本身,只是一个用于描述位置关系的锚点。
  • constrainAs:把某个 Composable 与一个约束引用绑定,并在花括号内声明它的位置规则。
  • linkTo:指定这个组件的某条边(topbottomstartend)与另一个约束目标的对应边建立关联。

2.2 为什么 Compose 还需要 ConstraintLayout

既然ColumnRowBox已经能组合出任何布局,为什么还要引入一套新 API?

答案就在“组合”这两个字上。ColumnRow天生适合线性排列,Box适合堆叠,但你若要表达“A 在 B 和 C 中间,且宽度由两者的位置决定”这种复杂关系,用线性布局就需要额外嵌套和测量辅助。

ConstraintLayout 的核心价值不是“做布局”,而是“降低布局复杂度”。它把多个组件放在同一个层级上描述关系,而不是通过物理嵌套实现关系。

一个典型的场景是底部操作栏:左侧是取消按钮,右侧是确认按钮,中间是标题,标题要居中但又不完全基于父容器中心点,而是要基于左右按钮之间的空间中心。这种需求在 XML 时代用ConstraintLayout非常自然,在 Compose 中用Row+weight也能做,但如果左右按钮宽度不固定,Row方案就会变得棘手。ConstraintLayout 可以用两组约束精确表达。

2.3 核心 API 一览

从官方 API 来看,Compose 的ConstraintLayout核心包括:

名称作用对应传统 View 中的概念
createRef()创建一个组件的引用锚点@+id/xxx
constrainAs()为组件声明约束规则layout_constraintXxx
createGuidelineFrom...()创建百分比或固定偏移的辅助线Guideline
createBarrier()创建屏障,使多个组件的边界对齐Barrier
Chain将多个组件连成一条链,实现分布和权重layout_constraintChainStyle

本篇文章会重点讲createRef()constrainAs()createGuidelineFrom...(),因为这是日常开发中使用频率最高的三个。

2.4 和传统 View 系统约束布局的关键区别

很多人从 View 切到 Compose 时,会下意识寻找 XML 中“属性”的对应物。但 Compose 的 ConstraintLayout 有两个显著不同:

第一,约束是内联代码,不是独立 XML 文件。这意味着约束逻辑与 UI 代码在同一个文件,看到组件就能看到它的位置规则,不需要在两个文件之间跳转。

第二,约束不依赖 id 字符串。传统 View 里要通过@+id/xxx来引用组件,拼写错误只有在运行或构建时才能发现。Compose 通过createRef()创建的引用是类型安全的,如果引用了不存在的引用,编译期就会报错。

这两点决定了 Compose 的 ConstraintLayout 更适合现代安卓开发工作流,但也要求开发者有更强的代码组织能力。

3. 环境准备与前置条件

在开始写代码之前,先确认你的工程环境。执行ConstraintLayout示例至少需要满足以下条件:

  • Android Studio 版本建议使用当前稳定版本,本文示例以较新的 Android Studio 环境为准。
  • 项目使用 Kotlin DSL 或 Groovy DSL 均可,不影响本文示例。
  • 已启用 Jetpack Compose 支持。

build.gradle.kts(模块级别)中,需要添加 Compose ConstraintLayout 依赖:

dependencies { implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1") }

如果你使用的是build.gradle(Groovy 语法),对应写法是:

dependencies { implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.1' }

这里要特别提醒:constraintlayout-compose和传统 View 时代的androidx.constraintlayout:constraintlayout是两个完全不同的库。前者专门为 Compose 编写,包名是androidx.constraintlayout.compose;后者是面向 View 系统的,不能在 Compose 中直接使用。很多从老项目迁移过来的同学第一次踩坑就在这。

添加依赖后,需要同步 Gradle。然后可以在一个 Composable 函数中使用:

import androidx.constraintlayout.compose.ConstraintLayout

环境准备就绪后,我们来拆解核心流程。

4. 约束布局的核心流程拆解

这一节我会用一个常见的“卡片式布局”来演示 ConstraintLayout 的完整使用过程。假设我们要实现这样一个界面:左上角是用户头像,头像右侧是用户名,用户名下方是发布时间,卡片右下角有一个“关注”按钮,头像左下角有一个在线状态绿点。

在传统 View 时代,这个布局要么用RelativeLayout,要么用ConstraintLayout,层级大概 2 到 3 层。如果纯用 Compose 基础组件,可能需要RowColumn,再嵌套Box,大概 4 层以上。我们用 ConstraintLayout 来写,只需要一层。

4.1 第一步:创建约束引用

在 Composable 的ConstraintLayout块内,首先为每个需要定位的组件创建引用:

ConstraintLayout( modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { val avatarRef = createRef() val nameRef = createRef() val timeRef = createRef() val statusDotRef = createRef() val followButtonRef = createRef() }

这一步的作用是声明“布局中有哪些需要约束的元素”。注意,createRef()必须在ConstraintLayout的作用域内调用。

4.2 第二步:为组件绑定约束

接下来,在每个子组件上通过Modifier.constrainAs来声明约束关系:

Image( bitmap = avatarBitmap, contentDescription = "用户头像", modifier = Modifier .size(48.dp) .constrainAs(avatarRef) { top.linkTo(parent.top) start.linkTo(parent.start) } ) Text( text = "Android 开发技术栈", modifier = Modifier.constrainAs(nameRef) { top.linkTo(avatarRef.top) start.linkTo(avatarRef.end, margin = 12.dp) end.linkTo(followButtonRef.start, margin = 8.dp) width = Dimension.wrapContent } )

这段代码的含义是:

  • 头像位于父容器左上角。
  • 用户名顶部与头像顶部对齐,左侧在头像右侧 12dp 处。
  • 用户名的右侧要保持在关注按钮左侧 8dp 之前,避免文字过长顶到按钮。

这里需要解释Dimension.wrapContent。在 ConstraintLayout 中,组件的宽度默认遵循它的内容尺寸,但当你同时约束了startend时,组件会被拉伸。如果你不想让它被拉伸,就显式指定width = Dimension.wrapContent

这就是新手最常见的误区:给一个Text同时设置了start.linkToend.linkTo,结果发现文字被拉长了,左右都贴到边界。原因就是没有手动设置Dimension.wrapContent

4.3 第三步:处理相对定位和辅助线

继续完善卡片布局。发布时间在用户名下方,关注按钮需要垂直居中于头像位置,在线状态绿点要贴在头像右下角边缘。

Text( text = "5 分钟前发布", style = MaterialTheme.typography.bodySmall, color = Color.Gray, modifier = Modifier.constrainAs(timeRef) { top.linkTo(nameRef.bottom, margin = 4.dp) start.linkTo(nameRef.start) } ) Button( onClick = { /* 关注逻辑 */ }, modifier = Modifier.constrainAs(followButtonRef) { centerVerticallyTo(avatarRef) end.linkTo(parent.end) } ) { Text(text = "关注") } Box( modifier = Modifier .size(12.dp) .background(Color(0xFF4CAF50), CircleShape) .constrainAs(statusDotRef) { top.linkTo(avatarRef.top, margin = 34.dp) start.linkTo(avatarRef.start, margin = 34.dp) } )

这里用到了几个新 API:

  • centerVerticallyTo(avatarRef):让当前组件在垂直方向上与另一个引用居中。
  • margin参数:在linkTo中传入,表示边距。

在线状态绿点的处理方式比较粗暴,直接用固定 margin 把它挪到右下角。实际项目中还可以用createRef()bottomend链来实现,这里不赘述。

5. 完整示例代码实现

为了让读者能直接运行,我写一个完整的 Composable 示例。这段代码可以在一个空白的 Compose 工程中直接运行,展示一个包含头像、标题、描述、进度条和操作按钮的复杂卡片布局。

示例场景:一个视频课程卡片。卡片顶部是课程封面缩略图,左侧是课程标题和讲师名,右侧是收藏按钮,底部有一行小字显示学习进度,进度条位于卡片左下角到右下角。

package com.example.composeconstraintlayout import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { VideoCardExample() } } } } @Composable fun VideoCardExample() { ConstraintLayout( modifier = Modifier .fillMaxSize() .padding(16.dp) ) { val coverRef = createRef() val titleRef = createRef() val teacherRef = createRef() val favoriteRef = createRef() val progressRef = createRef() val progressTextRef = createRef() val statusDotRef = createRef() // 封面占位块 Box( modifier = Modifier .size(width = 80.dp, height = 100.dp) .clip(RoundedCornerShape(8.dp)) .background(Color(0xFFB39DDB)) .constrainAs(coverRef) { top.linkTo(parent.top) start.linkTo(parent.start) } ) // 课程标题 Text( text = "Jetpack Compose 声明式 UI 开发实战", style = MaterialTheme.typography.titleMedium, modifier = Modifier.constrainAs(titleRef) { top.linkTo(coverRef.top) start.linkTo(coverRef.end, margin = 12.dp) end.linkTo(favoriteRef.start, margin = 8.dp) width = Dimension.wrapContent } ) // 讲师名 Text( text = "讲师:陈老师", style = MaterialTheme.typography.bodyMedium, color = Color.Gray, modifier = Modifier.constrainAs(teacherRef) { top.linkTo(titleRef.bottom, margin = 6.dp) start.linkTo(titleRef.start) } ) // 收藏按钮 Button( onClick = { /* 收藏逻辑 */ }, modifier = Modifier.constrainAs(favoriteRef) { centerVerticallyTo(coverRef) end.linkTo(parent.end) } ) { Text(text = "收藏") } // 在线状态绿点 Box( modifier = Modifier .size(14.dp) .background(Color(0xFF4CAF50), CircleShape) .constrainAs(statusDotRef) { top.linkTo(coverRef.top, margin = 78.dp) start.linkTo(coverRef.start, margin = 68.dp) } ) // 进度条 LinearProgressIndicator( progress = { 0.6f }, modifier = Modifier.constrainAs(progressRef) { top.linkTo(coverRef.bottom, margin = 16.dp) start.linkTo(parent.start) end.linkTo(parent.end) width = Dimension.fillToConstraints } ) // 进度文字 Text( text = "60%", style = MaterialTheme.typography.bodySmall, color = Color.Gray, modifier = Modifier.constrainAs(progressTextRef) { top.linkTo(progressRef.bottom, margin = 4.dp) end.linkTo(parent.end) } ) } }

这段代码演示了几个关键点:

  • start.linkTo(coverRef.end, margin = 12.dp)可以代替嵌套Row实现水平排列。
  • centerVerticallyTo(coverRef)让按钮相对封面垂直居中。
  • width = Dimension.fillToConstraints让进度条从父容器起点延伸到终点。这在Column里需要用fillMaxWidth,但在约束布局里直接用约束规则完成。
  • 在线状态绿点使用固定 margin 的方式定位到封面右下角,这是一种快捷写法,虽然不优雅但直接有效。

5.1 如何运行和验证

在 Android Studio 中新建一个空 Compose 工程,将依赖添加完毕后,把上述代码放入MainActivity.kt,运行到模拟器或真机。预期看到的效果是:

  • 封面块显示紫色背景,位于卡片左侧。
  • 标题和讲师名位于封面右侧,标题不会被按钮挤压变形。
  • 收藏按钮位于卡片右上角,垂直方向和封面中心对齐。
  • 进度条横跨整个卡片宽度,进度文字在右下角。

如果界面与预期不符,优先检查每个组件是否绑定了正确的constrainAs引用。

6. 运行结果与效果验证

上面示例的运行结果在视觉上非常直观,但怎么从代码层面判断你的约束设置是否正确?我提供一个验证清单。

6.1 预期输出

运行后,界面应该呈现:

  • 紫色封面块位于屏幕左上角。
  • 标题文字在封面右侧,并且顶部对齐。
  • 讲师名在标题下方。
  • 收藏按钮位于屏幕右侧,垂直方向与封面中心对齐。
  • 绿色小圆点覆盖在封面右下角。
  • 进度条位于封面下方,宽度充满可用空间。
  • 60% 文本显示在进度条下方右侧。

6.2 判断是否成功的方法

  • 如果所有组件都在预期位置,说明约束关系正确。
  • 如果某个组件没有出现,检查它是否缺少constrainAs
  • 如果出现了但位置错乱,优先检查linkTo的起始点是否拼错。
  • 如果布局正常但组件被拉伸,查看是否设置了Dimension.wrapContent

6.3 运行失败第一步看哪里

如果启动崩溃,先在 Logcat 中搜索ConstraintLayout相关异常。最常见的崩溃原因是使用了一个引用来约束它自己,例如:

// 错误示例:引用自身 val boxRef = createRef() Box(modifier = Modifier.constrainAs(boxRef) { start.linkTo(boxRef.start) })

这种自己关联自己的约束会直接抛出异常。构建时不会报错,运行时才会。搜索Circular constraint可以快速定位。

7. 约束布局常见问题与排查方法

在实际开发中,下面几个问题出现的频率非常高。

问题现象可能原因排查方式解决方案
组件没有出现在预期位置没有绑定constrainAs检查组件修饰符中是否有constrainAs为组件绑定对应引用并声明约束
文字被拉长变形同时约束了startend检查宽度默认行为显式添加width = Dimension.wrapContent
运行崩溃:循环约束引用linkTo自身查看 Logcat 中的 Circular constraint检查引用变量是否错误指向自身
组件重叠约束冲突或使用了固定 margin确认两组约束是否指向同一位置使用辅助线或 barrier 明确位置规则
预览显示空白依赖未正确添加或未同步查看 Gradle 同步状态重新同步项目并确认依赖存在
引用不存在的对象约束变量写错名编译器会提示,运行前修复使用createRef()创建的变量名
使用传统 ConstraintLayout 依赖导致编译失败导入了 View 系统的类检查 import 语句使用androidx.constraintlayout.compose.ConstraintLayout

7.1 最容易忽略的细节

在 Compose 中,约束布局的很多 API 设计是“宽进严出”。写起来简单,但如果你不明白默认行为,很容易写出反直觉的布局。

我建议至少记住这三条经验:

  • constrainAs不是Modifier的普通扩展,它在ConstraintLayoutScope中才有意义,不要在其他作用域中使用。
  • createRef()每个组件需要独立调用,不能在循环中复用同一个引用,否则只有最后一个组件生效。
  • 约束是可以叠加的,组件可以同时拥有topbottomstartend四条边的约束,但越复杂越难维护,能用简单方式解决就不要上复杂规则。

8. 最佳实践与工程建议

8.1 不要为了用而用

ConstraintLayout 虽然强大,但它并不是 Compose 布局的银弹。一个页面如果只有三四个元素、线性排列清晰,直接用ColumnRow反而更简洁。

我的建议是:

  • 组件少于 5 个,且关系简单,优先使用ColumnRowBox
  • 需要多个组件互相参照位置,且有交叉对齐关系,使用 ConstraintLayout。
  • 单个列表项的布局复杂,使用 ConstraintLayout 提升测量性能。
  • 有动态位置变化的需求,例如某个元素要根据另一个元素是否显示来决定位置,使用 ConstraintLayout 更自然。

8.2 保持约束粒度适中

不要在同一个ConstraintLayout里塞几十个组件。约束系统擅长处理关系,但关系过多时理解成本同样很高。如果页面很复杂,可以拆分成多个子布局,每个子布局内部使用约束,再通过基础组件组合。

经验法则是:一个ConstraintLayout的约束引用数量控制在 10 个以内。超过这个数量,代码的可读性会明显下降。

8.3 善用指南线和屏障

createGuidelineFromTop()createGuidelineFromStart()可以创建不依赖具体组件的辅助线。比如你想让某个组件位于屏幕高度的 1/3 处,直接使用辅助线比计算固定偏移更稳妥。

拦截屏障createBarrier()则适合处理“多个动态高度组件”的对齐需求。比如一个Text在后面一个Text的下面,但前面的Text可能换行,用top.linkTo(text2.bottom)是不准确的。你应该:

val barrier = createEndBarrier(titleRef, teacherRef) Text( text = "描述信息", modifier = Modifier.constrainAs(descRef) { top.linkTo(barrier) } )

这样,无论titleRefteacherRef哪个更高,描述信息都会跟在较矮元素的边界之后。

8.4 在大型项目中使用 ConstraintLayout 的建议

在团队协作场景中,建议为 ConstraintLayout 的约束规则写简短注释。虽然代码即布局,但多个linkTo堆在一起时,读者很难一眼看出设计意图。输出一段类似下面的注释,能显著提高可维护性:

// 封面在左上角,标题在右,按钮在最右侧,进度条横跨底部

8.5 性能考量

Compose 官方文档指出,在测量阶段,ConstraintLayout 的测量性能优于同级别嵌套的线性布局。因为子组件可以并行测量,且不需要多层递归。但这不意味着你可以在一个页面中滥用它。

实际项目中,列表项的item布局是最值得优化的场景。一个常见做法是:在LazyColumn的 item 中,如果布局关系复杂,使用 ConstraintLayout 而不是多层嵌套。

最后提醒一点:ConstraintLayout 的Modifier.constrainAs内部会记录约束关系,这些对象在重组时会被复用。你不需要为每个引用创建状态,保持引用赋值在ConstraintLayout块内即可。

9. 常见问题再补三个实战细节

9.1 如何让组件在父容器中水平居中

如果组件需要水平居中且垂直位置跟随另一个组件,可以这样写:

Modifier.constrainAs(ref) { start.linkTo(parent.start) end.linkTo(parent.end) width = Dimension.wrapContent top.linkTo(otherRef.bottom, margin = 8.dp) }

核心是同时约束startend,并把宽度设为wrapContent。如果不设置宽度,组件会被拉满整个宽度,视觉上变成全宽,而不是居中。

9.2Dimension.fillToConstraintsDimension.wrapContent的区别

这两个属性是初学者最容易搞混的。

  • Dimension.wrapContent:组件尺寸按内容决定。适合文本、按钮。
  • Dimension.fillToConstraints:组件尺寸根据约束边界拉伸。适合进度条、分割线、需要撑满空间的块。
  • Dimension.preferredWrapContent:优先按内容尺寸,但如果有约束限制,可以压缩。适用于文本过长时自动换行并限制在约束内。

9.3constrainAsModifier顺序的影响

Modifier是有顺序语义的。如果你的代码是:

Modifier .padding(8.dp) .constrainAs(ref) { ... }

和:

Modifier .constrainAs(ref) { ... } .padding(8.dp)

效果完全不同。前者是先让组件内部有 8dp 内边距,然后执行约束布局;后者是先确定约束位置,再给组件加 8dp 内边距,视觉上会影响 constraining 的位置计算。

实际开发中建议把constrainAs放在Modifier链的最前面,这样约束规则优先确定位置,后续修饰符基于位置再添加边距、背景等效果。

10. 结语:什么时候真的该上约束布局

回到开头的问题:ConstraintLayout 在 Compose 中到底是不是必需品?

如果你只写简单页面,ColumnRowBox足够解决 90% 的问题。但当你开始接触真实项目,遇到列表项多样化、布局关系交叉、或者需要精准对齐的设计稿时,ConstraintLayout 会成为你工具箱里最有力的那一把扳手。

它的价值体现在三点:

  • 用约束表达关系,而不是用嵌套模拟关系。
  • 减少无效测量层级,提升复杂布局性能。
  • 代码结构更贴近设计稿,后期改间距、调位置更方便。

如果你正在学习 Jetpack Compose,建议你动手把上面的示例代码运行一遍,然后尝试改一改约束:比如把头像挪到右边、把进度条宽度改成 80%、把收藏按钮改成图标按钮。改完你就能理解约束系统的边界和优势了。

下一步可以继续研究createBarrier()Chain,这两块在处理复杂列表项时非常实用。也可以看看官方文档中的MotionLayout支持,它是基于约束系统实现动画的高级方案。先掌握本文的 API,再逐步往高级功能走,这条路是最稳妥的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 14:16:04

Prisma 跑不起来?3 步定位 Node.js 版本不兼容并修好它

Prisma 跑不起来?3 步定位 Node.js 版本不兼容并修好它 【免费下载链接】orm Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, MongoDB and CockroachDB 项目地址: https://gitcode.com/GitHub_Trending/pr…

作者头像 李华
网站建设 2026/9/4 16:26:23

Claude Code核心词汇与上手实践:从环境配置到报错排查

看到《Show HN:克劳德的核心词汇》这个标题时,我第一反应是:这会不会又是一份命令速查表?仔细想想才发现,这个角度比速查表更接近本质——Claude Code 的上手门槛,本质上是一道词汇门槛。很多人卡在安装阶段…

作者头像 李华
网站建设 2026/9/5 2:38:30

Exo 分布式推理:4 台 Mac 跑出 31.9 t/s 的 235B 集群

Exo 分布式推理:4 台 Mac 跑出 31.9 t/s 的 235B 集群 【免费下载链接】exo Run frontier AI locally. 项目地址: https://gitcode.com/GitHub_Trending/exo8/exo 200B 参数的模型塞不进单台机器,GPU 集群的采购成本又太高。Exo 做的事很简单&…

作者头像 李华
网站建设 2026/9/3 20:38:21

中断与内存屏障:Linux内核并发同步实战解析

最近在调一个网络驱动的收包路径时,被一个“诡异”的问题卡了一整天:中断处理函数里明明已经把 flag 置 1 了,主循环里却一直看不到更新。反复确认代码逻辑没问题,最后才发现是漏了内存屏障(memory barrier&#xff09…

作者头像 李华
网站建设 2026/9/6 10:18:54

用Scratch实现3D恐怖游戏:射线投射与迷宫渲染全解析

在 Scratch 里做一款 3D 恐怖游戏,听起来像是把 3D 建模、图形渲染和关卡设计全部塞进积木编程工具。实际做完后会发现,Scratch 本身虽然是 2D 舞台引擎,但只要理解一种叫“射线投射”的渲染思路,完全可以在不加载任何外部素材的情…

作者头像 李华