vue基础
vue基础 v-for更新监测
目标: 当v-for遍历的目标结构改变, Vue触发v-for的更新
情况1: 数组翻转
情况2: 数组截取
情况3: 更新值
口诀:
数组变更方法, 就会导致v-for更新, 页面更新
数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
   | <template>   <div>     <ul>       <li v-for="(val, index) in arr" :key="index">         {{ val }}       </li>     </ul>     <button @click="revBtn">数组翻转</button>     <button @click="sliceBtn">截取前3个</button>     <button @click="updateBtn">更新第一个元素值</button>   </div> </template>
  <script> export default {   data(){     return {       arr: [5, 3, 9, 2, 1]     }   },   methods: {     revBtn(){       // 1. 数组翻转可以让v-for更新       this.arr.reverse()     },     sliceBtn(){       // 2. 数组slice方法不会造成v-for更新       // slice不会改变原始数组       // this.arr.slice(0, 3)
        // 解决v-for更新 - 覆盖原始数组       let newArr = this.arr.slice(0, 3)       this.arr = newArr     },     updateBtn(){       // 3. 更新某个值的时候, v-for是监测不到的       // this.arr[0] = 1000;
        // 解决-this.$set()       // 参数1: 更新目标结构       // 参数2: 更新位置       // 参数3: 更新值       this.$set(this.arr, 0, 1000)     }   } } </script>
  <style>
  </style>
   | 
 
这些方法会触发数组改变, v-for会监测到并更新页面
push() 
pop() 
shift() 
unshift() 
splice() 
sort() 
reverse() 
这些方法不会触发v-for更新
slice() 
filter() 
concat()  
注意: vue不能监测到数组里赋值的动作而更新, 如果需要请使用Vue.set() 或者this.$set(), 或者覆盖整个数组
总结:  改变原数组的方法才能让v-for更新
vue基础_v-for就地更新
v-for 的默认行为会尝试原地修改元素而不是移动它们。
详解v-for就地更新流程(可以看ppt动画)

这种 虚拟DOM对比方式, 可以提高性能 - 但是还不够高
vue基础_虚拟dom
目标: 了解虚拟DOM的概念
.vue文件中的template里写的标签, 都是模板, 都要被vue处理成虚拟DOM对象, 才会渲染显示到真实DOM页面上
内存中生成一样的虚拟DOM结构(==本质是个JS对象==)
因为真实的DOM属性好几百个, 没办法快速的知道哪个属性改变了
比如template里标签结构
1 2 3 4 5
   | <template>     <div id="box">         <p class="my_p">123</p>     </div> </template>
   | 
 
对应的虚拟DOM结构
1 2 3 4 5 6 7 8 9
   | const dom = {     type: 'div',     attributes: [{id: 'box'}],     children: {         type: 'p',         attributes: [{class: 'my_p'}],         text: '123'     } }
  | 
 
 
以后vue数据更新
- 生成新的虚拟DOM结构
 
- 和旧的虚拟DOM结构对比
 
- 利用diff算法, 找不不同, 只更新变化的部分(重绘/回流)到页面 - 也叫打补丁
 
 
==好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)==
==好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)==
总结: 虚拟DOM保存在内存中, 只记录dom关键信息, 配合diff算法提高DOM更新的性能
在内存中比较差异, 然后给真实DOM打补丁更新上

vue基础_diff算法
vue用diff算法, 新虚拟dom, 和旧的虚拟dom比较
情况1: 根元素变了, 删除重建
旧虚拟DOM
1 2 3
   | <div id="box">     <p class="my_p">123</p> </div>
   | 
 
新虚拟DOM
1 2 3
   | <ul id="box">     <li class="my_p">123</li> </ul>
   | 
 
情况2: 根元素没变, 属性改变, ==元素复用==, 更新属性
旧虚拟DOM
1 2 3
   | <div id="box">     <p class="my_p">123</p> </div>
   | 
 
新虚拟DOM
1 2 3
   | <div id="myBox" title="标题">     <p class="my_p">123</p> </div>
   | 
 
vue基础_diff算法-key
情况3: 根元素没变, 子元素没变, 元素内容改变
无key - 就地更新
v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示
1 2 3 4 5 6 7
   | <ul id="myUL">     <li v-for="str in arr">         {{ str }}          <input type="text">     </li> </ul> <button @click="addFn">下标为1的位置新增一个</button>
   | 
 
1 2 3 4 5 6 7 8 9 10 11 12
   | export default {     data(){         return {             arr: ["老大", "新来的", "老二", "老三"]         }     },     methods: {         addFn(){             this.arr.splice(1, 0, '新来的')         }     } };
  | 
 

旧 - 虚拟DOM结构  和  新 - 虚拟DOM结构 对比过程

==性能不高, 从第二个li往后都更新了==
有key - 值为索引 
因为新旧虚拟DOM对比, key存在就复用此标签更新内容, 如果不存在就直接建立一个新的
1 2 3 4 5 6 7
   | <ul id="myUL">     <li v-for="(str, index) in arr" :key="index">         {{ str }}          <input type="text">     </li> </ul> <button @click="addFn">下标为1的位置新增一个</button>
   | 
 
1 2 3 4 5 6 7 8 9 10 11 12
   | export default {     data(){         return {             arr: ["老大", "新来的", "老二", "老三"]         }     },     methods: {         addFn(){             this.arr.splice(1, 0, '新来的')         }     } };
  | 
 
key为索引-图解过程 (又就地往后更新了)


v-for先循环产生新的DOM结构, key是连续的, 和数据对应
 
然后比较新旧DOM结构, 找到区别, 打补丁到页面上
最后补一个li, 然后从第二个往后, 都要更新内容
 
口诀: key的值有id用id, 没id用索引
有key - 值为id 
key的值只能是唯一不重复的, 字符串或数值
v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示
新DOM里数据的key存在, 去旧的虚拟DOM结构里找到key标记的标签, 复用标签
新DOM里数据的key存在, 去旧的虚拟DOM结构里没有找到key标签的标签, 创建
旧DOM结构的key, 在新的DOM结构里没有了, 则==移除key所在的标签==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
   | <template>   <div>     <ul>       <li v-for="obj in arr" :key="obj.id">         {{ obj.name }}         <input type="text">       </li>     </ul>     <button @click="btn">下标1位置插入新来的</button>   </div> </template>
  <script> export default {   data() {     return {       arr: [         {           name: '老大',           id: 50         },         {           name: '老二',           id: 31         },         {           name: '老三',           id: 10         }       ],     };   },   methods: {     btn(){       this.arr.splice(1, 0, {         id: 19,          name: '新来的'       })     }   } }; </script>
  <style> </style>
   | 
 
图解效果:


总结: 不用key也不影响功能(就地更新), 添加key可以提高更新的性能
阶段小结
v-for什么时候会更新页面呢?
vue是如何提高更新性能的?
虚拟DOM是什么?
diff算法如何比较新旧虚拟DOM?
- 根元素改变 – 删除当前DOM树重新建
 
- 根元素未变, 属性改变 – 更新属性
 
- 根元素未变, 子元素/内容改变
 
- 无key – 就地更新 / 有key – 按key比较
 
vue基础 动态class
目标: 用v-bind给标签class设置动态的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | <template>   <div>     <!-- 语法:       :class="{类名: 布尔值}"       使用场景: vue变量控制标签是否应该有类名      -->     <p :class="{red_str: bool}">动态class</p>   </div> </template>
  <script> export default {   data(){     return {       bool: true     }   } } </script>
  <style scoped>   .red_str{     color: red;   } </style>
   | 
 
总结: 就是把类名保存在vue变量中赋予给标签
vue基础-动态style
目标: 给标签动态设置style的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | <template>   <div>     <!-- 动态style语法       :style="{css属性名: 值}"      -->     <p :style="{backgroundColor: colorStr}">动态style</p>   </div> </template>
  <script> export default {   data(){     return {       colorStr: 'red'     }   } } </script>
  <style>
  </style>
   | 
 
总结: 动态style的key都是css属性名
案例-品牌管理(铺)
目标: 数据铺设
- 需求1: 把默认数据显示到表格上
 
- 需求2: 注意资产超过100的, 都用红色字体标记出来
 
细节:
    ① 先铺设静态页面 — 去.md文档里, 复制数据和标签模板
    ② 此案例使用bootstrap, 需要下载, 并导入到工程main.js中
    ③ 用v-for配合默认数据, 把数据默认铺设到表格上显示
    ④ 直接在标签上, 大于100价格, 动态设置red类名
图示:

- 因为案例使用了bootstrap, 工程化开发, 模块化用npm/yarn下载引入使用
 
- 在main.js - 引入bootstrap
 
1
   | import "bootstrap/dist/css/bootstrap.css" 
   | 
 
- 模板代码(在这个基础上写)
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
   | <template>   <div id="app">     <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 >             <td></td>             <td></td>
              <!-- 如果价格超过100,就有red这个类 -->             <td class="red"></td>             <td></td>             <td><a href="#" >删除</a></td>           </tr>         </tbody>           <!--          <tfoot >           <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="资产名称"             />           </div>         </div>                      <div class="form-group">           <div class="input-group">             <input               type="text"               class="form-control"               placeholder="价格"             />           </div>         </div>                      <!-- 阻止表单提交 -->         <button class="btn btn-primary">添加资产</button>       </form>     </div>   </div> </template>
  <script>
  export default {   data() {     return {       name: "", // 名称       price: 0, // 价格       list: [         { id: 100, name: "外套", price: 199, time: new Date('2010-08-12')},         { id: 101, name: "裤子", price: 34, time: new Date('2013-09-01') },         { id: 102, name: "鞋", price: 25.4, time: new Date('2018-11-22') },         { id: 103, name: "头发", price: 19900, time: new Date('2020-12-12') }       ],     };   },
  }; </script>
  <style > .red{   color: red; } </style>
   | 
 
==正确代码, 不可复制==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | <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 }}</td>         <td><a href="#" >删除</a></td>     </tr> </tbody>
  <script> // 1. 明确需求 // 2. 标签+样式+默认数据 // 3. 下载bootstrap, main.js引入bootstrap.css // 4. 把list数组 - 铺设表格 // 5. 修改价格颜色 </script>
   | 
 
案例-品牌管理(增)
目标: 数据新增
图示:

在上个案例代码基础上接着写
==正确代码,不可复制==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
   | <!-- 添加资产 -->       <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>                      <!-- 4. 阻止表单提交(刷新网页数据又回去了) -->         <button class="btn btn-primary" @click.prevent="addFn">添加资产</button>       </form>
  <script> // 目标: 新增 // 1. 按钮 - 事件 // 2. 给表单v-model绑定vue变量 export default {   // ...省略其他   methods: {     addFn(){       // 5. 判断是否为空       if (this.name.trim().length === 0 || this.price === 0) {         alert("不能为空")         return       }       // 3. 把值以对象形式-插入list       this.list.push({         // 当前数组最后一个对象的id+1作为新对象id值         id: this.list[this.list.length - 1].id + 1,         name: this.name,         price: this.price,         time: new Date()       })     }   } }; </script>
   | 
 
案例-品牌管理(删)
目标: 数据删除
图示:

在上个案例代码基础上接着写
正确的代码(==不可复制==)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   |  <td><a href="#" @click="delFn(obj.id)">删除</a></td>            <script> // 目标: 删除功能 // 1. 删除a标签-点击事件 // 2. 对应方法名 // 3. 数据id到事件方法中 // 4. 通过id, 找到这条数据在数组中的下标 // 5. splice方法删除原数组里的对应元素 // 6. 设置tfoot, 无数据给出提示 // 7. 无数据再新增, id要判断一下 export default {   // ...其他代码   methods: {     // ...其他代码     delFn(id){       // 通过id找到这条数据在数组中下标       let index = this.list.findIndex(obj => obj.id === id)       this.list.splice(index, 1)     }   } }; </script>
 
  | 
 
vue过滤器
vue过滤器-定义使用
目的: 转换格式, 过滤器就是一个函数, 传入值返回处理后的值
过滤器只能用在, ==插值表达式和v-bind表达式==
Vue中的过滤器场景
- 字母转大写, 输入”hello”, 输出”HELLO”
 
- 字符串翻转, “输入hello, world”, 输出”dlrow ,olleh”
 
语法: 
例子:
- 全局定义字母都大写的过滤器
 
- 局部定义字符串翻转的过滤器
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
   | <template>   <div>     <p>原来的样子: {{ msg }}</p>     <!-- 2. 过滤器使用       语法: {{ 值 | 过滤器名字 }}      -->     <p>使用翻转过滤器: {{ msg | reverse }}</p>     <p :title="msg | toUp">鼠标长停</p>   </div> </template>
  <script> export default {   data(){     return {       msg: 'Hello, Vue'     }   },   // 方式2: 局部 - 过滤器   // 只能在当前vue文件内使用   /*      语法:       filters: {        过滤器名字 (val) {          return 处理后的值        }      }   */   filters: {     toUp (val) {       return val.toUpperCase()     }   } } </script>
  <style>
  </style>
   | 
 
总结: 把值转成另一种形式, 使用过滤器, Vue3用函数替代了过滤器.
全局注册最好在main.js中注册, 一处注册到处使用
vue过滤器-传参和多过滤器
目标: 可同时使用多个过滤器, 或者给过滤器传参
- 语法:
- 过滤器传参:   vue变量 | 过滤器(实参) 
 
- 多个过滤器:   vue变量 | 过滤器1 | 过滤器2
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   | <template>   <div>     <p>原来的样子: {{ msg }}</p>     <!-- 1.       给过滤器传值       语法: vue变量 | 过滤器名(值)      -->     <p>使用翻转过滤器: {{ msg | reverse('|') }}</p>     <!-- 2.       多个过滤利使用       语法: vue变量 | 过滤器1 | 过滤器2      -->     <p :title="msg | toUp | reverse('|')">鼠标长停</p>   </div> </template>
  <script> export default {   data(){     return {       msg: 'Hello, Vue'     }   },   filters: {     toUp (val) {       return val.toUpperCase()     }   } } </script>
  <style>
  </style>
   | 
 
总结: 过滤器可以传参, 还可以对某个过滤器结果, 后面在使用一个过滤器
案例-品牌管理(时间格式化)
目标: 复制上个案例, 在此基础上, 把表格里的时间用过滤器+moment模块, 格式化成YYYY-MM-DD 格式
图示: 

下载moment处理日期的第三方工具模块
moment官网文档: http://momentjs.cn/docs/#/displaying/
 
定义过滤器, 把时间用moment模块格式化, 返回我们想要的格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | 
  import moment from 'moment'
 
 
  filters: {      formatDate (val){         return moment(val).format('YYYY-MM-DD')     } }
  <!-- 3. 使用过滤器 --> <td>{{ obj.time | formatDate }}</td>
 
  | 
 
 
vue计算属性
vue计算属性-computed
目标: 一个数据, 依赖另外一些数据计算而来的结果
语法:
注意: 计算属性也是vue数据变量, 所以不要和data里重名, 用法和data相同
总结: 一个数据, 依赖另外一些数据计算而来的结果
vue计算属性-缓存
目标: 计算属性是基于它们的依赖项的值结果进行缓存的,只要依赖的变量不变, 都直接从缓存取结果

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   | <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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | <tr style="background-color: #EEE">      <td>统计:</td>      <td colspan="2">总价钱为: {{ allPrice }}</td>      <td colspan="2">平均价: {{ svgPrice }}</td> </tr>
  <script> // 目标: 总价和均价显示 // 1. 末尾补tr - 显示总价和均价 export default {   // ...源代码省略   // 2. 计算属性   computed: {       allPrice(){           // 3. 求总价           return this.list.reduce((sum, obj) => sum += obj.price, 0)       },       avgPrice(){           // 4. 求均价 - 保留2位小数           return (this.allPrice / this.list.length).toFixed(2)       }   } } </script>
   | 
 
总结: 总价来源于所有数据计算而来的结果, 故采用计算属性
vue计算属性-完整写法
目标: 计算属性也是变量, 如果想要直接赋值, 需要使用完整写法
语法:
1 2 3 4 5 6 7 8 9 10
   | computed: {     "属性名": {         set(值){                      },         get() {             return "值"         }     } }
  | 
 
需求: 
页面准备输入框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
   | <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方法
案例-小选影响全选
目标: 小选框都选中(手选), 全选自动选中
分析:
① 先静态后动态, 从.md拿到静态标签和数据
② 循环生成复选框和文字, 对象的c属性和小选框的选中状态, 用v-model双向绑定
③ 定义isAll计算属性, 值通过小选框们统计c属性状态得来
图示:

模板标签和数据(==直接复制在这基础上写vue代码==)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
   | <template>   <div>     <span>全选:</span>     <input type="checkbox"/>     <button>反选</button>     <ul>       <li>         <input type="checkbox"/>         <span>任务名</span>       </li>     </ul>   </div> </template>
  <script> export default {   data() {     return {       arr: [         {           name: "猪八戒",           c: false,         },         {           name: "孙悟空",           c: false,         },         {           name: "唐僧",           c: false,         },         {           name: "白龙马",           c: false,         },       ],     };   } }; </script>
   | 
 
==正确代码,不可复制==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
   | <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: 获取到全选状态 – 改装isAll计算属性
 
- 需求2: 全选状态同步给所有小选框
 
分析:
①: isAll改成完整写法, set里获取到全选框, 勾选的状态值
②: 遍历数据数组, 赋给所有小选框v-model关联的属性
图示:

==正确代码,不可以复制==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | <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>
   | 
 
案例-反选
目标: 反选功能
- 需求: 点击反选, 让所有小选框, 各自取相反勾选状态
 
分析:
①: 小选框的勾选状态, 在对象的c属性
②: 遍历所有对象, 把对象的c属性取相反值赋予回去即可
图示:

==正确代码,不可以复制==
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   |  <button @click="btn">反选</button>
  <script> export default {   // ...其他代码省略   methods: {     btn(){       // 8. 让数组里对象的c属性取反再赋予回去       this.arr.forEach(obj => obj.c = !obj.c)     }   } }; </script>
 
  | 
 
vue侦听器
vue侦听器-watch
目标: 可以侦听data/computed属性值改变
语法:
总结: 想要侦听一个属性变化, 可使用侦听属性watch
vue侦听器-深度侦听和立即执行
目标: 侦听复杂类型, 或者立即执行侦听函数
语法:
1 2 3 4 5 6 7 8 9
   | watch: {     "要侦听的属性名": {         immediate: true,          deep: true,          handler (newVal, oldVal) {                      }     } }
  | 
 
 
完整例子代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
   | <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固定方法触发
案例-品牌管理(数据缓存)
目标: 侦听list变化, 同步到浏览器本地
分析:
    ① 在watch侦听list变化的时候, 把最新的数组list转成JSON字符串存入到localStorage本地
    ② data里默认把list变量从本地取值, 如果取不到给个默认的空数组
效果:
    新增/删除 – 刷新页面 – 数据还在
在之前的案例代码基础上接着写
==正确代码,不可复制==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | <script> import moment from "moment"; export default {   data() {     return {       name: "", // 名称       price: 0, // 价格       // 3. 本地取出缓存list       list: JSON.parse(localStorage.getItem('pList')) || [],     };   },   // ...其他代码省略   watch: {     list: {       handler(){         // 2. 存入本地         localStorage.setItem('pList', JSON.stringify(this.list))       },       deep: true     }   } }; </script>
   | 
 
面试题
Vue 中怎么自定义过滤器
    Vue.js允许自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和v-bind表达式
    全局的用Vue.filter()
    局部的用filters属性
Vue中:key作用, 为什么不能用索引
    :key是给v-for循环生成标签颁发唯一标识的, 用于性能的优化
    因为v-for数据项的顺序改变,Vue 也不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素
    :key如果是索引, 因为索引是连续的, 如果删除其中某一个, 会导致最后一个被删除
    当我们再删除的时候, :key再根据数据来把新旧的dom对比时, 删除:key不存在的对应的标签(添加也是一样的插入到指定位置, 别的都不会动)
数组更新有的时候v-for不渲染
    因为vue内部只能监测到数组顺序/位置的改变/数量的改变, 但是值被重新赋予监测不到变更, 可以用 Vue.set() / vm.$set()
附加练习_1.买点书练习
目标: 把数据铺设到页面上, 当用户点击买书按钮, 书籍数量增加1, 并且要计算累计的和
演示:

标签结构和数据(==复制接着写==): 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
   | <template>   <div>     <p>请选择你要购买的书籍</p>     <ul>     </ul>     <table border="1" width="500" cellspacing="0">       <tr>         <th>序号</th>         <th>书名</th>         <th>单价</th>         <th>数量</th>         <th>合计</th>       </tr>     </table>     <p>总价格为: </p>   </div> </template>
  <script> export default {   data() {     return {       arr: [         {           name: "水浒传",           price: 107,           count: 0,         },         {           name: "西游记",           price: 192,           count: 0,         },         {           name: "三国演义",           price: 219,           count: 0,         },         {           name: "红楼梦",           price: 178,           count: 0,         },       ],     };   } }; </script>
   | 
 
正确答案(==不可复制==)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
   | <template>   <div>     <p>请选择你要购买的书籍</p>     <ul>       <li v-for="(item, ind) in arr" :key="ind">         <span>{{ item["name"] }}</span>         <button @click="buy(ind)">买书</button>       </li>     </ul>     <table border="1" width="500" cellspacing="0">       <tr>         <th>序号</th>         <th>书名</th>         <th>单价</th>         <th>数量</th>         <th>合计</th>       </tr>       <tr v-for="(item, index) in arr" :key="index">         <td>{{ index + 1 }}</td>         <td>{{ item["name"] }}</td>         <td>{{ item["price"] }}</td>         <td>{{ item["count"] }}</td>         <td>{{ item["price"] * item["count"] }}</td>       </tr>     </table>     <p>总价格为: {{ allPrice }}</p>   </div> </template>
  <script> export default {   data() {     return {       arr: [         {           name: "水浒传",           price: 107,           count: 0,         },         {           name: "西游记",           price: 192,           count: 0,         },         {           name: "三国演义",           price: 219,           count: 0,         },         {           name: "红楼梦",           price: 178,           count: 0,         },       ],     };   },   methods: {     buy(index) {       this.arr[index]["count"]++;     },   },   computed: {     allPrice() {       // 数组里放的是对象, 而对象是复杂类型, 引用关系, 值改变会触发计算属性重新执行       return this.arr.reduce((sum, obj) => {         return (sum += obj["price"] * obj["count"]);       }, 0);     },   }, }; </script>
  <style> </style>
   | 
 
附加练习_2.选你爱我求和
目标: 把用户选中的数字, 累计求和显示
提示: 
- v-model绑定的变量是数组, 可以收集checkbox的value属性呦
 
演示:

数据(复制):
1
   | [9, 15, 19, 25, 29, 31, 48, 57, 62, 79, 87]
   | 
 
正确答案:(==先不要看==)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | <template>   <div>          <div       v-for="(item, index) in arr"       style="display: inline-block"       :key="index"     >       <input type="checkbox" v-model="checkNumArr" :value="item" />       <span>{{ item }}</span>     </div>     <p>你选中的元素, 累加的值和为: {{ theSum }}</p>   </div> </template>
  <script> export default {   data() {     return {       arr: [9, 15, 19, 25, 29, 31, 48, 57, 62, 79, 87],       checkNumArr: [],      };   },   computed: {     theSum() {       return this.checkNumArr.reduce((sum, val) => {         return (sum += val);       }, 0);     },   }, }; </script>
  <style> </style>
   | 
 
总结, 当计算属性函数里引用的vue变量发生改变, 函数就执行并重新返回结果并缓存起来