一、计算属性
什么是计算属性
计算属性,类似于过滤器,用于处理某个或者几个属性的复杂展示逻辑,但是并不会改变源数据;
示例;
{...computed: {sum() {return this.products.filter(item => item.isSelected).reduce((prev, next) => {return prev + next.productPrice * next.productCount;}, 0)}},...}
使用计算属性
计算属性默认和普通数据一样,直接在 HTML 中使用即可;示例
<td colspan="6">总价格:{{sum | toFixed}}</td>
计算属性的set
- computed 中的属性都有一个 get 和 set 方法,当获取这个属性时,会执行 get 方法,属性值是 get 方法的返回值;当设置 computed 中的属性时,会触发 set 方法;
 
{...computed: {checkAll: {get() {return this.products.every(i => i.isSelected);},set(val) {this.products.forEach(i => i.isSelected = val);}}},...}
- computed 中的属性都会被 vm 所代理,最终都要放到 vm 上;
 - computed 中的属性和 data 中的属性不能同名,也不能和 methods 中的属性同名
 - computed 计算属性还可以是一个函数,相当于只设置 get 的情况,函数的返回值就是计算属性的值,但是不能设置
 - 如果一个属性依赖于其他属性计算而来,那么这个属性最好用 computed
 
计算属性不能用在异步处理中
- 计算属性的处理不能写在异步处理程序中,如定时器、ajax等
 
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div id="app"><input type="text" v-model="sth"> <br>{{msg}}</div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {sth: ''},computed: {msg () {// 计算属性的处理不能写在异步处理程序中,如定时器、ajax等setTimeout(() => {if (this.sth.length > 5) {return '太长了'}if (this.sth.length < 3) {return '太少了'}}, 0)}}})</script></body></html>
计算属性示例代码
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css"></head><body><div id="app"><!--caption 只能放在 table 中使用:表头--><!--bootstrap--><div class="container"><div class="row"><h2 class="text-center text-danger">珠峰购物车</h2><table class="table table-bordered"><tr><td>全选 <input type="checkbox" v-model="checkAll"/></td><td>商品</td><td>单价</td><td>数量</td><td>小计</td><td>操作</td></tr><tr v-for="(product, index) in products" :key="index"><!-- {"isSelected": true,"productCover": "https://img10.360buyimg.com/cms/s80x80_jfs/t6094/107/710811867/382815/4d54717/592bf165N755a88f0.jpg","productName": "深入浅出Node.js","productInfo": "颜色:Node.js学习","productPrice": 57.8,"productCount": 3}--><td><input type="checkbox" v-model="product.isSelected"></td><td><img :src="product.productCover" :title="product.productName" alt="">{{product.productInfo}}</td><td>{{product.productPrice}}</td><td><input type="number" v-model="product.productCount" min="1"/></td><td>{{product.productPrice * product.productCount | toFixed(2)}}</td><td><button class="btn btn-danger" @click="remove(product)">删除</button></td></tr><tr><td colspan="6">总价格:{{sum | toFixed}}</td></tr></table></div></div></div><script src="axios.js"></script><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {products: []},filters: {toFixed(val, num = 2) {return '¥' + val.toFixed(num)}},created() {this.getData();},computed: {sum() {return this.products.filter(item => item.isSelected).reduce((prev, next) => {return prev + next.productPrice * next.productCount;}, 0)},checkAll: {get() {return this.products.every(i => i.isSelected);},set(val) {this.products.forEach(i => i.isSelected = val);}}},methods: {getData() {axios.get('carts.json').then(({data}) => {this.products = data;})},remove(val) {// val 点击时删除的 productthis.products = this.products.filter(item => item !== val);},changeAll() {// 如果全选为 true,即 this.checkAll 为 true,下面的商品的 checkbox 都要选中,如果是全选是 false,那么商品的 checkbox 也是 falsethis.products.forEach(item => item.isSelected = this.checkAll)},changeOne() {// 点击每一个 input 的复选框时,去校验是否有是所有的 product 中的 isSelected 都为 true,如果都是 true,那么 checkAll 结果也是 true,如果有一个是 false,那么 checkAll 就是 false,使用 every 方法this.checkAll = this.products.every(item => item.isSelected);}}})</script></body></html>
二、侦听器 watch
什么是侦听器属性
watch: 侦听器属性,用于监听某个属性改变,如果发生改变就会触发对应的函数
示例:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div id="app"><input type="text" v-model="sth"> <br>{{msg}}</div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {sth: '',msg: '',},watch: {// computed: 计算属性// watch: 侦听器属性,用于监听某个属性改变,如果发生改变就会触发对应的函数sth(newVal, oldVal) {// 监听属性 sth,当 sth 属性发生变化时就会触发这个函数// 这个函数有两个参数,第一个是 sth 的最新值,第二个是 sth 的上一个值console.log(newVal);console.log(oldVal);setTimeout(() => {if (newVal.length > 5) {this.msg = '太长了'}if (newVal.length < 3) {this.msg = '太少了'}}, 0)}}});// 在工作中能使用 computed 尽量使用 computed 而不要使用 watch;</script></body></html>
三、computed 和 watch
computed 计算属性
- 页面加载时就求值;支持缓存,如果依赖的数据发生改变,才会重新计算;
 - 不支持异步
 - 如果一个属性是由其他属性计算而来,这个属性依赖其他属性,依赖发生变化时自动求取最新的计算结果
 
watch 计算属性
- 页面加载时不求值,依赖值发生改变时才求值
 - watch 支持异步
 - watch 只能监听一个属性的变化,如果有属性依赖这个结果,那么需要手动去重新计算这个结果;
 
示例代码
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div id="app"><input type="text" v-model="firstName" /><input type="text" v-model="lastName" />{{fullName}}</div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {firstName: '宾',lastName: '马',fullName: ''},computed: {/*fullName () {return this.lastName + this.firstName;}*/},watch: {firstName(newVal, oldVal) {this.fullName = this.lastName + newVal;},lastName(nawVal, oldVal) {this.fullName = newVal + this.firstName;}}});// computed vs watch// computed// 1. 页面加载时就求值;支持缓存,如果依赖的数据发生改变,才会重新计算;// 2. 不支持异步// 3. 如果一个属性是由其他属性计算而来,这个属性依赖其他属性,依赖发生变化时自动求取最新的计算结果// watch// 1. 页面加载时不求值,依赖值发生改变时才求值// 2. watch 支持异步// 3. watch 只能监听一个属性的变化,如果有属性依赖这个结果,那么需要手动去重新计算这个结果;</script></body></html>
四、v-bind:class
什么是v-bind:class
- v-bind: 绑定动态属性,v-bind 可以简写成 :
 - v-bind:class 动态绑定类名,属性值有以下常用情况:;
- 如果是对象,对象的属性名是 class 名,属性值为 true 时类名生效,为 false 时不生效
 - 如果是三元运算符,三元运算符表达式的返回结果是生效的 class
 
 - :class 和原有的 class 并不会冲突,如果不同,最终会合并,如果相同会覆盖原有的;
 
代码示例:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>.x {font-size: 30px;}.y {color: red;}.z {background: lightgreen;}</style></head><body><div id="app"><div class="x" :class="{y: true, z: flag}">A</div><div :class="1 ? 'y' : 'z'">C</div></div><script src="vue.js"></script><script>// v-bind: 绑定动态属性,v-bind 可以简写成 :// v-bind:class 属性值有以下常用情况:;// 如果是对象,对象的属性名是 class 名,属性值为 true 时类名生效,为 false 时不生效// 如果是三元运算符,三元运算符表达式的返回结果是生效的 class// :class 和原有的 class 并不会冲突,如果不同,最终会合并,如果相同会覆盖原有的;let vm = new Vue({el: '#app',data: {class1: 'y',flag: true}})</script></body></html>
五、v-bind:style
什么是v-bind:style?
v-bind:style 用来绑定元素的动态样式;
:style的常见值
v-bind:style 属性值是一个对象或者数组,如果是对象,里面可以存储具体样式;数组中可以放多个样式对象;
代码示例:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><div id="app"><div :style="{color: 'red',width: '100px'}">A</div><div :style="[style1, style2]">B</div></div><script src="vue.js"></script><script>// v-bind:style 属性值是一个对象或者数组,如果是对象,里面可以存储具体样式;数组中可以放多个样式对象;let vm = new Vue({el: '#app',data: {style1: {background: 'pink'},style2: {color: '#000'}}})</script></body></html>
六、自定义指令
- 自定义指令可以给元素增加一些特殊的功能;以拖拽为例;
 
代码示例:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>#app div {position: absolute;width: 100px;height: 100px;background: red;-webkit-user-select: none;}#app div:nth-child(2) {top: 200px}</style></head><body><div id="app"><div v-drag>1</div><div v-drag>2</div></div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',directives: {// 对象中存储的自定义指令drag: {inserted(el) {// 这个函数定义了指令的具体行为 第一个参数是使用这个元素的元素el.onmousedown = function (e) {this.startX = e.pageX - this.offsetLeft;this.startY = e.pageY - this.offsetTop;document.onmousemove = function (e) {el.style.left = e.pageX - el.startX + 'px';el.style.top = e.pageY - el.startY + 'px';};document.onmouseup = function () {document.onmousemove = document.onmouseup = null;}}}}}})</script></body></html>
七、todoList
代码示例:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>.del {text-decoration: line-through;color: #ccc;}</style></head><body><div id="app"><div class="container"><div class="row"><div class="col-md-8"><div class="panel panel-success"><div class="panel-heading bg-warning"><h2 class="text-danger">亲~^_^,你有2件事要完成</h2><input type="text" class="form-control" @keyup.enter="add" v-model="title"></div><div class="panel-body"><ul class="list-group"><li class="list-group-item" v-for="(todo, index) in filterTodo" :key="index"><span :class="{del: todo.isSelected}"><input type="checkbox" v-model="todo.isSelected"> {{todo.title}}</span><button class="btn btn-danger pull-right btn-xs" @click="remove(todo)">删除</button></li></ul></div><div class="panel-footer"><a href="#all">全部任务</a><a href="#finish">已完成</a><a href="#unfinish">未完成</a></div></div></div></div></div></div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {todos: [{isSelected: false,title: '睡觉'},{isSelected: false,title: '吃饭'}],title: '',hash: '#all'},directives: {focus(el) {el.focus()}},created() {window.addEventListener('hashchange', () => {// 一旦页面中的 hash 值发生变化,对 data 中的 hash 重新赋值this.hash = window.location.hash;})},methods: {add () {this.todos.push({isSelected: false,title: this.title});this.title = '';},remove(val) {this.todos = this.todos.filter(item => item !== val);}},computed: {filterTodo() {switch (this.hash) {case '#all':return this.todos;case '#finish':return this.todos.filter(item => item.isSelected);case '#unfinish':return this.todos.filter(i => !i.isSelected);}}}})</script></body></html>
【发上等愿,结中等缘,享下等福,择高处立,寻平处住,向宽处行】
