1.1 computed
一个数据, 依赖另外一些数据计算而来的结果
语法:
computed: { "计算属性名" () { return "值" }}
需求: 求2个数的和显示到页面上
<template> <div> <p>{{ num }}</p> </div> </template> <script> export
default { data(){ return { a: 10, b: 20 } }, // 计算属性: // 场景:
一个变量的值, 需要用另外变量计算而得来 /* 语法: computed: { 计算属性名 () { return 值
} } */ // 注意: 计算属性和data属性都是变量-不能重名 // 注意2: 函数内变量变化, 会自动重新计算结果返回 computed: {
num(){ return this.a + this.b } } } </script> <style> </style>
计算属性也是vue数据变量, 所以不要和data里重名, 用法和data相同
1.2 缓存
计算属性是基于它们的依赖项的值结果进行缓存的,只要依赖的变量不变, 都直接从缓存取结果
<template> <div> <p>{{ reverseMessage }}</p> <p>{{ reverseMessage
}}</p> <p>{{ reverseMessage }}</p> <p>{{ getMessage() }}</p> <p>{{
getMessage() }}</p> <p>{{ getMessage() }}</p> </div> </template> <script>
export default { data(){ return { msg: "Hello, Vue" } }, // 计算属性优势:
// 带缓存 // 计算属性对应函数执行后, 会把return值缓存起来 // 依赖项不变, 多次调用都是从缓存取值 // 依赖项值-变化,
函数会"自动"重新执行-并缓存新的值 computed: { reverseMessage(){
console.log("计算属性执行了"); return this.msg.split("").reverse().join("") }
}, methods: { getMessage(){ console.log("函数执行了"); return
this.msg.split("").reverse().join("") } } } </script> <style> </style>
计算属性根据依赖变量结果缓存, 依赖变化重新计算结果存入缓存, 比普通方法性能更高
1.3 vue计算属性-完整写法
计算属性也是变量, 如果想要直接赋值, 需要使用完整写法
语法:
computed: { "属性名": { set(值){ }, get() {
return "值" } } }
需求: 计算属性给v-model使用
页面准备输入框
<template> <div> <div> <span>姓名:</span> <input
type="text" v-model="full"> </div> </div> </template> <script> // 问题:
给计算属性赋值 - 需要setter // 解决: /* 完整语法: computed: { "计算属性名" (){},
"计算属性名": { set(值){ }, get(){
return 值 } } } */ export default { computed: {
full: { // 给full赋值触发set方法 set(val){
console.log(val) }, // 使用full的值触发get方法 get(){
return "无名氏" } } } } </script> <style>
</style>
想要给计算属性赋值, 需要使用 set 方法
1.4 案例-小选影响全选
小选框都选中(手选), 全选自动选中
分析:
① 先静态后动态, 从.md拿到静态标签和数据
② 循环生成复选框和文字, 对象的c属性和小选框的选中状态, 用v-model双向绑定
③ 定义isAll计算属性, 值通过小选框们统计c属性状态得来
<template> <div> <span>全选:</span> <!-- 4. v-model 关联全选 - 选中状态 -->
<input type="checkbox" v-model="isAll"/> <button>反选</button> <ul>
<li v-for="(obj, index) in arr" :key="index"> <!-- 3. 对象.c - 关联 选中状态
--> <input type="checkbox" v-model="obj.c"/> <span>{{ obj.name
}}</span> </li> </ul> </div> </template> <script> // 目标: 小选框 -> 全选
// 1. 标签+样式+js准备好 // 2. 把数据循环展示到页面上 export default { data() { return {
arr: [ { name: "猪八戒", c: false, }, {
name: "孙悟空", c: false, }, { name: "唐僧",
c: false, }, { name: "白龙马", c: false, },
], }; }, // 5. 计算属性-isAll computed: { isAll () { // 6.
统计小选框状态 -> 全选状态 // every口诀: 查找数组里"不符合"条件, 直接原地返回false return
this.arr.every(obj => obj.c === true) } } }; </script>
1.5 案例-全选影响小选
全选影响小选
*
需求1: 获取到全选状态 – 改装isAll计算属性
*
需求2: 全选状态同步给所有小选框
分析:
①: isAll改成完整写法, set里获取到全选框, 勾选的状态值
②: 遍历数据数组, 赋给所有小选框v-model关联的属性
<script> export default { // ...其他代码 // 5. 计算属性-isAll computed: { isAll:
{ set(val){ // 7. 全选框 - 选中状态(true/false)
this.arr.forEach(obj => obj.c = val) }, get(){ // 6. 统计小选框状态
-> 全选状态 // every口诀: 查找数组里"不符合"条件, 直接原地返回false return
this.arr.every(obj => obj.c === true) } } } }; </script>
1.6 案例-反选
需求: 点击反选, 让所有小选框, 各自取相反勾选状态
分析:
①: 小选框的勾选状态, 在对象的c属性
②: 遍历所有对象, 把对象的c属性取相反值赋予回去即可
<button @click="btn">反选</button> <script> export default { // ...其他代码省略
methods: { btn(){ // 8. 让数组里对象的c属性取反再赋予回去 this.arr.forEach(obj =>
obj.c = !obj.c) } } }; </script>
2. vue侦听器
2.1 watch
可以侦听 data/computed 属性值改变
语法:
watch: { "被侦听的属性名" (newVal, oldVal){ }}
完整例子代码:
<template> <div> <input type="text" v-model="name"> </div> </template>
<script> export default { data(){ return { name: "" } }, // 目标:
侦听到name值的改变 /* 语法: watch: { 变量名 (newVal, oldVal){ //
变量名对应值改变这里自动触发 } } */ watch: { // newVal: 当前最新值 // oldVal: 上一刻值
name(newVal, oldVal){ console.log(newVal, oldVal); } } } </script>
<style> </style>
2.2 深度侦听和立即执行
侦听复杂类型, 或者立即执行侦听函数
watch: { "要侦听的属性名": { immediate: true, // 立即执行 deep: true, // 深度侦听复杂类型内变化
handler (newVal, oldVal) { } } }
完整例子代码:
<template> <div> <input type="text" v-model="user.name"> <input
type="text" v-model="user.age"> </div> </template> <script> export default {
data(){ return { user: { name: "", age: 0 } } },
// 目标: 侦听对象 /* 语法: watch: { 变量名 (newVal, oldVal){ //
变量名对应值改变这里自动触发 }, 变量名: { handler(newVal, oldVal){ },
deep: true, // 深度侦听(对象里面层的值改变) immediate: true // 立即侦听(网页打开handler执行一次)
} } */ watch: { user: { handler(newVal, oldVal){ //
user里的对象 console.log(newVal, oldVal); }, deep: true,
immediate: true } } } </script> <style> </style>
immediate 立即侦听
deep 深度侦听
handler 固定方法触发
2.3 案例-品牌管理(数据缓存)
侦听list变化, 同步到浏览器本地
*
需求: 把品牌管理的数据实时同步到本地缓存
分析:
① 在watch侦听list变化的时候, 把最新的数组list转成JSON字符串存入到localStorage本地
② data里默认把list变量从本地取值, 如果取不到给个默认的空数组
效果:
新增/删除 – 刷新页面 – 数据还在
<template> <div> <div class="container"> <!-- 顶部框模块 --> <div
class="form-group"> <div class="input-group"> <h4>品牌管理</h4>
</div> </div> <!-- 数据表格 --> <table class="table
table-bordered table-hover mt-2"> <thead> <tr>
<th>编号</th> <th>资产名称</th> <th>价格</th>
<th>创建时间</th> <th>操作</th> </tr> </thead>
<tbody> <tr v-for="obj in list" :key="obj.id"> <td>{{
obj.id }}</td> <td>{{ obj.name }}</td> <!--
如果价格超过100,就有red这个类 --> <td :class="{ red: obj.price > 100 }">{{
obj.price }}</td> <td>{{ obj.time | formatDate }}</td>
<td><a href="#" @click="delFn(obj.id)">删除</a></td> </tr>
<!-- 4. 统计得有数据才显示 --> <tr v-if="list.length !== 0"
style="background-color: #eee"> <td>统计:</td> <td
colspan="2">总价钱为: {{ allPrice }}</td> <td colspan="2">平均价: {{
avgPrice }}</td> </tr> </tbody> <tfoot
v-show="list.length === 0"> <tr> <td colspan="5"
style="text-align: center">暂无数据</td> </tr> </tfoot>
</table> <!-- 添加资产 --> <form class="form-inline"> <div
class="form-group"> <div class="input-group"> <input
type="text" class="form-control"
placeholder="资产名称" v-model="name" /> </div>
</div> <div class="form-group">
<div class="input-group"> <input type="text"
class="form-control" placeholder="价格"
v-model.number="price" /> </div> </div>
<button class="btn btn-primary"
@click.prevent="addFn">添加资产</button> </form> </div> </div> </template>
<script> // 目标: 侦听list改变 - 同步到本地localStorage里 // 1. 侦听器-list export default {
data(){ return{ name: "", // 名称 price: 0, // 价格 //
3. 本地取出缓存list list: JSON.parse(localStorage.getItem('pList')) || [], };
}, methods: { addFn() { if (this.name.trim().length === 0 ||
this.price === 0) { alert("不能为空"); return; } let id =
this.list.length === 0 ? 100 : this.list[this.list.length - 1].id + 1;
this.list.push({ // 当前数组最后一个对象的id+1作为新对象id值 id: id,
name: this.name, price: this.price, time: new Date(), });
}, delFn(id){ // 通过id找到这条数据在数组中下标 let index =
this.list.findIndex(obj => obj.id === id) this.list.splice(index, 1) }
}, computed: { allPrice(){ return this.list.reduce((sum, obj) =>
sum += obj.price, 0) }, avgPrice(){ return (this.allPrice /
this.list.length).toFixed(2) } }, watch: { list: { handler(){
// 2. 存入本地 localStorage.setItem('pList', JSON.stringify(this.list))
}, deep: true } } }; </script> <style scoped> .red { color: red; }
</style>