最近这道面试题出现的频率不低尤其面试 Vue 相关岗位时冷不丁就会被问到“说说 Vue 过滤器是什么有哪些使用场景”很多人第一反应是“用过”但真让展开讲讲又容易和计算属性、方法混在一起说不清楚。今天我就按自己平时带项目、做面试辅导的经验把 Vue 过滤器从底层原理到使用场景再到面试里容易踩的坑完整过一遍。不管你是刚入门 Vue 的新手还是准备跳槽的老手这篇文章都能直接拿来当复习材料。1. 先把过滤器讲清楚它到底是个什么角色1.1 从模板语法看本质Vue 过滤器本质上就是一个普通的 JavaScript 函数专门用来对模板中的文本做格式化处理。它在模板里用管道符|调用和其他编程语言里的管道处理逻辑很像左边的值作为输入经过右边函数处理后输出一个新的值。比如你从接口拿到一个时间戳想在页面上显示成2025-03-12这种格式直接在插值表达式里写一大段日期处理逻辑很难看也复用不了。这时候过滤器就派上用场了template div{{ rawDate | formatDate }}/div /template script export default { data() { return { rawDate: 1741766400000 } }, filters: { formatDate(value) { if (!value) return -- const date new Date(value) const y date.getFullYear() const m String(date.getMonth() 1).padStart(2, 0) const d String(date.getDate()).padStart(2, 0) return ${y}-${m}-${d} } } } /script注意一点过滤器不会修改原始数据它只是对输入值做一次加工后返回新值。这就好比厨房里的“调味环节”菜还是那道菜出锅前撒点调料但不会改变食材本身。这里还要提一个容易被忽略的点过滤器内部拿不到组件实例this。原因后面面试追问部分会细说但先记住结论——过滤器应该是一个纯函数。所谓纯函数就是同样的输入永远得到同样的输出不依赖也不改变外部状态。这样的设计让过滤器天然适合做展示层的轻量格式化也方便单元测试。1.2 过滤器、计算属性、方法的区别很多面试者倒在这一步明明用计算属性也能解决问题为什么还要用过滤器其实两者定位完全不同用错地方才会觉得“这东西没用”。对比维度过滤器计算属性方法用法位置模板插值、v-bind 绑定模板插值、绑定等事件处理、模板调用返回值使用方式{{ value | filter }}{{ computedName }}{{ methodName() }}缓存机制无每次渲染重新执行有依赖不变时缓存无调用才执行典型场景简单文本格式化依赖多个数据的复杂计算交互逻辑、需要参数的操作复用性可串联、可全局注册单组件内复用可抽模块单组件内复用可抽模块看完表格你应该能感觉到计算属性更像“加工车间”适合处理依赖多、逻辑重、需要缓存的场景方法更像“手动按钮”适合在事件回调里主动触发而过滤器就是一个“轻量小巧的模板化妆师”只在展示前做一次格式化。我见过很多项目把过滤器用成计算属性比如在过滤器里写循环、做大量字符串拼接甚至发起请求。这种用法从语法上不会报错但很别扭。因为过滤器每次渲染都会执行一旦逻辑复杂性能代价比计算属性高得多。记住这句话过滤器只做“脸面上的简单化妆”复杂计算请去计算属性里做。2. 实战里真正高频的过滤器使用场景2.1 日期与金额格式化在实际业务中我用得最多的过滤器场景就是日期和金额格式化。日期好理解后端接口经常返回时间戳每个列表页、详情页都要展示成人类可读的字符串。金额则是另一个高频场景尤其是电商、财务类项目原始数据可能是4599页面要展示成4,599.00甚至带上货币符号。一个典型的金额格式化过滤器可以这样写filters: { formatPrice(value, symbol ¥) { if (value null || value undefined || value ) return -- const num Number(value) if (isNaN(num)) return value return symbol num.toLocaleString(zh-CN, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) } }模板里使用p{{ price | formatPrice }}/p p{{ price | formatPrice($) }}/p这里我额外提一个经验如果你用的是像 dayjs、moment 这类日期库建议把日期格式化逻辑统一封装成过滤器或工具函数而不是每个组件里各自import后手动调。之前在一个后台管理系统里我们团队就约定所有日期展示必须走同一个formatDate过滤器后来需求从“显示年月日”改成“超过今天显示具体时间否则显示今天”只改一个地方就全局生效了。这种统一收益在维护期特别明显。2.2 字符串处理与空值兜底除了日期和金额字符串处理也很常见。比如用户昵称长度控制、文章摘要截断、状态文案转换这些用过滤器写起来干净利落。举个例子状态码转文字span{{ order.status | orderStatusText }}/spanfilters: { orderStatusText(status) { const map { 0: 待支付, 1: 已支付, 2: 已发货, 3: 已完成 } return map[status] || 未知状态 } }再比如空值兜底。后端字段缺失时返回null或空字符串页面直接展示会留下难看的空白。过滤器可以统一处理filters: { placeholder(value, fallback --) { if (value null || value undefined || value ) { return fallback } return value } }这种写法看着简单但能省掉很多v-if的判断。我之前有个项目里所有字段展示都统一加了placeholder兜底后来测试提了一堆“空数据显示”bug结果只改了过滤器一处比在几十个模板里逐个修强太多。2.3 列表渲染和属性绑定中的“化妆师”过滤器除了用在插值表达式里还可以用在v-for循环中对每一项的展示字段做格式化。比如商品列表销量超过一万显示成1.2万这种需求非常适合用过滤器解决template ul li v-foritem in products :keyitem.id span{{ item.name }}/span span{{ item.sales | formatCount }}/span /li /ul /template另外过滤器也可以用在v-bind绑定属性上比如根据状态动态生成类名或样式div :classitem.status | statusClass{{ item.statusText }}/divfilters: { statusClass(status) { return { status-success: status 1, status-warning: status 2, status-error: status 3 } } }这个地方要特别提醒虽然v-bind支持过滤器但v-if、v-for、v-model这些指令不支持过滤器。因为v-if判断的是表达式整体真假过滤器只能处理输出值没法改变指令的控制流程。我曾经看到有人尝试v-ifitem.status | isShow这种写法Vue 2 里会直接报错。遇到这种需求请老老实实使用计算属性或者先把数据映射好。3. 面试高频追问注册方式、串联调用和参数规则3.1 全局过滤器与局部过滤器怎么选过滤器注册方式分为全局和局部两种。局部过滤器定义在组件的filters选项中只能当前组件使用export default { filters: { formatDate(value) { /* ... */ } } }全局过滤器通过Vue.filter()注册任何组件模板都能直接使用// main.js import Vue from vue import { formatDate } from ./utils/format Vue.filter(formatDate, formatDate)两者的选择原则不算复杂。如果某个格式化逻辑在多个组件里都会用到比如时间、金额就可以注册成全局过滤器如果只在一个组件内部用局部过滤器足够没必要挂到全局。这里有几个细节需要特别注意第一全局过滤器一定要在创建 Vue 实例之前注册否则挂载模板时找不到过滤器会渲染成原字符串并报错。第二组件内的局部过滤器和全局过滤器重名时局部优先。第三全局过滤器一旦注册就会常驻项目大了以后要小心命名冲突所以实际开发中我更倾向于用常规工具函数来替代全局过滤器这个后面专门讲。3.2 串联调用与参数传递规则过滤器支持串联调用也就是一个值经过多个管道处理。比如先做空值兜底再做截断p{{ content | placeholder(暂无) | truncate(20) }}/p执行顺序是从左到右content先传给placeholder得到的返回值再传给truncate。串联确实能让模板读起来很“流式”但别过度使用管道链一旦超过三个维护的人就要在脑子里模拟数据流很容易绕晕。我在评审代码时会建议控制在两三步以内再多就提取到计算属性或方法里。过滤器的参数传递规则也是高频考点。模板里写{{ value | filter(a, b) }}对应的函数签名是filters: { filter(value, param1, param2) { // value 是管道左侧传入的值 // param1 a // param2 b } }换句话说管道左边的值永远是过滤器函数的第一个参数后面跟的括号参数从第二个参数开始。很多人第一次写带参过滤器时容易搞反以为左边值和括号参数是并列关系实际并不是。还有一个隐性规则过滤器内部拿不到this。Vue 文档里明确写了这一点因为过滤器是在模板渲染阶段被调用的不是作为组件方法调用所以它的执行上下文不在组件实例上。如果确实需要用组件数据来参与格式化最直接的做法是在methods里封装一个方法或者干脆改成计算属性。3.3 Vue 3 移除了过滤器怎么应对这个问题几乎每次面试都会涉及。官方在 Vue 3 里正式移除了过滤器原因主要是过滤器功能本身不难用普通函数或组合式函数替代而且它的语法与 JavaScript 原生按位或|很接近容易让新手产生困惑。如果你是 Vue 3 项目最直接的替代方案是定义一个普通函数在模板里直接调用template p{{ formatDate(rawTime) }}/p /template script setup import { formatDate } from /utils/format const rawTime ref(1741766400000) /script这种写法其实比过滤器更“显式”读者一眼就能看出formatDate是函数调用而不是模板里的魔法语法。对于整个函数的可测试性也比依赖全局注册要友好得多。我在新项目里基本不再使用过滤器统一走工具函数方案只有维护老 Vue 2 项目时才会继续沿用。面试时如果被问到“Vue 3 为什么不支持过滤器”不要只说“官方移除了”最好补一句“过滤器本质上就是函数调用模板中直接用函数表达式也能达到类似效果且可维护性更好”。这个回答能体现出你关注技术演进而不是死背 API。4. 把这道面试题答成一份高分答案4.1 一句话回答框架面试官问“什么是 Vue 过滤器”时最怕你上来背概念背到停不下来。我建议按“一句话定义 场景举例 边界说明”的结构来回答时长控制在两分钟以内。可以这样说Vue 过滤器是 Vue 2 提供的一种模板格式化工具本质是管道函数使用|符号把数据交给指定的处理函数得到格式化后的展示值。它一般用来处理日期、金额、字符串截断、状态文案等简单展示逻辑和计算属性、方法的定位完全不同。它适合做轻量、同步、无副作用的文本格式化不适合做复杂的依赖计算、异步请求或 DOM 操作。同时 Vue 3 已经移除过滤器推荐用普通函数或计算属性替代。这段话不需要背把几个关键词记牢模板格式化、管道函数、展示层逻辑、Vue 2 特性。面试官只要听到这几个点就知道你是真用过。4.2 动手实现一个可运行的示例纸上谈兵不如给一段能跑的例子。面试现场如果允许你可以直接在代码里演示一个综合示例template div classorder-card p订单号{{ order.id }}/p p下单时间{{ order.createTime | formatDate }}/p p订单金额{{ order.amount | formatPrice }}/p p配送状态{{ order.deliveryStatus | deliveryText }}/p /div /template script export default { data() { return { order: { id: A1001, createTime: 1741766400000, amount: 12999, deliveryStatus: 2 } } }, filters: { formatDate(value) { if (!value) return -- const date new Date(value) const y date.getFullYear() const m String(date.getMonth() 1).padStart(2, 0) const d String(date.getDate()).padStart(2, 0) return ${y}-${m}-${d} }, formatPrice(value) { if (value null || value undefined) return -- return ¥${Number(value).toLocaleString(zh-CN, { minimumFractionDigits: 2 })} }, deliveryText(status) { const map { 0: 待发货, 1: 运输中, 2: 已签收 } return map[status] || 未知 } } } /script这段代码覆盖了日期格式化、金额格式化、状态映射三个最常见的过滤器用途。面试时讲完概念再把这个例子结构摆出来说服力完全不一样。如果是 Vue 3 项目把filters选项去掉在script setup里引入同样名字的工具函数模板里直接调用即可。两种方式的差异只在 API 形式业务逻辑完全一样。4.3 列举几个面试官可能追问的方向面试官问完基础概念后大概率会顺着往下追问。我整理了被问得最多的几个问题你可以当作自检清单第一过滤器支持异步操作吗不支持。过滤器应该是同步纯函数如果在里面发请求或者做setTimeout你甚至没法通过返回值让模板更新。异步格式化请用计算属性配合监听器或者直接在方法里处理。第二过滤器可以做数据校验吗尽量避免。校验逻辑通常需要根据表单整体状态判断并且往往要控制交互流程放过滤器里除了能映射一下错误文案起不到真正的校验作用。第三全局过滤器用多了有坏处吗有。命名冲突、重复格式化逻辑分散、渲染时每次都要查找过滤器函数都会带来可维护性问题。这也是为什么现在很多团队约定“能用函数就不用过滤器”。第四自定义指令和过滤器能一起用吗能但两者完全不同。自定义指令操作的是 DOM过滤器处理的是展示值。有些场景两者还能配合比如指令负责监听滚动过滤器负责滚动内容的标题格式化。5. 实践中的避坑清单与可维护性建议5.1 三个容易踩的坑第一个坑是修改原始数据。很多新手在过滤器里直接操作传入的数组或对象比如value.push(...)或者value.status 2。这是非常危险的因为过滤器没有自己的数据响应系统你改了源数据还容易造成渲染结果和预期不一致。正确做法永远是拷贝一份再处理或者只返回新值。第二个坑是过滤器链过长导致调试困难。之前维护过一个老项目模板里出现{{ text | filterA | filterB | filterC | filterD }}这种五连管道出问题时得一层层拆开才知道哪一步把数据弄丢了。建议链条超过三段时把处理流程合并成一个独立的格式化函数既方便单测又方便断点调试。第三个坑是滥用全局注册。我见过一个后台管理系统在main.js里挂了几十个全局过滤器团队成员只负责不断往里面加命名从format1到format19。后来排查问题根本不知道某个模板里用的format17到底是干什么的。如果你一定要用全局过滤器至少找一个filters/index.js集中管理按业务模块拆分导出并且每个过滤器都要写清楚输入输出和用途。5.2 更优雅的过滤器组织方式工具函数替代从 Vue 3 迁移开始我逐渐养成了一个习惯把过滤器的逻辑全部抽成纯工具函数放在utils/format.js里导出然后在组件里按需引用。这样做有三个明显好处。第一可测试性更强。直接对函数传入各种边界值做单元测试比在组件渲染里验证直观得多。比如写一个parseAmountToThousand的测试用例只需要调用函数断言返回值即可。第二便于 tree-shaking。组件只打包它实际引用的格式化函数不会把整包全局过滤器都打进来。第三职责边界清楚。工具函数可以被任何地方使用过滤器则必须在 Vue 模板上下文中才能发挥作用。对于正在维护 Vue 2 老项目的朋友也可以先抽成工具函数再在filters里做一个很薄的转发层import { formatDate } from /utils/format export default { filters: { formatDate } }这样以后升级到 Vue 3只需要删掉转发层模板里改成直接调用函数改动成本可控。5.3 能体现深度的两三个加分点想在面试里靠这道题加分可以在基础概念之外再补充一两个“内功型”的知识点。比如提到 Vue 2 过滤器的实现原理模板编译时过滤器会被编译成_f(filterName)(value)这样的渲染函数调用。如果你能说出这一层面试官就知道你不仅会 API还看过模板编译产物。再比如提到过滤器和纯函数的关系过滤器应该保持无副作用、幂等、同步这种约束让它非常适合做展示层的确定化处理。你可以顺便说一句“这也是为什么我不在过滤器里做异步请求的原因”立刻显得思考深入。还比如提到团队约定不要说“我们项目很少用过滤器”那会显得实践不足可以说“我习惯把展示层的格式化统一收敛到一个format工具模块里组件只是薄薄的一层引用这样即使将来框架升级迁移成本也低”。这比单纯背诵过滤器定义要加分很多。我个人在实际带项目时的最大体会是过滤器看着简单但真正能体现水平的不是会不会用而是知道什么场景不该用。面试题问得越基础越要警惕自己回答得“太基础”。把 Vue 2 的过滤器、Vue 3 的函数替代方案、工具函数组织方式串起来讲这道题就是你的加分项而不只是过场题。最后再分享一个小技巧如果面试官让你手写一个过滤器别急着写日期格式化这种烂大街例子优先写一个“空值兜底 文字截断”的组合过滤器。因为它既展示了参数传递规则又展示了串联用法还能体现你对实际页面细节的关注度。这种组合拳式的回答比单独写一个toUpperCase要有说服力得多。