Vue 3.0 全家桶实践:组合式API、Vite、Pinia、Router与TypeScript落地指南
刚开始把项目从 Vue 2 往 Vue 3.0 迁移时我犯过一个特别典型的错误把原本 Options API 那套写法原封不动塞进 setup 里。结果组件膨胀得比以前还快一个 setup 里塞了两百行逻辑同事看我的眼神都不太对劲了。后来我把 Vue 3.0 全家桶完整过了一遍才意识到问题根本不在语法而在于整套思维模型已经变了——从“用选项组织代码”变成了“用逻辑组织代码”。这篇指南就是把我整理后的组合式 API、响应式 API、Vite、Pinia、Router 和 TypeScript 实践一次性梳理清楚。不打算复述官方文档只讲我在真实项目里验证过的用法、踩过的坑和取舍判断。适合两类人准备从 Vue 2 切入 Vue 3 新项目的前端以及已经写了半年多 Vue 3、却总觉得整体架构不顺手、想重新梳理一遍的开发者。1. 组合式API不是写法变化而是组件逻辑的编排方式升级1.1 setup 到底替代了什么生命周期视角的重新理解很多刚从 Vue 2 过来的人都会问一句setup 是不是就是 created 换个名字真不是。setup 在组件实例创建之前执行所以它里面没有 this这是最容易让人不适的一点。原来在 created 里写this.fetchData()的地方现在要写成fetchData()这个差别看起来小但它逼你把逻辑从“依赖 this 上的魔法”转向“依赖显式传入的参数和返回值”。生命周期也跟着变了beforeCreate和created直接由 setup 取代mounted对应onMountedbeforeDestroy改成onBeforeUnmountdestroyed改成onUnmounted。这里我有一张常用的对照表贴给团队新人非常省事Vue 2 选项Vue 3 组合式写法触发时机beforeCreate / createdsetup组件实例创建前props 已解析mountedonMountedDOM 挂载完成后beforeUpdateonBeforeUpdate数据变化后、DOM 更新前updatedonUpdatedDOM 更新完成后beforeDestroyonBeforeUnmount卸载前destroyedonUnmounted卸载后组合式 API 真正的价值是把原来散落在 data、computed、methods、watch 里的同一段业务逻辑聚合到一起。Vue 2 的 Options API 强制按压碎分类组织代码当一个组件里有搜索、筛选、分页、权限判断一堆功能时你要在几个区块之间来回跳。组合式 API 允许你以功能为单位写代码搜索相关的状态和方法放一起分页相关的放一起这才是它存在的意义。1.2 用 composable 替代 mixin 的逻辑复用Vue 2 时代做逻辑复用大部分人第一反应是 mixin。mixin 的问题有三命名冲突、来源不明、难以测试。一个组件同时混入两个定义了data里同名变量的 mixin运行时不报错但行为已经不可预测你看到一个fetchList方法根本不知道它来自哪个 mixin测试的时候还得把 mixin 的依赖链一起关注起来。组合式 API 的答案是 composable本质就是一个普通函数。我举一个最简单的计数器复用export function useCounter(initialValue 0) { const count ref(initialValue) const increment () count.value const decrement () count.value-- return { count, increment, decrement } }组件里这样用script setup langts import { useCounter } from /composables/useCounter const { count, increment } useCounter(10) /script template button clickincrement{{ count }}/button /template这个例子看着简单但它揭示了 composable 的核心一个独立的逻辑单元输入是参数输出是响应式状态和行为方法。没有隐式共享没有命名空间污染测试时也可以直接当做普通函数调用。1.3 拆分 composable 的边界经验用了一段时间组合式 API 后我给自己定了三条拆分原则。第一一个 composable 只解决一件事。useUserInfo只管用户信息的状态和刷新逻辑useTableList只管列表加载、分页参数和筛选条件不要在useUserInfo里顺手做权限判断。第二如果 composable 内部管理多个强相关状态优先返回 ref 而不是 reactive。ref 解构后照样响应式使用者拿到count和increment直接用没有任何心智负担。第三凡是创建了监听器、定时器、DOM 事件的 composable必须在内部提供卸载清理逻辑或者在文档注释里明确提醒调用方调用onUnmounted清理。还有一个小细节很多人会忽略setup 只在组件初始化时执行一次。如果你在 composable 里做异步请求比如const { list, load } useTableList() onMounted(() load()) // 而不是在 composable 内部直接 load()这样做的好处是请求时机由组件把控composable 只负责提供能力和状态便于复用和测试。2. ref、reactive与toRefs响应式API选型的判断依据与踩坑记录2.1 ref 和 reactive 的分工比想象中更微妙Vue 3 的响应式底层是 Proxyref 和 reactive 在运行时都会被包装成 Proxy 对象。它们的设计意图不同reactive 面向对象把整个对象的深层属性都代理掉ref 则是给基础类型一个容器内部包一个{ value: xxx }的结构通过.value读写。但在模板里ref 会自动解包写{{ count }}就行不用写{{ count.value }}。这个“自动解包”是好意也造成了混乱——很多新人一会儿写.value一会儿不写全靠试。我自己在写代码时的处理原则是在script setup里访问就统一带.value在模板里一律不带用习惯消除歧义。关于选型我见过两种主流方案全 ref 流所有状态都用ref声明包括对象。优点是类型统一、解构安全、心智负担小。reactive 流对象用reactive基础类型用ref。优点是语义清晰写起来更像 Vue 2 的 data。对比项refreactive适用数据类型任意包括对象仅对象 / 数组访问方式脚本中.value模板自动解包直接.属性解构响应性解构后仍然响应式解构后丢失需配合 toRefsTypeScript 推导类型明确对复杂类型推导有时偏“宽”我现在的团队统一用“全 ref 为主个别场景 reactive”的方案因为代码审查时只需要关注.value是否写错即可规则简单。2.2 解构丢失响应性的三大高频场景这个坑我至少教过三个新人。第一个场景最经典const state reactive({ count: 0, name: vue }) const { count } state // 丢失响应性想要保住响应性要用toRefsconst { count, name } toRefs(state)第二个场景出现在 props 上。Vue 3 的 props 本身是响应式的但你直接解构const props defineProps{ visible: boolean }() const { visible } props // 丢失响应性正确做法是用toRef单独取某个 prop或者干脆别解构在模板里直接用props.visible。如果你特别需要解构可以toRef(props, visible)。第三个场景是 Pinia store 的解构后文会专门讲storeToRefs。这三个场景本质都是同一个问题响应式状态必须通过代理访问才能触发依赖收集解构成普通变量后就绕过了 Proxy。2.3 watch 和 watchEffect 的触发时机怎么控制watch和watchEffect的选择也常让人纠结。简单说watch需要显式指定数据源只有这个源变化时才回调watchEffect会自动收集回调里引用的所有响应式依赖任何依赖变化都会执行。watchEffect写起来爽但它更像一个“副作用注册器”依赖一变就跑如果你的回调里有网络请求很容易造成频繁触发。我通常这样分工需要监听特定状态变化比如“地址切换后重新拉列表”用watch明确指定() address.value需要把一些逻辑被动地跟很多状态联动比如“把当前搜索条件同步到 URL query”用watchEffect。还有一个容易忽略的flush选项watch(source, async (newVal) { const dom await nextTick() dom.scrollTop 0 }) // 或者更明确地 watch(source, (newVal) { // 此时 DOM 已经更新 }, { flush: post })默认情况下 watch 回调在组件更新前触发如果你想在回调里访问更新后的 DOM就要设置flush: post或者直接在回调里await nextTick()。这个细节在写滚动定位、编辑器同步这类场景时会卡你很久。3. Vite构建链路冷启动、热更新、环境变量和生产部署的取舍3.1 Vite为什么能快开发态与生产态的两套逻辑如果你从 webpack 时代过来第一次打开 Vite 项目的感受肯定是这也太快了。根本原因是 Vite 开发服务器直接基于浏览器原生 ES Module冷启动时只做依赖预构建不打包整个应用。浏览器请求哪个模块Vite 才编译哪个模块。而 webpack 启动时要先遍历整个依赖图把一切打包成 bundle项目越大启动越慢。但要注意Vite 生产构建走的是 Rollup和开发时的加载逻辑不同。这种双轨制在绝大多数场景下不会给你惹麻烦真正需要注意的是依赖的格式兼容。开发时 Vite 会把 CommonJS 依赖预构建成 ESM如果某个第三方库同时导出了 ESM 和 CJS配置优先级以你package.json里的exports字段为准。还有一个很实际的注意点Vite 5 和之前版本对 Node 版本要求不同老项目升级前先确认环境。部署时如果服务器上 Node 版本太低构建阶段就会直接报错这种问题常常要排查半天才发现是版本不匹配。3.2 import.meta.env 与模式匹配多环境配置的正确姿势Vite 的环境变量有一套约定很容易被忽略只有以VITE_开头的变量才会暴露给客户端代码。服务端无关的变量不会进import.meta.env这是避免把密钥意外打包进前端的重要防线。实际项目中我一般准备三个文件.env.development # 开发环境默认 .env.production # 生产环境默认 .env.test # 测试环境运行 --mode test 时生效.env.development里写VITE_API_BASE/api VITE_APP_TITLE本地环境代码里这样用const apiBase import.meta.env.VITE_API_BASE if (import.meta.env.DEV) { // 开发环境逻辑 } if (import.meta.env.PROD) { // 生产环境逻辑 }有一个小坑我必须提醒修改.env文件后必须重启 dev server不是热更新能搞定的。因为环境变量在编译期就被内联替换到代码里了。另外import.meta.env是一个扁平对象你拿不到不存在的变量时是undefined别用import.meta.env.VITE_SOME ?? 这种默认值写法最好显式判断。3.3 常见的 Vite 配置坑alias、base 与 HMR失效路径别名是标配但只配 Vite 不够tsconfig 也要同步// vite.config.ts resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } }// tsconfig.json { compilerOptions: { paths: { /*: [./src/*] } } }如果两者不一致编辑器可能不报错但vue-tsc类型检查或生产构建时会突然挂掉。base配置则和部署路径强相关。如果前端资源部署在域名子路径下比如https://example.com/admin/就必须设置base: /admin/。忘记配 base 的表现是页面能访问 HTML但 JS、CSS、图片全部 404这个错误在实际项目里出现过好多次。HMR 失效的原因多数不在 Vite 配置而在项目结构。我遇到最多的几种组件文件用了大小写不一致的路径引用、循环依赖导致模块图更新不完整、以及手动修改了node_modules里的代码。如果你在node_modules里临时改了东西做验证记得之后删掉否则 HMR 可能会一直不更新或者报奇怪的错。4. Pinia 接棒 Vuex状态管理从模板化到组合化的关键变化4.1 没有 mutations 之后代码发生了什么变化Vuex 时代的要求是改 state 必须同步 commit mutation异步逻辑放 action 里再 commit。这套约束对大型团队有审计价值但小团队更容易感受到的是无意义的样板代码。Pinia 直接去掉了 mutations在 action 里可以同步改 state状态变更逻辑归拢到 action 一个通道同时也保留了 devtools 的时间旅行调试能力。但这不意味着你可以毫无约束地改状态。我的团队约定是组件里只允许通过 store 暴露的 action 修改状态读状态可以走 storeToRefs 解构写状态一律调用 action。这条约定不靠框架强制靠 code review 执行效果比 Vuex 的 mutations 更好——因为代码量少了审查者会更有耐心去关注实际逻辑。Pinia 的第二个大变化是模块化更轻量。Vuex 要写modules: { user: userModule }Pinia 直接一个defineStore定义一个 store按需导入天然模块化。配合 tree-shakingstore 没被用到就不会打包进产物。4.2 setup Store 写法和选项式 Store 应该选哪个Pinia 提供了两种写法选项式和 setup 式。选项式长这样export const useUserStore defineStore(user, { state: () ({ name: , token: }), getters: { isLogin: (state) !!state.token }, actions: { login(token: string, name: string) { this.token token this.name name } } })setup 式长这样export const useUserStore defineStore(user, () { const name ref() const token ref() const isLogin computed(() !!token.value) function login(newToken: string, newName: string) { token.value newToken name.value newName } return { name, token, isLogin, login } })两种我都用过。选项式对新手更友好结构固定getters 还能自动缓存setup 式更贴近组合式 API 的思维特别是当你需要在 store 里复用已有 composable 时setup 式可以直接调用外部函数非常自然。比如export const useOrderStore defineStore(order, () { const { list, load } useTableList() // 复用 composable return { list, load } })这个能力是选项式做不到的。我的建议是新项目统一用 setup 式给团队带来的思维方式迁移成本最低。4.3 跨 store 调用、异步 action 与持久化setup 式 store 里调用另一个 store 很直接export const useCartStore defineStore(cart, () { const items ref([]) async function checkout() { const userStore useUserStore() if (!userStore.isLogin) { // 引导登录 } // 提交订单逻辑 } return { items, checkout } })跨 store 调用通常发生在业务联动场景比如去结算时必须拿到用户信息直接用useUserStore()即可不需要额外的事件总线或复杂参数传递。异步 action 里直接用async/await组件里await store.checkout()就能拿到 Promise 的结果错误处理也一样用 try/catch 包住调用处即可。持久化问题几乎是每个中后台项目都要面对的。我自己的做法是写一个极简插件store 里监听关键状态变化时同步到 localStorage初始化时读取。第三方持久化插件虽然方便但偶尔会遇到版本不兼容而且它默认的序列化策略对嵌套比较深的响应式对象不友好。简单场景自己写一个不过几十行出问题也好排查。5. Vue Router 4 的权限路由守卫返回值、动态 addRoute 和刷新恢复5.1 路由组织与组件加载的现代写法Vue Router 4 的入门配置不复杂但实际项目里我建议用import.meta.glob批量加载视图组件避免写几十个 import 语句// router/index.ts const viewModules import.meta.glob(../views/**/*.vue) function loadView(path: string) { return viewModules[../views/${path}.vue] }这样做的好处是新增页面时不需要再改路由文件顶部的导入区域路由配置里直接写组件路径字符串即可。配合后台配置的动态菜单这个模式几乎是最好的选择。404 兜底路由的写法也和 Vue 2 不一样{ path: /:pathMatch(.*)*, name: NotFound, component: () import(/views/error/NotFound.vue) }/:pathMatch(.*)*表示匹配所有未匹配路径这个路由一定要放在最后。5.2 守卫返回值的坑false、true 和路由对象分别意味着什么Vue Router 4 的全局前置守卫beforeEach的返回值决定导航行为很多人会忽略这里隐含的规则返回值行为true / undefined放行false取消当前导航停在原路由路由对象 / 字符串路径重定向到该路由最常见的坑是死循环。比如router.beforeEach((to) { if (to.path /login) return true if (!isLogin) return { path: /login } })这段逻辑本身没问题但如果你在{ path: /login }后没有加“已登录就跳首页”的判断而是统一返回登录页就可能在某些场景下循环重定向。定位死循环的土办法是在守卫开头加一行console.log(to.path)看路径序列就能看出循环点。还有一个值得注意的细节守卫里如果触发了异步操作比如去后端拉用户权限必须保证守卫回调在异步完成后再返回最终结果。别在异步回调外面同步 return否则守卫立即放行权限还没加载完。5.3 动态路由与刷新恢复最常见的权限方案落地中后台系统几乎都要求按角色显示菜单和页面。常规做法是登录后根据用户权限列表动态调用router.addRoute()// 假设从后端拿到权限路由配置 const permissionRoutes await fetchPermissionRoutes() for (const item of permissionRoutes) { router.addRoute({ path: item.path, name: item.name, component: loadView(item.component) }) }但这套方案有一个必踩的坑刷新页面后点击登录后的动态路由会全部丢失因为router.addRoute是在 JS 运行时添加的刷新后内存重新初始化动态路由自然就没了。解决思路是把“权限路由配置数据”持久化。刷新后在app启动的守卫里判断如果本地已经存过权限配置且当前路由还没注册就重新addRoute然后用router.replace(to.fullPath)重新进入目标路由。这里的关键是加一个“路由是否已注册”的判断否则每次导航都会重复注册同名路由控制台会报警告。我实际落地的伪代码大概这样const ROUTER_HAS_DYNAMIC Symbol(router_has_dynamic) router.beforeEach(async (to) { const hasStore localStorage.getItem(permissionRoutes) if (hasStore !router.hasRoute(ROUTER_HAS_DYNAMIC)) { const routes JSON.parse(hasStore) for (const item of routes) { router.addRoute(item) } router.addRoute({ path: /:pathMatch(.*)*, redirect: /404 }) return to.fullPath // 重新进入目标路径 } return true })这段代码不够完整但思路是核心持久化权限路由、判断是否已注入、刷新后重新注入并重放一次导航。6. TypeScript 和 script setup 的组合实践类型真的是省出来的6.1 用类型声明代替运行时校验defineProps 和 defineEmits 应该这么写script setup里定义 props 有几种写法我强烈推荐纯类型声明script setup langts interface Props { visible: boolean title?: string } const props withDefaults(definePropsProps(), { title: 默认标题 }) /scriptwithDefaults用来设置默认值类型检查依然生效。这里注意一个坑如果你要导入外部类型必须用import type否则打包时可能把类型当作运行时依赖带进去。emits 的类型声明相对繁琐但写习惯后就发现它可以避免误传事件名script setup langts const emit defineEmits{ (e: update:visible, value: boolean): void (e: delete, id: number): void }() emit(update:visible, false) /script6.2 组件 ref、defineExpose 与实例类型推导父组件要调用子组件暴露的方法是很多人的盲区。Vue 2 时代this.$refs.child.someMethod()随便调Vue 3 默认情况下子组件的内部状态和方法不会暴露给父组件必须用defineExpose显式声明!-- 子组件 -- script setup langts function scrollToTop() { // ... } defineExpose({ scrollToTop }) /script父组件里获取类型安全的 refimport ChildComp from /components/ChildComp.vue const childRef refInstanceTypetypeof ChildComp | null(null) function handleClick() { childRef.value?.scrollToTop() }注意InstanceType拿到的是子组件暴露出来的实例类型而不是子组件内部所有的属性和方法。如果你没写defineExpose父组件这个 ref 拿到的可能是一个没有任何业务方法的空壳。很多项目 TS 报错查不到原因最后发现就是少了这一行。还有一个从 Vue 2 带过来的习惯要改掉不要在onMounted里立刻调用子组件方法。子组件挂载虽然完成了但内部可能还在做异步初始化直接调用容易拿到 undefined。更稳妥的方式是子组件内部把“初始化完成”作为一个可监听状态暴露出来父组件 watch 它再执行后续动作。6.3 模板里的类型推断与常见编译器报错vue-tsc比普通 TS 检查严格很多常见的报错集中在两类。第一类是事件参数类型模板里changehandleChange如果 handleChange 声明为(value: string) void但change传的是Event对象类型就不匹配。解决办法是function handleChange(event: Event) { const value (event.target as HTMLInputElement).value }第二类是关于v-model自定义的。Vue 3.4 之后的defineModel虽然好用但考虑兼容性我目前仍然偏好标准写法!-- 子组件 -- script setup langts const props defineProps{ visible: boolean }() const emit defineEmits{ (e: update:visible, value: boolean): void }() function close() { emit(update:visible, false) } /script父组件直接用v-model:visibleshowDialog。这套写法从 Vue 3.0 到最新版都完全兼容不会踩到版本差异的坑。关于 TypeScript 在 Vue 3 里的体会我就一句话宁可前期多花半小时把类型写对也不要用any先糊过去。因为 Vue 的模板类型推导链是靠组件 props、emits、ref 的类型声明串联起来的任意一个any都会切断这条链后续排查成本会成倍上升。最后再分享一点我个人的体会。组合式 API 确实更灵活但它对写代码的人的自律要求明显更高。Options API 自带强制分层哪怕写得再乱至少数据、方法、计算属性各有归处组合式 API 如果不注意拆分setup 很容易变成一个没有结构的长函数。我现在面对规模很小的页面组件时依然会考虑用 Options API 或简单的 setup 直接写不是因为它“适合新手”而是它自带约束。Vue 3.0 真正带来的是“你可以选择怎么组织代码”的自由而不是“你必须用某一种写法”的命令。这种自由度才是全家桶体系中最容易被低估的一部分。

相关新闻

深入理解CSS浮动:原理、清除方案与实战排查

深入理解CSS浮动:原理、清除方案与实战排查

我从2012年左右开始写前端页面,那会儿最高频的布局关键词就是“CSS浮动”。现在flex、grid已经满天飞,很多新同学甚至觉得浮动是过时技术,看一眼老代码就头疼。但说实话,如果你真能把这套已经存在了快二十年的规则彻底搞明白&…

2026/10/18 15:35:07 阅读更多 →
深入理解递归与迭代:调用栈、终止条件与工程选型

深入理解递归与迭代:调用栈、终止条件与工程选型

Day 5,函数的递归和迭代,几乎每个学编程的人都会在这里卡一次。我自己当年也是这样:迭代老老实实写循环,一点问题没有;一碰到递归,看代码能看懂,真让自己写就懵,总觉得"这句话调…

2026/10/18 15:35:07 阅读更多 →
LDAP配置与测试实战:从基础概念到踩坑排查

LDAP配置与测试实战:从基础概念到踩坑排查

1. 为什么到了今天,LDAP仍然值得认真配置很多从事运维或后端开发的朋友,一听到 LDAP 配置与测试,第一反应往往是“这是上个时代的东西,现在不都用 OAuth、JWT 吗”。说实话,我刚工作前两年也是这么想的,直到…

2026/10/18 15:35:07 阅读更多 →

最新新闻

HelixDB+S3:给AI Agent加长期记忆的极简方案

HelixDB+S3:给AI Agent加长期记忆的极简方案

你维护过 AI Agent 的话,多半见过这个场面:昨天还能准确复述用户偏好的智能体,今天一重启,就像失忆一样反问“你说的是哪次对话里的内容?”。我说句直白的话,这不是模型变笨了,而是根本没帮它把…

2026/10/18 16:33:37 阅读更多 →
i茅台自动预约系统快速上手:campus-imaotai 部署与使用完整指南

i茅台自动预约系统快速上手:campus-imaotai 部署与使用完整指南

i茅台自动预约系统快速上手:campus-imaotai 部署与使用完整指南 【免费下载链接】campus-imaotai i茅台app自动预约,每日自动预约,支持docker一键部署(本项目不提供成品,使用的是已淘汰的算法) 项目地址:…

2026/10/18 16:33:37 阅读更多 →
ANSYS Workbench导体温升仿真:电流焦耳热与温度场耦合全流程

ANSYS Workbench导体温升仿真:电流焦耳热与温度场耦合全流程

简介:这份资源面向电气、电子及热设计方向的工程师与高年级学生,聚焦导体在通电条件下的焦耳热与温升问题,提供一套基于ANSYS Workbench的完整仿真工程文件,帮助读者理解电流—温度耦合分析的建模思路与求解流程。压缩包共34个文件…

2026/10/18 16:33:37 阅读更多 →
Java类和对象深度解析:从内存布局到多态与常见陷阱

Java类和对象深度解析:从内存布局到多态与常见陷阱

1. 类与对象:先用“图纸和房子”把概念落地如果你问一个刚学完语法的人“类和对象到底是什么”,他可能会背出教科书上的定义:类是抽象的概念,对象是具体的实例。这话没错,但听完往往还是不知道怎么用。我自己带过不少新…

2026/10/18 16:33:37 阅读更多 →
零样本学习实战:从语义对齐到CLIP升级的完整复现指南

零样本学习实战:从语义对齐到CLIP升级的完整复现指南

简介:本资源是一份面向人工智能方向研究者与进阶学习者的零样本学习(Zero-Shot Learning)实践套件,聚焦于算法复现与语义建模能力训练,适用于计算机视觉、跨模态理解等场景下的小样本泛化问题研究。压缩包共11个文件&a…

2026/10/18 16:33:37 阅读更多 →
从模糊标题到可交付项目:拆解“rea”背后的React、Read、Real-time与Reasoning

从模糊标题到可交付项目:拆解“rea”背后的React、Read、Real-time与Reasoning

1. 从“rea”这个标题说起:一个被低估的通用缩写第一次看到“rea”这个标题,很多人会愣一下——三个字母,没有上下文,没有说明,像是谁打字打了一半就发出去了。但恰恰是这种极简的标题,在真实的项目协作场景…

2026/10/18 16:32:37 阅读更多 →

日新闻

基于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/18 12:16:16 阅读更多 →