带着问题看源码——为何vue中filters访问不了vue实例?
前言:我们在vue项目中,经常会使用到filters过滤器,用来对数据进行二次处理,使用起来特别方便,但是有些业务场景,确让人特别头疼,需要在filters依赖到vue实例上的属性,或者是方法。但是使用的时候往往不尽人意,会报错。然后打印this发现竟然是undefined。这是为什么呢?索性我就去看一下vue处理filters时的源码。
提出问题:
(1). 问题1:为什么访问不了vue实例?
首先我们找到vue源码vue.js文件,全局搜索filters发现关联到 一个方法 resolveFilter
function installRenderHelpers (target) {
// 省略一部分代码
target._s = toString;
target._f = resolveFilter; // 核心代码
target._v = createTextVNode;
// 省略一部分代码
}
// 重要
function renderMixin (Vue) {
// install runtime convenience helpers
installRenderHelpers(Vue.prototype); // 将_s/_f等方法都挂载到vue的原型上
// ....此处省略N行代码
}
// 核心代码
function resolveFilter (id) {
return resolveAsset(this.$options, 'filters', id, true) || identity
}
我发现vue在调用filters时,并不是直接调用。而是通过 resolveFilter方法获取到指定的过滤器函数,然后再执行这个过滤器函数。
知道这个之后,我们还需要知道vue中在模板解析中会将过滤器解析成什么形式。通过翻阅资料发现在vue中filters会解析成如下:
(function() {
with(this) {
return _c('标签名',[
_v(_s(_f("过滤器函数名")('传入的参数')))
])
}
}
// _c 是渲染函数
// _v 通过 上述 installRenderHelpers函数我们能知道 是创建文本节点的方法
// _s 通过 上述 installRenderHelpers函数我们能知道 是将内容转换为string的方法
// _f 通过 上述 installRenderHelpers函数我们能知道 就是resolveFilter函数,用来返回一个过滤函数
with解析:
我们发现这个执行渲染的过程是执行在with作用域内的,with的作用呢,将给定的表达式添加到在评估语句时使用的作用域链上。上述作用相当与在执行with大括号内的代码时,访问_c/_v/_s/_f函数时会先在vue实例上找,如果找不到再向上找(作用域链)
执行解析:
虽然说_f挂载到了vue实例原型上,但是我们会发现_f函数并不是真正的过滤器函数,它只是一个获取过滤器的一个函数,_f(resolveFilter函数)函数会返回一个resolveAsset函数用于获取指定的过滤器函数,而resolveAsset函数并没有挂载到vue原型上,所以此时resolveAsset函数内的执行上下文(this)并不是vue实例,而是window,所以真正的过滤器函数内部的this并没有指向vue,而是指向window,所以我们在filters中访问不了vue实例
(2)问题2:为什么有时候访问到的this是undefined而不是window?
因为使用 use strict 严格模式,在严格模式下,所有函数自执行this指向将不再是window而是undefined。