过滤器篇
个人更喜欢使用computed,而且在Vue3中filters也是去掉了
使用方式
有两种使用的方式,下面两种
!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
过滤器的定义
可以在一个组件里定义过滤器
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
也可以在全局定义过滤器
这里其实就是【存储】
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
工作原理
下面代码其实就是把存储的函数,拿出来
定义位于源码的 src/core/instance/render-helpers/ 中
关键函数 resolveFilter
// resolve-filter.js
import {
identity,
resolveAsset
} from "core/util/index";
export function resolveFilter(id) {
/* 调用resolveAsset 获取返回值 或者 返回identity */
return resolveAsset(this.$options, "filters", id, true) || identity;
}
/**
* Return same value
*/
export const identity = (_) => _;
/*
调用该函数时传入了 4 个参数,
分别是当前实例的 $options 属性, type 为 filters ,
id 为当前过滤器的 id
*/
export function resolveAsset (
options: Object,
type: string,
id: string,
warnMissing?: boolean
): any {
/* ID必须是一个字符串 */
if (typeof id !== 'string') {
return
}
/*
获取type对应的对象
*/
const assets = options[type]
// check local registration variations first
/*
如果有这个属性的话,直接返回值
*/
if (hasOwn(assets, id)) return assets[id]
/*
中横线转化成驼峰式,再返回
*/
const camelizedId = camelize(id)
if (hasOwn(assets, camelizedId)) return assets[camelizedId]
/*
转化成首字母大写
*/
const PascalCaseId = capitalize(camelizedId)
if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
// fallback to prototype chain
/*
再从原型链中查找
如果还是找不到,就抛出警告
*/
const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
warn(
'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
options
)
}
return res
}
所谓 _f 函数其实就是 resolveFilter 函数的别名,在 resolveFilter 函数内部是根据过滤器 id 从当前实例的 $options 中的 filters 属性中获取到对应的过滤器函数,在之后执行渲染函数的时候就会执行获取到的过滤器函数。
解析过滤器
下面代码白话就是把拿出来的函数来调用
使用的时候,有这两种方式
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
function processAttrs (el) {
// 省略无关代码...
if (bindRE.test(name)) { // v-bind
// 省略无关代码...
value = parseFilters(value)
// 省略无关代码...
}
// 省略无关代码...
}
export function parseText (text,delimiters){
// 省略无关代码...
const exp = parseFilters(match[1].trim())
// 省略无关代码...
}
parseFilters函数分析
parseFilters函数的定义位于源码的src/complier/parser/filter-parser.js文件中
该函数的作用的是将传入的形如'message | capitalize'这样的过滤器字符串转化成_f("capitalize")(message)
/*
该函数的作用的是将传入的
形如'message | capitalize'这样的过滤器字符串转化成_f("capitalize")(message)
*/
export function parseFilters (exp: string): string {
let inSingle = false
let inDouble = false
let inTemplateString = false
let inRegex = false
let curly = 0
let square = 0
let paren = 0
let lastFilterIndex = 0
let c, prev, i, expression, filters
/*
从头遍历,exp的每一个字符
*/
for (i = 0; i < exp.length; i++) {
prev = c
c = exp.charCodeAt(i)
if (inSingle) {
if (c === 0x27 && prev !== 0x5C) inSingle = false
} else if (inDouble) {
if (c === 0x22 && prev !== 0x5C) inDouble = false
} else if (inTemplateString) {
if (c === 0x60 && prev !== 0x5C) inTemplateString = false
} else if (inRegex) {
if (c === 0x2f && prev !== 0x5C) inRegex = false
} else if (
c === 0x7C && // pipe
exp.charCodeAt(i + 1) !== 0x7C &&
exp.charCodeAt(i - 1) !== 0x7C &&
!curly && !square && !paren
) {
if (expression === undefined) {
// first filter, end of expression
lastFilterIndex = i + 1
expression = exp.slice(0, i).trim()
} else {
pushFilter()
}
} else {
switch (c) {
case 0x22: inDouble = true; break // "
case 0x27: inSingle = true; break // '
case 0x60: inTemplateString = true; break // `
case 0x28: paren++; break // (
case 0x29: paren--; break // )
case 0x5B: square++; break // [
case 0x5D: square--; break // ]
case 0x7B: curly++; break // {
case 0x7D: curly--; break // }
}
if (c === 0x2f) { // /
let j = i - 1
let p
// find first non-whitespace prev char
for (; j >= 0; j--) {
p = exp.charAt(j)
if (p !== ' ') break
}
if (!p || !validDivisionCharRE.test(p)) {
inRegex = true
}
}
}
}
if (expression === undefined) {
expression = exp.slice(0, i).trim()
} else if (lastFilterIndex !== 0) {
pushFilter()
}
function pushFilter () {
(filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
lastFilterIndex = i + 1
}
if (filters) {
for (i = 0; i < filters.length; i++) {
expression = wrapFilter(expression, filters[i])
}
}
return expression
}
该函数接收一个形如'message | capitalize'这样的过滤器字符串作为,最终将其转化成_f("capitalize")(message)输出。在parseFilters函数的内部是通过遍历传入的过滤器字符串每一个字符,根据每一个字符是否是一些特殊的字符从而作出不同的处理,最终,从传入的过滤器字符串中解析出待处理的表达式expression和所有的过滤器filters数组。
最后,将解析得到的expression和filters数组通过调用wrapFilter函数将其构造成_f函数调用字符串。