Vue过滤器从入门到实践:核心用法、链式调用与避坑指南

发布时间:2026/10/16 1:05:00
Vue过滤器从入门到实践:核心用法、链式调用与避坑指南 1. 从一次页面渲染异常说起为什么需要过滤器先讲个真实场景。某次我在维护一个后台管理系统订单列表页需要把后端返回的状态码展示成可读文案。后端给的是1、2、3、4页面上要显示待支付、已支付、已发货、已完成。一开始我直接在模板里写了三四个三元表达式嵌套代码大概长这样span{{ order.status 1 ? 待支付 : order.status 2 ? 已支付 : order.status 3 ? 已发货 : 已完成 }}/span说实话这种写法在首次上线时勉强能用但后续需求一变更就崩了。产品经理说要把4改成已完成可评价同时新增一个5表示退款中。我翻遍模板找到那行三元表达式改了五分钟才改对。后来仔细一琢磨这种状态映射逻辑根本不应该写在模板里应该用过滤器统一处理。Vue 过滤器就是用来解决这类问题的。它的本质是一个可以在模板插值或v-bind表达式中使用的函数接收原始数据处理后再返回展示结果。比如上面的场景用过滤器写就是span{{ order.status | statusText }}/spanfilters: { statusText(value) { const map { 1: 待支付, 2: 已支付, 3: 已发货, 4: 已完成可评价, 5: 退款中 }; return map[value] || 未知状态; } }模板瞬间清爽了状态和文案的映射关系也集中到了一个地方后续改文案只动过滤器即可。很多人觉得过滤器是个无关紧要的小功能但实际项目里用好了对模板可读性的提升是肉眼可见的。这篇文章我就把 Vue 过滤器的核心用法、注册方式、注意事项和踩坑经验一次性讲透适合正在用 Vue 2 做项目、或者从 Vue 2 迁移 Vue 3 时被过滤器问题卡住的开发者参考。内容基于实际项目经验整理不是官方文档的复读重点放在“怎么用最合理”和“哪些坑必须避开”上。2. Vue 过滤器的核心机制与适用场景2.1 过滤器到底是什么Vue 官方文档里的定义是过滤器可以用在两个地方——双花括号插值和v-bind表达式。它由“管道符”|触发前面的值会作为第一个参数传入过滤器函数。!-- 在双花括号插值中使用 -- p{{ message | capitalize }}/p !-- 在 v-bind 中使用 -- div v-bind:idrawId | formatId/div需要看穿的一点是过滤器的执行时机是组件渲染过程中而不是数据变更时。也就是说过滤器在每次重新渲染时都会重新执行这一点影响性能评估。如果一个过滤器内部做了很重的计算比如大量数据遍历、复杂正则匹配每次都跑一遍性能问题就会积累。这一点下面细讲。过滤器的设计思路和 Unix 管道命令很像。数据像水流一样从左到右流动每一级管道做一次处理。多重过滤器也是这么链式调用的p{{ text | lowercase | capitalize }}/ptext的值先传给lowercase处理完的结果再传给capitalize。注意顺序是从左到右执行的内容变换的方向别搞反了。2.2 过滤器和计算属性的分工初学者经常纠结一个问题同样的数据处理用computed和用filter都能实现到底该选哪个我自己的实践经验是如果处理结果要绑定给多个属性、或者要参与其他计算逻辑用computed如果纯粹是模板展示环节的格式转换用filter。举个例子商品价格可能是1299页面要显示¥1,299.00。如果你在多个地方用到这个格式化结果——列表页、详情页、购物车——那我会倾向于写成过滤器因为它是纯粹的展示格式化不参与任何业务计算p{{ price | formatPrice }}/p但如果你要根据价格是否超过 1000 来展示不同等级的优惠标签那建议用computed来派生computed: { priceLevel() { if (this.price 1000) return high; if (this.price 500) return mid; return low; } }核心判断标准就一条这个数据变了之后是会影响到多个视图的展示格式还是会影响一段业务逻辑格式问题交给过滤器逻辑问题交给计算属性。2.3 过滤器的能力边界过滤器不是万能的。Vue 2 官方明确规定过滤器只能用在插值和v-bind表达式中不能用在v-model上不能用在指令的指令参数里也不能在 JS 逻辑代码中直接调用。如果你想在组件的methods里复用某个格式化逻辑正确做法是把核心逻辑单独抽成一个函数然后过滤器和 methods 都引用它// utils/format.js export function formatPrice(value) { return ¥${Number(value).toFixed(2)}; } // 组件内 import { formatPrice } from ../utils/format; filters: { formatPrice }, methods: { handleCheckout() { const displayPrice formatPrice(this.price); } }单纯依赖过滤器做所有格式化很容易遇到场景无法覆盖的时候。把逻辑抽离出来再复用才是更健康的代码组织方式。3. 从入门到实战过滤器的注册与参数传递3.1 全局过滤器注册全局过滤器通过Vue.filter()注册注册后所有组件实例都能使用不需要在组件里重复定义。// main.js Vue.filter(formatDate, (value, format YYYY-MM-DD) { if (!value) return ; const date new Date(value); const pad n String(n).padStart(2, 0); if (format YYYY-MM-DD) { return ${date.getFullYear()}-${pad(date.getMonth() 1)}-${pad(date.getDate())}; } return date.toISOString(); });全局过滤器适合高频通用场景日期格式化、金额千分位、字符串截断、文本清洗。我通常把全局过滤器集中放在一个filters/index.js文件里统一管理// filters/index.js import Vue from vue; const filters { formatDate: ..., formatPrice: ..., trimText: ..., upperFirst: ... }; Object.keys(filters).forEach(key { Vue.filter(key, filters[key]); });在入口文件直接import ./filters即可不用在组件里逐个引入非常省事。3.2 组件内过滤器注册组件内过滤器只在当前组件中生效注册方式是在组件的filters选项中定义。和全局过滤器相比组件内过滤器的优势是隔离性好不会污染全局命名空间。如果某个格式化逻辑只会在这一个组件里用到就别注册全局的写在组件内部更干净export default { data() { ... }, filters: { fileSize(bytes) { if (!bytes) return 0 B; const units [B, KB, MB, GB, TB]; const i Math.floor(Math.log(bytes) / Math.log(1024)); return ${(bytes / Math.pow(1024, i)).toFixed(2)} ${units[i]}; } } }文件大小格式化、性别数字转文字、枚举状态转标签类名这种场景特别适合组件内过滤器。它们往往和当前业务强相关复用性不高全局注册反而增加认知负担。3.3 参数传递过滤器的高级用法过滤器的第一个参数永远是管道符左边的值。如果要传递额外参数直接写在过滤器名后面用括号传参即可。p{{ amount | toFixed(2) }}/p p{{ content | sliceText(0, 20, ...) }}/p对应的过滤器函数定义里value是管道符号前的数据后面的参数依次填入filters: { toFixed(value, digits) { return Number(value).toFixed(digits); }, sliceText(value, start, end, ellipsis ...) { if (!value) return ; if (value.length end) return value; return value.slice(start, end) ellipsis; } }多条参数管道组合使用也很常见但要注意别过度封装。我见过有人写出一行五六个过滤器串在一起的模板排查问题简直噩梦p{{ rawData | filterA | filterB(1, 2) | filterC | filterD }}/p这种代码第一时间看不出每一级到底做了什么出错时只能一级一级去查。建议一个模板里的过滤器链控制在两三个以内如果逻辑确实复杂老老实实抽到computed里吧。3.4 过滤器中的this指向陷阱一个高频踩坑点过滤器函数内部的this不是当前组件实例。Vue 2 源码中过滤器函数是直接调用的不绑定组件上下文。所以你在过滤器里写this.someData拿不到想要的东西。filters: { formatWithConfig(value) { // 这里 this 不是组件实例 return ${this.prefix}${value}; } }如果你确实需要在过滤器中依赖组件上下文的数据比如根据当前用户权限决定展示格式有几个替代方案把依赖数据作为参数传入过滤器模板中直接传this.xxx对应的变量注意模板中可直接用prefix改用computed或methods正确写法p{{ content | formatWithConfig }}/p配合filters: { formatWithConfig(value, prefix) { return ${prefix}${value}; } }模板里改成p{{ content | formatWithConfig(prefix) }}/p这样数据从外部传入过滤器保持纯函数特性测试和维护都更舒服。4. 过滤器、串联过滤器的链式调用与性能思考4.1 链式调用的执行顺序首先把执行顺序彻底说透。对于p{{ text | filterA | filterB }}/p执行流程是1. text 作为参数调用 filterA得到 resultA 2. resultA 作为参数调用 filterB得到 resultB 3. resultB 输出到模板顺序是从左到右。有些人习惯反过来理解看到管道符就想到后面的数据处理前面的这是误区。实际验证一下最直接写两个过滤器一个加字符串前缀PRE_一个添加后缀_SUF顺序调换后结果完全不同。4.2 过滤器性能开销的真实情况过滤器在渲染期间执行意味着组件任何响应式数据变化导致重新渲染过滤器全部重新跑一遍。哪怕过滤器依赖的数据完全没变也一样重新执行。这在列表数据很大的场景下会产生明显卡顿。我在一个近千行的表格页里踩过这个坑。每行五六列都用了全局过滤器做格式化过滤器中还有正则匹配。当表格数据更新时整页渲染耗时肉眼可见地增加。后来优化方案是把格式化逻辑迁移到computed里在数据进入表格前就处理好computed: { formattedRows() { return this.rows.map(row ({ ...row, orderStatusText: this.statusText(row.orderStatus), priceText: this.formatPrice(row.price), dateText: this.formatDate(row.createdAt) })); } }循环里一次性处理好所有格式化模板里只做简单绑定渲染性能和可追踪性都提升不少。过滤器更推荐用在单一、轻量格式化的场景复杂数据处理交给computed或后端完成。4.3 数据清洗过滤器的隐藏用法除了展示格式化过滤器还可以承担一部分数据清洗工作。比如接口返回的富文本里经常带着乱七八糟的标签直接v-html有 XSS 风险配合一个清洗过滤器会好很多div v-htmlcontent | sanitizeHtml/divsanitizeHtml(value) { if (!value) return ; // 这里建议用成熟库替代简单正则比如 DOMPurify return value.replace(/script[^]*[\s\S]*?\/script/gi, ); }但必须强调过滤器不能替代真正的安全防护。过滤器的输出是在客户端做展示处理真正的 XSS 防护必须由后端在输入阶段做严格校验前端过滤器只是兜底措施。尤其是涉及v-html的场景不要只用简单的字符串替换就完事否则等于把安全防线漏了个大洞。这种“清洗型过滤器”用好了体验很好。比如用户输入的手机号展示时需要脱敏p{{ phone | maskPhone }}/pmaskPhone(value) { if (!value || value.length 7) return value; return value.replace(/(\d{3})\d{4}(\d{4})/, $1****$2); }本质上过滤器就是一个“展示前管道处理器”做格式化、脱敏、清洗都是它的适合领地。5. 必须避开的五个大坑与排除实践5.1 滤器名称冲突的坑全局过滤器和组件内过滤器允许同名同名时优先使用组件内过滤器。这个规则本身不复杂但多人协作时很容易踩坑。比如说全局定义了一个formatDate某个组件内又定义了同名但逻辑不同的formatDate排查 bug 时极难发现——你以为是全局逻辑实际被组件覆盖了。规范建议组件内过滤器命名时前缀加上组件名或业务前缀例如orderStatusText、userPhoneMask尽量避免和全局过滤器同名。同时新增过滤器之前先在filters/index.js里扫一眼确认不存在同名情况。5.2 过滤器里改传入参数的坑JavaScript 中对象和数组是引用传递。如果你在过滤器里对传入的对象参数做了修改会直接改动原始数据。filters: { addLabel(item) { item.label 新标签; // 这里改了原始对象 return item; } }模板渲染时原始对象被污染可能导致其他依赖该数据的地方出现难以预料的 bug。过滤器应当是纯函数——相同输入永远是相同输出且不修改外部状态。需要返回修改后的值先拷贝再操作filters: { addLabel(item) { const copy { ...item, label: 新标签 }; return copy; } }5.3 日期格式化时区问题这是国际化项目里常见的坑。后端返回时间戳或 ISO 字符串前端直接new Date(value)后格式化会遇到时区偏移的问题。比如某后端返回2024-06-01T16:00:00.000ZUTC 时间在 UTC8 时区下本地显示是2024-06-02 00:00:00。直接按getFullYear()取取到的是本地时区的日期可能是第二天。处理方案过滤器中明确指定使用哪个时区和格式或者后端统一返回已经处理好的本地时间字符串。如果必须前端处理最好借助成熟的日期库注意项目需自行评估依赖风险和许可证适合场景再引入自己徒手实现 UTC 转本地且处理夏令时之类的问题代码会变得非常不可控。5.4 Vue 3 中过滤器被移除的迁移问题如果你在做 Vue 3 升级这里有个必须提前知道的点Vue 3 官方移除了过滤器。原因是从设计上看过滤器不够“显性”且已有计算属性和方法可以覆盖它的应用场景。迁移方案有两个方案一替换为方法methodsp{{ formatDate(date) }}/pmethods: { formatDate(value) { // ... } }方案二替换为计算属性computed这种方法适合格式化逻辑依赖响应式数据且会被多处引用的场景computed: { formattedDate() { return this.formatDate(this.date); } }如果项目里用了大量过滤器我建议先写个扫描脚本把| filterName全部找出来逐个替换。尤其注意在 Vue 3 中模板里存在|管道符号不会报错但会被当作非法表达式直接编译失败。所以升级前做一次全局检索很关键。还有一个过渡用的思路有的团队会在 Vue 3 项目中通过自定义指令或混入来模拟过滤器但我个人不建议这么做——绕了一圈只是维持旧习惯最终还是要学会用方法或计算属性思维来组织代码。直接一步到位迁移到 methods/computed 更省钱。5.5 忘记注册直接使用的坑模板里写了{{ value | myFilter }}但忘了在filters里定义或者全局注册里没写上。报错信息通常是Failed to resolve filter: myFilter排查思路很简单先在源码里全局搜myFilter确认是否存在于filters/index.js或组件filters选项里。如果是按需加载的模块化结构还要确认当前组件是否真的 import 了对应文件。这种问题排查起来不难但每次出现都会浪费不少时间最好在代码提交前做一次模板过滤器的完整检索。6. 一套完整的序化方案从真实项目中提炼6.1 场景回顾我曾经参与维护过一个中等规模的后台管理前端技术栈是 Vue 2 Element UI。页面涉及订单、用户、商品、营销等多个模块格式化需求非常多状态码转文案、金额千分位、时间戳转日期、手机号脱敏、文本截断、枚举转标签颜色等等。早期代码里每个组件都零散地定义着自己的格式化方法同一个“创建时间格式化”逻辑在五个组件里复制了五份。后来一次重构我们把这块系统性地整理了一遍。6.2 分层设计思路最终方案分三层第一层全局通用格式化逻辑工具函数层放在utils/format.js是纯函数集合不依赖 Vue 任何特性方便在任意 JS 文件中调用和单元测试// utils/format.js export function formatPrice(value, currency ¥) { if (value null || value undefined) return --; return ${currency}${Number(value).toLocaleString(zh-CN, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}; } export function formatDateTime(value) { if (!value) return ; const d new Date(value); // ... } export function maskPhone(phone) { // ... }第二层全局过滤器注册层把工具函数包装成过滤器在filters/index.js统一注册。工具函数和过滤器一一对应函数名保持统一避免隐藏映射// filters/index.js import Vue from vue; import * as formatters from ../utils/format; const filterMap { formatPrice: formatters.formatPrice, formatDateTime: formatters.formatDateTime, maskPhone: formatters.maskPhone }; Object.keys(filterMap).forEach(name { Vue.filter(name, filterMap[name]); });第三层组件内局部过滤器业务强绑定的格式化逻辑比如“订单状态徽标类名、用户类型标签文案”这类只在该模块使用的写在组件内filters中同样建议从工具函数引入而不是直接在过滤器里写复杂逻辑// 组件内 import { formatDateTime } from ../utils/format; export default { filters: { orderTime: formatDateTime, statusBadge(status) { const map { pending: warning, paid: success, shipped: primary }; return map[status] || info; } } };6.3 模板中的调用规范我们对设计规范和命名规范的约束如下过滤器命名一律采用驼峰式语义清晰避免缩写如fmt、t之类的。串联过滤器超过 2 个强制抽到computed。过滤器内部禁止修改传入参数。过滤器内部禁止访问this依赖外部状态时改用参数传入。新增加过滤器必须同步补充对应的工具函数和基本注释。模板效果template div p成交金额{{ order.amount | formatPrice }}/p p下单时间{{ order.createdAt | formatDateTime }}/p p联系手机{{ order.phone | maskPhone }}/p el-tag :typeorder.status | statusBadge {{ order.status | statusText }} /el-tag /div /template这样模板核心只表达“展示什么”不再关心“怎么转换”维护时只需要在一个地方修改。6.4 对既有项目的渐进式改造做完这套整理后我总结出一个对旧项目友好的迁移节奏先建立utils/format.js把项目中重复的格式化逻辑收敛成纯函数。再建立filters/index.js用最常用的几个全局过滤器替换模板中高频出现的格式化调用。最后逐个页面清理组件内重复的格式化代码能复用全局的就不复制粘贴。这个过程不需要大版本重构每走一步都能正常上线风险逐个隔离。对于已经用了很久的 Vue 2 项目来说这是最平缓的演进方式。7. 做一个决策可行的总结过滤器用不用、怎么用聊了这么多最后梳理一下我个人的核心判断方便你在实际项目里快速做决策。过滤器适合解决的场景简单格式转换日期、金额、文本截断。高频内容脱敏手机号、身份证、邮箱。枚举状态码转展示文案。模板插值和v-bind上的小型数据清洗。不建议用过滤器的场景需要依赖组件实例数据的逻辑用computed。逻辑复杂、多个步骤处理的流程该方法不阻塞的情况下但用函式方法更清晰。数据需要被多处使用的场景用computed缓存。新写的 Vue 3 项目直接用methods或computed。如果你还在维护 Vue 2 项目过滤器作为一种轻量级模板工具用得恰当能大幅度提升代码可读性。如果你正准备转向 Vue 3也不用过度遗憾——把原来写在过滤器里的逻辑迁到methods/computed中代码表达实际上更直白。我自己做了几次替换之后反而觉得强制不用过滤器逼着你把格式化逻辑提到组件层虽然之前需要改动的地方多一些但整体代码结构确实更加清晰可测了。最后分享一个小习惯我现在每写一个过滤器都会顺手在旁边写两三个测试用例。过滤器是纯函数测试成本极低效果却立竿见影。遇到时区问题、边界值为空之类的情况写完测试的时候已经能提前排掉一半的坑。如果你还没有这个习惯下次写过滤器时尝试加上应该能感受到明显的好处。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询