阅读视图

发现新文章,点击刷新页面。

宇树科技移动机器人新专利获授权

36氪获悉,爱企查App显示,近日,宇树科技股份有限公司申请的“一种移动机器人”专利获授权,发明人为王兴兴、王凯。摘要显示,该专利提供的一种移动机器人,包括机身和广角激光雷达。所述机身形状为竖直放置的、上细下粗的半椭圆形球体;所述广角激光雷达设于机身上方,安装位置的投影位于机身的几何中心附近。

潘兴广场拟94亿欧元现金加股票收购环球音乐集团

亿万富翁投资者比尔·阿克曼旗下潘兴广场(Pershing Square)当地时间4月6日宣布,拟以94亿欧元现金加股票的方式收购环球音乐集团,已提交不具约束力的提案。现金和股票的总对价预计每股价值30.40欧元,较环球音乐集团的股价溢价78%。(界面)

智元开源具身数据集AGIBOT WORLD 2026

36氪获悉,智元机器人正式开源AGIBOT WORLD 2026数据集。据了解,该数据集基于海量真实场景,围绕五大具身领域研究主题构建,每个主题设有专属采集方法与精细化的标注体系。数据集将分五个阶段持续开源,覆盖更多主题与场景。

法国央行卖出所有在美托管金条

法国央行近期披露了一个特殊项目,即其卖掉了该行在美国托管的黄金储备,然后在欧洲买回相应重量的黄金。此举保证了2026年初时该行的黄金储备与2025年初一致,但又因为买进卖出时的价格差异而产生了可观的外汇收益。该交易涉及129吨非标准金条,占法国央行金条储备的5%,目前该行已将全部黄金储备集中到巴黎金库。法国央行行长强调,将金条留在巴黎而不是纽约的决定并非出于政治动机。(财联社)

欣天科技:公司射频产品主要应用于通信基站端,并非手机端

36氪获悉,欣天科技在互动平台表示,公司射频产品主要应用于通信基站端,并非手机端。除基站端射频器件元器件外,公司持续配合客户研发新能源结构件、医疗器械结构件、热管理结构件等产品。未来公司将持续巩固移动通信业务优势,积极拓展新业务。

Solana开发(1)- 核心概念扫盲篇&&扫雷篇

本篇文章针对的人群是有一些Rust基础,同时想要了解Solana开发的同学。如果您对这些没有任何了解的话,建议您直接点赞、收藏、评论即可,无需往下阅读~ Solana中文开发教程 Solana Rust相关教程

1. 前言

最近在学习Solana开发(别问为什么,问就是前端已死...),以为自己有一些Rust的基础学起来会比较简单,结果没想到阅读上面的教程看的云里雾里,看完了之后就看完了,完全串不起来,各种概念,账户,token、私钥、公钥、合约傻傻分不清楚。

我自己的学习思路是学一个大的概念,也就是把对这个东西的大的框架搭建起来,然后在尝试一点点的去填空。所以这篇内容的话也算是自己理解的整个Solana大框架内容,细节的话感觉上面的教程已经说的很清楚了,推荐如果想学的话先把这篇内容消化一下 然后再看上面的教程会好理解很多。

我把这几天的“填坑日记”整理出来,尽可能的使用大白话的方式来给大家讲清楚Solana的相关基础知识以及其背后的设计理念,尽可能避免一些专有名词的干扰,让你看完了能大概明白是怎么回事。

建议点赞、收藏、评论之后慢慢细品🤣🤣😄😄~~~

2. 核心概念

2.1 账户(Account)

2.1.1 是什么

在 Solana 中,一切皆账户。无论是你的钱包余额、你的代码、还是你发行的 Token 数据,甚至是你创建的智能合约,以及官方代币程序Token Program,全都是保存在一个个“账户”里的。 账户是Solana用于存储状态的基本数据单元。网络将所以状态存储在一个键值对数据库中,每个键是一个32字节的地址,每个值就是一个账户。 image.png

2.1.2 关键信息

  1. 结构:每个账户都包含相同的五个字段:lamports、data、owner、executable、rent_epoch。
  2. 地址:每个账户由唯一的 32 字节地址标识(可以是 Ed25519 公钥或 PDA)。
  3. 所有权:只有账户的 owner 程序可以修改其数据或扣除 lamports。任何程序都可以向任何可写账户充值 lamports。
  4. rent:每个账户都必须持有与其数据大小成比例的最低 lamport 余额,才能保持链上状态。

2.1.3 账户分类

  1. 可执行账户 (Executable Accounts/Program Accounts): 也就是我们常说的 Program (程序/合约)。这类账户里存放的是编译后的 BPF 字节码。它就像一个“只读可执行文件”,只能被调用

    • Program Account用于存储可执行的代码。每个Program Account都由Loader Program拥有。当Program部署时,运行时会创建一个Program Account来存放其字节码。
      image.png
  2. 非可执行账户 (Non-Executable Accounts): 也就是 数据账户 (Data Account)。它是专门用来存状态的(比如余额、游戏积分)。它通过一个 Owner 字段指明哪个程序有权操作它。

    • 程序状态账户:程序会将其状态存储在数据账户中,创建程序状态账户需要两个步骤:

      • 调用 System Program 创建账户。System Program 会将账户所有权转移给指定的程序。
      • 拥有该账户的程序根据其 instructions 初始化账户的 data 字段。 image.png
    • 系统状态账户:创建后仍由 System Program 拥有的账户称为 system account。首次向新地址发送 SOL 时,会在该地址创建一个由 System Program 拥有的新账户。

      • 所有的钱包账户都是System Account。交易的手续费支付者必须是System Account。因为只有System Program拥有的账户才能支付交易手续费 image.png

2.1.4 关键点:程序(Program)是无状态的!

你的 Rust 代码上链后,它只是个“只读的函数库”。它不存你的分数、不存你的余额。数据存哪?存在专门开辟的 Account(账户) 里。 说人话就是每个账户只能是可执行账户、或者是非可执行账户,不可以又包含数据,又包含状态,这就是Solana的设计哲学:数据和状态分离代码和可变状态的分离意味着程序只需部署一次,即可管理任意数量的数据账户。

2.2 指令

指令是对Solana程序执行特定功能的请求。指令是链上操作的基本构建块。 每条指令只指定一个要调用的程序、所需的账户、以及由程序解析的字节数组数据。 每条指令的执行逻辑都存储在程序中,每个程序定义了自己的指令集。要与Solana网络交互,需要将一条或多条指令添加到交易中,并发送到网络进行处理。

image.png

2.2.1 关键点

  1. **单一程序:**每条指令只针对一个程序,通过program_id
  2. 账户元数据: accounts 数组为指令读取或写入的每个账户提供账户元数据。
  3. 不透明数据: data字段是一个字节数组,其格式由目标程序定义。

2.2.2 指令结构

  1. program_id: 被调用的程序的ID 指令的program_id是包含该指令执行逻辑的程序的公钥地址。运行时会使用此字段将指令路由到正确的程序进行处理。
  2. accounts: 一个账户元数据数组 指令的accounts数组是一个有序的Account Meta 结构体列表。每个指令交互的账户都必须提供元数据。validator会利用这些元数据判断哪些交易可以并行运行。写入不同账户的交易可以并行执行。
    image.png
  3. data: 包含额外 数据 的字节数组,供指令使用。 指令的 data 字段是一个字节数组。用于告知程序要调用哪个函数,并为该函数提供参数。数据通常以一个判别符或索引字节开头,用于标识目标函数,后面跟着序列化的参数。 常见的编码约定:
    • 核心程序(System、Stake、Vote): 使用Bincode序列化的枚举变体索引,后跟序列化参数。
    • Anchor程序:使用8字节判别符,后跟Borsh序列化参数。

2.3 交易

按照上面的图片中的标识,一笔交易实际就是多个指令的集合。任何指令失败,整个交易失败,所有的状态更改都会被回滚。

2.3.1 如何界定交易包含多少指令

  • 原子性绑定: 如果指令A 失败,指令B就不能做,那就打包到一起;
  • 容量限制:单个交易包不能超过1232字节(约等于能塞下十几个账户地址);
  • 计算限制:逻辑太复杂会爆CU(计算单元),需要拆分或申请提高预算;

2.4 SPL(Solana Program Library)代币

代币是代表不同类别资产所有权的数字资产。代币化使财产权利的数字化成为可能。

2.4.1 是什么:

Solana 上的所有代币本质上都是由 Token Program 拥有的 数据账户。 基于数据账户这个说明,其实代币有两个核心”概念性“的账户:

  1. 铸币账户 (Mint Account) —— 代表“币”的本体 代表这种 Token 的本质。比如你今天发了一个 "USDT",这个 USDT 的总量、精度(Decimals)、管理员是谁,存在 Mint 账户里
  2. 代币账户 (Token Account) —— 代表“某人的口袋” 用来装这种 Token 的“口袋”。你的主钱包地址(Wallet)不能直接装 Token,必须名下挂一个专属的 "口袋(Token Account)" 才能装 USDT。 代币中的账户
    • Mint: 该token account持有的代币
    • Owner: 有权从该token account转移代币的账户
    • Amount: 该token account当前持有的代币数量

2.5 私钥、公钥、签名

2.5.1 是什么

代表着你在区块链世界的身份凭证和密码

2.5.2 关联

  • 公钥 (PublicKey) :就是我们口头说的“钱包地址”或“账户地址”,它是向外界展示的门牌号。
  • 私钥 (Secret Key) :能对这扇门发号施令的唯一钥匙,常以 id.json 文件的形式存在。
  • 签名 (Signature) :每次转账或修改账户数据时,用你的私钥“盖的章”。节点一验章,就知道“这确实是他本人操作的”,也就是授权。

2.6 RPC (远程过程调用节点)

2.6.1 是什么

区块链外围的服务亭(比如你刚试过的 https://api.devnet.solana.com 或 Helius 的节点)。

2.6.2 作用

不论你是要在网页上查询你的 SOL 余额(Query),还是通过 Web3 SDK 把签名好的交易(Transaction)丢上链,统统得通过 HTTP(甚至是 WebSocket 实时订阅)发给 RPC 节点。节点负责将这些包裹进一步分发到区块链主干网上的那些验证器进行执行打包。

2.7 DApp (去中心化应用前端)

2.7.1 是什么

为了不让用户对着命令行或者代码界面发呆而写的图形化网页系统。

2.7.2 典型框架

像目前 Solana 官方推崇的 @solana/wallet-adapter 组件。它能在网页上悬浮一个类似于 Phantom、Solflare 的插件图标,让用户在可视化的界面里点击一键登录,并对后端的转账交易弹出授权框。

3. 避坑第一条:别跟“代理”较劲!

刚开始跑 solana airdrop 就给我吃了一记闭门羹:connection closed via error

真相大白: Solana CLI 底层用的 Rust reqwest 库,在处理 macOS 代理时简直是“洁癖”。curl 都能通,它就是不通。咱也不知道为啥,但是查了很多资料之后直接放弃死磕到底,走捷径还是很轻松的。

  • 基础知识点: Solana 节点通信走的是 JSON-RPC 协议
  • 代码补充: 只要 RPC 节点配置对,其实一行代码就能查余额。
    // Web3.js 基础:连接节点并查询
    const connection = new Connection("https://api.devnet.solana.com");
    const balance = await connection.getBalance(myPublicKey);
    
  • 解决方案: 别死磕官方 RPC 了。换个 QuickNodeHelius 的免费专用节点,关掉代理直连,瞬间丝滑。

4. SPL Token:为什么我发个币要建一堆“口袋”?

在 Solana 上发币,全网只有一个主程序(Token Program)。你发币其实只是在“填表格”:

  1. Mint Account(铸币账户):这就是币的“身份证”,定义了谁能印钱。
  2. Token Account(代币账户):你的主钱包不能直接装币!想接某种币,必须在名下挂一个专属的“口袋账户”。
    • 核心逻辑: 所有的转账,本质上是 Token Program 这个“大管家”在帮你修改 A 口袋 and B 口袋里的数字。

5. 钱包、签名、公钥:三位一体的“印章”逻辑

  • 钱包(Phantom/CLI):它是你的印章保管员。私钥永远不出钱包,只负责在交易包上盖个戳(签名)。
  • 公钥(Public Key):你的收款码/身份证照,全网公开。
  • 签名验证(Ed25519):节点不需要你的私钥,它通过你的“签名结果”+“公钥照片”+“交易内容”进行数学推导,一眼就能看出这戳是不是印章盖的。

6. 终极奥义:Solana 凭什么这么快?

这绝对是面试必考题。Solana 的“多核并发”不是吹的。

1. 强制“提前报备”(Sealevel 引擎)

在以太坊,你不知道交易会动谁。而在 Solana,每个交易必须明确声明:我要读写哪些账户

  • 专业解释: 调度器一旦发现两笔交易操作的账户互不干扰,就会把它们分给不同的 CPU 核心并行处理

2. Rust 的天生优势

Solana 选 Rust 是有原因的。Rust 的**所有权(Ownership)**和内存安全机制,让它在多核高并发读写内存时,依然能保证不发生数据竞争(Data Race)。

3. PoH(历史证明)

给全网加了个“精准节拍器”。节点之间不用再在那儿磨叽“现在几点了”,大家看时间戳就能排好队,CPU 闷头猛算就行。

总结:Solana 的工程哲学

如果你习惯了 Web2 或者以太坊,初见 Solana 可能会觉得它设计得太繁琐。但一旦你接受了 “数据与代码分离”、“声明式读写” 这种设定,你会发现这是一个极致追求性能的分布式操作系统

“首形科技”获新一轮数亿元A1轮融资

36氪获悉,超高仿生情感交互机器人企业“首形科技”宣布完成数亿元人民币A1轮融资。本轮由华控基金及某互联网大厂联合领投,嘉御资本、鹏瑞基金、亦庄国投、上海半导体产投、南山战新投,以及老股东招商局创投、顺为资本、弘晖基金、厚雪资本、东方富海跟投。本轮资金将主要用于多模态具身交互系统与情绪基座模型的持续迭代升级,仿生面部核心部件与材料体系的规模化优化,以及标准化交付与全球市场拓展。

AI 时代的管理后台框架,应该是什么样子?

这些年我一直在做 Fantastic-admin 这套管理后台框架。也一直在关注这个圈子的发展,虽然“技术栈在升级”、“UI 风格也在变化”,但管理后台框架核心一直在不断解决同一个问题:

如何把那些反复出现、又特别容易失控的工程问题,提前收敛成一套系统能力。

早期,这个问题的答案是“给我一个能跑起来的脚手架”;后来变成“帮我把常见页面骨架搭好”;再后来,变成“不要让我被框架反过来绑架”;而到了今天,在 AI 和 Agent 已经真的进入开发现场之后,我觉得问题已经变成了:

一个管理后台框架,能不能同时服务开发者和 Agent ?

这也是我写这篇文章的原因。在我看来,AI 当下的管理后台,已经不能只是一个后台模板,它必须是一套面向长期协作的工程系统。

再聊之前,不妨先回顾下管理后台框架的发展史。这里以 Vue 生态下的管理后台为主。

第一阶段:脚手架时代,解决了“从 0 到 1”

这个阶段最核心的诉求非常朴素:

  • 不要让我从空目录开始搭项目
  • 不要让我自己接 Vue、路由、状态管理、权限、登录、Mock、构建配置,哪怕其中有些我不用,但也最好有

在这个阶段,vue-element-admin 是绕不过去的一款产品,它除了解决了开发者的基本诉求外,还提供了一套非常前卫的设计:用路由驱动导航菜单

今天看这件事很自然,但在当时,这其实是很关键的一步:

  • 导航菜单不再需要额外维护一份数据
  • 路由结构和导航菜单结构天然一致
  • 标题、图标、权限这类信息可以集中管理

为什么这一步重要?因为后台和普通内容网站不一样,导航本身就是产品的信息架构。导航一乱,整个后台的认知成本就会上去。

所以在我看来,第一个阶段最重要的历史贡献就是这个路由即导航的设计,影响了几乎所有后来诞生的后台框架。

第二阶段:模板繁荣时代,开始出现“虚假的强大”

随着 Vue 3 发布,以及 vue-element-admin 作者的停更,大量新的管理后台框架开始出现。

这一阶段有一个非常明显的现象:与其说是框架,更像是“模板展厅”。因为你会看到:

  • 第三方插件集成示例越来越多
  • 图表、地图、编辑器、拖拽控件、可视化页面一应俱全

很容易让人觉得“这个框架很强”,但真的是这样么?

我们不可能在一个项目中把这些所有插件都用上,即便会用到其中几个,提供的这些示例页面也未必能满足实际的需求。而绝大多数真实业务团队,日常最高频的需求反而是:

  • 列表页怎么高效搭建
  • 搜索区、分页区、操作区怎么统一
  • 新增、编辑、详情页怎么组织
  • 菜单、路由、权限、缓存怎么协同

也就是说,这个阶段很多后台框架在解决的是“看起来像个成熟后台”的问题,而不是“怎样真正高效地服务开发者”的问题。

这是我做 Fantastic-admin 时非常警惕的一件事:

不要把框架做成一个演示效果很强、真正落地时却帮不上太多忙的样子货。

第三阶段:后台框架开始回到“系统能力”本身

如果说第二阶段有不少东西是在做“展示能力”,那么从第三阶段开始,我觉得后台框架终于慢慢回到了更本质的问题上:

它到底能不能成为一套真正服务业务的系统。

在我看来,这一阶段出现了两条很清晰的路线。

一条路线是向内走:把框架本身做得更完整

这条路线的核心是尽量扩充框架自身的系统能力,也是我开发 Fantastic-admin 时侧重的一条路。因为我发现,真正影响一个后台项目长期体验的,往往不是那些最显眼的东西,而是:

  • 导航布局够不够灵活
  • 页面布局能不能适配不同产品形态
  • 路由元信息够不够细
  • 标签栏、工具栏、偏好设置是不是成体系
  • 页面保活是不是只停留在“开/关”两档
  • 有没有合理的扩展位,而不是逼着开发者去改框架源码
  • 等等

这些能力平时开发使用未必会注意到,但它们决定了一个项目在需求扩张的时候,能否让开发者放心,不用担心框架没有提供这个能力的问题。

比如页面保活这件事,我一直觉得很多框架做得太粗了,通常都只是提供一个 keepAlive: true 的开关,虽然能解决一部分问题,但真实后台项目的诉求往往更复杂:

  • 从列表进详情,希望列表保活
  • 从列表跳其他模块,希望列表不保活
  • 标签页合并(Fantastic-admin专有功能)后,有些页面要保活,有些页面返回时必须释放保活

基于这些场景,我更想做的是一套可控的保活策略,而不是一个粗糙的开关,因为这才是业务开发者真正会长期依赖的能力。

另一条路线是向外走:继续靠近业务开发本身

另一条路线也很重要,因为一个事实是:后台大量业务页面,本质上高度重复。

  • 结构重复
  • 交互重复
  • 列表重复
  • 表单重复
  • 弹窗抽屉重复

总的来说就是大量 CRUD 模块高度重复,既然重复,那就不应该每次都从基础组件重新拼。

所以有框架开始探索更高层的业务抽象,比如 vben 就提供了更成熟的 CRUD 能力、更高集成度的表格表单组件,这些方向我都认为目前还是对的。

岔开聊一句,为什么说目前还是对的,因为高集成度的封装和抽象,本质上是减轻人类开发者的工作,假设我们面对一个5000-6000行的代码文件,想要理解它是很痛苦的,所以工程化、组件化的理念才如此重要。但这种大文件却刚好很契合 AI ,毕竟如果文件拆分太多,AI 频繁需要跨文件引入,上下文变得碎片化,必然会出现链路过长,信息丢失的情况,反而不适合 AI 优先的开发模式。

但不管怎么说,从这一步开始,后台框架的竞争终于不再停留在“模板多不多”,而是进入了更实在的层面:

谁能真正把业务开发里的重复劳动继续向上抽象。

补充一点:框架开始和 UI 组件库解耦

第三阶段继续往前走,我自己又越来越强烈地感受到另一个问题:

几乎所有后台框架和某个 UI 组件库绑定死了。

这会直接带来几个问题:

  • 开发者认同你的工程设计,但不认同你的 UI 风格
  • 框架代码和某个 UI 库深度绑定,更换 UI 库成本巨大
  • 一旦 UI 组件库停止维护或维护不积极时,整套系统都会受到牵连

发现这个问题后,我就知道不能把 Fantastic-admin 绑死在某个 UI 库上。

shadcn/ui 以及后来社区出现的 shadcn-vue ,对我来说是一个非常关键的信号。

它带来的最重要启发,不是某个按钮或者弹窗组件本身,而是它在强调一件事:

  • 组件代码应该是开放的
  • 组件应该是可读、可改、可延展的
  • 设计系统应该掌握在项目自己手里
  • 组件不是黑盒消费品,而是工程资产

shadcn/ui 官方甚至直接强调自己 不是传统组件库,而是一种构建组件的方式

当侧边导航、弹窗、抽屉、消息通知等等这些基础组件和 UI 组件库解耦后,Fantastic-admin 彻底变成了一套独立的,不再是某个 UI 组件库生态下的管理后台框架。

第四阶段:Agent 爆发之后,后台框架应该被重新定义

到了今天,AI 和 Agent 的爆发,不是在给后台框架“增加一个新卖点”,而是在逼着整个领域重新回答一个问题:

如果 AI 已经能读代码、改代码、理解目录、执行任务,那么管理后台框架应该如何被重新设计?

我自己简单分析了一下,在 AI 时代,一个管理后台框架至少应该具备下面 5 个特征:

1. 必须能让 AI 看懂项目全貌

这里就绕不开 monorepo 的架构了,过去我们说 monorepo 很多时候是在说工程治理、依赖复用、多应用扩展。

但今天我越来越觉得 monorepo 还有一个非常现实、而且会越来越重要的价值:

它天然更适合让 AI 建立完整上下文,能让 AI 拥有完整信息版图。

当应用代码、公共组件、主题、框架设置、文档、各种CI/CD脚本、技能定义都放在同一个结构清晰的仓库里时,AI 更容易快速理解:

  • 哪些是业务层
  • 哪些是公共能力
  • 哪些是配置边界
  • 哪些是复用资产
  • 哪些是项目约定,哪些只是偶然写法

Google 在那篇著名的 monorepo 文章里,把 monorepo 的价值概括为“common source of truth”。

我不想机械照搬这句话,但在 AI 协作语境下,它确实给了我很强的启发:

统一的代码真相源,也意味着统一的 Agent 理解入口。

这当然不是说用了 monorepo 架构,AI 就自动变聪明了。但至少它更容易看到全貌,减少 AI 幻觉的产生。

2. 必须有一套 AI 能稳定读取的项目协议

只有代码结构还不够,要想让 AI 想稳定工作,还必须有一层项目级协议,也就是 AGENTS.md ,或者 CLAUDE.md

它们本质上都在解决同一件事:

AI 协作不能只靠一次次聊天,而是需要项目内置的长期说明。

这意味着一个现代项目,未来不只是有给人看的 README,也应该有给 Agent 看的 README。

3. 应该把高频任务产品化为 Skills

Prompt 适合解决临时问题,但不适合承载高频、稳定、可复用的项目流程。

后台项目最常见的动作其实非常固定:

  • 生成 CRUD 模块
  • 新增表单页
  • 增加路由
  • 配置国际化
  • 修改框架设置
  • 生成 store
  • 定制主题
  • 优化/美化页面

如果这些事情每次都靠人重新组织一段 Prompt,AI 的表现一定会飘忽不定。这也让我决定要把这些高频动作沉淀成 Skills,把目录约定、实现策略、文件位置、限制条件、注意事项全部前置进去。这样做的好处非常直接:

  • AI 不再靠猜
  • 生成结果更接近项目现有风格
  • 不同 Agent 工具之间更容易复用同一套知识
  • 项目经验不再只存在聊天记录里,而会沉淀成长期资产

在我看来,这一步很重要,因为它意味着我们开始从“会用 AI”走向“把 AI 纳入工程系统”。

4. 必须把“可修改”放在“可调用”前面

在 AI 时代,我越来越觉得一个被黑盒包裹得太深的组件体系,长期价值其实会下降。

因为 Agent 最擅长的,不只是调用 API,而是:

  • 阅读现有代码
  • 理解现有代码
  • 修改现有代码
  • 基于现有代码继续延展

如果组件只是一个外部依赖包里的抽象壳,AI 的可操作空间是受限的;但如果组件体系是开放的、分层清晰的、仓库内可读的,AI 的工作质量通常会高很多。

相信这也是 shadcn/ui 爆火的原因之一。

这里说一个暴论,目前国内比较火的 UI 库,我一直都没有看到官方有提供 skills ,在一个既没有 skill ,AI 又无法直接阅读 UI 库的源码,这在当前环境下,很有可能会被逐渐弃用。

未来的软件系统,不只是给人维护的,也会越来越多地交给 AI 一起维护。

所以我理解的现代管理后台,不是“我有一堆组件”就够了,而应该是:

  • 有可读的组件实现
  • 有统一的组件约定
  • 有能沉淀后台业务场景的内建组件层
  • 有可替换的底层 UI 能力

5. 最终服务的是“长期协作”,而不只是“快速生成”

很多人一谈 AI,就会把重点放在“生成更快”上。但我做后台项目这些年越来越觉得:快,从来不是唯一问题,甚至很多时候都不是核心问题。

真正重要的是:

  • 生成出来以后,能不能做 code review
  • 多个页面之间风格能不能保持一致(UI风格、代码风格)
  • 多应用、多主题、多品牌场景下会不会慢慢失控
  • 人和 Agent 或多 Agents 混合协作时,项目是否仍然稳定

所以在我看来,AI 时代最好的后台框架,不一定是第一次生成最惊艳的那个,而应该是:

最适合持续迭代、持续扩展、持续被 AI 正确理解的那个。

最后聊一聊 Fantastic-admin 即将发布的 6.0 版本

v6-is-coming.png

如果把前面这几个阶段串起来看,其实就很容易理解,为什么 Fantastic-admin 要在这个阶段发布一个大版本更新。

因为对我来说,它已经不只是“一个 Vue 3 管理后台框架”,而是在尝试回答一个更具体的问题:

如果管理后台框架要面向下一个阶段,它应该提前长成什么样子?

1. 一套可长期演进的工程底座

Fantastic-admin v6 采用了 pnpm monorepo 架构,仓库里把应用、公共包、文档、脚本、技能清晰拆开:

fantastic-admin/
├── apps/              # 应用目录
│   ├── core           # 应用源码
│   └── example        # 示例应用
├── packages/          # 公共包目录
├── docs/              # 文档站点
├── scripts/           # 脚本工具
├── skills/            # AI 技能
└── package.json       # 根目录 package.json

这么做当然也有工程治理层面的考虑,但更重要的是,我希望“代码、文档、约定、技能”能够在同一个仓库里形成闭环。对于人来说,这是更清楚的工程边界;对于 Agent 来说,这是一张更完整的信息地图。

2. 把项目协议写进了仓库

仓库根目录有 AGENTS.md 文件,里面明确说明了:

  • 项目技术栈
  • 目录结构
  • 开发命令
  • 开发规范
  • 注意事项
  • 对技能使用的补充约束

这么做的原因很简单:我不希望 AI 每次都靠对话去猜这个项目是什么样子。

3. 把高频动作沉淀成了一套 Skills

目前已经有的 Skills ,包括但不限于:

  • CRUD 模块生成
  • 表单页生成
  • 路由生成
  • 国际化管理
  • 框架设置管理
  • 页面优化
  • 预留插槽创建
  • Store 生成
  • 主题定制

Skill 一方面是可以节省 token ,另一方面是将我的能力和我对框架的理解,形成了一套任何人都可以直接复用的标准,这是一份给 AI 的指导方针,让 AI 不再是猜测你的需求,或者可以说相当于你“雇用”了作者本人帮你完成需求😁。

4. 一套更加完善的系统设计

Fantastic-admin 一直以来的重点,都不是去堆砌多少示例页面,而是把后台真正核心的问题做成一套可配置化的系统:

这些能力拆开看都不算噱头,但组合在一起,我认为它们构成的不是一个“模板展示项目”,而是一套真正的后台基础设施。


image.png

至此,Fantastic-admin 即将发布的 6.0 全新版本就是我对 AI 时代管理后台框架的全部理解

如果你对 Fantastic-admin 开始感兴趣了,现在已经发布了 6.0 beta 版,欢迎来尝试体验,我将在4月中旬左右发布正式版本。

莱珀妮回应中国市场涨价传闻:仅部分产品调整

有市场传闻称包括La Prairie莱珀妮、兰蔻、阿玛尼、植村秀在内的多个国际美妆品牌将于4月上调中国市场价格。对此,莱珀妮母公司拜尔斯道夫回应称,目前仅对部分产品进行价格调整,未设统一涨幅。拜尔斯道夫表示,莱珀妮产品均为进口,在制定建议零售价时需从全球视角综合考量多重因素,包括原材料成本、供应链变化、汇率波动以及行业整体价格体系等。其同时强调,奢侈护肤品价格的调整并非个别品牌行为,而是行业中周期性进行的常规动作。亦向兰蔻、阿玛尼等品牌的母公司欧莱雅集团求证前述涨价传闻,但截至发稿未获回复。(界面)

重新思考模板语言与 TypeScript 的结合:一条可落地的新路径

前端框架语法大致可以分为两类:模板语言框架(如 Vue、Svelte、Qingkuai)和 JSX/TSX 框架(如 React、Solid)。

在模板语言中,开发者通常可以在嵌入脚本块里获得接近原生 JS/TS 的编写体验,同时借助更简洁的模板语法完成常见渲染逻辑;代价是组件文件的灵活性会受到一定约束。JSX/TSX 则几乎让你在整份文件里都处在 JS/TS 的表达体系中,灵活性更高,但也会让 HTML 标签、CSS 样式与 JavaScript 代码深度交织,语法边界相对模糊。

以上只是对两类语法核心差异的简化描述,具体体验因人而异。本文聚焦一个长期存在的痛点:模板语言如何更好地支持 TypeScript

一、组件中的类型声明

在日常使用模板语言时,我一直有一个明显感受:主流框架对 TypeScript 的支持虽然已经很强,但在关键场景仍有不小门槛。最典型的就是几乎所有组件化框架都会遇到的 props 类型声明。

在这件事上,VueSvelte 采用了相近思路:通过编译标记(不同框架术语略有区别,例如 Vue 常称为编译器宏)声明类型。对于简单 props 这套方案基本够用;但进入泛型场景后,通常需要在 <script> 标签上额外声明泛型作用域,例如:

<script generics="T extends { id: number; name: string }"></script>

这在一定程度上背离了模板语言的核心优势:在嵌入脚本里提供一个纯净的 JS/TS 编程环境,让开发者专注业务逻辑,而不是额外语法细节。

更现实的问题是隐性成本。比如在 generics 属性中,是否可以访问嵌入脚本块内声明的类型?经过测试,Vue 与 Svelte 的表现一致但并不理想:

对于导入的外部类型,generics 可以访问;对于脚本块内部声明的类型,则无法访问。

导入类型.png

内部类型.png

我推测这与泛型组件的导出形态有关:语言服务可能需要将组件默认导出处理为函数,而 import 声明只能位于模块顶层,因此需要提升到函数外部,进而产生这种可见性差异。无论具体实现原因如何,这都会增加开发成本,并削弱模板语言应有的流畅体验。

这也是我在 Qingkuai 中做的一个核心取舍:保留 Props 作为组件全局类型声明。只要声明了 Props,就等于声明了 props 类型。这样一来,嵌入脚本块的编写体验和普通 JS/TS 基本一致。

props类型声明.png

这个设计还有一个额外收益:在非 TypeScript 项目中,仍可通过 JSDoc 注释声明 Props 类型,从而获得类型检查与补全能力。

jsdoc定义组件类型.png

二、泛型实参的传递

除了 props 声明之外,另一个高频痛点是:无法为组件泛型参数传递实参

在 Vue 与 Svelte 中,目前都缺少一套明确机制来向组件泛型传入实参。这会导致调用方即使具备明确的业务上下文,也无法通过显式传入泛型实参来收窄并主动约束组件类型。

组件泛型实参.png

三、插槽上下文类型推导

在插槽上下文类型推导上,模板语言相较 JSX/TSX 其实有天然优势:多数模板语言通过 slot 标签声明插槽出口,并可在标签上直接绑定要传递给插槽的数据。这为自动推导提供了明确入口,不必强迫开发者在组件内部增加额外类型标注。

反过来看 JSX/TSX(如 React),其并没有原生插槽概念,通常只能通过 children 模拟类似能力。这样一来,类型推导会明显更难,往往需要开发者手工声明函数类型来描述 children 的参数与返回值。

遗憾的是,当前主流模板语言仍未实现插槽上下文自动推导。Vue 支持手动标注插槽上下文类型;Svelte v4 使用 slot 定义插槽但不能标注其类型,v5 虽引入 Snippet 机制,仍需要开发者手动标注片段上下文类型,二者都不支持自动推导。

但从可行性看,这件事并不遥远。通过编译期静态分析、IR 标记与 TypeScript 语言服务提取类型的组合,模板语言完全可以实现插槽上下文自动推导。例如下面两个组件中,组件内部没有额外类型标注,调用方仍可获得完整推导与补全,甚至在纯 JavaScript 项目中也能自动推导插槽上下文类型:

插槽上下文类型推导.png

插槽上下文自动推导的价值不只在于减少类型声明成本,更在于 IDE 交互质量。借助 查找定义查找引用,开发者可以直接跳转到上下文定义源头,而不是落在类型定义中转层。

qingkuai插槽跳转.gif

vue插槽跳转.gif

在复杂组件里,这个差异非常直观。没有自动推导时,你往往需要先定位 <slot>,再分析绑定字段,最后回溯字段定义;有自动推导时,只需在插槽内容处执行一次 查找定义,即可直达源头,开发效率和可维护性都会明显提升。

四、组件类型导出

目前几乎所有模板语言都不要求手工定义组件导出类型,语言服务会根据组件内部声明自动推导默认组件类型。这本身是合理且高效的设计。

但另一个问题是:推导出的导出类型是否足够可读。Vue 可能因兼容历史语法而导致类型展示偏冗长;Svelte 虽然更简洁一些,但仍会暴露部分内部类型细节,容易增加理解成本。

vue组件导出类型.png

svelte组件导出类型.png

通过更清晰的导出类型结构设计,这个问题是可以优化的:

qingkuai组件导出类型.png

另外,很多开发者在写组件时都会习惯把鼠标悬停在组件标签上查看类型,但 Vue 与 Svelte 对这一体验的支持仍不理想:

svelte组件标签查看类型.png

vue组件标签查看类型.png

如果通过 TypeScript 语言服务的 TypeChecker 提取组件导出类型,并在标签悬停中返回该类型,落地并不复杂:

qingkuai组件标签查看类型.png

五、总结

本文从四个问题展开:组件内类型声明、泛型实参传递、插槽上下文类型推导,以及组件导出类型可读性。它们看似分散,本质上都指向同一个目标:让模板语言中的 TypeScript 体验尽可能接近“普通 TypeScript 文件”的直觉与效率。

从工程实践看,真正决定体验的往往不是语法表层,而是类型流是否连续。只要类型信息在“组件定义 -> 编译产物 -> 语言服务 -> IDE 交互”链路上断裂,开发者就会被迫用额外声明、注释和心智记忆去补洞。

围绕这一点,Qingkuai 采取了两项关键策略:

  1. 减少模板内额外语法负担:通过内置 Props / Refs 约定,将组件属性类型声明收敛到标准 TS 类型定义。
  2. 增强语言服务侧类型恢复能力:在编译期保留足够结构化标记,再由 TypeScript 语言服务提取并回填类型,用于补全、跳转与错误检查。

以插槽上下文为例,采用“编译期 IR 标记 + LSP TypeChecker 提取”路径后,类型推导不再依赖开发者逐处手工维护,IDE 也能把定义关系直接连接回真实源头。这不仅降低了类型维护成本,也显著改善了代码阅读与重构体验。

最终结论可以归纳为三点:

  1. 模板语言并不天然弱于 TS 体验:关键在于是否将类型系统纳入语言与工具链的一体化设计。
  2. 编译器与语言服务应协同设计:编译器负责可追踪标记,语言服务负责语义恢复与交互反馈。
  3. 高质量类型体验可以工程化落地:只要类型链路闭环,补全、跳转、诊断与可维护性就能同步提升。

这也意味着,这套思路并不局限于 Qingkuai。本质上,它为其他模板语言也提供了一条可行路线:在尽量保持模板语法简洁的前提下,通过编译器与语言服务协同设计,持续提升 TypeScript 体验。Qingkuai 的后续工作也可以沿着这条路径推进:补齐更多边界场景(复杂泛型、条件类型、跨文件符号映射),并以真实项目数据验证这套机制在大型代码库中的稳定性与性能表现。若你想进一步了解实现细节或直接上手验证,可以参考qingkuai文档在线体验qingkuai

告别 Vuex 的繁琐!Pinia 如何以更优雅的方式重塑 Vue 状态管理

引言:Vue状态管理的演进之路

在Vue生态系统中,状态管理一直是构建复杂应用的核心挑战。从早期的Vuex到如今的Pinia,Vue状态管理方案经历了显著的演进。随着Vue 3的发布和组合式API的普及,Pinia凭借其简洁性、类型安全性和卓越的开发体验,迅速成为Vue 3项目的首选状态管理库。

本文将深入探讨Pinia为何成为Vue 3状态管理的最佳实践,通过对比Vuex揭示其设计哲学的优势,并深入挖掘其底层实现机制。

一、Pinia的核心优势:为什么是Vue 3的最佳选择?

1.1 极简的API设计

Pinia摒弃了Vuex中复杂的mutations概念,允许开发者直接修改状态或通过actions进行修改。这种设计大幅减少了样板代码,使状态管理更加直观。

// Pinia的简洁写法
const store = useCounterStore()
store.count++ // 直接修改
// 或
store.increment() // 通过action修改

// 对比Vuex的繁琐流程
store.commit('INCREMENT') // 必须通过mutation

1.2 一流的TypeScript支持

Pinia在设计之初就充分考虑了TypeScript的支持,提供了完整的类型推断,无需额外的类型定义文件。

// 自动类型推断
const userStore = useUserStore()
userStore.name // string类型自动推断
userStore.login() // 参数和返回值类型自动推断

1.3 模块化的扁平结构

Pinia采用扁平化的store结构,每个store都是独立的,避免了Vuex中复杂的模块嵌套和命名空间问题。

// Vuex的嵌套模块结构
store/
├── index.js
├── modules/
│   ├── user.js
│   ├── cart.js
│   └── product.js

// Pinia的扁平结构
stores/
├── useUserStore.js
├── useCartStore.js
└── useProductStore.js

1.4 与组合式API的深度集成

Pinia完美契合Vue 3的组合式API哲学,提供了与refcomputed一致的使用体验。

二、Pinia vs Vuex:架构与设计哲学对比

2.1 架构对比图

传统Vuex架构 vs 现代Pinia架构
┌─────────────────────────────────────┐ ┌─────────────────────────────────────┐
│           Vuex Store                │ │            Pinia Ecosystem          │
│  ┌─────────────────────────────┐  │ │  ┌─────────────────────────────┐  │
│  │         Root State          │  │ │  │      Independent Store 1    │  │
│  └─────────────────────────────┘  │ │  │  ┌─────────────────────┐  │  │
│  ┌─────────────────────────────┐  │ │  │  │   Reactive State    │  │  │
│  │         Getters             │  │ │  │  └─────────────────────┘  │  │
│  └─────────────────────────────┘  │ │  │  ┌─────────────────────┐  │  │
│  ┌─────────────────────────────┐  │ │  │  │    Computed Getters │  │  │
│  │        Mutations            │  │ │  │  └─────────────────────┘  │  │
│  └─────────────────────────────┘  │ │  │  ┌─────────────────────┐  │  │
│  ┌─────────────────────────────┐  │ │  │  │      Actions        │  │  │
│  │         Actions             │  │ │  │  └─────────────────────┘  │  │
│  └─────────────────────────────┘  │ │  └─────────────────────────────┘  │
│  ┌─────────────────────────────┐  │ │  ┌─────────────────────────────┐  │
│  │         Modules             │  │ │  │      Independent Store 2    │  │
│  │  ┌─────────────────────┐  │  │ │  │  ┌─────────────────────┐  │  │
│  │  │      Module A       │  │  │ │  │  │   Reactive State    │  │  │
│  │  └─────────────────────┘  │  │ │  │  └─────────────────────┘  │  │
│  │  ┌─────────────────────┐  │  │ │  │           ...             │  │
│  │  │      Module B       │  │  │ │  └─────────────────────────────┘  │
│  │  └─────────────────────┘  │  │ │  ┌─────────────────────────────┐  │
│  └─────────────────────────────┘  │ │  │      Independent Store N    │  │
└─────────────────────────────────────┘ └─────────────────────────────────────┘

2.2 设计哲学差异

Vuex的设计哲学:

  • 严格的状态变更流程(必须通过mutations)
  • 中心化的store管理
  • 强调可预测性和调试能力
  • 适合大型企业级应用

Pinia的设计哲学:

  • 灵活的状态管理(可直接修改)
  • 去中心化的store组织
  • 强调开发体验和简洁性
  • 适合现代Vue 3应用开发

2.3 详细特性对比表

特性维度 Vuex 4 Pinia 影响分析
学习曲线 陡峭(4个核心概念) 平缓(3个核心概念) Pinia上手更快
TypeScript支持 需要类型辅助 一流的自动推断 Pinia开发效率更高
包体积 ~10KB (gzipped) ~5KB (gzipped) Pinia更轻量
性能表现 良好 优秀(更少的包装层) Pinia略有优势
代码组织 模块嵌套,需要命名空间 扁平化store,天然隔离 Pinia更清晰
状态修改 必须通过mutations 可直接修改或通过actions Pinia更灵活
组合式API支持 兼容但不够自然 深度集成,体验一致 Pinia更现代
DevTools支持 完善 同等完善 两者都优秀
插件生态 丰富但复杂 简洁且易扩展 各有优势

三、Pinia底层实现深度解析

3.1 核心架构实现

Pinia的核心架构基于Vue 3的响应式系统和依赖注入机制,以下是其简化实现:

// 简化的Pinia核心实现
class Pinia {
  constructor() {
    this._s = new Map() // store注册表
    this._a = null // 当前活跃的pinia实例
    this._e = new Map() // 扩展插件
  }
  
  // store工厂函数
  defineStore(idOrOptions, setup) {
    return function useStore(pinia) {
      // 获取或创建store实例
      pinia = pinia || currentPinia
      
      if (!pinia._s.has(id)) {
        // 创建响应式store
        const store = createSetupStore(id, setup, pinia)
        pinia._s.set(id, store)
      }
      
      return pinia._s.get(id)
    }
  }
}

// store创建过程
function createSetupStore($id, setup, pinia) {
  let scope
  
  // 创建响应式上下文
  const partialStore = {
    _p: pinia,
    $id,
    // ... 其他属性和方法
  }
  
  // 使用effectScope管理响应式依赖
  const setupStore = pinia._e.run(() => {
    scope = effectScope()
    return scope.run(() => setup())
  })
  
  // 合并store
  const store = reactive(
    Object.assign(partialStore, setupStore)
  )
  
  // 添加store方法
  store.$patch = function $patch(partialStateOrMutator) {
    // 实现状态批量更新
  }
  
  store.$subscribe = function $subscribe(callback, options = {}) {
    // 实现状态订阅
  }
  
  return store
}

3.2 响应式系统集成

Pinia深度集成Vue 3的响应式系统,其数据流如下图所示:

Pinia响应式数据流
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Component A   │    │   Pinia Store   │    │   Component B   │
│                 │    │                 │    │                 │
│  ┌───────────┐  │    │  ┌───────────┐  │    │  ┌───────────┐  │
│  │   State   │◄─┼────┼──│   State   │──┼────┼──│   State   │  │
│  │  (ref)    │  │    │  │  (ref)    │  │    │  │  (ref)    │  │
│  └───────────┘  │    │  └───────────┘  │    │  └───────────┘  │
│                 │    │                 │    │                 │
│  ┌───────────┐  │    │  ┌───────────┐  │    │  ┌───────────┐  │
│  │  Getter   │◄─┼────┼──│  Getter   │──┼────┼──│  Getter   │  │
│  │(computed) │  │    │  │(computed) │  │    │  │(computed) │  │
│  └───────────┘  │    │  └───────────┘  │    │  └───────────┘  │
│                 │    │                 │    │                 │
│  ┌───────────┐  │    │  ┌───────────┐  │    │  ┌───────────┐  │
│  │  Action   │──┼────┼─►│  Action   │◄─┼────┼──│  Action   │  │
│  │ (method)  │  │    │  │ (method)  │  │    │  │ (method)  │  │
│  └───────────┘  │    │  └───────────┘  │    │  └───────────┘  │
└─────────────────┘    └─────────────────┘    └─────────────────┘
        │                        │                        │
        │                        │                        │
        ▼                        ▼                        ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Vue Reactivity│    │  Effect Scope   │    │  DevTools Hook  │
│     System      │    │   Management    │    │    Integration  │
└─────────────────┘    └─────────────────┘    └─────────────────┘

3.3 依赖注入机制

Pinia利用Vue 3的provide/inject API实现store的依赖注入:

// Pinia的依赖注入实现
const piniaSymbol = Symbol('pinia')

// 安装Pinia插件
function install(app, pinia) {
  // 提供pinia实例到整个应用
  app.provide(piniaSymbol, pinia)
  
  // 全局混入,方便Options API使用
  app.mixin({
    beforeCreate() {
      const options = this.$options
      if (options.pinia) {
        // 根组件设置pinia
        this._provided = {
          ...this._provided,
          [piniaSymbol]: options.pinia
        }
      }
    }
  })
}

// 获取store实例
function useStore(pinia) {
  // 从当前组件实例获取pinia
  const instance = getCurrentInstance()
  pinia = pinia || instance && inject(piniaSymbol)
  
  if (!pinia) {
    throw new Error('Pinia实例未找到')
  }
  
  return pinia._s.get(storeId)
}

3.4 插件系统架构

Pinia的插件系统基于中间件模式,允许在store生命周期中注入逻辑:

// 插件系统实现
function createPinia() {
  const pinia = {
    _s: new Map(),
    _p: [], // 插件列表
    use(plugin) {
      this._p.push(plugin)
      return this
    },
    _e: {
      // 插件执行上下文
      run(fn) {
        const runners = this._p.map(plugin => plugin({ pinia }))
        try {
          return fn()
        } finally {
          runners.forEach(cleanup => cleanup && cleanup())
        }
      }
    }
  }
  return pinia
}

// 持久化插件示例
const persistencePlugin = ({ store }) => {
  // 从localStorage恢复状态
  const stored = localStorage.getItem(store.$id)
  if (stored) {
    store.$patch(JSON.parse(stored))
  }
  
  // 订阅状态变化
  return store.$subscribe((mutation, state) => {
    localStorage.setItem(store.$id, JSON.stringify(state))
  })
}

四、性能优化与最佳实践

4.1 性能优化策略

1. 响应式优化

// ❌ 避免:频繁解构store
computed(() => {
  const { items, filters } = useProductStore()
  return items.filter(/* ... */)
})

// ✅ 推荐:一次性解构
const productStore = useProductStore()
const { items, filters } = storeToRefs(productStore)

const filteredItems = computed(() => 
  items.value.filter(item => 
    filters.value.some(filter => item.tags.includes(filter))
  )
)

2. 计算属性缓存

// 使用computed进行缓存
const expensiveComputation = computed(() => {
  // 复杂计算逻辑
  return heavyCalculation(store.data)
})

// 避免在模板中直接计算
// ❌ <div>{{ heavyCalculation(store.data) }}</div>
// ✅ <div>{{ expensiveComputation }}</div>

4.2 代码组织最佳实践

src/
├── stores/
│   ├── index.ts              # 统一导出
│   ├── useUserStore.ts       # 用户相关状态
│   ├── useCartStore.ts       # 购物车状态
│   ├── useProductStore.ts    # 商品状态
│   ├── useUIStore.ts         # UI状态
│   └── types/               # 类型定义
│       ├── user.ts
│       ├── product.ts
│       └── index.ts
├── composables/             # 组合式函数
│   ├── useCartLogic.ts
│   └── useProductFilter.ts
└── plugins/                 # Pinia插件
    └── persistence.ts

五、迁移策略与升级指南

5.1 从Vuex迁移到Pinia

逐步迁移策略:

  1. 并行运行阶段:Vuex和Pinia共存
  2. 模块迁移:按功能模块逐个迁移
  3. 清理阶段:移除Vuex依赖

代码迁移示例:

// Vuex模块
// store/modules/user.js
export default {
  namespaced: true,
  state: () => ({
    name: '',
    token: null
  }),
  mutations: {
    SET_USER(state, user) {
      state.name = user.name
      state.token = user.token
    }
  },
  actions: {
    async login({ commit }, credentials) {
      const user = await api.login(credentials)
      commit('SET_USER', user)
    }
  }
}

// 对应的Pinia Store
// stores/useUserStore.js
export const useUserStore = defineStore('user', {
  state: () => ({
    name: '',
    token: null
  }),
  actions: {
    async login(credentials) {
      const user = await api.login(credentials)
      // 直接修改状态,无需mutation
      this.name = user.name
      this.token = user.token
    }
  }
})

5.2 兼容性处理

// 适配层:让Pinia兼容Vuex风格的代码
const createVuexCompatLayer = (piniaStore) => {
  return {
    state: piniaStore.$state,
    getters: new Proxy({}, {
      get(target, key) {
        return piniaStore[key]
      }
    }),
    commit: (mutation, payload) => {
      // 将mutation映射到action或直接修改
    },
    dispatch: (action, payload) => {
      return piniaStore[action](payload)
    }
  }
}

六、未来展望与社区生态

6.1 Pinia 2.0路线图

  • 更好的SSR支持
  • 性能优化(更小的包体积)
  • 增强的DevTools集成
  • 更多的官方插件

6.2 生态系统

  • pinia-plugin-persistedstate:状态持久化
  • pinia-plugin-debounce:action防抖
  • pinia-plugin-undo:状态撤销/重做
  • @pinia/testing:测试工具

结论

Pinia作为Vue 3的官方状态管理库,代表了Vue状态管理的新方向。它通过简洁的API设计、一流的TypeScript支持和现代化的架构,解决了Vuex在开发体验和类型安全方面的痛点。

核心价值总结:

  1. 开发效率:减少50%以上的样板代码
  2. 类型安全:完整的TypeScript支持,减少运行时错误
  3. 架构清晰:扁平化的store组织,易于维护和扩展
  4. 性能优异:更轻量的实现,更好的Tree-shaking支持
  5. 未来友好:深度集成Vue 3生态,持续活跃的社区

对于新项目,特别是基于Vue 3的项目,Pinia无疑是最佳选择。对于现有Vuex项目,建议制定渐进式迁移计划,逐步享受Pinia带来的开发体验提升。

在Vue状态管理的演进道路上,Pinia不仅是一个工具升级,更是开发理念的进步——它证明了简洁性、类型安全和开发体验可以完美共存,为Vue生态的未来发展奠定了坚实基础。

监控系统出现漏洞,300多人被带走?海康威视回应:假的

4月7日,一则关于海康威视的传言在社交平台迅速传播。网传图片显示,海康威视监控系统出现漏洞,总部300多人被带走调查。针对这一传言,记者以投资者身份致电海康威视,接线工作人员明确表示,上述传言系谣言,公司不存在上述情况,并强调“不信谣不传谣”。据该工作人员个人了解,公司目前在伊朗暂无相关业务。(21财经)

领益智造:已为国内外头部客户供应折叠屏终端硬件

36氪获悉,领益智造在互动平台表示,公司已为国内外头部客户供应折叠屏终端硬件,核心产品涵盖不锈钢/钛合金/碳纤维等材质的折叠屏支撑件及中框、铜/不锈钢/钢铜复合/铝合金/钛等材质的VC均热板、折叠屏转轴模组、模切功能件/结构件、充电器等关键组件。目前公司折叠屏各类项目推进顺利,正按规划推进量产爬坡及客户交付。

Cursor + Claude Code 组合使用心得:我为什么不只用一个 AI 编程工具

Hi~大家好呀,我是清汤饺子。

之前写了不少 Cursor 和 Claude Code 的单独教程,但最近被问得最多的一个问题其实是: "你平时到底用哪个?两个都装了吗?"

我的答案是:都用。

不是因为我钱多烧得慌,而是这两工具真的互补。用了一段时间组合拳之后,我的开发效率又上了一个台阶。今天就来聊聊我的真实使用心得。


一、我踩过的坑:只用其中一个的局限

1. 只用 Cursor?热情过头容易翻车

刚开始我是 Cursor 重度用户,毕竟 IDE 里直接写代码的感觉很顺。但用久了发现一个问题:Cursor 太"积极"了

有时候我只是想改个小样式,它能给你整出来一套组件抽象。我只是想加个简单的加载状态,它直接给你上了一整套状态管理方案。

不是说不好,而是有时候我真的就只想——快点搞定,别整那么多花活。

2. 只用 Claude Code?终端操作有局限

后来切到 Claude Code,发现上下文理解确实强,代码质量也更高。但问题是:它毕竟是 命令行工具 ,对 GUI 项目有点不太友好

比如我想在浏览器里调试一个 CSS 动画,想在某个特定的光标位置试试某个交互——这种事在终端里做起来就没有那么直接。

3. 结论:没有银弹

所以我的结论是:两个工具各有各的擅长领域,硬要二选一反而是给自己找麻烦。组合使用才是最优解。


二、场景分工:什么时候用哪个

经过几个月的磨合,我是这么分工的:

1. Cursor 更适合这些场景

1. 快速原型搭建

当我要验证一个新想法或者快速搭一个 demo,Cursor 的多文件编辑能力很强,能一次性给你生成一整套页面。这种场景 Claude Code 也可以,但 Cursor 更"短平快"。

2. 批量文件修改

比如我要把项目里 20 个组件的样式从 less 迁移到 styled-components,或者批量替换某个 API 调用——这种事 Cursor 的批量编辑效率很高。

3. GUI 调试

在浏览器里点点改改,看看效果,这种事 Cursor 集成得更好。Claude Code 的话你得靠终端里的预览,体验稍差。

2. Claude Code 更适合这些场景

1. 代码审查

把一堆代码丢给 Claude Code,让它帮我 review 一下质量、看看有没有潜在的 Bug——这种事 Claude Code 做得很漂亮。上下文理解能力强,能发现一些我自己可能忽略的问题。

2. 复杂重构

当我要对一个大文件或者多个模块做重构的时候,我会用 Claude Code 出方案,然后让它一步步来。Cursor 虽然也能做,但复杂场景下 Claude Code 的规划能力更强。

3. 长对话需求

比如我要和 AI 讨论一个架构设计,或者让它帮我分析一段老代码的逻辑——这种需要多轮对话的场景,Claude Code 的体验明显更好。


三、工作流串联:1+1>2 的组合拳

光说场景可能还是有点虚,来说说我每天是怎么组合用这两个工具的。

1. 新功能开发流

  1. 用 Cursor 快速搭架子:新功能的页面和基础组件,用 Cursor 快速生成第一版
  2. 用 Claude Code 审查优化:把代码丢给 Claude Code,让它帮忙看看有没有改进空间
  3. 回到 Cursor 执行:根据 Claude Code 的建议,在 Cursor 里做精细调整

2. Bug 修复流

  1. 用 Cursor 定位问题:在代码里直接搜索定位,Cursor 的跳转和搜索功能比较好用
  2. 用 Claude Code 分析根因:把相关代码丢给 Claude Code,让它帮忙分析可能的原因
  3. 回到 Cursor 修复:确认方案后在 Cursor 里执行修复

3. 代码重构流

  1. 用 Claude Code 出方案:让它先分析现有代码,设计重构方案
  2. 用 Cursor 执行:根据方案在 Cursor 里一步步改,Cursor 的修改精度更高

4. 我的每日模板

早上开工:
→ 用 Claude Code 过一遍昨天的代码,快速 review
→ 用 Cursor 开始今天的功能开发

下午:
→ 遇到复杂问题切 Claude Code 对话
→ 批量修改切 Cursor 执行

收工前:
→ Claude Code 总结今天改动,生成 commit message

四、实战心得:三个月磨合出来的经验

1. Rules 和提示词怎么差异化配置

Cursor 和 Claude Code 我配的 Rules 不太一样:

Cursor侧重于项目的技术规范——用什么组件库、用什么命名方式、样式写在哪个文件里。这种偏"执行层"的东西 Cursor 更容易遵守。

Claude Code侧重于架构和设计原则——为什么要这样设计、有什么权衡、哪种方案更合理。这种偏"思考层"的东西 Claude Code 更擅长。

2. 两个工具的上下文如何互补

我有个小技巧:用 Claude Code 建立项目记忆

每次项目有重大架构调整或者加了新模块,我会用 Claude Code 做一个详细的分析和总结,然后把这个结论记在项目文档里。下次 Cursor 接手这个项目的时候,就能通过读取文档快速了解上下文。

3. 避免"两个 AI 打起来"

有时候两个工具会给出一致的建议,那挺好;但有时候它们意见不一致,甚至互相否定对方的方案。

我的处理方式是:听更了解项目上下文那个的

比如这个模块是我用 Cursor 写的,Cursor 更清楚细节,那就以 Cursor 为主。反之亦然。


五、最后

说了这么多,其实就想说一点:工具是手段,不是目的

不管你用 Cursor 还是 Claude Code,还是两个组合用,最重要的还是解决实际问题。不要为了用工具而用工具,也不要因为某个工具火就跟风。

找到最适合你自己的工作流,让它为你服务——这才是正经事。

也欢迎关注我的公众号「清汤饺子」,获取更多技术干货!

❌