一、计算属性
什么是计算属性
计算属性,类似于过滤器,用于处理某个或者几个属性的复杂展示逻辑,但是并不会改变源数据;
示例;
{
...
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 点击时删除的 product
this.products = this.products.filter(item => item !== val);
},
changeAll() {
// 如果全选为 true,即 this.checkAll 为 true,下面的商品的 checkbox 都要选中,如果是全选是 false,那么商品的 checkbox 也是 false
this.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>