Vue提供了全局过滤器和全局过滤器,主要用在花括号插值以及v-bind表达式中。我们先看看全局过滤器在花括号插值中的用法,
并分析其原理。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue demo</title>
</head>
<body>
<script src="./vue.js"></script>
<div id="app">
{{message | addVue}}
</div>
<script>
Vue.filter("addVue", function(value){
return value + " " + "Vue";
});
new Vue({
el:"#app",
data:{
message: "hello"
}
})
</script>
</body>
</html>
运行这个例子会渲染出hello Vue。我们看看是怎么实现的。在Vue.js中并没有搜索到Vue.filter函数,搜索filter关键字看到了:
var ASSET_TYPES = [
'component',
'directive',
'filter'
];
我们在前面文章分析注册全局组件时用到了这个数组。我们看看initGlobalAPI 函数中的initAssetRegisters函数:
function initAssetRegisters (Vue) {
/**
* Create asset registration methods.
*/
ASSET_TYPES.forEach(function (type) {
Vue[type] = function (
id,
definition
) {
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (type === 'component') {
validateComponentName(id);
}
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id;
definition = this.options._base.extend(definition);
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition };
}
this.options[type + 's'][id] = definition;
return definition
}
};
});
}
和注册全局组件一样注册了全局过滤器,在这里进行了注册this.options[type + 's'][id] = definition;
通过type(filter) 和 id(addVue) 就可以找到这个过滤器。那么什么地方用到这个过滤器呢?
通过打印我们看看{{message | addVue}} 被编译成的render函数:_f("addVue")(message) , 所以我们要找_f函数resolveFilter:
function resolveFilter (id) {
return resolveAsset(this.$options, 'filters', id, true) || identity
}
调用了resolveAsset函数:
function resolveAsset (
options,
type,
id,
warnMissing
) {
/* istanbul ignore if */
if (typeof id !== 'string') {
return
}
var assets = options[type];
// check local registration variations first
if (hasOwn(assets, id)) { return assets[id] }
var camelizedId = camelize(id);
if (hasOwn(assets, camelizedId)) { return assets[camelizedId] }
var PascalCaseId = capitalize(camelizedId);
if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] }
// fallback to prototype chain
var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];
if (warnMissing && !res) {
warn(
'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
options
);
}
return res
}
之前在介绍父子组件props通信原理时就分析过这个函数,当时是通过全局组件名称找到对应的组件对象,现在是通过全局过滤器名称找到对应的过滤器函数。
执行render函数时就执行过滤器函数,message作为参数传递给过滤器函数。
下面我们再看看局部过滤器。先看下其用法,然后再看看实现原理。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue demo</title>
</head>
<body>
<script src="./vue.js"></script>
<div id="app">
{{message | addVue}}
</div>
<script>
new Vue({
el:"#app",
data:{
message: "hello"
},
filters:{
addVue : function(value){
return value + " " + "Vue";
}
}
})
</script>
</body>
</html>
这个局部过滤器函数内容和全局过滤器函数内容是一样的,所以渲染出来的内容也是一样的。注意在选项内的过滤器的关键字是filters,而全局过滤器函数是Vue.filter 。一个是filter 一个是filters。接下来我们看看局部过滤器实现原理。
在模板解析过程中看看对{{message | addVue}}的解析过程:
function parseText (
text,
delimiters
) {
var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE;
if (!tagRE.test(text)) {
return
}
var tokens = [];
var rawTokens = [];
var lastIndex = tagRE.lastIndex = 0;
var match, index, tokenValue;
while ((match = tagRE.exec(text))) {
index = match.index;
// push text token
if (index > lastIndex) {
rawTokens.push(tokenValue = text.slice(lastIndex, index));
tokens.push(JSON.stringify(tokenValue));
}
// tag token
var exp = parseFilters(match[1].trim());
tokens.push(("_s(" + exp + ")"));
rawTokens.push({ '@binding': exp });
lastIndex = index + match[0].length;
}
if (lastIndex < text.length) {
rawTokens.push(tokenValue = text.slice(lastIndex));
tokens.push(JSON.stringify(tokenValue));
}
return {
expression: tokens.join('+'),
tokens: rawTokens
}
}
通过打印看到parseText的参数text 就是{{message | addVue}} delimiters为false 所以tagRe 为defaultTagRE,defaultTagRE是个正则表达式,用来处理插值字符串的。
var defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;
用正则表达式处理完插值字符串后,match[1] 就是message | addVue 字符串,通过打印看到var exp = parseFilters(match[1].trim()); exp为_f("addVue")(message) 这个_f函数在全局过滤器中已经分析完了,看resolveFilter函数:
function resolveFilter (id) {
return resolveAsset(this.$options, 'filters', id, true) || identity
}
看这个函数中的this.$options 就是VUE实例中的 选项里面包含有data,filters 。传给resolveFilter函数参数id 的是addVue,通过id就取得filters对应的函数。