过滤器的实现原理
首先介绍一下过滤器的使用,过滤器是来用格式化文本的,它可以用在两个地方:双花括号和v-bind表达式。:
<!-- 双花括号 --!>
{{ 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)
}
}
或者在创建vuejs实例之前定义全局过滤器:
Vue.filter('capitalize', function(value) {
if(!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
...
})
过滤器会将表达式的值作为第一个参数,比如message的值作为第一个参数。
此外,过滤器可以连写,{{ message | filterA | filterB }}。然后message的值先被filterA执行回调,回调的值再进入filterB执行回调。并且因为过滤器是函数,所以也能接收参数:{{ message | filterA('arg1', arg2)}},这里filterA被定义为接收三个参数的过滤器函数,其中message作为第一个函数,arg1作为第二个,arg2作为第三个参数。
过滤器原理
过滤器原理并不复杂,用之前的例子举例:
{{ message | capitalize }}
这个过滤器再模板编译阶段会编译成下面的样子:
_s(_f('capitalize ')(message))
其中_f函数是resolveFilter的别名,其作用是从this.\(options.filters中找出注册的过滤器并返回。因此,上面例子中的_f('capitalize')与this.\)options.filters['capitalize ']相同。而this.$options.filters['capitalize ']就是我们注册的capitalize 过滤器函数,因此,_f('capitalize ')(message)其实就算执行了过滤器capitalize 并传递了参数message。
_s函数是toString函数的别名,toString函数的代码如下:
function toString(val) {
return val == null
? ''
: typeof val === 'object'
? JSON.stringify(val, null, 2)
: String(val)
}
所以整个过程就是,先将message传入capitalize 运行,然后将处理后的结果传递给toString函数,并最终保存到VNode中的text属性中,然后返回结果直接被拿去渲染视图。
串联过滤器
连续使用过滤器:
{{ message | capitalize | suffix }}
本地定义的过滤器如下:
filters: {
capitalize: function(value) {
if(!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(0)
},
suffix: function(value, symbol = '~') {
if(!value) return ''
return value + symbol
}
}
最后在模板编译阶段回编译成下面的样子:
_s(_f("suffix")(_f("capitalize")(message)))
从代码中可以看出只是一层一层的递归而已,先将message传入capitalize函数,然后将结果传入suffix函数,最后传入到toString中,之后渲染到界面。
过滤器接收参数
前面说过滤器可以接收参数,例如:
{{ message | capitalize | suffix('!) }}
设置了参数的过滤器最终被编译成这样:
_s(_f("suffix")(_f("capitalize")(message), '!'))
可以看到,两者唯一的区别就是,当模板编译之后,会将在模板中给过滤器设置的参数添加在过滤器函数的参数中。注意:这里是从第二个参数开始,第一个参数永远都是之前操作链的结果。
行百里者半九十