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);
},
// 计算属性的 setter
checkAll: {
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. 把鼠标左键抬起事件委托给 document
document.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-focus
class="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 方法是专门用来求和的
// 0
let 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 最后的返回结果是最后一次回调执行的结果