概述
质上就是一个 JS 函数
用法
双花括号插值中和在 v-bind 表达式中
1 2 3
| {{ message | capitalize }}
v-bind:id="rawId | formatId"
|
定义
组件的选项中定义局部过滤器,优先级高
1 2 3 4 5 6 7 8 9
| export default { filters: { capitalize: function (value) { if (!value) return ""; value = value.toString(); return value.charAt(0).toUpperCase() + value.slice(1); }, }, };
|
创建 Vue 实例之前使用全局 API,Vue.filter 定义全局过滤器
1 2 3 4 5
| Vue.filter("capitalize", function (value) { if (!value) return ""; value = value.toString(); return value.charAt(0).toUpperCase() + value.slice(1); });
|
串联过滤器
{{ message | filterA | filterB }}
- message 做为参数传给 A,运算结果在穿过 B
{{ message | filterA('arg1', arg2) }}
- filterA 被定义为接受 3 个参数,message 为第一个参数
原理
src/core/instance/render-helpers.js
- 模板编译阶段会生成 _f 函数调用字符串,即 resolveFilter 函数,当执行渲染函数的时候,就会执行 _f 函数,从而让过滤器生效
- 根据过滤器 id 获取到对应的过滤器函数,然后传入参数调用即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| export function resolveFilter(id) { return resolveAsset(this.$options, "filters", id, true) || identity; }
export const identity = (_) => _;
export function resolveAsset(options, type, id, warnMissing) { if (typeof id !== "string") { return; } const assets = options[type]; 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]; 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; }
|
串联过滤器
与单个过滤器有所区别的是: 对于多个串联过滤器,在调用过滤器函数传递参数时,后一个过滤器的输入参数是前一个过滤器的输出结果
1 2 3 4 5 6 7 8 9 10 11
| {{ message | capitalize }} // ===> 编译成 _f("capitalize")(message) // 1. 截取字符串拿到 _f("capitalize") 中定义的 capitalize 函数 // 2. 传入 message 参数执行 capitalize 函数
{{ message | filterA | filterB }} // ===> 编译成 _f("filterB")(_f("filterA")(message))
{{ message | filterA | filterB(arg) }} // ===> 编译成 _f("filterB")(_f("filterA")(message), arg) // 当过滤器接收其余参数时,它的参数都是从第二个参数开始往后传入的
|
解析过滤器
src/complier/parser/filter-parser.js
解析花括号中的
- 当作标签文本解析,在 parseHTML 过程中的 chars 钩子函数里调用 parseText,在此调用 parseFilters
解析 v-bind 中的
- 当作标签属性解析,在 parseHTML 过程中的 processAttrs 里调用 parseFilters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| export function parseFilters(exp) { 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;
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 && exp.charCodeAt(i + 1) !== 0x7c && exp.charCodeAt(i - 1) !== 0x7c && !curly && !square && !paren ) { if (expression === undefined) { 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; 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; }
function wrapFilter(exp, filter) { const i = filter.indexOf("("); if (i < 0) { return `_f("${filter}")(${exp})`; } else { const name = filter.slice(0, i); const args = filter.slice(i + 1); return `_f("${name}")(${exp}${args !== ")" ? "," + args : args}`; } }
|