Vue3 深坑:v-model 对象字段不更新?Proxy+computed 封装 useVModel
1. 从一个真实翻车现场说起1.1 表面正常实则断链的 v-model先还原我当时在内部后台项目里遇到的一个诡异问题。父组件维护了一个表单对象const form ref({ user: { name: 张三, age: 28 }, tags: [前端, Vue3] })然后把它传给子组件UserForm v-modelform /子组件内部按常规姿势取出双向绑定对象const model useVModel(props, modelValue, emit) // 用户改了用户名 model.user.name 李四执行完这一步父组件页面上“当前用户名”那块区域纹丝不动。我第一时间排查了事件名、props 命名、组件嵌套层级全部正常最后打断点才发现update:modelValue压根没有触发。如果你也踩过这个坑多半和我一样把v-model理解成了“真正的双向绑定”。但它在 Vue 3 里只是一层语法糖UserForm v-modelform / !-- 等价于 -- UserForm :model-valueform update:model-valueform $event /所谓双向绑定本质是单向数据流加事件回写。子组件必须通过emit(update:modelValue, newValue)把“希望父组件换成的值”交出去父组件收到后修改自己的变量闭环才算成立。useVModel这类工具的正确性完全建立在“每次变化都 emit”这条契约上。1.2 断链具体发生在哪一步VueUse 里useVModel的极简实现本质上就是一个 computedimport { computed } from vue function useVModel(props, key, emit) { return computed({ get: () props[key], set: (value) emit(update:${key}, value) }) }注意computed 的 setter 只在“给model本身赋值”时触发比如model.value newObj。而执行model.user.name 李四时真正发生的是下面几步读取model.value拿到的就是props.modelValue从里面取出user这个子对象给user.name这个字段赋值。整条操作里根本没有“给 model 赋值”的动作自然进不了 computed 的 setter。所以update:modelValue永远不触发。那为什么很多人在实际项目里感觉“好像没问题”这里有个迷惑点如果父组件的form是用ref包裹的Vue 的ref会对对象做深度响应式代理子组件model.user.name 李四实际上改的是父组件同一份底层数据的深层字段父组件的渲染依赖会被自动触发页面看起来是同步的。但这条路径完全绕过了 v-model 的事件契约属于脆弱的“伪同步”。一旦父组件的数据不是标准响应式对象——比如接口返回后直接赋值、从某个非深度响应式的数据结构里取出来的、或者父组件依赖update:modelValue做埋点、持久化等额外业务——断链立刻现形。1.3 守住 emit 的价值不止于“能用”我后来才发现坚持走事件链路还有个被很多人忽略的好处它可以让你在组件树的每一层都保持“可控”。子组件再往下一层传孙组件孙组件改了字段每一层都通过 emit 通知上层时整条链路是可靠且可追踪的。父组件想监听update:modelValue事件做额外事情也会因为事件真实发生而生效。等到项目里出现“子组件改了嵌套字段父组件不更新”这类问题时再回头给所有组件补 emit 逻辑成本可比一开始就设计好高得多。这也是我后来决定自己封装一个真正好用的useVModel的直接原因。2. 方案选型为什么偏偏是 computed Proxy2.1 先盘点那些“看起来能行”的替代方案在决定用 Proxy 之前我把当时网上常见的几种思路都试了一遍各有各的麻烦。整理成一张表方便对照方案优点致命缺点deep watch 监听 props 对象实现简单Vue 原生支持每次变更都要递归遍历整棵对象对象一深一大多次修改就浪费watch 回调里再 emit 容易引发渲染震荡手动逐字段 emit逻辑最直观表单有多少字段就要写多少个 handler换一个对象结构又要重写完全不具备通用性defineModel 宏官方写法模板里简洁本质还是 props emit 的封装对象子字段的深度变更同样存在类似断链风险深层对象方案仍要自己补computed Proxy精确拦截每次嵌套读写O(1) 级别成本实现细节多递归代理、缓存、边界情况都要自己处理table 里说的 deep watch 方案可能很多人实际用过它的伪代码大概长这样watch(() props.modelValue, (val) { emit(update:modelValue, clone(val)) }, { deep: true })问题在于 deep watch 是“全量快照差异对比”对象每深一层每次修改的遍历成本都会成倍增加。我自己就亲眼见过一个三层嵌套配置对象子组件输入框敲一个字watch 要把整棵对象重新读一遍控制台性能面板一拉全是红条。更麻烦的是deep watch 在初始化阶段也会触发一次如果你在 watcher 里做了 emit得额外处理“首次不 emit”的判断。2.2 computed 管协议Proxy 管感知我要的核心能力有两个对外仍然是一个可读写的 ref模板里能自动解包赋值时能触发事件对内能感知到“任何一层字段的变化”包括user.name这种深层修改。这两个能力正好被拆给两个 Vue 原生的东西computed负责值的存取协议。model.value xxx被它拦截变成emit(update:xxx, xxx)读取model.value时返回当前 props 里对应的值。这是 v-model 语法糖要求的接口。Proxy负责深层变化的感知。对象属性的读取会被gettrap 拦下写入会被settrap 拦下连删除属性都能被deletePropertytrap 捕获。我们只要在陷阱里补一个“通知上层”的方法深层修改就能反推回 computed 的事件链路。这就像给 v-model 装了一根根感应神经computed 是大脑Proxy 是分布在对象每一层的神经末梢。你在最深的子字段上动一下神经末梢立刻向大脑汇报大脑向父组件发出事件。2.3 defineModel 也没绕开这个本质Vue 3.4 之后官方推荐的defineModel()确实让很多业务代码瘦了身// 子组件 const model defineModel() model.value hello // 正常 emit但如果你写的是model.user.name 李四本质上是“先读 model.value 取到对象再修改对象里的字段”和前面分析的一样整个流程没有触发model.value xxx。所以官方宏加深层对象子字段同样存在断链风险。社区里对这种问题的主流做法仍然是配合 deep watch 或者手写一个类似本文的 Proxy 封装。这也说明这条坑是 Vue 组件模型本身的结构性特点不是哪个库偷懒。3. 手写实现从能用到达标3.1 第一版只拦最外层的 set最简单的思路是computed 的 getter 里不要直接返回 props 里的原始对象而是返回一个 Proxy 包装后的对象并且在settrap 里补上 emit。第一次实现长这样export function useVModel(props, key modelValue, emit) { return computed({ get() { return new Proxy(props[key], { set(target, property, value, receiver) { const result Reflect.set(target, property, value, receiver) if (result) { emit(update:${key}, { ...target }) } return result } }) }, set(value) { emit(update:${key}, value) } }) }这一版能解决“修改一层字段”的场景比如model.user { name: 李四 }。但三个致命问题很快暴露只拦一层model.user.name 李四里的model.user返回的是原始user对象name的赋值发生在原始对象上完全不经过 Proxy引用不稳定每次get都会new一个 Proxymodel.user model.user结果是 false模板里和组件内多处引用同一个字段时代理状态难以保持一致数组变化会连环触发push可能同时触发 index 和 length 的两次 set事件会被污染。3.2 递归代理 WeakMap 缓存解决深层和引用稳定性要解决“深层拦截”有一个朴素但好用的思路在gettrap 里如果读到的值还是一个对象就把这个对象也包一层 Proxy再返回出去。这样代理是按需创建的——只有真正访问到某个子对象时才会给它套壳而不是一次性把整棵树全部代理完性能上很划算。为了避免同一个对象被重复包成多个 Proxy需要用 WeakMap 做缓存以原始对象为 key存它对应的 Proxyfunction wrap(target, onChange) { if (target null || typeof target ! object) { return target } if (proxyCache.has(target)) { return proxyCache.get(target) } const proxy new Proxy(target, { get(t, key, receiver) { const value Reflect.get(t, key, receiver) if (typeof value function) { return value.bind(t) } return wrap(value, onChange) }, set(t, key, value, receiver) { const result Reflect.set(t, key, value, receiver) if (result) { onChange() } return result }, deleteProperty(t, key) { const result Reflect.deleteProperty(t, key) if (result) { onChange() } return result } }) proxyCache.set(target, proxy) return proxy }WeakMap 为什么选它而不是普通 Map因为 WeakMap 的 key 是弱引用不阻碍原始对象被垃圾回收。当父组件整体替换掉modelValue旧的原始对象不再被引用时缓存条目能自动被回收避免内存泄漏。引用稳定性也可以顺便说明一下因为缓存存在model.user model.user永远是 true模板里多个地方读同一个子对象拿到的都是同一个代理状态一致不会出现“两个代理各改各的”这种诡异现象。3.3 两个容易翻车的边界函数 this 与 Symbol大括号里的第一个坑是方法。假如对象里有个getFullName方法它内部用了this.nameconst user { name: 张三, getFullName() { return this.name } }直接返回Reflect.get的结果调用proxy.getFullName()时this指向的是 Proxy而不是原始对象。虽然大多数情况下this.name会通过 Proxy 的 get trap 再走一遍绕一圈也能拿到值但一旦方法内部做了“引用相等判断”或者把this传给别的库就可能因为 Proxy 身份导致意外。保险做法是在 get trap 里把函数绑定到原始对象上if (typeof value function) { return value.bind(t) }这样this永远指向原始对象行为最接近“没有代理”时的状态。第二个坑是 Symbol。模板里用v-for遍历数组、或者对代理对象做JSON.stringify时Vue/JS 内部会读取Symbol.iterator、Symbol.toPrimitive、Symbol.toStringTag这类符号属性。如果我们的 get trap 把 Symbol 也拿去递归包装轻则返回一个奇怪对象重则直接抛错。最稳妥的写法是一开始就把符号属性分流出去if (typeof key symbol) { return Reflect.get(t, key, receiver) }同理set和deleteProperty里也应该只对普通字符串 key 触发onChange()符号属性的写入比如自定义迭代器不该污染业务事件。3.4 emit 时传浅拷贝别传原对象再想一个问题嵌套字段变了emit 出去的值应该是原始对象本身还是一个新对象如果传原始对象父组件执行form $event时拿到的还是同一个引用。虽然内容已经变了但某些场景下父组件依赖“引用变化”来判断更新特别是数据源本身不是深度响应式代理时很容易出现“值明明改了页面还是旧的”这种尴尬。更稳妥的做法是传一个浅拷贝让父组件明确感知到“这是一个新值”function cloneValue(value) { if (Array.isArray(value)) { return value.slice() } if (value typeof value object) { return { ...value } } return value }注意数组不能直接用对象展开[...arr]和arr.slice()才是正解。浅拷贝只复制顶层嵌套对象还是引用传递这个不用改因为父组件真正需要感知的只是“整体值变了”内部结构我们已经在原地更新过了。3.5 最终版本带批量更新的完整代码把上面的设计整合起来再补一个批量更新的选项。嵌套字段可能在一次操作里改多次比如数组push会同时改 length 和索引如果每个 set 都同步 emit父组件会收到一堆重复事件。默认用nextTick做节流同一轮事件循环内只发一次import { computed, nextTick } from vue export function useVModel(props, key modelValue, emit, options {}) { const eventName options.eventName || update:${key} const cloneOnEmit options.cloneOnEmit ! false const batch options.batch ! false const proxyCache new WeakMap() const proxySet new WeakSet() let pending false function emitChange() { if (batch) { if (pending) return pending true nextTick(() { pending false emit(eventName, cloneOnEmit ? cloneValue(props[key]) : props[key]) }) } else { emit(eventName, cloneOnEmit ? cloneValue(props[key]) : props[key]) } } function cloneValue(value) { if (Array.isArray(value)) return value.slice() if (value typeof value object) return { ...value } return value } function wrap(target) { if (target null || typeof target ! object) return target if (proxySet.has(target)) return target if (proxyCache.has(target)) return proxyCache.get(target) const proxy new Proxy(target, { get(t, key, receiver) { if (typeof key symbol) { return Reflect.get(t, key, receiver) } const value Reflect.get(t, key, receiver) if (typeof value function) { return value.bind(t) } return wrap(value) }, set(t, key, value, receiver) { const result Reflect.set(t, key, value, receiver) if (result typeof key ! symbol) { emitChange() } return result }, deleteProperty(t, key) { const result Reflect.deleteProperty(t, key) if (result typeof key ! symbol) { emitChange() } return result } }) proxyCache.set(target, proxy) proxySet.add(proxy) return proxy } return computed({ get() { return wrap(props[key]) }, set(value) { emit(eventName, value) } }) }batch默认开启的好处是数组push、批量字段赋值这类操作只在同一事件循环里触发一次update:modelValue父组件不会收到连环轰炸。如果你确实需要同步拿到事件结果把batch: false传入即可。4. 实战接入表单、数组、多 v-model4.1 一个完整的表单组件改造回到开头那个后台项目父组件代码几乎不用改script setup langts import { ref } from vue import UserForm from ./UserForm.vue const form ref({ user: { name: 张三, age: 28 }, tags: [前端, Vue3] }) /script template UserForm v-modelform / p当前用户名{{ form.user.name }}/p /template子组件核心代码改成script setup langts import { useVModel } from ./useVModel const props defineProps{ modelValue: { user: { name: string; age: number }; tags: string[] } }() const emit defineEmits{ (e: update:modelValue, value: typeof props.modelValue): void }() const model useVModel(props, modelValue, emit) /script template div input v-modelmodel.user.name placeholder用户名 / input v-model.numbermodel.user.age typenumber placeholder年龄 / /div /templatemodel在模板里会被自动解包所以v-modelmodel.user.name实际编译出来的代码是:valuemodel.user.name和inputmodel.user.name $event.target.value。这个赋值动作会先读取model.value得到最外层 Proxy然后逐层走 get trap最后在name这个字段的 set trap 里触发 emit。整条链路完全不需要手写 onChange。4.2 数组字段的注意事项数组是这套方案里最容易踩坑的地方但我们的实现已经顺手处理了大部分// 新增一条 tag model.tags.push(VueUse) // 删除一条 tag model.tags.splice(0, 1) // 修改指定下标 model.tags[1] Vue3push底层会设置数组的新索引和 length 两个属性两个 set trap 都会触发但因为默认开了批量更新一轮事件循环内只会 emit 一次。splice涉及删除和移位会触发 set 和 deleteProperty同样被批量逻辑收敛。还有一个小建议表格里用v-for渲染可编辑数组时不要用 index 作 key否则输入框和数组项错位后编辑体验会非常折磨。尽量给每项一个稳定的唯一 id用v-modelmodel.tags[index].value这种形式绑定。4.3 多参数 v-model 的封装Vue 3 支持给同一个组件绑定多个 v-model比如富文本编辑器经常有title和content两个字段。我们的 hook 天然支持这种场景const title useVModel(props, title, emit) const content useVModel(props, content, emit)父组件侧这样写就行Editor v-model:titledoc.title v-model:contentdoc.content /每个 key 对应独立的事件名update:title、update:content互不干扰。这套逻辑是从 VueUse 的接口风格里继承过来的团队里有人用过 VueUse 的话几乎零学习成本。4.4 快速验证更新链路没时间搭完整测试的情况下可以用一个极简模拟把链路验证一遍。核心思路是模拟“父组件收到事件后把值写回 props”这个过程import { ref, nextTick } from vue function setup() { const parentValue ref({ user: { name: Vue } }) // 模拟 props注意这里用普通对象第一次赋值取 parentValue.value const props { modelValue: parentValue.value } const emitted [] const model useVModel(props, modelValue, (_, value) { emitted.push(value) // 模拟父组件拿到新值后回写 props.modelValue value parentValue.value value }) return { model, emitted, parentValue } } const { model, emitted, parentValue } setup() model.user.name Vue3 await nextTick() console.log(parentValue.value.user.name) // Vue3 console.log(emitted.length) // 1 console.log(emitted[0].user.name) // Vue3真实组件测试里用测试库的wrapper.emitted(update:modelValue)也能拿到同样的事件记录。关键是理解这个闭环Proxy 感知变化 → emit 浅拷贝 → 父组件回写 → props 更新 → 新的代理链路建立。5. 常见问题与避坑清单5.1 改了子字段父组件还是旧值这是我问自己最多的问题。排查顺序建议按这几步来先确认父组件用来接v-model的变量是不是ref。如果用的是reactivev-model 的“整体替换”语义和 reactive 的“原地修改”语义天然不匹配Parent 那行form $event实际上把变量重新指向了普通对象响应式就废了。建议对象型 v-model 统一用ref。再确认useVModel的 key 名和模板里的v-model:xxx是否对应。比如写的是v-model:useruseVModel 的 key 就是user事件名是update:user别写成了modelValue。最后检查是不是绕过了代理。如果你在组件里先const copy { ...model.value }再改copy.user.name那改的是普通对象代理链路完全断掉。直接要求在代理对象上原地修改。5.2 Maximum recursive updates exceeded这个报错在 v-model 和 watch 一起用的时候特别容易出现。典型场景是父组件 watch 了form的深度变化收到更新后又回写某个字段触发子组件再 emit形成闭环。我们的批量更新能治标因为它把同一轮事件循环内的多次 emit 合并成一次。但根因还是出在业务逻辑上建议在“监听 v-model 更新后再修改同一对象”的代码里加一个脏检查或比较避免循环回写。5.3 展开运算符会“脱掉”代理这是个非常隐蔽的坑。model.value是一个 Proxy但{ ...model.value }会读取所有自有属性并组装出一个全新的普通对象这个对象的字段没有任何代理。如果你先展开再赋值新对象变更不 emit如果你直接model.value { ...model.value }那走的是 computed setter整体替换又是正常的。所以记住一条代理对象在形态上跟普通对象很像但只有保持引用走代理深层更新才有效。需要整体替换时优先用model.value newObj不要既想改内部又想用新对象。5.4 和 VueUse deep 选项怎么选VueUse 的useVModel提供了deep: true选项内部实现是深度监听。选择标准很简单对象小、结构固定、只求快用deep: true省心对象大、嵌套深、修改频繁、在意性能用 Proxy 方案更精确因为它只在真正发生变化的属性路径上通知而不是把整棵树遍历一遍。另外proxy 方案对“删除属性”这类操作是显式支持的delete model.tags[0]也能正确触发更新这是 deep watch 那种“整体快照对比”很难直观处理的地方。5.5 控制台调试别直接打印 Proxy最后分享一个调试习惯。console.log(model.value)在浏览器控制台里会显示一层套一层的Proxy结构读起来非常痛苦。我现在的习惯是打印JSON.stringify(model.value, null, 2)可读性立刻上一个台阶。JSON 序列化走的是 toJSON 和符号属性分流逻辑我们已经做了放行处理序列化结果就是干净的数据本体不会递归爆炸。这套实现我用了大半年线上表单、配置面板、批量编辑组件都在跑没再出现过“子字段改了父组件不更新”的诡异问题。如果项目里 v-model 绑定的是字符串、数字这类基础类型直接上defineModel或者 VueUse 默认版就够一旦出现对象型 v-model就换这套 Proxy 方案。最后再留个小技巧排查这类更新链路问题时在 emit 的地方临时打一行console.log(emit, eventName, value)比盯着组件树猜原因快得多定位完再删掉就好。

相关新闻

artcraft项目实战:从零搭建生成艺术与创意编程的轻量级方案

artcraft项目实战:从零搭建生成艺术与创意编程的轻量级方案

1. 从“artcraft”这个名字说起:它到底想解决什么问题第一次看到“artcraft”这个标题,我脑子里蹦出来的第一反应是“艺术”加“手艺”的组合词。直觉告诉我,这不是一个纯技术框架的名字,也不是某个具体产品的品牌名,它…

2026/10/18 10:13:47 阅读更多 →
基于Python与大数据的高校毕业生招聘信息推荐系统设计与实现

基于Python与大数据的高校毕业生招聘信息推荐系统设计与实现

1. 从"手动刷招聘"到"自动筛岗":这个项目的出发点又到一年毕业季,我在某高校的信息中心帮忙做就业数据相关的技术支持。一位负责就业指导的A老师跟我聊起一个很现实的问题:他们学校每年毕业生几千人,就业信息…

2026/10/17 7:50:14 阅读更多 →
从基础语法到工程级实践解析Python错误处理的完整指南

从基础语法到工程级实践解析Python错误处理的完整指南

前言 语法层面的 try 与 except,几乎没人不会写;但把它放到一个真实项目里,麻烦立刻来了:这个函数该不该捕获?捕获了之后是处理掉还是往上抛?日志该在哪一层打?为什么线上日志里满屏是「出错了」…

2026/10/17 7:50:14 阅读更多 →

最新新闻

光伏电站灰损治理:轨道式智能清洁机器人全栈方案

光伏电站灰损治理:轨道式智能清洁机器人全栈方案

做光伏电站的人都知道组件脏了要洗,但真正把“灰尘偷走多少度电”这笔账算清楚的人不多。业内把灰尘遮挡组件导致的发电损失叫“灰损”,听着专业,本质很简单:组件表面被蒙上一层灰,光线进不去,电池片出力直…

2026/10/18 10:49:51 阅读更多 →
掌握Prompt工程核心技巧,让AI用大白话解释技术概念

掌握Prompt工程核心技巧,让AI用大白话解释技术概念

直接让 AI 解释一个技术概念,十次里有八次会得到一篇“字都认识但连起来看不懂”的答案。就拿标题里这个例子来说,一句“用简单语言解释 Transformer”,看起来问得很清楚,大模型也确实给了回应,但你仔细读一遍就会发现…

2026/10/18 10:49:51 阅读更多 →
飞控计算机硬实时操作系统选型与实操指南

飞控计算机硬实时操作系统选型与实操指南

1. 飞控计算机为什么非用硬实时系统不可搞飞控的人都有一个共识:飞控计算机跟咱们平时用的电脑、手机完全是两码事。你手机上的操作系统卡个半秒,顶多就是滑动不跟手;但飞控计算机要是卡个几毫秒,飞机可能就已经偏离姿态了。这就是…

2026/10/18 10:49:51 阅读更多 →
境外市场行情接入实战:WebSocket实时行情与K线聚合全解析

境外市场行情接入实战:WebSocket实时行情与K线聚合全解析

接了个活儿,要把某境外证券市场(按习惯大家都叫它美股)的实时行情和K线数据接进内部系统,做量化分析和实时盯盘展示。这类项目看着不算复杂,但实际操作起来有不少暗坑,尤其是行情源选型、WebSocket消息解析…

2026/10/18 10:49:51 阅读更多 →
香港云服务器访问慢、不稳定?从线路选型到内核调优的优化指南

香港云服务器访问慢、不稳定?从线路选型到内核调优的优化指南

做香港云服务器这几年,我接过的“为什么这么慢、这么不稳定”类咨询,至少几十次起步。大部分朋友第一反应是“机房带宽不够”或者“配置太低”,但实际排查下来,真正的问题往往出在线路选型、网络路径和系统参数上。香港机房本身处…

2026/10/18 10:49:51 阅读更多 →
OpenRT实战:多模态大模型红队安全评估从入门到落地

OpenRT实战:多模态大模型红队安全评估从入门到落地

如果你最近在盯多模态大模型的安全测评,大概已经听过OpenRT这个名字——一个开源的LLM红队框架,主打对多模态模型做系统化的健壮性评估。前阵子我正好把它用在一个客服问答系统的上线前检查里,发现很多人对它的理解还停留在“又一个提权工具”…

2026/10/18 10:48:51 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →