1. 计算属性
<div id="app"><p>{{ reverseMsg }}</p><p>{{msg}}</p></div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {msg: 'hello'},computed: {reverseMsg() {return this.msg.split('').reverse().join('')}}});// 计算属性:处理某个或某些属性复杂展示逻辑,不会改变源数据;目的就是不要在模板中写太多的逻辑;</script>
2. computed
什么时候使用计算属性?
- 数据用来展示
- 需要展示的数据依赖其他数据,通过其他数据计算出来的;
- 当它依赖的数据发生变化时,会重新计算;
computed注意事项:
- computed 里面的属性会被 vm 所代理;
- computed 里面的属性和 data / methods / filters / 都不能重名;
- computed 的计算属性可以是一个函数还可以是一个对象;对象中有 get 和 set 方法,取值的时候执行 get,设置的时候执行 set;而函数形式的计算属性,只有 get 的情况,只能获取不能设置,如果设置会报错;
- 如果一个值需要依赖其他属性计算而来,这个时候最好用计算属性;
<div id="app"><div class="container"><div class="row"><table class="table table-bordered"><tr><td>全选:<input type="checkbox"v-model="checkAll"></td><td>商品</td><td>数量</td><td>单价</td><td>小计</td></tr><tr v-for="(product, index) in carts" :key="index"><td><input type="checkbox"v-model="product.isSelected"@change="changeOne"></td><td>{{product.name}}</td><td><input type="number" v-model="product.count" min="1"></td><td>{{product.price}}</td><td>{{product.count * product.price | toRMB}}</td></tr><tr><td colspan="5">总价:{{total | toRMB}}</td></tr></table><input type="text" v-model="total"></div></div></div><script src="vue.js"></script><script>// npm install 依赖包@版本号 指定版本号安装;如果不指定,就会按照最新的装;let vm = new Vue({el: '#app',data: {carts: [{isSelected: true,count: 3,price: 57.86,name: '车厘子'},{isSelected: true,count: 1,price: 6666,name: 'iPhoneX'}]},filters: {toRMB(val) {return '¥' + val.toFixed(2)}},methods: {changeAll() {},changeOne() {}},computed: {// computed 里面的属性最终也会被 vm 代理,这些属性都会在 vm 身上也有一份;total: function () { // 计算属性的 getter 形式,这样声明的 total 只能读,不能写;这个属性不能修改,修改它会报错;// 首先把打钩的商品筛选出来let selected = this.carts.filter(i => i.isSelected);return selected.reduce((prev, next) => {// next 是数组项,现在是对象return prev + next.count * next.price;}, 0);},// 计算属性的 settercheckAll: {get() {// 当获取 checkAll 的时候就会执行 get 方法,并且取到的值是 get 方法的返回值return this.carts.every(item => item.isSelected);},set(val) {// 当修改 checkAll 属性的时候,会触发 set 方法,并且 val 会接收到 checkAll 的新值;// console.log(val); val 就是修改 checkAll 的时候传过来的值this.carts.forEach(item => item.isSelected = val)}}}});</script>
3. 小 demo
当 input 中输入的内容长度大于5的时候提示“太长了”,当输入的内容小于3的时候提示“太短了”
<div id="app"><input type="text" v-model="text"> <br>{{msg}}</div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {text: ''},computed: {msg () {if (this.text.length > 5) {return '太长了'}if (this.text.length < 3) {return '太短了'}// 计算属性不能写在异步处理程序:ajax、定时器、Promise 的 then}}})</script>
4. 侦听器属性 watch
侦听器属性:watch 当我们需要监听一个属性的改变,当它改变的时候我们要做某些事,此时我们就需要使用侦听器属性
let vm = new Vue({el: '#app',data: {text: '',msg: ''},watch: {// watch 就是侦听器属性;写在这个对象中的属性都是要被监控的;// 当被监控的属性的值发生变化的时候,就会触发对应的函数;// 属性名:被监控的属性名,例如 text;属性值是一个函数text(newVal, oldVal) {// console.log(newVal, oldVal);// newVal 是被监控属性的新值// oldVal 是被监控的属性的旧值// 侦听器属性可以使用异步;setTimeout(() => {if (newVal.length > 5) {this.msg = '太长了';} else if (newVal.length < 3) {this.msg = '太短了';} else {this.msg = '';}}, 0)}// 能用表单的事件就用事件或者使用计算属性;这两种都不行的时候再用 watch;},methods: {fn() {console.log(this.text);}}})
5. watch 和 computed 区别
computed:
- 页面加载时就求值,当依赖的数据发生改变时会重新求值;
- 不支持异步
- computed 可以依赖多个属性,如果被依赖的属性有一个发生变化,就会重新求值;(相当于监控多个属性)
watch:
- 页面加载时,watch 不执行,只有被监控的数据发生变化时才会执行对应的函数
- 支持异步
- 一个函数对应一个被监控的属性,只有当这个属性的值发生变化时才会执行这个函数
6. v-bind-class
v-bind 绑定动态属性
v-bind:class 动态绑定类名,实现动态操作样式;
v-bind:class 常见的形式:
- 对象,{className1: 属性值1, className2: 属性值2,…} 当属性值为 true 的时候,对应的 className 生效;
- 三元运算符,三元运算符的返回值是生效的类名;如果条件为 true,返回条件成立的值,否则返回条件不成立的值;
- 还可以是一个方法调用,最终生效的类名是方法的返回值;
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>.x {background: red;height: 20px;}.y {float: left;}.z {font-size: 30px;}.a {color: green;}</style></head><body><div id="app"><div :ok="flag">{{notFlag}}</div><div :class="{x: flag, z: !flag, a: flag}"><!--元素浮动以后会脱离文档流,父级元素无法识别子元素的宽高;解决方案:清浮动、给父元素一个高度--><div class="y">A</div></div><div :class="0 ? 'x' : z">B</div> <!--如果要使用的类名就叫做 x,要用引号包裹 'x',因为 v-bind:class 以后,等号右侧变成了 js 表达式,不带引号的都是变量--><div :class="getClass()">C</div></div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {flag: true,z: 'zig-zag'},computed: {notFlag () {return !this.flag;}},methods: {getClass() {return 'abcdefg';}}});</script></body></html>
7. v-bind-style
v-bind:style 动态绑定行内样式
常见的情况:
- 一个对象,对象中都是样式例如 {color: ‘#000’, background: ‘#fff’}
- 一个数组,数组项是样式对象,最终会把这些对象中的样式合并到一起;
- 方法调用,在方法中返回一个样式对象
<div id="app"><select v-model="fontColor"><option value="#ccc">灰色</option><option value="#fff">白色</option><option value="#00b38a">绿色</option></select><div :style="{color: fontColor, backgroundColor: '#000', paddingLeft: '100px'}">A</div><div :style="ary">BCDEFG</div><div :style="getStyle()">XYZ</div></div><script src="vue.js"></script><script>let vm = new Vue({el: '#app',data: {s1: {fontSize: '20px',background: '#00b38a'},s2: {fontStyle: 'italic',fontWeight: 'bold'},ary: [{fontSize: '20px',background: '#00b38a'},{fontStyle: 'italic',fontWeight: 'bold'}],fontColor: '#fff'},methods: {getStyle() {return {color: 'pink', background: '#000'}}}});</script>
8. 自定义指令
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>#app div {position: absolute;width: 100px;height: 100px;background: lightgreen;-webkit-user-select: none;}#app div:nth-child(2) {top: 120px;}</style></head><body><div id="app"><div v-drag>A</div><div v-drag>B</div></div><script src="vue.js"></script><script>// 指令:是以 v- 开头的行内属性,vue 赋予它特殊的功能;let vm = new Vue({el: '#app',data: {},filters: {},methods: {},computed: {},watch: {},created() {},directives: {drag: {// inserted 是指令的钩子函数inserted(el) {// el 是使用指令的元素对象// console.dir(el);el.onmousedown = function (e) {// 1. 记录初始位置this.startX = e.pageX - this.offsetLeft;this.startY = e.pageY - this.offsetTop;// 2. 把鼠标移动事件委托给 document,防止鼠标丢失document.onmousemove = function (e) {el.style.left = e.pageX - el.startX + 'px';el.style.top = e.pageY - el.startY + 'px';};// 3. 把鼠标左键抬起事件委托给 documentdocument.onmouseup = function () {document.onmousemove = null;document.onmouseup = null;}}}}}})</script></body></html>
9. todolist
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="bootstrap.css"><style>.panel-footer a {margin-left: 15px;}.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>{{undone}}</h2><input type="text"v-focusclass="form-control"@keydown.enter="add"v-model="title"></div><div class="panel-body"><ul class="list-group"><li v-for="(item, index) in filterTodo":key="index"class="list-group-item"><span><input type="checkbox"v-model="item.isSelected"><!--<del>{{item.title}}</del> 让文字有一种删除效果,横线划过文字--></span><span :class="{del: item.isSelected}">{{item.title}}</span><button class="btn btn-danger btn-xs pull-right"@click="remove(index)">删除</button></li></ul></div><div class="panel-footer"><a v-for="(item, index) in config":key="index":href="item.hash">{{item.name}}</a></div></div></div></div></div></div><script src="vue.js"></script><script>let config = [{name: '全部',hash: '#all'},{name: '已完成',hash: '#finished'},{name: '未完成',hash: '#unfinished'}];let vm = new Vue({el: '#app',data: {title: '',todoList: [{isSelected: false,title: '吃饭饭'},{isSelected: false,title: '睡觉觉'}],config,hash: '#all'},created() {window.addEventListener('hashchange', () => {this.hash = location.hash;});},computed: {undone() {let num = this.todoList.filter(i => !i.isSelected).length;let msg = '';if (num) {msg = `亲,^_^ 你还有${num}件事没干`;} else {msg = `亲,你好棒啊!今天的事情做完了,赶紧休息一下吧~`}return msg;},filterTodo() {// 根据当前 hash 的值过滤:switch (this.hash) {case '#all':return this.todoList;case '#finished':return this.todoList.filter(item => item.isSelected);case '#unfinished':return this.todoList.filter(item => !item.isSelected);}}},methods: {add() {// 添加任务this.todoList.push({title: this.title,isSelected: false});this.title = ''; // 添加到列表后,清空 input},remove(index) {this.todoList.splice(index, 1);}},directives: {focus: {inserted(el) {el.focus();}}}})</script></body></html>
10. vue 版选项卡
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><style>* {margin: 0;padding: 0;}ul, li {list-style: none;}.wrapper {width: 800px;margin: 30px auto;border: 1px solid #000;}.header {overflow: hidden;border-bottom: 1px solid #000;}.header li {float: left;box-sizing: border-box;width: 200px;height: 35px;line-height: 35px;text-align: center;border-right: 1px solid #000;cursor: pointer;-webkit-user-select: none;}.header li:last-child {border-right: none;}.header li.active {background: yellow;}.card {display: none;height: 400px;line-height: 400px;font-size: 40px;text-align: center;text-decoration: underline;}div.card.active {display: block;}</style></head><body><div id="app"><div class="wrapper"><ul class="header"><li v-for="(item, index) in headers":key="index":class="{active: selected == index}"@click="headerClick(index)">{{item}}</li></ul><div v-for="(item, index) in cards":key="index"class="card":class="{active: selected == index}">{{item}}</div></div></div><script src="vue.js"></script><script>let headers = ['唱', '跳', 'RAP', '篮球'];let cards = ['《鸡你太美》', '钢管', 'RAP-鸡你太美', 'NBA-鸡你太美'];let vm = new Vue({el: '#app',data: {selected: 0,headers,cards},methods: {headerClick(index) {this.selected = index;}}})</script></body></html>
补充
什么是 disable?
disabled 属性:为 true 时按钮会被禁用;为 false 时是可用状态
disabled 属性是特殊的属性,存在就是 true;v-bind 以后,vue 发现它绑定值是 false 的时候,会移除这个属性;
reduce 方法是专门用来求和的
// 0let ary = [1, 2, 3, 4];// ary.reduce((前一个, 后一个) => {}, 初始值);let total = ary.reduce((prev, next) => {// 第一次执行的时候:prev 是初始值0;初始值不传的时候 prev 是第一项;// 后面每一次执行的时候,prev 是上一次回调函数的返回值console.log(prev, next);return prev + next;}, 0);console.log(total); // reduce 最后的返回结果是最后一次回调执行的结果
