面试题:你了解 Vue 中的过滤器吗?它有哪些应用场景?

Vue 过滤器详解

1. 过滤器基础概念

Vue 过滤器主要用于文本格式化,可以在模板中直接使用。注意:Vue 3 已移除过滤器,但在 Vue 2 中仍然常用。

基本语法

<template>
  <!-- 使用过滤器 -->
  <div>{{ message | capitalize }}</div>

  <!-- 链式调用 -->
  <div>{{ price | currency | uppercase }}</div>

  <!-- 带参数的过滤器 -->
  <div>{{ date | formatDate('YYYY-MM-DD') }}</div>
</template>

2. 过滤器的定义方式

全局过滤器

// main.js
Vue.filter('capitalize', function(value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

Vue.filter('currency', function(value, symbol = '$') {
  return symbol + Number(value).toFixed(2)
})

局部过滤器

export default {
  filters: {
    capitalize(value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    },

    truncate(value, length = 10) {
      if (!value) return ''
      if (value.length <= length) return value
      return value.substring(0, length) + '...'
    }
  }
}

3. 常见应用场景

文本格式化

// 首字母大写
filters: {
  capitalize(value) {
    if (!value) return ''
    return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase()
  }
}

// 在模板中使用
// {{ 'hello world' | capitalize }} → "Hello world"

金额格式化

filters: {
  currency(value, symbol = '¥', decimals = 2) {
    const numericValue = parseFloat(value)
    if (isNaN(numericValue)) return symbol + '0.00'

    return symbol + numericValue.toFixed(decimals).replace(/\d(?=(\d{3})+\.)/g, '$&,')
  }
}

// 使用:{{ 1234.56 | currency }} → "¥1,234.56"
// 使用:{{ 1234.56 | currency('$') }} → "$1,234.56"

日期格式化

filters: {
  formatDate(value, format = 'YYYY-MM-DD') {
    if (!value) return ''
    const date = new Date(value)

    const year = date.getFullYear()
    const month = String(date.getMonth() + 1).padStart(2, '0')
    const day = String(date.getDate()).padStart(2, '0')
    const hours = String(date.getHours()).padStart(2, '0')
    const minutes = String(date.getMinutes()).padStart(2, '0')

    return format
      .replace('YYYY', year)
      .replace('MM', month)
      .replace('DD', day)
      .replace('HH', hours)
      .replace('mm', minutes)
  }
}

// 使用:{{ new Date() | formatDate }} → "2024-01-15"
// 使用:{{ new Date() | formatDate('YYYY/MM/DD HH:mm') }} → "2024/01/15 14:30"

文件大小格式化

filters: {
  fileSize(bytes) {
    if (bytes === 0) return '0 Bytes'

    const k = 1024
    const sizes = ['Bytes', 'KB', 'MB', 'GB']
    const i = Math.floor(Math.log(bytes) / Math.log(k))

    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
  }
}

// 使用:{{ 1048576 | fileSize }} → "1 MB"

状态文本转换

filters: {
  statusText(value) {
    const statusMap = {
      '0': '待处理',
      '1': '处理中', 
      '2': '已完成',
      '3': '已取消'
    }
    return statusMap[value] || '未知状态'
  }
}

// 使用:{{ '1' | statusText }} → "处理中"

手机号脱敏

filters: {
  phoneMask(value) {
    if (!value) return ''
    return value.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
  }
}

// 使用:{{ '13812345678' | phoneMask }} → "138****5678"

HTML 内容处理

filters: {
  stripTags(value) {
    if (!value) return ''
    return value.replace(/<[^>]*>/g, '')
  }
}

// 使用:{{ '<p>Hello World</p>' | stripTags }} → "Hello World"

4. 在 methods 和 computed 中使用过滤器

export default {
  data() {
    return {
      price: 99.9
    }
  },
  filters: {
    currency(value) {
      return '¥' + Number(value).toFixed(2)
    }
  },
  computed: {
    // 在 computed 中使用过滤器逻辑
    formattedPrice() {
      return this.$options.filters.currency(this.price)
    }
  },
  methods: {
    // 在 methods 中使用过滤器逻辑
    formatPrice(price) {
      return this.$options.filters.currency(price)
    }
  }
}

5. Vue 3 中的替代方案

由于 Vue 3 移除了过滤器,可以采用以下替代方案:

使用 methods

export default {
  methods: {
    capitalize(value) {
      if (!value) return ''
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
  }
}

// 在模板中使用:{{ capitalize('hello') }}

使用 computed

export default {
  data() {
    return {
      message: 'hello world'
    }
  },
  computed: {
    capitalizedMessage() {
      return this.message.charAt(0).toUpperCase() + this.message.slice(1)
    }
  }
}

// 在模板中直接使用:{{ capitalizedMessage }}

使用全局方法

// 创建工具函数
const filters = {
  currency(value) {
    return '¥' + Number(value).toFixed(2)
  }
}

// 注册为全局属性
app.config.globalProperties.$filters = filters

// 在组件中使用
export default {
  mounted() {
    console.log(this.$filters.currency(100))
  }
}

6. 过滤器的优缺点

优点

  • 声明式:在模板中直接使用,代码简洁
  • 可复用:一次定义,多处使用
  • 可组合:支持链式调用
  • 职责分离:将数据转换逻辑从业务逻辑中分离

缺点

  • 调试困难:在模板中调试不如在 JavaScript 中方便
  • 性能开销:频繁调用可能影响性能
  • Vue 3 不支持:需要迁移到其他方案

7. 最佳实践

  1. 保持纯净:过滤器应该是纯函数,不修改原始数据
  2. 处理边界情况:始终处理 null、undefined 等边界值
  3. 命名清晰:使用清晰的命名表明过滤器用途
  4. 避免复杂逻辑:复杂的转换逻辑建议放在 computed 或 methods 中
  5. 考虑性能:对于大数据量的处理,考虑使用 computed 缓存结果

虽然 Vue 3 已经移除了过滤器,但在 Vue 2 项目中,合理使用过滤器可以显著提高代码的可读性和可维护性。在 Vue 3 中,可以采用方法调用或计算属性来实现相同的功能。

THE END
喜欢就支持一下吧
点赞8 分享