zoukankan      html  css  js  c++  java
  • 第十六章:过滤器的奥秘

    vue官方文本介绍:

    1)使用:

    Vue.js允许我们自定义过滤器来格式化文本。它可以用在两个地方:双花括号和v-bind表达式。它应该被添加在JavaScript表达式的尾部,由”管道“符号指示:

    1.在双花括号中

    {{ message | capitalize}}

      2.在v-bind中

    <div v-bind:id="rawId | formatId"></div>

    2)定义过滤器(在组件中定义和全局定义两种):

      1.在组件的选项中定义本地的过滤器:

    filter:{
      capitalize:function(value){
          if(!value) return ''
          value = value.toString()
          return value.charAt(0).toUpperCase() + value.slice(1)          
      }      
    }

      2.在Vue.js实力之前全局定义过滤器:

    Vue.filter('capitalize',function(value){
          if(!value) return ''
          value = value.toString()
          return value.charAt(0).toUpperCase() + value.slice(1)               
    })
    
    new Vue({
        //... ...
    })

    3)补充:

    1、过滤器函数总是将表达式的值(之前操作链的结果)作为第一个参数。在上述例子中,capitalize过滤器函数会将收到的message的值作为第一个参数。

    2.同时使用多个过滤器,也就是说过滤器可以串联:

    {{ message | filterA | filterB }}

       在上述例子中,filterA被定义为接受单个参数的过滤器,表达式message的值将作为参数传入到filterA过滤器函数中。然后继续调用同样被定义为接受单个参数的过滤器函数filterB,将过滤器函数filterA的执行结果当作参数传递给filterB函数。

    3.过滤器函数可以接收参数,因为过滤器是JavaScript函数。

    {{ message | filterA('arg1',arg2) }}

      这里,filterA被定义为接收三个参数的过滤器函数。其中message的值作为第一个参数,普通字符串‘arg1’作为第二个参数,表达式arg2的值作为第三个参数。

    16.1过滤器原理概述(过滤器是如何执行的)

    原理:

    例子:{{ message | capitalize }}

    上述例子的过滤器在模版编译阶段会编译成下面的样子:

    _s(_f("capitalize")(message))

    其中_f函数是resolveFilter的别名,其作用是从this.$options.filters中找出注册的过滤器并返回。

    因此,上面的例子中的_f("capitalize")与this.$options.filters['capitalize']相同。而this.$options.filters['capitalize']就是我们注册是capitalize过滤器函数:

    filter:{
      capitalize:function(value){
          if(!value) return ''
          value = value.toString()
          return value.charAt(0).toUpperCase() + value.slice(1)          
      }      
    }

     因此,_f("capitalize")(message)其实就是执行了过滤器capitalize并传递了参数message。

       

    _s是toString函数的别名。toString函数代码如下:

    function toString(val) {
        return val = null
            ? ' '
            : typeof val === 'object'
                ? JSON.Stringify(val,null,2)
                : String(val)
    }

     简单来说,其实就是执行了capitalize过滤器函数并把message当作参数传递进去,接着将capitalize过滤器处理后的结果当作参数传递给toString函数。

    最终toString函数执行后的结果会保存到VNode中的text属性中。换句话说,这个返回结果直接被拿去渲染视图了。

    16.1.1  串联过滤器

    {{ message | capitalize | suffix }}
    
    filters:{
       capitalize:function(value){
             if  (!value) return ' '
             value = value.toString()
             return value.charAt().toUpperCase() + value.slice(1)
       },
       suffix:function(value,symbol = '~'){
             if(!value) return ' '
             return value + symbol
       }
    }

    最终在模板编译阶段会编译成下面的样子:

    _s(_f("suffix")(_f("capitalize")(message)))

    从代码中可以看出,表达式message的值将作为参数传入到capitalize过滤器函数中,而capitalize过滤器的返回结果通过参数传递给了suffix过滤器,也就是说capitalize过滤器的输出是suffix过滤器的输入。

    下图为编译后的串联过滤器,清晰地展示了过滤器的串联过程。

     渲染结果:渲染出来的文本的首字母大写并且最后携带~后缀。

    16.1.2 滤器接受参数

    {{ message | capitalize | suffix('!') }}

    设置了参数的过滤器最终被编译后变成这样:

    _s(_f("suffix")(_f("capitalize")(message),'!'))

    可以看到,加了参数的过滤器和没加参数的过滤器之间唯一的区别就是,当模板被编译之后,会将在模板中给过滤器设置的参数添加在过滤器函数的参数中。

    注意:这里是从第二个参数开始,这是因为第一个参数永远都是之前操作链的结果。

    下图是接收参数的过滤器与不接收参数的过滤器之间的区别。

    16.1.3 resolveFilter的内部原理

    通过上文已经大致了解了过滤器是如何运行的,但是还不清楚_f函数是如何找到过滤器的。

    _f函数是resolveFilter函数的别名。resolveFilter函数的代码如下:

    import { identity, resolveAsset } from 'core/util/index'
    
    export function resolveFilter (id){
        return resolveAsset(this.$options, 'filters', id , true) || identity  
    }

      调用resolveFilter函数查找过滤器,如果找到了,就将过滤器返回;如果找不到,则返回identity。identity函数代码如下:

    /** 返回相同的值 **/
    export const identity = _=>_

    该函数会返回同参数相同的值。

     现在看一下resolveAsset函数是如何查找过滤器的,代码如下图:

      ①首先判断参数id的类型(它是过滤器id),它必须是字符串类型,如果不是,则使用return语句终止函数继续执行。

      ②然后声明变量assets并将options[type]保存到该变量中。(事实上,resolceAsset函数除了可以查找过滤器外,还可以查找组件和指令。)本例中变量assets中保存的是过滤器集合。

      ③然后通过hasOwn函数检查assets自身是否存在id属性,如果存在,则直接返回结果。(hasOwn函数基于Object.prototype.hasOwnProperty实现。)④如果不存在,则使用函数camelize将id驼峰化之后再检查assets身上是否存在将id驼峰化之后的属性。⑥如果驼峰化后的属性也不存在,那么使用capitalize函数将id的首字母大写后再次检查assets中是否存在,⑦如果还是找不大,那么按照前面的顺序重新查找一遍属性,不同的是这次将检查原型链。

      查找原型链很简单:只需要访问属性即可。如果找到,则返回过滤器。如果找不到,那么在非生产环境下在控制台打印警告。最后,无论是否找到,都返回查找结果。

      注册过滤器有两种途径:注册全局过滤器和在组件的选项中定义本地的过滤器。

      全局注册的过滤器会保存在Vue构造函数中。

      而resolveAsset函数在查找过滤器的过程中并没有去Vue构造函数中搜索过滤器。这是因为在初始化Vue.js实例时,把全局过滤器与组件内注册的过滤器合并到this.$options.filters中了,而this.$options.filters其实同时保存了全局过滤器和组件内注册的过滤器。resolveAsset只需要从this.$options.filters中查找过滤器即可。

    16.2 解析过滤器(模版中的过滤器语法是如何编译成过滤器函数来调用表达式的)

    例子:

    {{ message | capitalize }}

    上面的capitalize过滤器是如何被编译成下面的样子的:

    _s(_f("capitalize")(message))

    在Vue.js内部,src/compiler/parser/filter-parser.js文件中提供了一个parseFilters函数,专门用来解析过滤器的,它可以将模板过滤器解析成过滤器函数调用表达式。逻辑代码如下,我们只需要在解析出过滤器列表后,循环过滤传奇列表并拼接一个字符串即可。

     注意:在真是的Vue.js源码中多了很多边界条件判断,所以代码会比上面的例子稍微复杂一点。

     上面的split方法将模板字符串切割成过滤器列表,并将列表中的第一个元素赋值给变量expression,然后循环过滤器列表并调用wrapFilter函数拼接字符串。wrapFilter函数接受两个参数(exp,filter),

     代码先通过indexOf判断过滤器字符串中是否包含(,如果包含,说明过滤器携带了其他参数,如果不包含,说明过滤器并没有传递其他参数。

      不包含字符(时,参数filter就是过滤器ID,所以值需要将它拼接到_f函数的参数并将exp当作过滤器的参数拼接到一起即可。

      包含字符(时,需要先从参数filter中将过滤器名和过滤器参数解析出来,而字符( 的左边时过滤器名,右边是参数。例如:filterA('arg1',arg2),字符(左边是过滤器名filterA,右边是参数'arg1',arg2)。由于解析出来的参数右边多了一个小括号,所以在接下来拼接字符串时需要去掉右边的小括号:return '_f("${name}")(${exp},${args}'

    16.3 总结

      使用Vue.js开发应用时,过滤器是一个常用的功能,用于格式化文本。

      用法:

      1. 基本用法
      2. 串联
      3. 接收参数

    过滤器的原理:在编译阶段将过滤器编译成函数调用,串联的过滤器编译后是一个嵌套的函数调用,前一个过滤器函数的执行结果是后一个过滤器函数的参数。

    编译后的_f函数是resolveFilter函数的别名,resolveFilter函数的作用是找到对应的过滤器并返回。

     模板编译过程中过滤器是如何被编译成过滤器函数调用的:1解析,2拼接字符串

  • 相关阅读:
    express 的安全中间件 helmet 简介
    一个设置过期时间的方案
    vscode 中的 vue 格式化
    linux 中部署不同版本 node.js 并同时使用的方案
    webpack 多页面模式配置
    1.assert
    我是一个线程(转)
    Android FragmentTransactionExtended:使Fragment以多种样式动画切换
    Android ORM应用开发框架KJFrameForAndroid使用详解
    Android Studio插件推荐(PreIOC,GsonFormat)
  • 原文地址:https://www.cnblogs.com/meiyanstar/p/14759634.html
Copyright © 2011-2022 走看看