动力节点Vue3笔记——第二章Vue核心技术

动力节点Vue笔记第二章 Vue核心技术
20/100
发布文章
assdfgdfgjhtdo
未选择任何文件
new

2 Vue核心技术

2.1 事件处理

2.1.1 事件处理的核心语法

  1. 指令的语法格式:<标签 v-指令名:参数=”表达式”></标签>
  2. 事件绑定的语法格式:v-on:事件名。例如鼠标单击事件的绑定使用v-on:click。
  3. 绑定的回调函数需要在Vue实例中使用methods进行注册。methods可以配置多个回调函数,采用逗号隔开。
  4. 绑定回调函数时,如果回调函数没有参数,( )可以省略。
  5. 每一个回调函数都可以接收一个事件对象event。
  6. 如果回调函数有参数,并且还需要获取事件对象,可以使用$event进行占位。
  7. v-on:click可以简写为@click。简写的语法格式:@事件名
  8. 回调函数中的this是vm。如果回调函数是箭头函数的话,this是window对象,因为箭头函数没有自己的this,它的this是继承过来的,默认这个this是箭头函数所在的宿主对象。这个宿主对象其实就是它的父级作用域。而对象又不能构成单独的作用域,所以这个父级作用域是全局作用域,也就是window。
  9. 回调函数并没有在vm对象上,为什么通过vm可以直接调用函数呢?尝试手写Vue框架。
  10. 可以在函数中改变data中的数据,例如:this.counter++,这样会联动页面上产生动态效果。

2.1.2 事件修饰符

.stop - 调用 event.stopPropagation()。

*<div** @click="san"**>**  
3.     **<div** @click.stop="er"**>**  
4.         **<button** @click="yi"**>**{
    
    {
    
    name}}**</button>**  
5.     **</div>**  
6. **</div>**  

.prevent - 调用 event.preventDefault()。

 **<a** href="http://www.bjpowernode.com" @click.prevent="yi"**>**  
3. {
    
    {
    
    name}}  
4. **</a>** 

.capture - 添加事件侦听器时使用 capture 模式。

**<div** @click.capture="san"**>**  
3.     **<div** @click.capture="er"**>**  
4.         **<button** @click="yi"**>**{
    
    {
    
    name}}**</button>**  
5.     **</div>**  
6. **</div>**

注意:只有添加了capture修饰符的元素才会采用捕获模式。(或者说带有capture修饰符的优先触发)

.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。

 **<div** @click="san"**>**  
 1.     **<div** @click.self="er"**>**  
 2.         **<button** @click="yi"**>**{
    
    {
    
    name}}**</button>**  
 3.     **</div>**  
 4. **</div>**  

.once - 只触发一次回调。
5.

**<button** @click.once="yi"**>**  
 6.     {
    
    {
    
    name}}  
 7. **</button>** 

.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
1.无需等待,直接继续(立即)执行事件默认行为。(对wheel事件有效果)
2. .passive 和 .prevent 修饰符不能共存。

2.1.3 按键修饰符

常用的按键修饰符包括:
.enter
.tab (只能配合keydown使用)
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right

可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。

<input type=”text” @keyup.page-down=”getInfo”>

可以通过全局 config.keyCodes 对象自定义按键修饰符别名

Vue.config.keyCodes.huiche = 13

2.1.4 系统修饰键

系统修饰键包括4个
.ctrl
.alt
.shift
.meta
系统修饰键在使用时应注意:

  1. 只有当系统修饰键和其他键组合使用,并且组合键释放时,才会触发keyup事件。

  2. 只要按下系统修饰键,就会触发keydown事件。

小技巧 <input type=”text” @keyup.ctrl.c=”getInfo”/>

2.2 计算属性

  1. 案例:用户输入信息,然后翻转用户输入的字符串。
  2. 插值语法可以实现,但是有三个问题
    1. 代码可读性差
    2. 代码不可复用
    3. 代码难以维护
  3. 可以使用methods方式实现,存在1个问题
    1. 效率低,即使数据没有发生变化,但每一次仍然会调用method。
  4. 使用计算属性可以解决以上问题。
  5. 什么是计算属性?

data中的是属性。用data的属性经过计算得出的全新的属性就是计算属性。

  1. 计算属性的使用
**<div** id="app"**>**  
3.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
4.     输入的信息:**<input** type="text" v-model="info"**><br>**  
5.     反转的信息:{
    
    {
    
    reversedInfo}} **<br>**  
6.     反转的信息:{
    
    {
    
    reversedInfo}} **<br>**  
7.     反转的信息:{
    
    {
    
    reversedInfo}} **<br>**  
8.     反转的信息:{
    
    {
    
    reversedInfo}} **<br>**  
9. **</div>**  
10. **<script>**  
11.     const vm = new Vue({
    
      
12.         el : '#app',  
13.         data : {
    
      
14.             msg : '计算属性-反转字符串案例',  
15.             info : ''  
16.         },  
17.         computed : {
    
      
18.             reversedInfo:{
    
      
19.                 get(){
    
      
20.                     console.log('getter被调用了');  
21.                     return this.info.split('').reverse().join('')  
22.                 },  
23.                 set(val){
    
      
24.                     //this.reversedInfo = val // 不能这样做,这样会导致无限递归  
25.                     this.info = val.split('').reverse().join('')  
26.                 }  
27.             }  
28.         }  
29.     })  
30. **</script>**
  1. 计算属性需要使用:computed
  2. 计算属性通过vm. d a t a 是无法访问的。计算属性不能通过 v m . data 是无法访问的。计算属性不能通过vm. data是无法访问的。计算属性不能通过vm.data访问。
  3. 计算属性的getter/setter方法中的this是vm。
  4. 计算属性的getter方法的调用时机:
    1. 第一个时机:初次访问该属性。
    2. 第二个时机:计算属性所依赖的数据发生变化时。
  5. 计算属性的setter方法的调用时机:
    1. 当计算属性被修改时。(在setter方法中通常是修改属性,因为只有当属性值变化时,计算属性的值就会联动更新。注意:计算属性的值被修改并不会联动更新属性的值。)
  6. 计算属性没有真正的值,每一次都是依赖data属性计算出来的。
  7. 计算属性的getter和setter方法不能使用箭头函数,因为箭头函数的this不是vm。而是window。
  8. 计算属性的简写形式

只考虑读取,不考虑修改时,可以启用计算属性的简写形式。

computed : {
    
      
2.     reversedInfo(){
    
      
3.         console.log('getter被调用了');  
4.         **return** **this**.info.split('').reverse().join('')  
5.     }  
6. }  

2.3 侦听属性的变化

  1. 侦听属性的变化其实就是监视某个属性的变化。当被监视的属性一旦发生改变时,执行某段代码。
  2. 监视属性变化时需要使用watch配置项。

使用watch实现:比较数字大小的案例

**<div** id="app"**>**  
2.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
3.     数值1**<input** type="text" v-model="number1"**><br>**  
4.     数值2**<input** type="text" v-model="number2"**><br>**  
5.     比较大小:{
    
    {
    
    compareResult}}  
6. **</div>**  
7. **<script>**  
8.     const vm = new Vue({
    
      
9.         el : '#app',  
10.         data : {
    
      
11.             msg : '侦听属性的变化',  
12.             number1 : 1,  
13.             number2 : 1,  
14.             compareResult : ''  
15.         },  
16.         watch : {
    
      
17.             number1 : {
    
      
18.                 immediate : true,  
19.                 handler(newVal, oldVal){
    
      
20.                     let result = newVal - this.number2  
21.                     if(result **>** 0){
    
      
22.                         this.compareResult = newVal + '**>**' + this.number2  
23.                     }else if(result **<** **0**){
    
      
24.                         this.compareResult = newVal + '**<**' + this.number2  
25.                     }else{
    
      
26.                         this.compareResult = newVal + '=' + this.number2  
27.                     }  
28.                 }  
29.             },  
30.             number2 : {
    
      
31.                 immediate : true,  
32.                 handler(newVal, oldVal){
    
      
33.                     let result = this.number1 - newVal  
34.                     if(result **>** 0){
    
      
35.                         this.compareResult = this.number1 + '**>**' + newVal  
36.                     }else if(result **<** **0**){
    
      
37.                         this.compareResult = this.number1 + '**<**' + newVal  
38.                     }else{
    
      
39.                         this.compareResult = this.number1 + '=' + newVal  
40.                     }  
41.                 }  
42.             }  
43.         }  
44.     })  
45. **</script>** 

运行效果:


  1. 如何深度监视:
    1. 监视多级结构中某个属性的变化,写法是:’a.b.c’ : {}。注意单引号哦。
    2. 监视多级结构中所有属性的变化,可以通过添加深度监视来完成:deep : true
  2. 如何后期添加监视:
    1. 调用API:vm.$watch(‘number1’, {})
  3. watch的简写:
    1. 简写的前提:当不需要配置immediate和deep时,可以简写。
    2. 如何简写?
      1. watch:{ number1(newVal,oldVal){}, number2(newVal, oldVal){} }
    3. 后期添加的监视如何简写?
      1. vm.$watch(‘number1’, function(newVal, oldVal){})
  4. computed和watch如何选择?
    1. 以上比较大小的案例可以用computed完成,并且比watch还要简单。所以要遵守一个原则:computed和watch都能够完成的,优先选择computed。
    2. 如果要开启异步任务,只能选择watch。因为computed依靠return。watch不需要依赖return。
  5. 关于函数的写法,写普通函数还是箭头函数?
    1. 不管写普通函数还是箭头函数,目标是一致的,都是为了让this和vm相等。
    2. 所有Vue管理的函数,建议写成普通函数。
    3. 所有不属于Vue管理的函数,例如setTimeout的回调函数、Promise的回调函数、AJAX的回调函数,建议使用箭头函数。

2.4 class与style绑定

数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式。因为 class 和 style 都是 attribute,我们可以和其他 attribute 一样使用 v-bind 将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue 专门为 class 和 style 的 v-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。

2.4.1 class绑定

2.4.1.1 绑定字符串

适用于样式的名字不确定,需要动态指定。

. <!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**class绑定字符串形式**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7.     **<style>**  
8.         .static{
    
      
9.             border: 1px solid black;  
10.             background-color: beige;  
11.         }  
12.         .big{
    
      
13.             width: 200px;  
14.             height: 200px;  
15.         }  
16.         .small{
    
      
17.             width: 100px;  
18.             height: 100px;  
19.         }  
20.     **</style>**  
21. **</head>**  
22. **<body>**  
23.     **<div** id="app"**>**  
24.         **<h1>**{
    
    {
    
    msg}}**</h1>**  
25.         **<div** class="static" :class="c1"**></div>**  
26.     **</div>**  
27.     **<script>**  
28.         const vm = new Vue({
    
      
29.             el : '#app',  
30.             data : {
    
      
31.                 msg : 'class绑定字符串形式',  
32.                 c1 : 'small'  
33.             }  
34.         })  
35.     **</script>**  
36. **</body>**  
37. **</html>** 

运行效果:

使用vue开发者工具修改c1的small为big:

通过测试可以看到样式完成了动态的切换。

2.4.1.2 绑定数组

适用于绑定的样式名字不确定,并且个数也不确定。

 <!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**class绑定数组形式**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7.     **<style>**  
8.         .static{
    
      
9.             border: 1px solid black;  
10.         }  
11.         .active{
    
      
12.             background-color: green;  
13.         }  
14.         .text-danger{
    
      
15.             color: red;  
16.         }  
17.     **</style>**  
18. **</head>**  
19. **<body>**  
20.     **<div** id="app"**>**  
21.         **<h1>**{
    
    {
    
    msg}}**</h1>**  
22.         **<div** class="static" :class="['active','text-danger']"**>**数组形式**</div>**  
23.         **<br><br>**  
24.         **<div** class="static" :class="[activeClass,errorClass]"**>**数组形式**</div>**  
25.         **<br><br>**  
26.         **<div** class="static" :class="classArray"**>**数组形式**</div>**  
27.     **</div>**  
28.     **<script>**  
29.         const vm = new Vue({
    
      
30.             el : '#app',  
31.             data : {
    
      
32.                 msg : 'class绑定数组形式',  
33.                 activeClass : 'active',  
34.                 errorClass : 'text-danger',  
35.                 classArray : ['active', 'text-danger']  
36.             }  
37.         })  
38.     **</script>**  
39. **</body>**  
40. **</html>**  

运行效果:

使用vue开发者工具删除数组中的一个样式:

2.4.1.3 绑定对象

适用于样式名字和个数都确定,但是要动态决定用或者不用。

. <!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**class绑定对象形式**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7.     **<style>**  
8.         .static{
    
      
9.             border: 1px solid black;  
10.         }  
11.         .active{
    
      
12.             background-color: green;  
13.         }  
14.         .text-danger{
    
      
15.             color: red;  
16.         }  
17.     **</style>**  
18. **</head>**  
19. **<body>**  
20.     **<div** id="app"**>**  
21.         **<h1>**{
    
    {
    
    msg}}**</h1>**  
22.         **<div** class="static" :class="{active : true, 'text-danger' : true}"**>**对象形式**</div>**  
23.         **<br><br>**  
24.         **<div** class="static" :class="classObject"**>**对象形式**</div>**  
25.     **</div>**  
26.     **<script>**  
27.         const vm = new Vue({
    
      
28.             el : '#app',  
29.             data : {
    
      
30.                 msg : 'class绑定对象形式',  
31.                 classObject : {
    
      
32.                     active : true,  
33.                     'text-danger' : false  
34.                 }  
35.             }  
36.         })  
37.     **</script>**  
38. **</body>**  
39. **</html>**  

运行效果:

使用vue开发者工具修改text-danger为true:

2.4.2 style绑定

2.4.2.1 绑定对象

**<div** id="app"**>**  
2.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
3.     <!-- 静态写法 -->  
4.     **<div** class="static" style="font-size: 20px;"**>**对象形式**</div><br><br>**  
5.     <!-- 动态写法1 -->  
6.     **<div** class="static" :style="{fontSize: 40 + 'px'}"**>**对象形式**</div><br><br>**  
7.     <!-- 动态写法2 -->  
8.     **<div** class="static" :style="styleObject"**>**对象形式**</div><br><br>**  
9. **</div>**  
10. **<script>**  
11.     const vm = new Vue({
    
      
12.         el : '#app',  
13.         data : {
    
      
14.             msg : 'style绑定对象形式',  
15.             styleObject : {
    
      
16.                 fontSize : '40px'  
17.             }  
18.         }  
19.     })  
20. **</script>** 

2.4.2.2 绑定数组

**<div** id="app"**>**  
2.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
3.     <!-- 静态写法 -->  
4.     **<div** class="static" style="font-size: 40px; color: red;"**>**数组形式**</div><br><br>**  
5.     <!-- 动态写法1 -->  
6.     **<div** class="static" :style="[{fontSize:'40px'},{color:'red'}]"**>**数组形式**</div><br><br>**  
7.     <!-- 动态写法2 -->  
8.     **<div** class="static" :style="styleArray"**>**对象形式**</div><br><br>**  
9. **</div>**  
10. **<script>**  
11.     const vm = new Vue({
    
      
12.         el : '#app',  
13.         data : {
    
      
14.             msg : 'style绑定对象形式',  
15.             styleArray : [  
16.                 {
    
    fontSize:'40px'},  
17.                 {
    
    color:'red'}  
18.             ]  
19.         }  
20.     })  
21. **</script>** 

2.5 条件渲染

2.5.1 v-if

指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回true时才被渲染

**<div** id="app"**>**  
2.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
3.     温度:**<input** type="number" v-model="temprature"**><br>**  
4.     天气:  
5.     **<span** v-if="temprature <= 10"**>**寒冷**</span>**  
6.     **<span** v-if="temprature > 10 && temprature <= 25"**>**凉爽**</span>**  
7.     **<span** v-if="temprature > 25"**>**炎热**</span>**  
8. **</div>**  
9. **<script>**  
10.     const vm = new Vue({
    
      
11.         el : '#app',  
12.         data : {
    
      
13.             msg : '条件渲染',  
14.             temprature : 10  
15.         }  
16.     })  
17. **</script>** 

运行效果:

2.5.2 v-else-if、v-else

顾名思义,v-else-if 提供的是相应于 v-if 的“else if 区块”。它可以连续多次重复使用。
一个使用 v-else-if 的元素必须紧跟在一个 v-if 或一个 v-else-if元素后面。
你也可以使用 v-else 为 v-if 添加一个“else 区块”,当然,v-else元素也是必须紧跟在一个 v-if 或一个 v-else-if元素后面。

 **<div** id="app"**>**  
2.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
3.     温度:**<input** type="number" v-model="temprature"**><br>**  
4.     天气:  
5.     **<span** v-if="temprature <= 10"**>**寒冷**</span>**  
6.     **<span** v-else-if="temprature <= 25"**>**凉爽**</span>**  
7.     **<span** v-else**>**炎热**</span>**  
8. **</div>**  
9. **<script>**  
10.     const vm = new Vue({
    
      
11.         el : '#app',  
12.         data : {
    
      
13.             msg : '条件渲染',  
14.             temprature : 10  
15.         }  
16.     })  
17. **</script>** 

2.5.3 与v-if

因为 v-if 是一个指令,他必须依附于某个元素。但如果我们想要切换不止一个元素呢?在这种情况下我们可以在一个 元素上使用 v-if,这只是一个不可见的包装器元素,最后渲染的结果并不会包含个 元素。v-else 和 v-else-if 也可以在 上使用。

 **<div** id="app"**>**  
2.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
3.     温度:**<input** type="number" v-model="temprature"**><br>**  
4.     天气:  
5.     **<template** v-if="temprature <= 10"**>**  
6.         **<span>**寒冷**</span>**  
7.     **</template>**  
8.     **<template** v-else-if="temprature <= 25"**>**  
9.         **<span>**凉爽**</span>**  
10.     **</template>**  
11.     **<template** v-else**>**  
12.         **<span>**炎热**</span>**  
13.     **</template>**  
14. **</div>**  
15. **<script>**  
16.     const vm = new Vue({
    
      
17.         el : '#app',  
18.         data : {
    
      
19.             msg : '条件渲染',  
20.             temprature : 10  
21.         }  
22.     })  
23. **</script>** 

2.5.4 v-show

另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:

 **<div** id="app"**>**  
2.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
3.     温度:**<input** type="number" v-model="temprature"**><br>**  
4.     天气:  
5.     **<span** v-show="temprature <= 10"**>**寒冷**</span>**  
6.     **<span** v-show="temprature > 10 && temprature <= 25"**>**凉爽**</span>**  
7.     **<span** v-show="temprature > 25"**>**炎热**</span>**  
8. **</div>**  
9. **<script>**  
10.     const vm = new Vue({
    
      
11.         el : '#app',  
12.         data : {
    
      
13.             msg : '条件渲染',  
14.             temprature : 10  
15.         }  
16.     })  
17. **</script>**  

不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。
v-show 不支持在 元素上使用,也不能和 v-else 搭配使用。

2.5.5 v-if VS v-show

v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建
v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display属性会被切换。
总的来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。

2.6 列表渲染

语法格式:v-for指令。该指令用在被遍历的标签上。

  1. v-for="(element, index) in elements" :key="element.id"

或者

v-for="(element, index) of elements" :key="element.id"  

2.6.1 遍历数组、对象、字符串、指定次数

 **遍历数组**
2. **<div** id="app"**>**  
3.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
4.     **<h2>**遍历数组**</h2>**  
5.     **<ul>**  
6.         **<li** v-for="(product, index) in products" :key="product.id"**>**  
7.             商品名称:{
    
    {
    
    product.name}},单价:{
    
    {
    
    product.price}}/千克,下标:{
    
    {
    
    index}}  
8.         **</li>**  
9.     **</ul>**  
10. **</div>**  
11. **<script>**  
12.     const vm = new Vue({
    
      
13.         el : '#app',  
14.         data : {
    
      
15.             msg : '列表渲染',  
16.             products : [  
17.                 {
    
    id:'111',name:'西瓜',price:20},  
18.                 {
    
    id:'222',name:'苹果',price:10},  
19.                 {
    
    id:'333',name:'香蕉',price:30}  
20.             ]  
21.         }  
22.     })  
23. **</script>** 

运行效果:

  1. 遍历对象
*<div** id="app"**>**  
3.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
4.     **<h2>**遍历对象**</h2>**  
5.     **<ul>**  
6.         **<li** v-for="(propertyValue, propertyName) of dog" :key="propertyName"**>**  
7.             {
    
    {
    
    propertyName}}:{
    
    {
    
    propertyValue}}  
8.         **</li>**  
9.     **</ul>**  
10. **</div>**  
11. **<script>**  
12.     const vm = new Vue({
    
      
13.         el : '#app',  
14.         data : {
    
      
15.             msg : '列表渲染',  
16.             dog : {
    
      
17.                 name : '拉布拉多',  
18.                 age : 3,  
19.                 gender : '雄性'  
20.             }  
21.         }  
22.     })  
23. **</script>**

运行结果:

  1. 遍历字符串
**<div** id="app"**>**  
3.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
4.     **<h2>**遍历字符串**</h2>**  
5.     **<ul>**  
6.         **<li** v-for="char,index of str" :key="index"**>**  
7.             {
    
    {
    
    index}}:{
    
    {
    
    char}}  
8.         **</li>**  
9.     **</ul>**  
10. **</div>**  
11. **<script>**  
12.     const vm = new Vue({
    
      
13.         el : '#app',  
14.         data : {
    
      
15.             msg : '列表渲染',  
16.             str : '动力节点'  
17.         }  
18.     })  
19. **</script>**  

运行结果:

  1. 遍历指定次数
**<div** id="app"**>**  
3.     **<h1>**{
    
    {
    
    msg}}**</h1>**  
4.     **<h2>**遍历指定次数**</h2>**  
5.     **<ul>**  
6.         **<li** v-for="number,index of 10" :key="index"**>**  
7.             下标:{
    
    {
    
    index}},数字:{
    
    {
    
    number}}  
8.         **</li>**  
9.     **</ul>**  
10. **</div>**  
11. **<script>**  
12.     const vm = new Vue({
    
      
13.         el : '#app',  
14.         data : {
    
      
15.             msg : '列表渲染'  
16.         }  
17.     })  
18. **</script>** 

运行结果:

2.6.2 虚拟dom和diff算法

所谓的虚拟dom就是内存当中的dom对象。vue为了提高渲染的效率,只有真正改变的dom元素才会重新渲染。

2.6.3 v-for的key的作用以及实现原理

  1. 用index作为key
<!DOCTYPE html**>**  
3. **<html** lang="en"**>**  
4. **<head>**  
5.     **<meta** charset="UTF-8"**>**  
6.     **<title>**key的原理**</title>**  
7.     **<script** src="../js/vue.js"**></script>**  
8. **</head>**  
9. **<body>**  
10.     **<div** id="app"**>**  
11.         **<h1>**{
    
    {
    
    msg}}**</h1>**  
12.         **<button** @click="addFirst"**>**在数组第一个位置添加tom**</button>**  
13.         **<button** @click="addLast"**>**在数组最后位置添加vue**</button>**  
14.         **<table>**  
15.             **<tr>**  
16.                 **<th>**序号**</th>**  
17.                 **<th>**姓名**</th>**  
18.                 **<th>**邮箱**</th>**  
19.                 **<th>**选择**</th>**  
20.             **</tr>**  
21.             **<tr** v-for="(vip,index) of vips" :key="index"**>**  
22.                 **<td>**{
    
    {
    
    index + 1}}**</td>**  
23.                 **<td>**{
    
    {
    
    vip.name}}**</td>**  
24.                 **<td>**{
    
    {
    
    vip.email}}**</td>**  
25.                 **<td><input** type="checkbox"**></td>**  
26.             **</tr>**  
27.         **</table>**  
28.     **</div>**  
29.     **<script>**  
30.         const vm = new Vue({
    
      
31.             el : '#app',  
32.             data : {
    
      
33.                 msg : 'key原理(虚拟dom与diff算法)',  
34.                 vips : [  
35.                     {
    
    id:'100',name:'jack',email:'jack@123.com'},  
36.                     {
    
    id:'200',name:'lucy',email:'lucy@123.com'},  
37.                     {
    
    id:'300',name:'james',email:'james@123.com'}  
38.                 ]  
39.             },  
40.             methods : {
    
      
41.                 addFirst(){
    
      
42.                     this.vips.unshift({
    
    id:'400',name:'tom',email:'tom@123.com'})  
43.                 },  
44.                 addLast(){
    
      
45.                     this.vips.push({
    
    id:'500',name:'vue',email:'vue@123.com'})  
46.                 }  
47.             }  
48.         })  
49.     **</script>**  
50. **</body>**  
51. **</html>** 

运行效果:

全部选中:

添加tom:

可以看到错乱了。思考这是为什么?

  1. 用vip.id作为key

运行和测试结果正常,没有出现错乱。为什么?

  1. key的作用

key存在于虚拟dom元素中,代表该虚拟dom元素的唯一标识(身份证号)。

  1. diff算法是如何比较的?

新的虚拟dom和旧的虚拟dom比较时,先拿key进行比较:

  1. 如果key相同:则继续比较子元素:
    1. 子元素不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素。
    2. 子元素相同:直接复用之前的真实dom。
  2. 如果key不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素。
  3. index作为key存在两个问题
  4. 效率较低。
  5. 对数组的非末尾元素进行增删时,容易错乱。
  6. index作为key和vip.id作为key对比

当index作为key时:

当vip.id作为key时:

2.7 列表过滤

使用watch和computed分别进行实现:

 <!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**列表过滤**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7.     **<style>**  
8.         table,tr,th,td{
    
      
9.             border: 1px solid blue;  
10.         }  
11.     **</style>**  
12. **</head>**  
13. **<body>**  
14.     **<div** id="app"**>**  
15.         **<h1>**{
    
    {
    
    msg}}**</h1>**  
16.         **<input** type="text" placeholder="请输入搜索关键词" v-model="keyword"**>**  
17.         **<table>**  
18.             **<tr>**  
19.                 **<th>**序号**</th>**  
20.                 **<th>**姓名**</th>**  
21.                 **<th>**邮箱**</th>**  
22.             **</tr>**  
23.             **<tr** v-for="(vip,index) of filterVips" :key="vip.id"**>**  
24.                 **<td>**{
    
    {
    
    index+1}}**</td>**  
25.                 **<td>**{
    
    {
    
    vip.name}}**</td>**  
26.                 **<td>**{
    
    {
    
    vip.email}}**</td>**  
27.             **</tr>**  
28.         **</table>**  
29.     **</div>**  
30.     **<script>**  
31.         const vm = new Vue({
    
      
32.             el : '#app',  
33.             data : {
    
      
34.                 keyword : '',  
35.                 msg : '列表过滤',  
36.                 vips : [  
37.                     {
    
    id:'100',name:'jack',email:'jack@123.com'},  
38.                     {
    
    id:'200',name:'lucy',email:'lucy@123.com'},  
39.                     {
    
    id:'300',name:'james',email:'james@123.com'}  
40.                 ],  
41.                 //filterVips : []  
42.             },  
43.             /* watch : {  
44.                 keyword : {  
45.                     immediate : true,  
46.                     handler(newValue, oldValue){  
47.                         this.filterVips = this.vips.filter((v) =**>** {  
48.                             return v.name.indexOf(newValue) **>**= 0  
49.                         })  
50.                     }  
51.                 }  
52.             }, */  
53.             computed : {
    
      
54.                 filterVips(){
    
      
55.                     return this.vips.filter((v) =**>** {
    
      
56.                         return v.name.indexOf(this.keyword) **>**= 0  
57.                     })  
58.                 }  
59.             }  
60.         })  
61.     **</script>**  
62. **</body>**  
63. **</html>**

2.8 列表排序

<!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<meta** http-equiv="X-UA-Compatible" content="IE=edge"**>**  
6.     **<meta** name="viewport" content="width=device-width, initial-scale=1.0"**>**  
7.     **<title>**列表排序**</title>**  
8.     **<script** src="../js/vue.js"**></script>**  
9.     **<style>**  
10.         table,tr,td,th{
    
      
11.             border:1px solid black;  
12.         }  
13.     **</style>**  
14. **</head>**  
15. **<body>**  
16.     **<div** id="app"**>**  
17.         **<h1>**{
    
    {
    
    msg}}**</h1>**  
18.         **<input** type="text" placeholder="输入关键字搜索" v-model="keyword"**><br>**  
19.         **<button** @click="type = 1"**>**按照名字升序**</button><br>**  
20.         **<button** @click="type = 2"**>**按照名字降序**</button><br>**  
21.         **<button** @click="type = 0"**>**按照名字原始顺序**</button><br>**  
22.         **<table>**  
23.             **<tr>**  
24.                 **<th>**序号**</th>**  
25.                 **<th>**姓名**</th>**  
26.                 **<th>**邮箱**</th>**  
27.                 **<th>**操作**</th>**  
28.             **</tr>**  
29.             **<tr** v-for="(vip, index) in filterVips" :key="vip.id"**>**  
30.                 **<td>**{
    
    {
    
    index+1}}**</td>**  
31.                 **<td>**{
    
    {
    
    vip.name}}**</td>**  
32.                 **<td>**{
    
    {
    
    vip.email}}**</td>**  
33.                 **<td><input** type="checkbox"**></td>**  
34.             **</tr>**  
35.         **</table>**  
36.     **</div>**  
37.     **<script>**  
38.         const vm = new Vue({
    
      
39.             el : '#app',  
40.             data : {
    
      
41.                 msg : '列表排序',  
42.                 vips : [  
43.                     {
    
    id:'100',name:'jack',email:'jack@123.com'},  
44.                     {
    
    id:'200',name:'lucy',email:'lucy@123.com'},  
45.                     {
    
    id:'300',name:'james',email:'james@123.com'},  
46.                     {
    
    id:'400',name:'lilei',email:'lilei@123.com'},  
47.                 ],  
48.                 keyword : '',  
49.                 type : 0  
50.             },  
51.             computed : {
    
      
52.                 filterVips(){
    
      
53.                     // 筛选  
54.                     let arr = this.vips.filter((vip) =**>** {
    
      
55.                         return vip.name.indexOf(this.keyword) **>**= 0  
56.                     })   
57.                     // 根据排序类型进行排序  
58.                     if(this.type){
    
      
59.                         arr.sort((v1, v2) =**>** {
    
      
60.                             console.log('@')  
61.                             return this.type == 1 ? v1.name.localeCompare(v2.name) : v2.name.localeCompare(v1.name)  
62.                         })  
63.                     }  
64.                     // 返回  
65.                     return arr  
66.                 }  
67.             }  
68.         })  
69.     **</script>**  
70. **</body>**  
71. </html>

2.9 收集表单数据

 <!DOCTYPE html**>**  
2. **<html** lang="en"**>**  
3. **<head>**  
4.     **<meta** charset="UTF-8"**>**  
5.     **<title>**收集表单数据**</title>**  
6.     **<script** src="../js/vue.js"**></script>**  
7. **</head>**  
8. **<body>**  
9.     **<div** id="app"**>**  
10.         **<h1>**{
    
    {
    
    msg}}**</h1>**  
11.         **<form** @submit.prevent="send"**>**  
12.             **<label** for="username"**>**用户名:**</label>**  
13.             **<input** id="username" type="text" v-model.trim="user.username"**><br><br>**  
14.             密码:**<input** type="password" v-model="user.password"**><br><br>**  
15.             年龄:**<input** type="number" v-model.number="user.age"**><br><br>**  
16.             性别:  
17.**<input** type="radio" name="gender" v-model="user.gender" value="1"**>**  
18.**<input** type="radio" name="gender" v-model="user.gender" value="0"**><br><br>**  
19.             爱好:  
20.                 运动**<input** type="checkbox" name="interest" value="sport" v-model="user.interest"**>**  
21.                 旅游**<input** type="checkbox" name="interest" value="travel" v-model="user.interest"**>**  
22.                 唱歌**<input** type="checkbox" name="interest" value="sing" v-model="user.interest"**><br><br>**  
23.             学历:  
24.                 **<select** v-model="user.grade"**>**  
25.                     **<option** value=""**>**请选择学历**</option>**  
26.                     **<option** value="zk"**>**专科**</option>**  
27.                     **<option** value="bk"**>**本科**</option>**  
28.                     **<option** value="ss"**>**硕士**</option>**  
29.                 **</select><br><br>**  
30.             简介:**<textarea** cols="30" rows="10" v-model.lazy="user.introduce"**></textarea><br><br>**  
31.             **<input** type="checkbox" v-model="user.isAgree"**>**阅读并接受协议**<br><br>**  
32.             **<button>**注册**</button>**  
33.         **</form>**  
34.     **</div>**  
35.     **<script>**  
36.         const vm = new Vue({
    
      
37.             el : '#app',  
38.             data : {
    
      
39.                 msg : '收集表单数据',  
40.                 user : {
    
      
41.                     username : '',  
42.                     password : '',  
43.                     age : '',  
44.                     gender : '0',  
45.                     interest : ['sport'],  
46.                     grade : 'ss',  
47.                     introduce : '',  
48.                     isAgree : ''  
49.                 }  
50.             },  
51.             methods : {
    
      
52.                 send(){
    
      
53.                     console.log(JSON.stringify(this.user))  
54.                 }  
55.             }  
56.         })  
57.     **</script>**  
58. **</body>**  
59. **</html>** 

页面展示效果:

运行结果:

2.10 过滤器

过滤器filters适用于简单的逻辑处理,例如:对一些数据进行格式化显示。他的功能完全可以使用methods,computed来实现。过滤器可以进行全局配置,也可以进行局部配置:

  1. 全局配置:在构建任何Vue实例之前使用Vue.filter(‘过滤器名称’, callback)进行配置。
  2. 局部配置:在构建Vue实例的配置项中使用filters进行局部配置。

过滤器可以用在两个地方:插值语法和v-bind指令中。
多个过滤器可以串联:{ {msg | filterA | filterB | filterC}}
过滤器也可以接收额外的参数,但过滤器的第一个参数永远接收的都是前一个过滤器的返回值。

2.11 Vue的其它指令

2.11.1 v-text

将内容填充到标签体当中,并且是以覆盖的形式填充,而且填充的内容中即使存在HTML标签也只是会当做一个普通的字符串处理,不会解析。功能等同于原生JS中的innerText。

2.11.2 v-html

将内容填充到标签体当中,并且是以覆盖的形式填充,而且将填充的内容当做HTML代码解析。功能等同于原生JS中的innerHTML。
v-html不要用到用户提交的内容上。可能会导致XSS攻击。XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript。
例如:用户在留言中恶意植入以下信息:

其他用户上当了:如果点击了以上的留言,就会将cookie发送给恶意的服务器。

2.11.3 v-cloak

v-cloak配置css样式来解决胡子的闪现问题。
v-cloak指令使用在标签当中,当Vue实例接管之后会删除这个指令。
这是一段CSS样式:当前页面中所有带有v-cloak属性的标签都隐藏起来。
[v-cloak] {
display : none;
}

2.11.4 v-once

初次接触指令的时候已经学过了。只渲染一次。之后将被视为静态内容。

2.11.5 v-pre

使用该指令可以提高编译速度。带有该指令的标签将不会被编译。可以在没有Vue语法规则的标签中使用可以提高效率。不要将它用在带有指令语法以及插值语法的标签中。

2.12 vue的自定义指令

函数式:
directives : {
‘text-reverse’ : function(element, binding){
// element 是真实dom对象(可以通过 element instanceof HTMLElement 判断)
// binding 是绑定的对象
element.innerText = binding.value.split(‘’).reverse().join(‘’)
}
}

函数调用时机:
第一时机:模板初次解析时(元素与指令初次绑定)。
第二时机:模板重新解析时。
对象式:可以使用对象式完成更加细致的功能。
directives : {
‘bind-parent’ : {
// 元素与指令初次绑定时自动调用。
bind(element, binding){},
// 元素已经被插入页面后自动调用。
inserted(element, binding){},
// 模板重新解析时被自动调用。
update(element, binding){}
}
}
自定义指令的函数中的this是window。
以上是局部指令,全局指令怎么定义:
对象式:
Vue.directive(‘bind-parent’, {
bind(element, binding){},
inserted(element, binding){},
update(element, binding){}
})
函数式:
Vue.directive(‘text-reverse’, function(element, binding){})

2.13 响应式与数据劫持

  1. 什么是响应式?

修改data后,页面自动改变/刷新。这就是响应式。就像我们在使用excel的时候,修改一个单元格中的数据,其它单元格的数据会联动更新,这也是响应式。

  1. Vue的响应式是如何实现的?

数据劫持:Vue底层使用了Object.defineProperty,配置了setter方法,当去修改属性值时setter方法则被自动调用,setter方法中不仅修改了属性值,而且还做了其他的事情,例如:重新渲染页面。setter方法就像半路劫持一样,所以称为数据劫持。

  1. Vue会给data中所有的属性,以及属性中的属性,都会添加响应式。
  2. 后期添加的属性,不会有响应式,怎么处理?
  3. Vue.set(目标对象, ‘属性名’, 值)
  4. vm.$set(目标对象, ‘属性名’, 值)
  5. Vue没有给数组下标0,1,2,3…添加响应式,怎么处理?
  6. 调用Vue提供的7个API:

push()
pop()
reverse()
splice()
shift()
unshift()
sort()
或者使用:
Vue.set(数组对象, ‘index’, 值)
vm.$set(数组对象, ‘index’, 值)

2.14 Vue的生命周期

2.14.1 什么是生命周期

所谓的生命周期是指:一个事物从出生到最终的死亡,整个经历的过程叫做生命周期。
例如人的生命周期:

  1. 出生:打疫苗
  2. 3岁了:上幼儿园
  3. 6岁了:上小学
  4. 12岁了:上初中
  5. 55岁了:退休
  6. 临终:遗嘱
  7. 死亡:火化

可以看到,在这个生命线上有很多不同的时间节点,在不同的时间节点上去做不同的事儿

Vue的生命周期指的是:vm对象从创建到最终销毁的整个过程。

  1. 虚拟DOM在内存中就绪时:去调用一个a函数
  2. 虚拟DOM转换成真实DOM渲染到页面时:去调用一个b函数
  3. Vue的data发生改变时:去调用一个c函数
  4. Vue实例被销毁时:去调用一个x函数

在生命线上的函数叫做钩子函数,这些函数是不需要程序员手动调用的,由Vue自动调用,程序员只需要按照自己的需求写上,到了那个时间点自动就会执行。

2.14.2 掌握Vue的生命周期有什么用

研究Vue的生命周期主要是研究:在不同的时刻Vue做了哪些不同的事儿。
例如:在vm被销毁之前,我需要将绑定到元素上的自定义事件全部解绑,那么这个解绑的代码就需要找一个地方写一下,写到哪里呢?你可以写到beforeDestroy()这个函数中,这个函数会被Vue自动调用,而且是在vm对象销毁前被自动调用。像这种在不同时刻被自动调用的函数称为钩子函数。每一个钩子函数都有对应的调用时间节点。
换句话说,研究Vue的生命周期主要研究的核心是:在哪个时刻调用了哪个钩子函数

2.14.3 Vue生命周期的4个阶段8个钩子

Vue的生命周期可以被划分为4个阶段:初始阶段、挂载阶段、更新阶段、销毁阶段。
每个阶段会调用两个钩子函数。两个钩子函数名的特点:beforeXxx()、xxxed()。
8个生命周期钩子函数分别是:

  1. 初始阶段
    1. beforeCreate() 创建前
    2. created() 创建后
  2. 挂载阶段
    1. beforeMount() 挂载前
    2. mounted() 挂载后
  3. 更新阶段
    1. beforeUpdate() 更新前
    2. updated() 更新后
  4. 销毁阶段
    1. beforeDestroy() 销毁前
    2. destroyed() 销毁后

8个钩子函数写在哪里?直接写在Vue构造函数的options对象当中。
Vue官方的生命周期图:

翻译后的生命周期图:

2.14.4 初始阶段做了什么事儿

做了这么几件事:

  1. 创建Vue实例vm(此时Vue实例已经完成了创建,这是生命的起点)
  2. 初始化事件对象和生命周期(接产大夫正在给他洗澡)
  3. 调用beforeCreate()钩子函数(此时还无法通过vm去访问data对象的属性)
  4. 初始化数据代理和数据监测
  5. 调用created()钩子函数(此时数据代理和数据监测创建完毕,已经可以通过vm访问data对象的属性)
  6. 编译模板语句生成虚拟DOM(此时虚拟DOM已经生成,但页面上还没有渲染)

该阶段适合做什么?
beforeCreate:可以在此时加一些loading效果。
created:结束loading效果。也可以在此时发送一些网络请求,获取数据。也可以在这里添加定时器。

2.14.5 挂载阶段做了什么事儿

做了这么几件事:

  1. 调用beforeMount()钩子函数(此时页面还未渲染,真实DOM还未生成)
  2. 给vm追加 e l 属性,用它来代替” e l ”, el属性,用它来代替”el”, el属性,用它来代替elel代表了真实的DOM元素(此时真实DOM生成,页面渲染完成)
  3. 调用mounted()钩子函数

该阶段适合做什么?
mounted:可以操作页面的DOM元素了。

2.14.6 更新阶段做了什么事儿

做了这么几件事:

  1. data发生变化(这是该阶段开始的标志)
  2. 调用beforeUpdate()钩子函数(此时只是内存中的数据发生变化,页面还未更新)
  3. 虚拟DOM重新渲染和修补
  4. 调用updated()钩子函数(此时页面已更新)

该阶段适合做什么?
beforeUpdate:适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
updated:页面更新后,如果想对数据做统一处理,可以在这里完成。

2.14.7 销毁阶段做了什么事儿

做了这么几件事:

  1. vm.$destroy()方法被调用(这是该阶段开始的标志)
  2. 调用beforeDestroy()钩子函数(此时Vue实例还在。虽然vm上的监视器、vm上的子组件、vm上的自定义事件监听器还在,但是它们都已经不能用了。此时修改data也不会重新渲染页面了)
  3. 卸载子组件和监视器、解绑自定义事件监听器
  4. 调用destroyed()钩子函数(虽然destroyed翻译为已销毁,但此时Vue实例还在,空间并没有释放,只不过马上要释放了,这里的已销毁指的是vm对象上所有的东西都已经解绑完成了)

该阶段适合做什么?
beforeDestroy:适合做销毁前的准备工作,和人临终前写遗嘱类似。例如:可以在这里清除定时器。

猜你喜欢

转载自blog.csdn.net/weixin_54585403/article/details/130199898