过滤器定义

过滤器可以全局定义或局部定义。全局过滤器在所有组件中可用,局部过滤器只在当前组件中可用。

局部过滤器

在组件的 filters 选项中定义:

<div id="app">
  <p>{{ message | capitalize }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    message: 'hello vue'
  },
  filters: {
    capitalize(value) {
      if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
  }
})
</script>

多个过滤器

new Vue({
  el: '#app',
  data: {
    message: 'hello',
    price: 99.9
  },
  filters: {
    capitalize(value) {
      if (!value) return ''
      return value.charAt(0).toUpperCase() + value.slice(1)
    },
    currency(value) {
      return '¥' + value.toFixed(2)
    },
    uppercase(value) {
      return value.toUpperCase()
    }
  }
})

全局过滤器

在创建 Vue 实例之前定义:

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

new Vue({
  el: '#app'
})

批量注册

const filters = {
  capitalize(value) {
    if (!value) return ''
    return value.charAt(0).toUpperCase() + value.slice(1)
  },
  
  uppercase(value) {
    return value ? value.toUpperCase() : ''
  },
  
  lowercase(value) {
    return value ? value.toLowerCase() : ''
  },
  
  currency(value, symbol = '¥') {
    return symbol + Number(value).toFixed(2)
  },
  
  truncate(value, length = 20) {
    if (!value) return ''
    return value.length > length ? value.slice(0, length) + '...' : value
  }
}

Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key])
})

过滤器优先级

当全局过滤器和局部过滤器同名时,局部过滤器优先:

<div id="app">
  <p>{{ message | test }}</p>
</div>

<script>
Vue.filter('test', function(value) {
  return '全局: ' + value
})

new Vue({
  el: '#app',
  data: {
    message: 'Hello'
  },
  filters: {
    test(value) {
      return '局部: ' + value
    }
  }
})
</script>

输出:局部: Hello

在单文件组件中定义

<template>
  <div>
    <p>{{ message | capitalize }}</p>
    <p>{{ price | currency }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'hello',
      price: 99.9
    }
  },
  filters: {
    capitalize(value) {
      if (!value) return ''
      return value.charAt(0).toUpperCase() + value.slice(1)
    },
    currency(value) {
      return '¥' + Number(value).toFixed(2)
    }
  }
}
</script>

封装过滤器模块

创建 filters/index.js

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

export function uppercase(value) {
  return value ? value.toUpperCase() : ''
}

export function lowercase(value) {
  return value ? value.toLowerCase() : ''
}

export function currency(value, symbol = '¥') {
  return symbol + Number(value).toFixed(2)
}

export function truncate(value, length = 20) {
  if (!value) return ''
  return value.length > length ? value.slice(0, length) + '...' : value
}

export function date(value, format = 'YYYY-MM-DD') {
  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')
  
  return format
    .replace('YYYY', year)
    .replace('MM', month)
    .replace('DD', day)
}

export default {
  capitalize,
  uppercase,
  lowercase,
  currency,
  truncate,
  date
}

main.js 中注册:

import Vue from 'vue'
import App from './App.vue'
import filters from './filters'

Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key])
})

new Vue({
  render: h => h(App)
}).$mount('#app')

过滤器函数规范

良好的过滤器函数应该:

  1. 纯函数:不修改原值,返回新值
  2. 边界处理:处理空值、undefined
  3. 类型安全:检查输入类型
  4. 单一职责:只做一件事
Vue.filter('safeCapitalize', function(value) {
  if (value === null || value === undefined) return ''
  if (typeof value !== 'string') {
    value = String(value)
  }
  return value.charAt(0).toUpperCase() + value.slice(1)
})

完整示例

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <h2>局部过滤器</h2>
    <p>{{ message | capitalize }}</p>
    
    <h2>全局过滤器</h2>
    <p>{{ price | currency }}</p>
    <p>{{ description | truncate:20 }}</p>
  </div>

  <script>
    Vue.filter('currency', function(value, symbol = '¥') {
      return symbol + Number(value).toFixed(2)
    })
    
    Vue.filter('truncate', function(value, length = 20) {
      if (!value) return ''
      return value.length > length ? value.slice(0, length) + '...' : value
    })

    new Vue({
      el: '#app',
      data: {
        message: 'hello vue',
        price: 99.9,
        description: '这是一段很长的描述文字,需要截断显示'
      },
      filters: {
        capitalize(value) {
          if (!value) return ''
          return value.charAt(0).toUpperCase() + value.slice(1)
        }
      }
    })
  </script>
</body>
</html>

注意事项

  1. 命名冲突:局部过滤器优先于全局过滤器
  2. 性能考虑:过滤器在每次渲染时都会执行
  3. Vue 3 兼容:Vue 3 已移除过滤器,建议使用计算属性

掌握过滤器定义后,接下来学习如何在模板中使用过滤器。