Vue 2 响应式基石:Object.defineProperty 原理与实战

先说一个几乎人人都会遇到的现象:Vue 2 项目里,data 中声明好的属性随便改,视图同步刷新;但你运行中给某个对象新塞一个此前没有定义过的属性,页面纹丝不动。网上查到的答案是"用 Vue.set",照着写能跑就行。至于背后为什么这么设计、Vue.set 补了什么、数组方法为什么要重写、computed 和 watch 为什么能生效,这一长串问题,其实都指向同一个老 API——Object.defineProperty。

这篇文章就来彻底回顾 Vue 中的 Object.defineProperty 方法的使用。我会从 API 本身的参数细节讲起,再到 Vue 2 如何利用它实现响应式、它有哪些边界局限,最后对比 Vue 3 的 Proxy 方案。适合刚学完 Vue 基础、想搞懂响应式原理的读者,也适合准备面试、或者正在维护 Vue 2 老项目的同学。看完之后,你会对"数据变了视图自动更新"这句话有完全不同的理解。

1. 为什么 Vue 2 会选中一个 2009 年的老 API 当响应式基石

1.1 响应式到底在解决什么问题

在没有框架的年代,数据变化要带动页面更新,是纯手工活:你改一个变量,就要自己去查对应的 DOM 节点,然后手动更新。业务简单时还能接受,一旦页面复杂起来,漏更新、重复更新、更新顺序不对的 bug 会层出不穷。

响应式想做的事情,是把"数据变化 -> 视图更新"这条链路自动化。难点在于:JavaScript 语言本身并没有提供"属性被读取时执行回调"或"属性被修改时执行回调"这样一等公民的监听机制。所以框架必须想一个办法,在读写对象属性的瞬间做点额外的事情——读的时候记录一下谁在用这个数据,写的时候把"数据变了"广播出去。

Object.defineProperty 恰好提供了这种能力。它允许你精确定义对象某个属性的行为,甚至可以用 getter/setter 完全接管属性的读取和赋值逻辑。Vue 2 就是看中了这一点:不侵入业务代码,不需要你手动调用什么 update 方法,只要你用普通方式读写对象属性,底层就会自动触发拦截。

1.2 为什么不是轮询、不是代理,而是属性劫持

有人可能会问:能不能用定时轮询,每隔一段时间扫描一遍数据有没有变化?能,但效率太差,而且无法精确知道变化的粒度,只能做整棵数据树的 diff。AngularJS 时期的脏检查就是类似思路,它的问题在于需要显式触发检查,性能开销也不小。

能不能彻底用 Proxy 代理?理论上 Proxy 能覆盖更多场景,当时也已经是 ES6 标准的一部分,但 Vue 2 发布时需要考虑浏览器兼容性,Proxy 无法在低版本浏览器上 polyfill,而 Object.defineProperty 从 ES5 开始就存在,IE9 都能用。对一个面向大众开发者的框架来说,兼容性直接决定了能走到多远。

另外,属性劫持这种方案还有一个好处:你读属性时监听,写属性时触发,两者都能精确到某个 key。相比脏检查需要比对整份数据,粒度更细,代码实现也更直观。所以 Vue 2 选择 Object.defineProperty 不是拍脑袋,而是当时条件下性能和兼容性的最优解。

1.3 先建立整体链路:data 到视图之间发生了什么

在深入 API 之前,先把 Vue 2 响应式的整体链路画在脑子里,后面所有代码细节都是在给这条链路补零件。

  • 初始化阶段:Vue 拿到 data,递归遍历所有属性,对每个属性调用 Object.defineProperty,为它定义 getter 和 setter。
  • 渲染阶段:render 函数执行时,会读取 data 上的属性,触发 getter,这时把当前正在渲染的 Watcher 收集进依赖列表。
  • 更新阶段:你修改某个属性,触发 setter,setter 会通知依赖列表里所有 Watcher 更新。
  • 视图变化:Watcher 重新执行 render,生成新虚拟 DOM,最终由 diff 算法更新真实 DOM。

注意这里有两个关键环节:getter 里做依赖收集,setter 里做派发更新。这两件事是理解整个响应式系统的重点,后面我专门讲。接下来先把 Object.defineProperty 本身讲透,因为它是一切的地基。

2. Object.defineProperty 参数拆解:value、getter 与三个默认 false 的坑

2.1 基本语法与最简单的用例

Object.defineProperty 的语法非常简洁,三个参数:目标对象、属性名、描述符对象。描述符对象决定了这个属性的行为。

const person = {}; Object.defineProperty(person, 'name', { value: '小A', writable: false, enumerable: true, configurable: true }); console.log(person.name); // 小A person.name = '小B'; console.log(person.name); // 小A,因为 writable 为 false,赋值被静默忽略

这个例子展示了最基本的写法。value 表示属性值,writable 控制能否被赋值修改。如果你希望属性可枚举、可配置、可写,就得把对应开关全部显式打开,否则会掉进后面的默认值陷阱。

这里我可以先给 Vue 用一句话:Vue 内部几乎不用 value 型描述符,它用的是 getter/setter 型,因为只有在 getter/setter 里才能插入"收集依赖"和"派发更新"的额外逻辑。所以你去看 Vue 源码,几乎不会见到{ value: xxx }这种写法,清一色都是 get/set。

2.2 数据描述符和访问器描述符不能混用

描述符分两类:数据描述符和访问器描述符。

数据描述符包含 value、writable、enumerable、configurable;访问器描述符包含 get、set、enumerable、configurable。两类都可以控制 enumerable 和 configurable,但 value/writable 和 get/set 不能同时出现,否则直接抛异常:

const obj = {}; Object.defineProperty(obj, 'name', { get() { return 'x'; }, value: 'y' // TypeError: Invalid property descriptor });

这个限制背后的逻辑很容易理解:属性要么是"直接存一个值",要么是"由函数动态计算"。前者用 value,后者用 get/set,两套机制并存会让行为变得无法预测。实际使用中,如果你想让属性具备自定义的读写行为,你的描述符里就只写 get 和 set,不写 value;Vue 的响应式属性全部走这条路。

2.3 三个默认 false:和字面量定义行为完全不一样

这是新手最容易忽略的坑。用字面量定义对象属性时,三个开关默认都是 true;用 Object.defineProperty 新增属性时,默认全是 false:

const obj = {}; Object.defineProperty(obj, 'a', { value: 1 }); for (const key in obj) { console.log(key); // 什么都不会打印,enumerable 默认 false } obj.a = 2; console.log(obj.a); // 1,writable 默认 false delete obj.a; console.log(obj.a); // 1,configurable 默认 false

很多人第一次用 Object.defineProperty 添加属性后,发现 for-in、Object.keys 都看不到,赋值也改不动,就是这个原因。Vue 源码里凡是调用 defineReactive 的地方都会显式声明 enumerable: true、configurable: true,正是因为不能依赖默认值。这个细节也提醒我们:如果自己封装响应式工具,描述符的每个开关都要写清楚,别省。

3. 从 API 到响应式:getter/setter 劫持的关键一步

3.1 一个最简的 defineReactive 实现

理解了描述符的基本规则,下面用 Object.defineProperty 实现一个最简化的 Vue 响应式属性定义函数。Vue 源码里对应的函数名就叫 defineReactive,我按它的核心逻辑简化来看:

function defineReactive(obj, key, val) { Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { console.log(`读取了 ${key}`); return val; }, set(newVal) { if (newVal === val) { return; } console.log(`修改了 ${key}`); val = newVal; } }); } const data = { name: '小A' }; // 初始化时遍历 data,对每个 key 调用 defineReactive Object.keys(data).forEach((key) => { defineReactive(data, key, data[key]); }); data.name; // 读取了 name data.name = '小B'; // 修改了 name data.name; // 读取了 name,返回 小B

到这里,你已经能拦截属性的读写动作了。虽然还没做任何有用的"通知",但从 API 层面讲,一个属性已经从"被直接存储"变成了"被函数接管"。这是 Vue 响应式的地基:读和写这两条路径都被你控制住了,之后想在 getter 里加依赖收集、在 setter 里加通知,都是顺手的事。

3.2 闭包里保存的值:为什么返回 obj[key] 会死循环

很多同学第一次模仿这段代码时会写错,最常见的问题是 getter 里面直接返回 obj[key],setter 直接写 obj[key] = newVal,然后发现浏览器直接爆栈。原因很简单:getter 里访问 obj[key] 会再次触发 getter,形成无限递归;setter 里赋值 obj[key] 也一样,会再次触发 setter。

正确做法是把真实值保存在闭包里。function defineReactive 的参数 val 就是这个闭包变量,getter 返回 val、setter 修改 val,完全不经过对象本身。对象上挂的只是一个"性格偏执"的属性,它的 getter/setter 被完全重写,真实值反而藏在外层作用域里。这是 Object.defineProperty 实现响应式时最核心、也最容易出错的细节。

3.3 从"能读能写"到"能通知":defineProperty 只是开始

上面的 defineReactive 只能打印日志,离真正的响应式还差两步。Vue 源码里这个函数的完整逻辑要比我的简化版本复杂不少,主要体现在三处:

  • 如果 val 本身是对象,需要递归调用 Observer,保证深层属性也被劫持。
  • 每个 key 都对应一个 Dep 实例,getter 里调用 dep.depend() 收集依赖,setter 里调用 dep.notify() 通知更新。
  • 数组类型需要走单独的数组方法拦截逻辑,因为 defineProperty 对数组下标的监听能力有限。

字符串打印那两步,换成 dep 的收集与通知,响应式闭环就通了。所以我说 defineProperty 只是开始,它提供了"拦截读写"的能力,而 Dep 和 Watcher 提供了"谁在用、通知谁"的逻辑。这两套系统在 Vue 里是严格配合的。

4. 边界与教训:新增属性、数组下标、深层递归的真相

4.1 新增属性不响应:Vue.set 到底补了什么

回到开头的现象。初始化时 Vue 会遍历 data 已有的 key 并对它们做 defineReactive,后续你往对象上新增一个 key 时,这个过程不会执行,于是新属性没有 getter/setter,自然不会出现在依赖系统里。这就是为什么 this.user.age = 20 页面不更新。

Vue.set 的解决思路是:当目标对象是普通对象时,它手动调用 defineReactive 给新 key 补上 getter/setter,然后手动触发一次依赖通知;当目标是数组时,它调用 splice 相关逻辑。本质上 Vue.set 就是把你初始化阶段的劫持步骤,在运行期手动执行了一遍。

// 页面不更新 this.user.age = 20; // 页面更新 this.$set(this.user, 'age', 20);

所以解决方案的本质不是"调一个神奇 API",而是"补上 Object.defineProperty 应该做但没有机会做的事"。如果你在做自己的响应式封装,应该把"初始化劫持"和"动态添加属性后重新劫持"都封装好,避免调用者踩同样的坑。

4.2 数组下标为什么没有被逐项劫持

理论上,数组下标也是对象的 key,Vue 初始化时完全可以用 Object.defineProperty 对索引 0、1、2……逐个劫持。为什么 Vue 2 没这么做?

一是性能负担:数组元素可能非常多,而且经常被整体替换或增删,对每个下标做 defineProperty,开销远超收益。二是实际开发中,大家修改数组的主流方式是 push、pop、splice、shift、unshift、sort、reverse 这些方法,而不是直接改下标。针对方法做拦截,比针对下标做拦截覆盖更广、维护成本更低。

于是 Vue 2 采用了另一种策略:用拦截器包住数组原型上的 7 个变更方法,在调用这些方法时执行原方法,然后再做通知。这也是为什么 Vue 2 直接 this.list[0] = 'x' 不响应,而 this.list.splice(0, 1, 'x') 能响应。

4.3 深层对象递归与 Object.freeze 的提速思路

Object.defineProperty 只能处理一层属性。如果 data 里的某个值仍然是对象,Vue 会递归地把所有嵌套属性全部劫持。递归的好处是一旦初始化完成,深层数据修改也能精确触发响应;坏处是初始化开销随着数据规模线性增长,数据很深很宽时,首屏会卡一下。

顺着这个机制,Vue 2 留下了一个很实用的优化点:对于完全不需要响应式的数据,可以用 Object.freeze 冻结对象。Vue 初始化时会检查这个对象是否被冻结,如果已被冻结就跳过 defineProperty 流程,直接按普通值处理。比如接口返回的大字典表、枚举映射、静态配置,用 Object.freeze 能明显降低初始化耗时。

不过要注意,冻结后的对象无法被任何运行时逻辑修改,包括 Vue.set。如果你既要部分响应式、又要优化性能,应该把需要响应的字段和不需要响应的静态数据拆成两个对象分别处理。

5. 另一半系统:getter 收集依赖,setter 派发更新

5.1 依赖为什么要在 getter 里收集,而不是 setter 里

这是 Vue 响应式设计里一个很反直觉的点。直觉上,数据变了才知道谁需要更新,所以依赖应该发生在 setter;但如果 setter 时才去查谁在用,你只能通过全局扫描或手动注册,无法精确知道每个属性和哪些渲染函数有关。

换到 getter 就不同了。渲染函数执行时一定会读取它依赖的 data 属性,读取必然触发 getter,此时显然"当前正在执行的这段代码"就是这个属性的消费者。把这段代码封装成一个 Watcher,在 getter 里把它登记到当前属性的依赖列表,就能做到精确一对一。这也是"读时收集、写时通知"的由来:读的时候把谁在用的关系记下来,写的时候才找得准通知谁。

5.2 一个微型 Dep + Watcher 的完整闭环

我把上一节的思路用代码表达出来。先写 Dep,它负责管理某个属性的一堆订阅者:

class Dep { constructor() { this.subs = []; } depend() { if (Dep.target) { this.subs.push(Dep.target); } } notify() { this.subs.forEach((watcher) => watcher.update()); } } Dep.target = null;

再写一个极简 Watcher,它代表"一段需要在数据变化时重新执行的代码"。为了让依赖收集时知道"当前正在执行的 Watcher 是谁",get() 执行前把自身挂到 Dep.target 上,执行完再清掉:

class Watcher { constructor(getter) { this.getter = getter; this.get(); } get() { Dep.target = this; this.getter(); Dep.target = null; } update() { this.getter(); } }

接着把 defineReactive 升级成带 Dep 的版本:

function defineReactive(obj, key, val) { const dep = new Dep(); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { dep.depend(); return val; }, set(newVal) { if (newVal === val) { return; } val = newVal; dep.notify(); } }); }

执行一下完整流程:

const data = { info: { name: '小A' } }; Object.keys(data).forEach((key) => defineReactive(data, key, data[key])); let viewCount = 0; function render() { viewCount++; console.log(data.info.name, `渲染了 ${viewCount} 次`); } new Watcher(render); data.info.name = '小B'; // 控制台输出: // 小A 渲染了 1 次 // 小B 渲染了 2 次

这是整个响应式最核心的微型闭环:首次 render 通过 getter 收集依赖,修改属性后 setter 通知 Watcher,Watcher 重新执行 render。Vue 2 源码里的 Dep、Watcher、defineReactive 就是按照这个逻辑展开的,只是多了队列调度、虚拟 DOM 等处理。

5.3 数据变化到视图更新的时序与异步更新

有了微型实现,再把完整时序梳理一遍,你就能把 Vue 的初始化、渲染、更新三件事串起来:

  1. Vue 初始化 data,递归调用 defineReactive,为每个 key 创建 Dep。
  2. 创建渲染 Watcher,执行 render,render 读取 data 属性触发 getter,依赖收集到对应 Dep。
  3. 用户修改属性,触发 setter,Dep.notify 被调用。
  4. Watcher.update 触发,重新执行 render,生成新虚拟 DOM。
  5. diff 算法对比新旧虚拟 DOM,更新真实 DOM。

实际 Vue 2 中还有一步关键优化:多个数据变化可能在同一轮事件循环里发生,如果每次都立刻重新渲染,性能会很差。所以 Watcher.update 会把更新任务推入队列,等本轮同步代码跑完再统一执行,配合 nextTick 把 DOM 更新延迟到下一次事件循环。理解了这个机制,你就能明白为什么修改多个响应式属性后,页面只会更新一次。

6. Vue 3 换成 Proxy 之后,这套老 API 的价值还在哪

6.1 Proxy 解决的问题:新增属性、数组、懒代理

Vue 3 用 Proxy 重写了响应式系统,核心改进可以总结成三个词:全量、自动、懒。

  • 全量:Proxy 拦截的是整个对象,新增、删除属性都天然被覆盖,不再需要 Vue.set 这样的补丁 API。
  • 自动:数组下标赋值、length 变化也能被监听,不需要重写数组方法。
  • 懒:Proxy 可以在 getter 里才代理子对象,数据没被访问到的深层属性不初始化,性能开销大幅下降。

用表格对比会更直观:

对比维度Vue 2 + Object.definePropertyVue 3 + Proxy
新增属性不响应,需 $set天然响应
删除属性无法检测天然响应
数组下标赋值不响应响应
数组方法需要重写 7 个方法天然拦截
初始化开销递归全量劫持懒代理,按需劫持
浏览器兼容IE9+不支持老 IE

这套升级带来的开发体验提升是明显的:少记了很多"必须用 $set 才能更新"的规则,代码心智负担一下子降下来。但代价也很直接,Proxy 无法在低版本浏览器上 polyfill,所以 Vue 3 的浏览器底线直接拉到现代浏览器。

6.2 还在维护 Vue 2 项目?这些响应式边界值得注意

如果你手上还有 Vue 2 项目,下面几条是我在实际维护中总结出来的实操建议,可以帮你少踩坑:

  • 接口返回的数据如果包含动态字段,尽量在后端或数据处理层把最终会出现的 key 全部预设出来,哪怕初始值是 null 或空字符串。你预设了 key,Vue 就会在初始化时完成劫持。
  • 运行期确实要给对象加新字段时,统一用 this.$set,不要直接 this.obj.newKey = value。这是最省事也是最保险的写法。
  • 数组操作优先用 push、pop、splice 等方法,避免直接按下标赋值。必要时可以用 splice 代替下标赋值。
  • 不需要响应式的静态数据用 Object.freeze 包一层,尤其是大量只读字典数据,能明显减少初始化期间 defineProperty 的递归次数。
  • 排查"数据变了视图不更新"的 bug 时,先问自己三个问题:这个 key 初始化时存在吗?我用的是赋值还是方法调用?有没有被 Object.freeze 冻结?基本都能定位到根因。

6.3 换到 Vue 3 不等于可以忘掉 defineProperty

即便 Vue 3 全面转向 Proxy,Object.defineProperty 在 JavaScript 生态里依然没有过时。Vue 3 的 reactive 实现里,某些需要精确控制属性定义的场景仍会用到它;很多工具库、表单框架、状态管理库也在用它实现私有属性或不可变约束。更别说面试、源码阅读、写自己的响应式 demo 时,你总归要回来把这段逻辑弄明白。

所以我的建议是:不要因为 Vue 3 存在就跳过 Object.defineProperty 的学习,反而是先吃透这套老方案,再看 Proxy 方案,你才能真正理解 Vue 3 做对了哪些事、牺牲了什么。响应式编程的底层思路是通用的,区别只在于拦截手段。

最后分享一个个人体会。早年在 Vue 2 项目里排查一个"tab 切换后表格数据不更新"的 bug,折腾了一下午,最后发现是后端返回的数据里少了一个字段,前端初始化后动态塞进去,导致没有走响应式链路。当时如果早一点理解 Object.defineProperty 的劫持时机,这个问题十五分钟就能定位。从那之后我给自己定了个规矩:凡是用了框架,一定要先把它最底层的魔法弄清楚——不是自己实现一遍,而是至少要在头脑里把它的运行链路走通。Vue 的这条链路,起点就是 Object.defineProperty。

相关新闻

SwarmUI 深度解析:基于 .NET 的开源 AI 图像生成 Web UI 架构与部署实践

SwarmUI 深度解析:基于 .NET 的开源 AI 图像生成 Web UI 架构与部署实践

1. 为什么一个 .NET 开发者会盯上 AI 图像生成的 Web 界面第一次接触 SwarmUI 是在一个本地图像生成的小圈子里,当时群里有人丢了一句"这玩意儿是 .NET 写的,跑起来比想象中轻",我第一反应是有点意外。因为过去两年,围绕…

2026/10/21 7:17:54 阅读更多 →
车载AI长期记忆架构:从Generative Agents到智能座舱实践

车载AI长期记忆架构:从Generative Agents到智能座舱实践

1. 从一个被忽略的体验断层说起车载语音助手这几年进步很快,唤醒率、识别准确率、指令覆盖度都在肉眼可见地提升。你坐进车里说“我有点冷”,它能调空调;说“导航去公司”,它能规划路线;说“放首周杰伦”,它…

2026/10/21 7:17:54 阅读更多 →
Python无锁编程深度解析:原子操作与安全免锁实战

Python无锁编程深度解析:原子操作与安全免锁实战

明白,我将严格按照规范输出一篇围绕《Python 无锁编程深度解析:何时可以安全地不用锁?原子操作全指南》的完整博文。

2026/10/22 9:57:16 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/22 10:18:38 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/21 9:32:40 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/22 9:44:47 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/22 10:42:00 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/22 10:40:08 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/22 10:56:37 阅读更多 →

日新闻

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/22 1:47:45 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/21 21:32:28 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/22 5:12:39 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/21 21:50:56 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/21 21:51:55 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/21 16:24:48 阅读更多 →