VUE源码分析之filter函数原理

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对应的函数。

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值