Vue.js破冰系列-4计算属性、监视器与过滤器

1 computed计算属性

上一章我们提到指令的一般格式是:

指令名name[:参数arg][.修饰符modifiers][=表达式expression]

其中表达式expression会在其所属实例作用域下作为js代码被解析。前面我在v-on事件监听时,也提到过事件的处理有3种方式:

  • 将js代码加在指令的表达式中
  • 将事件的处理逻辑封装到方法中,把方法与事件绑定起来
  • 在表达式中使用内敛语句调用方法

这3种方式会在绑定元素所属的实例作用域下,会被当做js代码执行。当表达式中js代码的逻辑过于复杂的时候,前面的处理方式是使用方法引用的方式,这里我们再介绍一种方式,用计算属性作为表达式的内容。

1.1 计算属性的定义

计算属性放在computed选项中,既然是属性,那就存在getter和setter方法,所以正常的定义方式如下:

<div id="app">
  <!-- 使用js语句和计算属性作为指令表达式 -->
  <h4 v-text='`${this.firstName} ${this.lastName}`'></h4>
  <h4 v-text='fullName'></h4>
</div>
<script>
var vm = new Vue({
  el:"#app",
  data:{
    firstName:"Michael",
    lastName:"Jackson",
  },
  computed:{
    fullName:{
      get:function(){
        return `${this.firstName} ${this.lastName}`;
      },
      set:function(name){
        let tmp = name.split(' ');
        this.firstName = tmp[0];
        this.lastName = tmp[1];
      }
    }
  }
});
</script>

上面定义了fullName这个计算属性,并定义了其getter和setter方法。在指令的表达式中直接调用计算属性,它会调用fullName的getter方法。当为fullName赋值时,vue会调用其setter方法。通常,setter方法很少用到,所以vue为我们提供了只有getter方法的计算属性的简写形式。

var vm = new Vue({
  computed:{
    fullName:function(){
       return `${this.firstName} ${this.lastName}`;
    }
  }
});

简写方式是属性名后直接跟匿名函数,这个函数就是getter函数。也可以在属性名后接对象,在对象中定义getter函数,不定义setter函数,该方式不常用,所以这里不做代码演示。

1.2 计算属性与方法的区别

如上面的例子,如果我们也可以定义一个getFullName()方法,返回同样的字符格式,然后在指令的表达式中使用该方法,同样能够达到目的。这样看来,计算属性比起方法,其定义反而麻烦了许多。

其实不然,计算属性与方法的最大的区别在于,计算属性有缓存功能,使用方法时,每次都会计算并返回结果,而计算属性只有当其内部的依懒项发生变化时才会重新计算,返回并缓存结果,当内部依赖项没有发生变化时,属性会直接返回缓存结果,所以计算属性要比方法的效率稍高。这里的内部依懒项就是firstName和lastName两个数据项。

2 watch监听器

2.1 监视器的定义

Vue使用watch来监听数据的变化,这里的“数据”指代的范围比较广,它可以是data选项中的值,也可以是props选项的值,还可以是第三方插件的值(比如路由插件vue-route的中路由的变化)。

Vue使用watch选项来定义监听器,watch后接对象,这个对象的键是需要观察的数据或表达式,而其值是当该键发生变化后的处理逻辑,这个处理逻辑的形式包括以下几种:

  • 函数形式,该函数也叫回调函数,当监听的键发生变化后,vue会调用该函数,回调函数有2个参数,分别是变化之后的值(新值)和变化之前的值(旧值)
  • 方法名形式,在watch选项之外定义了一个处理方法,在watch中通过字符串的形式指定方法
  • 对象形式,对象形式除了指定回调函数外,还可以使用其他选项,以这种形式定义监听器,功能是最全的。对象的键包括:
    • handler键:指定回调函数
    • deep键:bool值,表示是否深度监听,当deep为true时,如果watch监听的是一个对象,那么这个对象的任何属性发生变化时(不管这个属性嵌套了多少层),该handler回调函数都会被调用。
    • immediate键:表示该监听是否立即被调用,正常情况,监听对象应先有一个值,当这个值发生变化后,在触发watch。但是,当设置immediate后,该对象在第一次赋值时,这个监听回调也会被触发。
  • 数组形式,上面所说的,不管是函数形式,方法名形式还是对象形式,他们都只能定义一个回调函数,而数组形式,是将上面的形式封装到数组中,这样,当值发生变化后,可以有多个回调函数。
<script>
    var vm = new Vue({
    el:"#app",
    data:{
        a: 1,
        b: 2,
        c: {
        c1: 3
        },
      d:4
    },
    watch:{
      //函数形式
      a:function(newValue,oldValue){},
      //方法名形式
      b:"callback",
      //对象形式
      c:{
        handler:function(newValue,oldValue){},
        deep:true,//可选
        immediate:true,//可选
      },
      //数组形式,可定义多个回调函数
      d:[
        //函数形式
        function(newValue,oldValue){},
        //方法名形式
        "callback",
        {
          //对象中使用方法名形式
          handler:"callback",
          deep:true,
          immediate:true,
        }
      ]
    },
    methods:{
      callback(newValue,oldValue){}
    }
  });
</script>

2.2 监视器实例

在使用vue-route插件做前端路由时,可以在面包屑组件中监听路由变化,改变面包屑的值。通过vue的插件功能将vue-route注入,注入后vue-route为我们提供了两个对象$router$route,我们可以在任何组件内通过 this.$router 访问路由器,通过 this.$route访问当前路由,后续章节会介绍route。

我们需要监听$route对象,当路由发生变化后做响应的改变即可:

var vm = new Vue({
  watch:{
   '$route':function(to, from) {
      // 对路由变化作出响应...
    }
  }
});

3 filters过滤器

过滤器常常用来做显示格式化,比如,我们从服务器上获取用户信息时,gender字段表示用户性别,0表示女,1表示男,2表示未知。如果直接将012的值显示在界面上,用户会不知这些数字代表的含义,这时,我们就可以使用过滤器来做显示格式化。

3.1 过滤器的定义

vue使用filters选项定义过滤器,filters选项后接对象,在对象中定义过滤器函数。在HTML模板中,在js表达式中使用管道符|添加过滤器。注意,在定义过滤器函数时,该函数的第一个参数为管道符前的表达式的计算结果值。

<body>
  <div id="app">
    <!-- 表达式 管道符 过滤器 -->
    <span>{{gender | genderFilter}}</span>
  </div>
  <script>
    var vm = new Vue({
      el:"#app",
      data:{
        gender:0,
      },
      filters:{
        //定义过滤器,value为管道符前的表达式的计算结果
        genderFilter:function(value){
          let strGender = "";
          switch(value){
            case 0:
              strGender = "女";
              break;
            case 1:
              strGender = "男";
              break;
            default:
              strGender = "未知";
              break;
          }
        }
      }
    });
  </script>
</body>

过滤器函数可以设置任意多参数,但第一个参数必须为管道前的表达式的计算结果,如下,我们定义了一个过滤器A,他有2个参数,在调用时,第一个参数是隐形传递的。

<body>
  <div id="app">
    <!-- 过滤器定义了两个参数,第一个为参数为隐式传递,第二个显示传递,所以这里的foo为第二个参数 -->
    <span>{{gender | genderFilter("foo")}}</span>
  </div>
  <script>
    var vm = new Vue({
      el:"#app",
      data:{
        gender:0,
      },
      filters:{
        //过滤器可以定义多个参数,但第一个必须为管道符之前表达式的计算结果
        filterA:function(value,arg1){}
      }
    });
  </script>
</body>

过滤器后还可以接过滤器,这里不再深入。

3.2 过滤器实例

枚举过滤器

在实际开发中,我们可以使用枚举过滤器来简化过滤器的使用,这里我们定义一个bool类型的枚举,js中没有枚举,我们使用的数组来定义:

//enums.js
export const BoolEnum = [
  {key:0,value:"否"},
  {key:1,value:"是"},
]

定义过滤器函数

//filters.js
export const enumFilter function(value,enums){
  for (let i = 0; i < enums.length; i++) {
        let item = enums[i];
        if (item.key == key) {
            return item.value;
        }
    }
    return "N/A";
}

在HTML模板中使用

<body>
  <div id="app">
    <span>是否启用:{{isActive | enumFilter(boolEnum)}}</span>
  </div>
  <script>
    import {BoolEnum as boolEnum} from 'enums.js'
    import {enumFilter} from 'filters.js'
    var vm = new Vue({
      el:"#app",
      data:{
        isActive:0,
      },
      filters: {
            enumFilter
        },
    });
  </script>
</body>

格式化货币过滤器

前面我们将自定义指令时,介绍了通过指令的方式格式化数字,现在我们使用过滤器的方式来实现该功能:

//filters.js
export const moneyFilter function(value){
  value = value + "";
  let hasDot = /\./g;
  let patt = /\B(?=(\d{3})+\.)/g
  if (!hasDot.test(value)) {
    //没有小数点的正则表达式
    patt = /\B(?=(\d{3})+$)/g
  }
  return value.replace(patt, ",");
}

在HTML模板中使用

<body>
  <div id="app">
    <input type="text" v-model="total">
    <br>
    <span>总价:{{total | moneyFilter}}</span>
  </div>
  
  <script>
    import {moneyFilter} from 'filters.js'
    var vm = new Vue({
      el: "#app",
      data: {
        total: 1234567890,
      },
      filters:{
        moneyFilter,
      }
    });
  </script>
</body>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容