前面的话

模板内的表达式常用于简单的运算,当其过长或逻辑复杂时,会难以维护。这篇文章将介绍计算属性与监听属性去解决该问题。

计算属性
[什么是计算属性?]

首先,来看一个字符串反转的例子:


    <div id="app1">
         {{ message.split('').reverse().join('') }}
    </div>
    <script>
    	new Vue({
            el: "#app1",
            data: {
                message: 'xiaoqi'
            }
        })
    </script>

上面这个例子,在模板中表达式包括3个操作,相对比较复杂,也不容易看懂。

所以在遇到复杂的逻辑时应该使用计算属性。

将上例进行改写:

<div id="app2">
    <p>原字符串:{{message}}</p>
    <p>计算后反转字符串: {{reverseMessage}}</p>
 </div>
<script>
new Vue({
        el: "#app2",
        data: {
            message: 'xiaoqi'
        },
        computed: {
            // 计算属性的getter
            reverseMessage: function() {
                return this.message.split('').reverse().join('');
            }
        }
    })
</script>

在这里插入图片描述
上面的模板中声明了一个计算属性reverseMessage。
提供的函数将用作属性reverseMessage的getter(用于读取)。
reverseMassage依赖于massage,在massage发生变化时,reverseMassage也会更新。

[ computed Vs methods]

我们发现computed属性完全可以由methods属性所代替,效果时完全一样的。既然使用methods就可以实现,那么为什么还需要计算属性呢?原因就是computed是基于它的依赖缓存,只有相关依赖发生改变时,才会重新取值。而使用methods,在重新渲染的时候,函数总会重新调用执行。可以说使用computed性能会更好。

[ 依赖缓存]

举一个更好说明computed是基于依赖缓存的例子:

 <div id="app3">
        <p>原字符串:{{message}}</p>
        <p>计算反转字符串:{{reverseMessage1}}</p>
        <p>计算反转字符串:{{reverseMessage1}}</p>
        <p>计算反转字符串:{{reverseMessage2()}}</p>
        <p>计算反转字符串:{{reverseMessage2()}}</p>
</div>
<script>
  var num = 1;
    new Vue({
        el: "#app3",
        data: {
            message: 'xiaoqi'
        },
        computed: {
            reverseMessage1:function () {
                num += 1;
                return num +  this.message.split('').reverse().join('');
            }
        },
        methods: {
            reverseMessage2() {
                num += 1;
                return num + this.message.split('').reverse().join('');
            }
        }
        
    })
</script>

在这里插入图片描述
这个例子中,num是一个独立的变量。在使用reverseMessage1这个计算属性时,num会变成2 。但是当再使用reverseMessage1属性时,num没有变化,依然是2。因为Vue实例的message数据没有发生变化 于是DOM渲染就直接用这个值,不会重复执行代码。而reverseMessage2这个方法只要用一个,就要执行一次,于是每次返回的结果都不一样。

[computed setter]

每一个计算属性都包含一个getter与一个setter,上面的实例中都是计算属性的默认用法,只是利用getter来读取。
computed属性默认只有getter,不过在需要时可以自己提供一个setter函数,当手动修改计算属性的值时,就会触发setter函数,执行自定义的操作。
例如:

<div id="app4">
    <p>{{ site }}</p>
</div>
<script>
 var vm =  new Vue({
        el: "#app4",
        data: {
            name: '淘宝',
            url: "http://www.Taobao.com"
        },
        computed: {
            site: {
                // getter
                get: function() {
                    return this.name + ' '+this.url;
                },
                // setter
                set: function(newValue) {
                    var names = newValue.split(' ');
                    this.name = names[0];
                    this.url = names[names.length -1];
                }
            }
        }
    });
    // vm.site的值是newValue对应的实参
    vm.site = 'baidu http://www.baidu.com';
    document.write('name:' + vm.name );
    document.write('<br>');
    document.write('url:'+ vm.url );
    </script>

在这里插入图片描述
上面的代码,当我们执行vm.site=‘baidu http://www.baidu.com’时,数据name与url都会相对更新,视图也会更新。

监听属性 watch

通过watch来响应数据的变化。
虽然大多数情况计算属性都可以满足需要,但有时还是需要使用侦听器。当需要在数据发生变化时执行异步操作或者开销较大的操作时,就需要自定义监听器。

[实例1]:通过使用watch实现计数器:

 <div id="app5">
        <p style = "font-size: 25px;">计数器:{{ counter }}</p>
        <button @click = "counter++" style="font-size: 25px"> 点击我</button>
 </div>
 <script> 
    var vm1 = new Vue({
        el: "#app5",
        data: {
            counter: 1
        }
    });
    vm1.$watch('counter',function(nval,oval) {
        alert('计数器值的变化:' + oval + '变为' + nval + "!");
    })
 </script>

注意:$watch是一个实例方法,后面是一个回调函数,这个回调函数将在counter值改变之后调用
在这里插入图片描述
在这里插入图片描述

[实例2]:千米与米之间的换算

  <div id="app6">
        千米:<input type="text" v-model='kilometers'>
        米:<input type="text" v-model='meters'>
        <p id="info"></p>
    </div>
     <script> 
     var vm2  = new Vue({
        el: '#app6',
        data: {
            kilometers:0,
            meters:0
        },
        watch: {
            // 监听kilometers数据
            kilometers: function(val) {
                console.log(val);
                this.kilometers = val;
                this.meters = this.kilometers * 1000;
            },
            // 监听meters数据
            meters: function(val) {
                this.meters = val;
                this.kilometers = val /1000;
            }
        }
    });
    // $watch是一个实例方法
    vm2.$watch("kilometers",function(newValue,oldValue) {
        // 这个回调函数在vm2.kilometers改变后调用
        document.getElementById('info').innerHTML =  "修改前值为: " + oldValue + ",修改后值为: " + newValue;
    })
 </script>

在这里插入图片描述

[computed与watch的区别]

简单来说:
1:computed是同步的,watch可以实现异步
2:computed中的函数都是带返回值的,wacth里面的函数可以不写返回值。

我们可以在watch属性的方法里执行异步操作,使用定时器来限制操作的频率吧,添加中间状态等等,这些操作都是无法用计算属性实现的。

 <div id="app">
      {{count}}
      <button @click="count++">点击加一</button>
 </div>
<script>
new Vue({
	el: "#app",
	data: {
	count: 1
	},
	watch: {
       count: function(val) {
 			var  that = this;
			window.setTimeout(function() {
				that.count = 0;
		},2000)
}
}
})
</script>

在这里插入图片描述
当watch监听到count值发生变化时,2秒之后归零
在这里插入图片描述

计算属性、指令实现简单实战

实例:通过计算属性、指令等实现简单的购物车

 <style>
        table {
             border: 1px solid black;
             width: 100%;
            }
      
        th {
            height: 50px;
            }
        th, td {
            border-bottom: 1px solid #ddd;
            }
    </style>
    <div id="app7">
        <table>
            <tr>
                <th>序号</th>
                <th>商品名称</th>
                <th>购买价格</th>
                <th>购买数量</th>
                <th>操作</th>
            </tr>
            <tr v-for="iphone in IP_Json">
                <td>{{iphone.id}}</td>
                <td>{{iphone.name}}</td>
                <td>{{iphone.price}}</td>
                <td>
                    <!-- v-bind指令的参数disabled,当iPhone.count === 0时,不可再点击 -->
                    <button :disabled="iphone.count === 0" @click="iphone.count-=1">-</button>
                    {{iphone.count}}
                    <button @click="iphone.count+=1">+</button>
                </td>
                <td>
                    <button @click="iphone.count=0">移除</button>
                </td>
            </tr>
        </table>
        总价:${{totalPrice}}
    </div>
    <script>
     var shop = new Vue({
        el:"#app7",
        data: {
            IP_Json: [{
                id: 1,
                name: "huawei",
                price: 5099,
                count: 1
              },
              {
                  id:2,
                  name: "xiaomi",
                  price: 4899,
                  count: 1
              },
              {
                  id:3,
                  name: "iphone x",
                  price: 8900,
                  count: 1
              },
            ]
        },
        computed : {
            totalPrice: function() {
                var totalP = 0;
                var len = this.IP_Json.length;
                for( var i= 0; i<len;i++){
                    totalP += this.IP_Json[i].price * this.IP_Json[i].count;
                }
                return totalP;
            }
        }
    })
</script>
Logo

前往低代码交流专区

更多推荐