
element-plus type.text is about to be deprecated in version 3.0.0, please use link instead.这条警告我在升级老后台项目时已经看了不下几十遍。可能你也在控制台见过它只是没当回事——毕竟项目还能跑业务没异常顶多日志里多一条黄色“废弃提示”有人干脆在vue.config.js里把 console 过滤掉图个清净。但如果你把 element-plus 锁在 2.x这条提示确实可以一直忽略可一旦团队决定升到 3.x它就不再是 warning而是直接变报错——typetext会从组件库中彻底消失。这篇文章适合两种人看一种是正被老项目里几百处typetext困扰、准备做 element-plus 大版本升级的前端工程师另一种是刚开始用 element-plus从网上复制了旧代码却总看到 deprecation 告警的新手。下面我会把这次废弃完整拆开官方为什么砍掉它、迁移前要做哪些盘点、具体怎么替换、会遇到哪些坑以及怎么避免下次再被废弃 API 打个措手不及。1. 这行警告到底在说什么1.1 从一个“旧时代的流行写法”说起el-button typetext这个写法经历过 Element UI 时代的人都不会陌生。在 element-ui 里文字按钮几乎是后台管理系统的标配表格操作列放“编辑”“删除”权限按钮组放“新增”“导出”它都能用最轻量的视觉形式承载。当年很多后台模板都是这么写的以至于后来大家复制代码时根本不会多想typetext就和el-table、el-pagination一样自然。element-plus 刚出的时候为了兼容老用户的使用习惯也保留了这个 API。但组件库不是一成不变的在 2.x 的某个 minor 版本我记得大概 2.2 开始官方在开发模式下加了这条 Warning提醒开发者“这种用法会在 3.0.0 正式删除请改用el-link。” 这其实是一个非常标准的“弃用预告”流程先通过 console 警告给出一整个大版本的过渡期让用户知道未来要改而不是在 3.0 里一声不吭直接删。不过这里有个容易踩的认知误区很多人以为typetext和el-link只是名字不同、换个标签名就行。实际上两者在语义层面就有本质区别后面我会详细对比。如果你只是机械地把typetext替换成el-link大概率能跑但交互细节、表单提交行为、整体样式都会出现不同程度的“违和感”视觉回归和功能 bug 往往就是这么冒出来的。1.2 官方执意砍掉 typetext 的理由先说结论官方不是闲得慌而是这个 API 的存在让组件库的语义边界变得很混乱。el-button本质是一个 button 元素语义是“触发一个动作”。它天然承担表单提交、弹窗确认、加载状态这些和交互动作强相关的职责。而typetext这种变体只是把按钮的视觉改成了“像文字一样”但它依然是 button依然可以提交表单、依然有按钮的内边距。问题就出在这里当你在一个操作列里放一排“文字按钮”时用户看到的是“链接”但它实际上不跳转、没有 href只是长得像链接的按钮。视觉和语义的割裂对无障碍访问a11y也不友好——屏幕阅读器会把它读作按钮而非链接键盘交互、焦点样式都和真正的链接不同。从维护角度讲typetext也让组件内部的样式逻辑变得很拧巴。element-plus 的按钮类型体系是 default / primary / success / info / warning / danger每一种对应一个语义颜色text混在里面既不是颜色语义也不是 size 语义而是一种“形态语义”和其他 type 完全不在一个维度上。为了支持它组件里要额外处理边框、背景、hover、focus 多套分支平白增加复杂度。总之一句话el-link已经能覆盖 99% 的文字按钮场景官方干脆在 3.0 大版本做一次 breaking change把历史包袱卸掉。这不只是 element-plus 的做法很多成熟组件库都会在大版本里收敛 API——把重复能力合并成一套把模糊语义归一化。理解了这一点你就能明白为什么迁移不能只靠“无脑替换”。2. 迁移前先摸清两者的能力边界2.1 el-button typetext 与 el-link 的能力对比动手之前我强烈建议你先做一次能力盘点搞清楚两个组件各自的边界。我把最关键的对比列一张表你在改造时照着看会清晰很多。能力维度el-button typetextel-link渲染标签buttona语义触发动作跳转/链接点击事件原生 click原生 click但要注意 anchor 默认行为href 属性不支持支持target新窗口需要自己写 window.open原生支持 target_blankdisabled支持支持loading支持不支持size 属性支持不支持默认内边距有按钮自带无纯文本hover 效果背景色变化显示下划线表单内提交能力可以触发 form submit不可以需要手动处理这张表里最需要关注的有两行loading 不支持和表单内提交能力。el-button typetext支持 loading这在一些异步操作按钮里很常见比如“保存中…”“提交中…”。改成el-link后没有 loading 属性你只能自己用一个局部v-loading指令或者干脆用一个动态 class 切换文案和 icon。这个改动不算复杂但很容易被忽略。表单提交就更隐蔽了。假设你原来在某个el-form里放了一个typetext的“查询”按钮它天然是 button 元素点击后会触发表单的 submit 事件。改成el-link后它是a标签根本不会提交表单。如果你没有意识到这一点功能会在毫无报错的情况下悄悄失效这是最让人头疼的一种 bug。2.2 动手前的全局代码盘点在批量替换之前先花半小时做一次全局代码盘点会大大降低后续的返工成本。我用得最多的方法就是 grep把项目里所有typetext先捞出来按使用场景分类。grep -rn type\text\ src --include*.vue如果你是 TS Vue 项目可能还有 tsx 或 render 函数里的写法也要一并扫grep -rn type: [\]text[\] src --include*.tsx grep -rn type: [\]text[\] src --include*.ts拿到结果后先不要急着改把每一处使用场景归类到下面几个典型类型里表格操作列最常见比如“编辑 / 删除 / 详情”基本是纯 click 触发迁移最安全。表单内的按钮比如“查询 / 提交 / 重置”这类要特别注意表单提交逻辑不能只换标签。页面里的文字入口比如“查看更多”“全部订单”这类本质是导航跳转换成el-link反而更合适。权限按钮组通常是根据角色动态渲染的一组操作项可能还混用了 icon、disabled、v-if需要逐项核对。做完分类后你心里就有数了哪些是“无脑替换”、哪些需要“特殊处理”。批量迁移最忌讳的就是一股脑全替换完结果打开页面才发现功能悄悄变了。3. 实操迁移三步把 typetext 换成 link3.1 第一步最简替换处理可以直接换的部分很多场景其实可以直接替换尤其是表格操作列里那种纯点击触发、没有任何表单语义的“编辑”“查看”按钮。我们拿一个最典型的例子来说。迁移前template el-table-column label操作 template #default{ row } el-button typetext clickhandleEdit(row)编辑/el-button el-button typetext clickhandleDelete(row)删除/el-button /template /el-table-column /template迁移后template el-table-column label操作 template #default{ row } el-link typeprimary clickhandleEdit(row)编辑/el-link el-link typedanger clickhandleDelete(row)删除/el-link /template /el-table-column /template表面看只改了标签但有两个细节需要马上确认。第一颜色语义变了。el-button typetext默认文字颜色是 primary 蓝但很多项目在主题里自定义过--el-color-primary所以实际渲染出来的蓝可能和你想象中不一样。而el-link的typeprimary会走同一套主色变量一般问题不大如果你原来想要的是灰黑色文字可以显式给el-link加typeinfo或者用一个自定义 class 覆盖。第二间距没了。按钮自带水平内边距所以在表格操作列里出现“编辑 删除”时两个按钮之间天然有间距el-link是纯文本多个链接挤在一起视觉上会粘成一团。这里我建议给操作列统一加一个间距类template div classtable-action-cell el-link typeprimary clickhandleEdit(row)编辑/el-link el-link typedanger clickhandleDelete(row)删除/el-link /div /template style scoped .table-action-cell .el-link .el-link { margin-left: 12px; } /style这个小小的间距处理经常是项目改完后“看起来怪怪的”的元凶。代码层面明明都换了但视觉上总觉得操作列没有以前整齐其实就是少了间距和 hover 反馈。3.2 第二步处理事件、加载状态与表单提交第二类场景需要多动一步主要是带disabled、loading、以及位于表单内的按钮。先看disabled这个可以直接映射template el-link typeprimary :disabled!hasPermission clickhandleExport 导出数据 /el-link /templateel-link在disabled状态下会失去点击反应并且应用降透明度的禁用样式基本可以等效替代。再看loading这个就比较麻烦了。el-button自带 loading 旋转圈换成el-link之后没有对应属性。我当时迁移时采用的方式是局部v-loading包裹或者在el-link内部手动渲染一个 Loading 图标再配合loading状态禁用点击。template el-link typeprimary :disabledloading clickhandleSave el-icon v-ifloading classis-loading :size14 stylevertical-align: middle Loading / /el-icon span stylevertical-align: middle{{ loading ? 保存中... : 保存 }}/span /el-link /template script setup import { Loading } from element-plus/icons-vue /script样式上给图标加一个is-loadingclass让它旋转起来。这样视觉交互能基本还原旧按钮的 loading 体验耗时也不会太长。最后是表单提交场景这是最容易出功能 bug 的地方。假设你原来在查询表单里放了一个文字按钮点击后触发表单提交并执行查询el-form :modelqueryForm submit.preventhandleSearch el-button typetext native-typesubmit查询/el-button /el-form直接替换成el-link后handleSearch不会被触发。正确做法是去掉对表单 submit 的依赖改成显式调用表单提交逻辑el-form refsearchFormRef :modelqueryForm submit.preventhandleSearch el-link typeprimary click.preventhandleSearch查询/el-link /el-formclick.prevent是为了阻止a标签的默认跳转行为因为el-link即使不传href有些情况下浏览器也会因为它是可点击元素产生多余行为加一层prevent更稳妥。如果你的查询逻辑依赖表单校验那就在handleSearch里先拿searchFormRef调用validate校验通过后再发起请求。3.3 第三步用一套自定义样式还原旧观感替换完成后大概率会出现一次视觉回归旧文字按钮有内边距、hover 时背景会淡淡变灰而el-link默认是纯文本、hover 出现下划线。如果你们的 UI 设计师对细节敏感这一步就得认真处理。我当时的做法是封装了一个.legacy-text-btn类在替换后的el-link上统一加上用一套 CSS 把旧文字按钮的观感还原回来.legacy-text-btn.el-link { padding: 0 4px; font-size: 14px; line-height: 22px; transition: color 0.2s, background-color 0.2s; border-radius: 4px; } .legacy-text-btn.el-link:hover { color: var(--el-color-primary); background-color: var(--el-color-primary-light-9); text-decoration: none; } .legacy-text-btn.el-link.is-disabled { color: var(--el-text-color-disabled); background-color: transparent; }这套样式主要做三件事恢复左右内边距让多个链接并排时不用额外加间距恢复 hover 时的浅色背景让鼠标悬停反馈接近旧文字按钮禁用状态下保持旧按钮的淡灰色而不是沿用el-link默认的禁用样式。实际应用时你们可以根据项目主题变量微调颜色值。如果你的项目里这种旧按钮用在很多地方我建议不要一个一个手写 class而是在公共样式文件里全局定义然后替换时顺手加上。这样以后想调整样式只需要改一个文件。4. 迁移过程中常见的坑和排查4.1 “明明改了为什么还报废弃警告”这应该是迁移结束后最容易碰到的问题。代码里已经找不到任何typetext了但控制台仍不断打印 deprecated 提示。排查顺序基本是下面三步。第一确认依赖树里是否还有旧版本的 element-plus。有些项目同时存在顶层依赖和嵌套依赖或者是 pnpm 的 hoist 策略导致node_modules里锁了一份旧版本。建议先跑一下npm ls element-plus如果发现项目中实际解析到的版本不是预期版本优先清理锁文件重新安装。第二检查第三方组件库或内部业务组件是否还在用旧 API。我遇到过这种情况自己页面的代码全改完了但某个自研表格组件内部封装了el-button typetext只要页面使用了那个组件警告就会一直出现。解决方案是全局再搜一次typetext把范围扩大到node_modules内的业务组件包或者让组件库负责人同步升级。第三确认构建产物被正确清理。有时候是开发服务器或 CI 缓存了旧的编译产物。尤其是用了 Vite 的项目node_modules/.vite缓存会保留旧的模块信息跑一遍vite --force或者删掉缓存目录再启动警告往往会消失。4.2 表格操作列的视觉回归比想象中更多在表格操作列里el-button typetext原本是有固定行高和内边距的所以表格行的高度、操作列的宽度都形成了微妙的平衡。替换成el-link后最直接的影响就是操作列宽度变窄、操作项之间的间距消失。排查时不要只盯着页面看建议打开浏览器 DevTools 对比一下操作列容器的实际宽度以及相邻操作项之间的空隙。如果出现错位顺手处理两个问题给外层容器加flex布局并设置align-items: center给操作项设置统一样式类用margin-right控制间距。另外还有一个比较容易漏的地方旧文字按钮在disabled状态下鼠标样式是not-allowedel-link默认也是但如果你自定义了.legacy-text-btn的cursor样式需要确认没把它覆盖掉。4.3 render 函数和 tsx 中的迁移写法如果你的项目里有动态渲染的列配置或者用h函数生成操作按钮替换写法也要跟着调整。我见过不少项目在columns配置里用render函数生成操作列这里的替换最容易踩类型坑。迁移前import { h } from vue import { ElButton } from element-plus const renderAction (row) { return h( ElButton, { type: text, onClick: () handleDelete(row), }, () 删除 ) }迁移后import { h } from vue import { ElLink } from element-plus const renderAction (row) { return h( ElLink, { type: danger, onClick: () handleDelete(row), }, () 删除 ) }这里需要注意两点。第一ElLink的类型定义中onClick接收的是MouseEvent如果你原来的处理函数里有event参数、并且用到了event.preventDefault()要确认类型仍然匹配。第二如果原来的 render 函数里给ElButton传了size属性迁移后ElLink原生不支持size需要在样式中补齐否则出现不明显的尺寸差异。4.4 常见问题速查表我把迁移中高频出现的问题整理成一张速查表方便你按图索骥。现象原因解决办法控制台仍报 deprecated 警告依赖树有旧版本 / 业务组件内部还在用npm ls element-plus排查外部包同步升级点击查询/提交按钮后表单不提交el-link渲染为a不是 submit 按钮改用click.prevent显式调用表单校验和提交操作列多个文字按钮挤在一起el-link没有按钮内边距统一加间距类如margin-left: 12px按钮 loading 效果消失el-link不支持 loading 属性用局部v-loading或自定义 icon 旋转旧 hover 背景色不见了两种组件 hover 机制不同追加.legacy-text-btn自定义样式disabled 后仍可点击检查是否传了disabled或样式被覆盖确认:disabled绑定设置cursor: not-allowedrender 函数中 TS 类型报错ElLink事件类型与ElButton不同调整onClick参数类型或整个事件定义5. 从一次废弃看组件库升级策略5.1 别再把 deprecation warning 当噪音这次迁移给我最大的感触不是替换了一百多处代码而是不要忽略 deprecation warning。很多前端团队习惯把控制台警告“绿色化”成日常看到黄色提示直接无视。但严格来说这类警告是组件库给你留的缓冲期它在 2.x 时代就明确告诉你了这个 API 未来会删现在还有时间改。等到 3.0 真的发布断崖式报错会直接把项目升级计划卡死到时候再慌慌张张做迁移成本和风险都翻倍。建议把仓库里的 deprecation warning 当成一张 TODO 清单来管理。比如这次迁移前可以统计一下警告出现次数、涉及页面、使用场景形成一张迁移任务表。这样不仅能顺利升级 element-plus以后遇到其他库的废弃告警也可以套用同一个思路。这里还联想到很多其他技术栈里相似的场景比如某些浏览器原生 API 的废弃、Node 版本升级时各种 npm 包告警、新版本构建工具不再兼容旧配置项等等。技术生态的演进本质上就是一段段从 deprecated 到 breaking change 的过程前端对这部分尤其敏感。我们能做的不是抱怨“又来了”而是建立一套应对废弃 API 的例行机制。5.2 一套可复制的升级前检查清单这次迁移结束后我顺手总结了一套组件库大版本升级的安全流程分享出来供你参考。第一步先冻结版本。在准备升级前把 element-plus 版本锁定在当前使用的小版本避免升级过程中依赖自动漂移带来额外变量。第二步全局扫描废弃 API。不要只搜typetext把项目里所有可能引发 warning 的 API 都扫一遍。像是 component name 的废弃、插槽改名、方法替换凡是控制台出现过提示的都记录在案。第三步按场景分批修改。把改动分成“纯替换”和“需重构”两类。纯替换的可以用 codemod 或正则批量改需重构的留给人工处理。我当时把表单内的按钮、带 loading 的按钮、render 函数里的按钮挑出来优先处理剩下的表格操作列节点最后统一批量改效率高很多。第四步用视觉回归作为验收标准。改完代码只是第一步页面视觉效果、交互细节都要对照旧版验收。有条件的话在改造前后分别截图做对比或者用 Playwright 这类工具跑一遍核心路径的截图 diff能快速发现间距、颜色、hover 效果的变化。第五步灰度上线。建议先用一个低流量页面或模块试运行观察有没有用户反馈交互异常确认稳定后再全量替换。虽然typetext到el-link是官方推荐的路径但每个项目的主题定制、全局样式覆盖度不同灰测能兜住最后一层风险。5.3 迁移完不妨回头审视其他历史代码typetext的迁移是一个很好的契机可以顺带把项目的组件使用习惯梳理一遍。很多老项目里不仅有typetext还有一堆早就该淘汰的旧写法比如手写window.open而不是使用统一的跳转封装在表格操作列里面写一整串重复的el-button没有抽象成列配置把el-link的活交给了el-button去做导致所有“看起来像链接”的交互都堆在按钮组件上。借着这次迁移我会建议大家在操作列组件层面做一层统一封装比如把“编辑 / 删除 / 详情”这些操作项抽成一个TableActions组件。这样即使以后某个操作项要加禁用、加权限、加 loading都只需要改一处。同时也把el-link真正用在该用的地方凡是带href跳转的一律用el-link凡是纯触发行为的继续用el-button。语义清晰了维护成本自然就降下来了。最后再分享一个小技巧如果你现在还在 2.x 版本、短期无法升 3.0不用急着把typetext全部改掉但务必在升级前完成迁移。反过来如果你刚开始一个新项目就直接使用el-link吧别再去复制过去那些老代码了。旧写法的“兼容期”不是无限期的越早做功能性替换后面的升级成本就越低。