vue2+vue3天禹老师版复习笔记

文章目录

  • 1.Vue2基础
    • 1.1.初始Vue
    • 1.2.模板语法
    • 1.3.数据绑定
    • 1.4.MVVM模型
    • 1.5.el和data的两种写法
    • 1.6.回顾defineProperty方法
    • 1.7.数据代理
    • 1.8.Vue中的数据代理
    • 1.9.事件处理
    • 1.10.计算属性
    • 1.11.监视属性(侦听属性)
    • 1.12.绑定样式
    • 1.13.条件渲染
    • 1.14.列表渲染
    • 1.15.列表过滤
    • 1.16.Vue检测数据变化的原理
    • 1.17.收集表单数据
    • 1.18.过滤器
    • 1.19.内置指令
    • 1.20.自定义指令
    • 1.21.生命周期
  • 2.Vue组件化
    • 2.1.创建非单文件组件
    • 2.2.组件的嵌套
    • 2.3.VueComponent构造函数
    • 2.4.重要的内置关系
    • 2.5.创建单文件组件
  • 3.Vue脚手架
    • 3.1.初始化脚手架
    • 3.2.目录说明
    • 3.3.利用脚手架实现2.5案例
    • 3.4.render
    • 3.5脚手架配置
    • 3.6.ref属性
    • 3.7.props配置项
    • 3.8.mixin
    • 3.9.插件
    • 3.10.scoped
    • 3.11.TodoList案例
    • 3.12.组件间数据传递
    • 3.13.WebStorage
    • 3.14.组件的自定义事件
    • 3.15全局事件总线
    • 3.16消息订阅与发布
    • 3.17.$nextTick
    • 3.18.过度和动画
  • 4.Vue中的AJAX
    • 4.1.使用axios
    • 4.2.跨域问题
    • 4.3.vue-resource
    • 4.4.插槽
  • 5.vuex
    • 5.1.vuex概念
    • 5.2.搭建vuex环境
    • 5.3.getters配置项
    • 5.4.四个map方法
    • 5.5.vuex的模块化
  • 6.路由
    • 6.1.路由的基本使用
    • 6.2.嵌套路由
    • 6.3.路由传参
    • 6.4.路由命名
    • 6.5.路由的props配置
    • 6.6.replace属性
    • 6.7.编程式路由导航
    • 6.8.缓存路由组件
    • 6.9.两个新的生命周期钩子
    • 6.10.路由守卫
    • 6.11.路由器的工作模式
  • 7.VueUI组件库
    • 7.1.移动端常用UI组件库
    • 7.2 PC 端常用UI组件库
    • 7.3.ElementUI简单使用
    • 7.4 按需引入
  • 8.Vue3
    • 8.1创建vue3.0工程
    • 8.2.常用Composition API
      • 8.2.1.setup函数
      • 8.2.2.ref函数
      • 8.2.3.reactive函数
      • 8.2.4.Vue3响应式原理
      • 8.2.5.reactive和ref的对比
      • 8.2.6.setup的两个注意点
      • 8.2.7.计算属性
      • 8.2.8监视属性
      • 8.2.9.Vue3生命周期
      • 8.2.10.自定义hook函数
      • 8.2.11.toRef和toRefs函数
    • 8.3.其他Composition API
      • 8.3.1.shallowRef和shallowReactive
      • 8.3.2.readonly和shallowReadonly
      • 8.3.3.toRaw和markRaw
      • 8.3.4.customRef
      • 8.3.5.provide和inject
      • 8.3.6.响应式数据的判断
      • 8.3.7.组合式API的优点
    • 8.4新的组件
      • 8.4.1.Fragment
      • 8.4.2.Teleport
      • 8.4.3.Suspense
    • 8.5其他
      • 8.5.1.全局API的转移
      • 8.5.2.其他改变

1.Vue2基础

Vue官网

1.1.初始Vue

<head>
    <meta charset="UTF-8">
    <title>初始Vuetitle>
    
    <script type="text/javascript" src="../js/vue.js">script>
head>
<body>
    
    <div id="root">
        <h1>hello {{name}}h1>
    div>
    
    <script type="text/javascript">
        //阻止Vue在启动时生成生产提示
        Vue.config.productionTip = false

        //创建Vue实例 参数是配置对象
        new Vue({
            el: '#root', //element用于指定当前Vue实例为哪个容器服务
            data: {name: 'Vue'}, //存储数据,提供给el指定容器使用
        })
    script>
body>

总结:

​ 1.想让Vue工作必须先创建Vue实例,并且传入配置对象

​ 2.容器中的代码依旧符合html规范

​ 3.Vue实例和容器只能一一对应

​ 4.{{xxx}}中的xxx要写js表达式,并且xxx可以自动读取data中的所有属性

1.2.模板语法

<body>
    <div id="root">
        <h1>你好,{{name}}h1>
        <hr>
        <a v-bind:href="web.url">点我跳转到{{web.name}}a>
         
        <a :href="web.url.toUpperCase()">点我跳转到{{web.name}}a>
    div>

    <script>
        Vue.config.productionTip = false

        new Vue({
            el: '#root',
            data: {
                name: 'jack',
                web:{
                    name: '百度',
                    url: 'http://www.baidu.com'
                }
                
            }
        })
    script>
body>

总结:

​ Vue模板语法分为两大类:

​ 1.插值语法:{{xxx}} xxx为js表达式

​ 2.指令语法:v-xxx xxx为js表达式

1.3.数据绑定

<body>
    <div id="root">
        单向数据绑定:<input type="text" v-bind:value="name">
        双向数据绑定:<input type="text" v-model:value="name">
        
        
        单向数据绑定:<input type="text" :value="name">
        双向数据绑定:<input type="text" v-model="name">
    div>

    <script>
        Vue.config.productionTip = false

        new Vue({
            el: '#root',
            data: {
                name:'单向绑定'
            }
        })
    script>
body>

总结:

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

​ 1.单向绑定(v-bind):数据只能从data流向页面

​ 2.双向绑定(v-model):数据不仅能从data流向页面也能从页面流向data

​ v-bind:属性="" —简写→ :属性=""

​ v-model:value="" —简写→ v-model=""

1.4.MVVM模型

vue2+vue3天禹老师版复习笔记_第1张图片
MVVM模型:

​ M(模型Model):对应data中的数据

​ V(试图View):模板

​ VM(视图模型ViewModel):Vue实例对象

总结:

​ data中的所有属性最后都出现在vm实例对象上

​ vm实例上的所有属性及Vue原型上所有属性都可以在Vue模板中直接使用

1.5.el和data的两种写法

el的两种写法


<script>
    Vue.config.productionTip = false

    const vm = new Vue({
        el: '#root', 
        data: {}
    })
script>


<script>
    Vue.config.productionTip = false

    const vm = new Vue({
        data: {}
    })
    vm.$mount("#root")
script>

data的两种写法


<script>
    Vue.config.productionTip = false

    const v = new Vue({
        el: '#root', 
        data: {
            name: 'jack'
        }
        
    })
script>


<script>
    Vue.config.productionTip = false

    const v = new Vue({
        el: '#root',
        data: function(){    //简写形式-> data(){...}
            return {
                name: 'jack'
            }
        }
    })
script>

总结:

el的两种写法:

​ 1.new Vue的时候配置el属性

​ 2.先创建Vue实例,然后再通过vm.$mount("选择器")指定容器

data的两种写法:

​ 1.对象式

​ 2.函数式

关于data函数式写法的重要原则:

​ 由Vue管理的函数只能写普通函数,不能使用箭头函数

​ 一旦写了箭头函数,this就不再指向Vue实例

1.6.回顾defineProperty方法

<body>
    <script type="text/javascript">
        let num = 18

        let person = {
            name: "张三",
            sex: "男",
            // age: num 无法动态的修改age属性的值
        }

        //给person对象添加age属性
        Object.defineProperty(person,"age",{
            // value: 18,
            // writable:true,//控制属性是否可以被修改
            // enumerable: true,//控制属性是否可以枚举
            // configurable:true,//控制属性是否可以被删除
            get(){ //当读取person的age属性时自动调用
                return num;
            },
            set(value){ //当修改person的age属性时自动调用
                num = value
            }
            //get()和set()不能与value和writable属性共存
        })
    script>
body>

折叠代码

//#region
//.....
//#endregion

1.7.数据代理

<script type="text/javascript">
    
    let obj = {x:100}
    let obj2 = {y:200}

    Object.defineProperty(obj2,"x",{
        get(){
            return obj.x
        },
        set(value){
            obj.x = value
        }
    })
script>

vue2+vue3天禹老师版复习笔记_第2张图片

在代理对象上通过Object.defineProperty()添加被代理对象的属性

通过obj2对象可以操作obj对象中的x属性,这就是数据代理

1.8.Vue中的数据代理

<script>
    Vue.config.productionTip = false

    let data = {
            name: "张三",
            age: "20"
        }

    const vm = new Vue({
        el: '#root',
        data
    })

script>

验证vm中的_data就是配置对象中的data
在这里插入图片描述
vm上添加的getter/setter
vue2+vue3天禹老师版复习笔记_第3张图片

如何实现数据代理?

​ 以name属性为例,当访问vm.name时会调用name属性的getter方法返回data中的name值

​ 当修改vm.name时会调用name属性的setter方法修改data中的name属性值
vue2+vue3天禹老师版复习笔记_第4张图片
总结:

​ 通过vm对象代理data对象的中属性

​ 原理:

​ 通过Object.defineProperty()把data对象的所有属性添加到vm上

​ (data相当于上例中的obj对象 vm相当于上例中的obj2对象)

​ 为每个添加到vm对象上的属性提供getter 和 setter 方法

​ 在getter/setter方法内部操作data中对应的属性

vue2+vue3天禹老师版复习笔记_第5张图片

1.9.事件处理

<body>
    <div id="root">
        <button v-on:click="showInfo">点我显示信息button>
        
        <button @click="showInfo">点我显示信息button>
        
        <button @click="showInfo2($event,66)">点我显示信息(传参)button>
    div>

    <script>
        Vue.config.productionTip = false

        new Vue({
            el: '#root',
            data: {},
            methods: {
                showInfo(event){
                	//默认传递event事件对象
                    console.log(event);//事件对象
                    console.log(this);//此处的this指向vm对象
                },
                showInfo2(event,number){
                    console.log(event,number);
                }
            }
        })
    script>
body>

事件的基本使用:

​ 1.使用v-on:xxx@xxx绑定事件,xxx是事件名

​ 2.事件的回调函数需要配置在methods对象中,最终会出现在vm上

​ 3.methods中配置的函数不要使用箭头函数

​ 4.传递参数时,默认不传递event对象,如果需要传递event对象,必须使用$event传递

事件修饰符

事件修饰符 说明
prevent 阻止默认事件
stop 阻止事件冒泡
once 事件只触发一次
capture 使用事件的捕获模式
self 只有event.target是当前操作元素时才触发事件
passive 事件的默认行为立即执行 无需等待回调函数执行完毕
<body>
    <div id="root">
        <a href="http://www.baidu.com" @click.prevent="show">百度a>
        
        <a href="http://www.baidu.com" @click.prevent.once="show">百度a>
    div>

    <script>
        Vue.config.productionTip = false

        new Vue({
            el: '#root',
            data: {},
            methods: {
                show(e){
                    //阻止默认行为,效果同 @click.prevent
                    //e.preventDefault()
                    alert('abc')
                }
            }
        })
    script>
body>

基础知识补充

css:
	overflow: auto; 空间不够显示滚动条
事件:
	
      scroll: 滚动条滚动时触发事件 wheel: 鼠标滚轮滚动时触发事件

      键盘事件

      Vue中常用的按键别名

      按键 别名
      回车 enter
      删除 delete
      退出 esc
      空格 space
      换行 tab
      上/下/左/右 up / down / left / right

      特殊的键:

      ​ Tab只能配合keydown使用

      ​ 系统修饰键:ctrl、alt、shift、meta(win)

      ​ 配合keyup使用:按下修饰键的同时按下其他键,然后释放其他键,事件才能被触发

      ​ 配合keydown使用:正常触发事件

      <body>
          <div id="root">
              <input type="text" placeholder="按下键盘显示提示" @keydown.enter="showTip">
              
              <input type="text" placeholder="按下键盘显示提示" @keydown.CapsLock="showTip">
              <input type="text" placeholder="按下键盘显示提示" @keydown.caps-lock="showTip">
              
              <input type="text" placeholder="按下Ctrl+Y显示提示" @keyup.ctrl.y="showTip">
      		<input type="text" placeholder="按下x或y显示提示" @keydown.x.y="showTip">
      
          div>
      
          <script>
              Vue.config.productionTip = false
      
              new Vue({
                  el: '#root',
                  data: {},
                  methods: {
                      showTip(event){
                          console.log(event.key,event.keyCode);
                      }
                  }
              })
      
          script>
      body>
      

      在Vue中也可以使用keyCode去指定具体的按键(不推荐)

      <input type="text" placeholder="按下键盘显示提示" @keydown.13="showTip">
      

      键盘上各个键对应的keyCode(了解)

      key keycode
      Escape 27
      CapsLock 20
      Shift 16
      Control 17
      Delete 46
      Backspace 8
      空格 32
      Tab 9
      Enter(回车) 13
      左/上/右/下 37/38/39/40
      a/b/c/d/… 65/66/67/68/…
      0/1/2/3/4/… 48/49/50/51/52/…

      自定义别名按键(了解)

      <body>
          <div id="root">
              <input type="text" placeholder="按下键盘显示提示" @keydown.huiche="showTip">
          div>
      
          <script>
              Vue.config.productionTip = false
              //自定义回车按键的别名
              Vue.config.keyCodes.huiche = 13
              new Vue({
                  el: '#root',
                  data: {},
                  methods: {
                      showTip(event){
                          console.log(event.key,event.keyCode);
                      }
                  }
              })
          script>
      body>
      

      1.10.计算属性

      姓名案例:当输入框中的内容发生变化时,姓名也会发生相应的变化
      vue2+vue3天禹老师版复习笔记_第6张图片

      
      <body>
          <div id="root">
              姓:<input type="text" v-model="firstName"><br>
              名: <input type="text" v-model="lastName"><br>
              姓名:<span>{{firstName}}-{{lastName}}span>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              new Vue({
                  el: '#root',
                  data: {
                      firstName: '张',
                      lastName: '三'
                  }
              })
          script>
      body>
      

      使用插值语法虽然可以实现这个案例,但是当需求发生变化时,{{xxx}}中的表达式可能会变得很复杂,代码可读性差

      
      <body>
          <div id="root">
              姓:<input type="text" v-model="firstName"><br><br>
              名:<input type="text" v-model="lastName"><br><br>
              姓名:<span>{{fullName()}}span>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              new Vue({
                  el: '#root',
                  data: {
                      firstName: '张',
                      lastName: '三'
                  },
                  methods: {
                      fullName(){
                          //这里可以对firstName/lastName操作
                          return this.firstName+'-'+this.lastName
                      }
                  }
              })
          script>
      body>
      

      上面这种方式效率较低

      
      <body>
          <div id="root">
              姓:<input type="text" v-model="firstName"><br><br>
              名:<input type="text" v-model="lastName"><br><br>
              姓名:<span>{{fullName}}span>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      firstName: '张',
                      lastName: '三'
                  },
                  //计算属性配置项
                 computed:{
                      //写在data中的是属性    
                      //写在computed中的是计算属性
                      fullName:{ //fullName就是计算属性
                          get(){
                              //什么时候调用
                              //1.初次读取fullName时
                              //2.所依赖的数据发生变化时
                              return this.firstName+'-'+this.lastName
                          },
                          set(value){
                              //set不是必须的
                              //fullName发生变化时调用
                              const arr = value.split('-')
                              this.firstName = arr[0]
                              this.lastName = arr[1]
                          }
                      }
                 }
              })
          script>
      body>
      

      计算属性的简写形式

      如果计算属性只读不改才可以使用简写形式

      computed:{
      	fullName(){
      		return this.firstName+'-'+this.lastName
      	}
      }
      

      总结:

      ​ 计算属性的定义:要用的属性不存在,要通过已有的属性计算得来

      ​ 计算属性最终会出现在vm身上,可直接使用

      ​ 相比于methods方法实现,这种方式内部有缓存机制,效率更高

      1.11.监视属性(侦听属性)

      天气案例:点击按钮切换天气(将炎热改为凉爽)
      vue2+vue3天禹老师版复习笔记_第7张图片

      <body>
          <div id="root">
              <h1>今天天气很{{weather}}h1>
              <button @click="changeWeather">点击切换天气button>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      isHot: true
                  },
                  computed: {
                      weather(){
                          return this.isHot ? '炎热' : '凉爽'
                      }
                  },
                  methods: {
                      changeWeather(){
                          this.isHot = !this.isHot
                      }
                  },
                  //监视属性的配置项
                  watch: {
                      //key为要监视的属性
                      isHot:{
                          //初始化时是否执行handler函数
                          immediate: false,
                          //handler函数可以接收被监视属性被修改前后的值
                          handler(newValue,oldValue){
                              console.log('天气被修改了',newValue,oldValue);
                          }
                      }
                  }
              })
          script>
      body>
      

      监视属性的另外一种写法(创建好vm对象后,使用$watch()函数添加监视属性)

      这种方式适合在创建vue实例对象时不明确要监视哪个属性

      vm.$watch('isHot',{
           immediate: false,
           handler(newValue,oldValue){
              console.log('天气被修改了',newValue,oldValue);
           }
      })
      

      监视属性的简写形式

      当监视属性只配置handler属性>时才能使用简写形式

      watch: {
          isHot (newValue,oldValue){
              console.log('天气被修改了',newValue,oldValue);
          }
      }
      //另一种简写形式
      vm.$watch('isHot',function(newValue,oldValue){
          console.log('天气被修改了',newValue,oldValue);
      })
      

      深度监视

      案例:点击按钮数值加1,并在控制台中打印变化的值
      vue2+vue3天禹老师版复习笔记_第8张图片

      <body>
          <div id="root">
              <h1>a的值是{{numbers.a}}h1>
              <h1>b的值是{{numbers.b}}h1>
              <button @click="numbers.a++">点击a+1button>
              <button @click="numbers.b++">点击b+1button>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      numbers: {
                          a:10,
                          b:20
                      }
                  },
                  watch: {
                      //a: { 当监视的是多级结构中的属性,这样写就无法监视到
                      'numbers.a':{ //正确写法
                          handler(newValue,oldValue){
                              console.log('a的值变化了',newValue,oldValue);
                          }
                      },
                      numbers:{
                          deep: true, //开启深度监视,用于监视多级结构中的属性
                          handler(){
                              console.log('numbers中的值变化了');
                          }
                      }
                  }
              })
      
          script>
      body>
      

      总结:
      1.Vue中的watch默认不监视对象内部的属性值的改变
      2.配置deep:true可以监视到对象内部属性的改变

      计算属性VS监视属性

      姓名案例watch实现

      
      <body>
          <div id="root">
              姓:<input type="text" v-model="firstName"><br><br>
              名:<input type="text" v-model="lastName"><br><br>
              姓名:<span>{{fullName}}span>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      firstName: '张',
                      lastName: '三',
                      fullName:'张-三'
                  },
                  watch:{
                      firstName(newVal){
                          this.fullName = newVal +'-'+ this.lastName
                      },
                      lastName(newVal){
                          this.fullName = this.firstName +'-'+ newVal
                      }
                  }
              })
          script>
      body>
      

      新需求:在修改完姓之后3秒页面的全名再发生变化

      //这种写法是错误的	
      computed:{
      	fullName(){
      		setTimeOut(()=>{
      			return this.firstName+'-'+this.lastName
      		},3000)
      	}
      }
      //这种写法是正确的	
      watch:{
          firstName(newVal){
              setTimeOut(()=>{
      			this.fullName = newVal +'-'+ this.lastName
      		},3000)
          },
          lastName(newVal){
              this.fullName = this.firstName +'-'+ newVal
          }
      }
      
      

      总结:

      ​ 1.computed能完成的功能 使用watch也都能完成

      ​ 2.watch能完成的功能 computed不一定能完成 例如watch可以进行异步操作

      ​ 3.计算属性中不能开启异步任务去维护数据

      ​ 4.两个重要的小原则:

      ​ 4.1所有被Vue管理的函数最好写成普通函数

      ​ 4.2所有不被Vue管理的函数最好写成箭头函数(定时器的回调、ajax的回调、Promise的回调)

      1.12.绑定样式

      css样式绑定

      
      <body>
          <div id="root">
              绑定样式::class="xxx" xxx可以是字符串、数组、对象
              
              <div class="basic" :class="classStr" @click="changeClass">div>
              
              <div class="basic" :class="classArr">div>
              
              <div class="basic" :class="classObj">div>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      classStr: 'style1',
                      classArr: ['style1','style2','style3'],
                      classObj:{
                          style1: true,  //使用style1样式
                          style2: true,
                          style3: false  //不使用style3样式
                      }
                  },
                  methods: {
                      changeClass(){
                          this.classStr = 'style2'
                      }
                  }
              })
          script>
      body>
      

      style样式绑定

      <body>
          <div id="root">
              <div :style="styleObj">绑定样式div>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      styleObj: {
                          fontSize: '40px',
                          color: 'red'
                      }
                  }
              })
          script>
      body>
      

      1.13.条件渲染

      <body>
          <div id="root">
              <div v-show="isShow">{{info}}div>  
              
              
              <div v-if="num === 1">Vuediv>
              <div v-else-if="num === 2">Reactdiv>
              <div v-else>Angulardiv>
      
              
              <template v-if="isShow">
                  <h1>Vueh1>
                  <h1>Reacth1>             
              template>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      info: 'hello vue',
                      isShow: true,
                      num: 0
                  }
              })
          script>
      body>
      

      总结:
      1.v-if
      写法:v-if="布尔表达式"
      适用于切换频率较低的场景
      不展示的DOM结点会被删除
      配合v-else使用时要求结构不能被打断
      2.v-show
      写法:v-show="布尔表达式"
      适用于切换频率较高的场景
      不展示的DOM结点不会被移除,仅仅使用样式隐藏

      1.14.列表渲染

      <body>
          <div id="root">
              <ul>
                  
                  <li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}li>
                  <li v-for="(p,index) in persons" :key="index">{{p}}-{{index}}li>
                  <li v-for="p of persons" :key="p.id">{{p.name}}-{{p.age}}li>
                  
                  <li v-for="(value,key) in persons[0]" :key="key">{{key}}-{{value}}li>
                  
                  <li v-for="(char,index) in str" :key="index">{{char}}-{{index}}li>
                  
                  <li v-for="(number,index) in 5" :key="index">{{number}}-{{index}}li>
              ul>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      persons: [
                          {id:'001',name:'张三',age:18},
                          {id:'002',name:'李四',age:19},
                          {id:'003',name:'王五',age:20},
                      ],
                      str: 'hello'
                  }
              })
          script>
      body>
      

      index作为key时diff算法过程
      vue2+vue3天禹老师版复习笔记_第9张图片
      id作为key时diff算法过程
      vue2+vue3天禹老师版复习笔记_第10张图片
      key的工作原理总结:

      ​ 1.key是虚拟DOM对象的标识

      ​ 2.当状态中的数据发生变化时,Vue会根据新数据生成新的虚拟DOM,再根据diff算法转换成真实DOM

      ​ 3.对比规则:

      ​ 3.1旧虚拟DOM中找到了与新虚拟DOM相同的key

      ​ a.若虚拟DOM中的内容没有改变,直接使用之前生成的真实DOM

      ​ b.若虚拟DOM中的内容发生改变,则生成新的真实DOM,替换掉之前的真实DOM

      ​ 3.2旧虚拟DOM中未找到与新虚拟DOM相同的key

      ​ 创建新的真实DOM,然后渲染到页面

      用index作为key可能会引发的问题

      ​ 若对数据进行逆序添加、删除等破坏顺序操作时

      ​ 1.如果结构中没有包含输入类DOM,会产生没必要的真实DOM更新

      ​ 2.如果结构中包含输入类DOM,会产生错误DOM更新

      如何选择key

      ​ 1.最好使用数据的唯一标识作为key

      ​ 2.如果不存在破坏顺序的操作,仅用于渲染列表用于展示,那么可以使用index作为key

      1.15.列表过滤

      案例:在输入框中输入关键字实现模糊搜索
      vue2+vue3天禹老师版复习笔记_第11张图片
      监视属性实现

      <body>
          <div id="root">
              <input type="text" placeholder="请输入关键字" v-model="keyWord"/>
              <ul>
                  <li v-for="(p,index) in filterPersons" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}li>
              ul>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      keyWord: '',
                      persons: [
                          {id:'001',name:'马冬梅',age:18,sex:'女'},
                          {id:'002',name:'周冬雨',age:19,sex:'女'},
                          {id:'003',name:'周杰伦',age:20,sex:'男'},
                          {id:'004',name:'温兆伦',age:20,sex:'男'},
                      ],
                      filterPersons: []
                  },
                  watch: {
                      keyWord: {
                          immediate: true,
                          handler(newValue){
                              this.filterPersons = this.persons.filter((p)=>{
                              return p.name.indexOf(newValue) !== -1
                          })
                          }
                      }
                  }
              })
          script>
      body>
      

      计算属性实现

      <body>
          <div id="root">
              <input type="text" placeholder="请输入关键字" v-model="keyWord"/>
              <ul>
                  <li v-for="(p,index) in filterPersons" :key="index">{{p.name}}-{{p.age}}-{{p.sex}}li>
              ul>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      keyWord: '',
                      persons: [
                          {id:'001',name:'马冬梅',age:18,sex:'女'},
                          {id:'002',name:'周冬雨',age:19,sex:'女'},
                          {id:'003',name:'周杰伦',age:20,sex:'男'},
                          {id:'003',name:'温兆伦',age:20,sex:'男'},
                      ],
                  },
                  computed:{
                      filterPersons(){
                          var arr = this.persons.filter((p)=>{
                              return p.name.indexOf(this.keyWord) !== -1
                          })
                          return arr
                      }
                  }
              })
          script>
      body>
      

      需求升级:为搜索的到的数据添加排序功能
      vue2+vue3天禹老师版复习笔记_第12张图片

      <body>
          <div id="root">
              <input type="text" placeholder="请输入关键字" v-model="keyWord"/>
              <input type="button"  @click="sortType = 2" value="按年龄升序"/>
              <input type="button"  @click="sortType = 1" value="按年龄降序"/>
              <input type="button"  @click="sortType = 0" value="按原顺序排序"/>
              <ul>
                  <li v-for="(p,index) in filterPersons" :key="index">{{p.name}}-{{p.age}}-{{p.sex}}li>
              ul>
          div>
      
          <script>
              Vue.config.productionTip = false
              
              const vm = new Vue({
                  el: '#root',
                  data: {
                      keyWord: '',
                      sortType: 0,
                      persons: [
                          {id:'001',name:'马冬梅',age:19,sex:'女'},
                          {id:'002',name:'周冬雨',age:21,sex:'女'},
                          {id:'003',name:'周杰伦',age:18,sex:'男'},
                          {id:'003',name:'温兆伦',age:20,sex:'男'},
                      ],
                  },
                  computed:{
                      filterPersons(){
                          var arr = this.persons.filter((p)=>{
                              return p.name.indexOf(this.keyWord) !== -1
                          })
                          if(this.sortType){
                              arr.sort((p1,p2)=>{
                                  return this.sortType === 2 ? p1.age-p2.age : p2.age-p1.age
                              })
                          }
                          return arr
                      }
                  }
              })
          script>
      body>
      

      1.16.Vue检测数据变化的原理

      模拟简单默认数据监视

      <body>
          <script>
              function Observer(obj) {
                  //获取对象中的所有key,放入到keys数组中
                  const keys = Object.keys(obj)
                  //遍历每个key,为其添加get/set函数
                  keys.forEach((key) => {
                      Object.defineProperty(this, key, {
                          get() {
                              return obj[key]
                          },
                          set(value) {
                              console.log(`${key}属性改变了,准备开始重新解析模板...`);
                              obj[key] = value
                          }
                      })
                  })
              }
      
              let data = {
                  name: 'jack',
                  age: 20
              }
      
              let obs = new Observer(data)
              let vm = {}
              vm._data = data = obs
              
          script>
      body>
      

      vue2+vue3天禹老师版复习笔记_第13张图片

      Vue.set()和vm.$set()

      Vue提供了Vue.set()和vm.$set()可以实现后添加的数据也能有响应式的功能

      语法:Vue.set(target,'key/index','value')vm.$set(target,'key/index','value')

      ​ target:往谁的身上追加属性,target不允许是vm或vm._data

      ​ key/index: 要追加的属性名或者数组下标

      ​ value: 属性值

      这种方法存在局限

      set()不能给data追加属性,只能给data中的对象追加属性

      数组监视

      <body>
          <div id="root">
              <ul>
                  <li v-for="(h, index) in hobby" :key="index">{{h}}li>
              ul>
          div>
          <script>
             Vue.config.productionTip = false
      
             const vm = new Vue({
              el:'#root',
              data: {
                  hobby:['唱歌','跳舞','打球']
              }
             })
          script>
      body>
      

      vue2+vue3天禹老师版复习笔记_第14张图片
      由于数组内并没有提供set和get,所以在修改hobby[0]时Vue监测不到数据发生变化,导致页面也不会变化

      Vue将被监听的数组的更变方法进行了包装,所以他们会触发视图的更新,这些被包装的方法有:

      • push() 在数组尾部插入元素
      • pop() 移除数组尾部元素
      • shift() 移除数组首部元素
      • unshift() 在数组首部插入元素
      • splice() 在指定位置替换、添加或删除数组元素
      • sort() 对数组进行排序
      • reverse() 反转数组

      Vue监视数据变化的原理

      1.Vue会监视data中所有层次的数据

      2.监视对象中的数据

      ​ 通过setter实现监视,且要在new Vue时就传入要监视的数据

      ​ 对象中后追加的属性Vue默认不做响应式处理

      ​ 如果需要对后追加的属性做响应式处理需要使用Vue.set()vm.$set()

      3.监视数组中的属性

      ​ 对于数组中非对象属性,Vue默认不提供setter和getter,直接修改vue监视不到变化

      ​ 通过包裹数组更新元素的函数实现

      注意:通过Vue.set()和vm.$set()不能给vm或vm._data添加属性

      1.17.收集表单数据

      <body>
          <div id="root">
              <form action="#" method="post" @submit.prevent="submit">
                  账号:<input type="text" v-model.trim="username"><br><br>
                  密码:<input type="password" v-model="password"><br><br>
                  年龄:<input type="number" v-model.number="age"><br><br> 
                  性别:
                  男<br><br>
                  爱好:
                  唱歌<input type="checkbox" name="hobby" v-model="hobby" value="sing">
                  跳舞<input type="checkbox" name="hobby" v-model="hobby" value="dance">
                  打球<input type="checkbox" name="hobby" v-model="hobby" value="ball"><br><br>
                  校区:
                  <select name="area" v-model="area">
                      <option value="">请选择校区option>
                      <option value="富春">富春校区option>
                      <option value="滨江">滨江校区option>
                  select><br><br>
                  其他信息:<textarea v-model.lazy="info">textarea><br><br>
                  <input type="checkbox" v-model="agree">
                  同意并接收<a href="#">《用户协议》a><br><br> 
                  <input type="submit" value="提交">
              form>
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      username: '',
                      password: '',
                      age:'',
                      sex: 'male',
                      hobby: [],
                      area: '',
                      info:'',
                      agree: ''
                  },
                  methods: {
                      submit(){
                          const data = JSON.stringify(thi._data)
                      }
                  },
              })
          script>
      body>
      

      总结:

      ​ 1.若收集的是 则v-model收集的是用户输入的value值

      ​ 2.若收集的是 则v-model收集的是value值且要配置value属性

      ​ 3.若收集的是 则v-model收集的是

      ​ 3.1没有配置value属性.收集checked的值(true/false)

      ​ 3.1配置了value属性.如果v-model的初始值是非数组那么收集的还是checked的值.如果v-model的初始值 是数组.那么收集的就是value组成的数组

      ​ 4.v-model的三个修饰符

      ​ 4.1 lazy 失去焦点再收集数据

      ​ 4.2 number 输入的字符串转为有效数组,一般配置type="number"使用

      ​ 4.3trim 去除输入首位多余空格

      1.18.过滤器

      BootCDN网址:https://www.bootcdn.cn/

      <head>
          <meta charset="UTF-8">
          <title>Documenttitle>
          <script src="../js/vue.js">script>
          <script src="../js/dayjs.min.js">script>
      head>
      <body>
          <div id="root">
              
              <div>{{formatTime}}div>
              
              <div>{{time | timeFormater}}div>
              
              <div>{{time | timeFormater('YYYY年MM月DD日')}}div>
              
              <div>{{time | timeFormater('YYYY年MM月DD日') | getYear}}div>
          div>
      
          <script>
              Vue.config.productionTip = false
              //配置全局过滤器
              Vue.filter('globalFilter',function(value){
                  return value.toUpperCase()
              })
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      time: 1656912659058,
                  },
                  computed: {
                      formatTime(){
                          return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
                      }
                  },
                  filters: {
                      timeFormater(value,formatStr = 'YYYY-MM-DD HH:mm:ss'){
                          //第一个参数一定是value为管道符|前面的值
                          //后面的参数为传递的参数
                          return dayjs(value).format(formatStr)
                      },
                      getYear(value){
                          return value.slice(0,5)
                      }
                  }
              })
          script>
      body>
      

      总结:

      ​ 1.定义:对显示的数据进行特定格式化后再显示(适用于简单逻辑的处理)

      ​ 2.语法:

      Vue.filter('filterName',callback)全局过滤器

      new Vue{filters:{}} 局部过滤器

      ​ 3.过滤器可以接收额外的参数,第一个参数是管道符前面的值

      ​ 4.过滤器可以串联使用

      ​ 5.过滤器不改变原本的数据而是产生对应新的数据

      1.19.内置指令

      内置指令 说明
      v-bind 单向绑定 可简写为:xxx
      v-model 双向绑定
      v-for 列表渲染
      v-on 绑定事件 可简写为@
      v-if 条件渲染
      v-show 条件渲染
      v-text 向其所在的结点中渲染文本内容,不解析标签
      v-html 向其所在结点中渲染html结构内容
      v-cloak 没有值,Vue创建完容器后会删掉v-cloak属性
      v-once 没有值,所在结点在初次动态渲染后就视为静态内容了
      v-pre 没有值,跳过所在结点的编译过程,加快编译速度
      <body>
          <div id="root">
              
              <div v-text="info">div>
              
              
              <div v-html="info">div>
              
              <div v-once>{{info}}div>
              
              <div v-pre>你好div>
              <div v-pre>{{info}}div> 
          div>
      
          <script>
              Vue.config.productionTip = false
      
              const vm = new Vue({
                  el: '#root',
                  data: {
                      info: '

      Hello Vue

      '
      } })
      script> body>
      <style>
          /* 一般配合css使用,可以解决网速慢时展示出未经编译的模板{{xxx}} */
          [v-cloak]{
              display: none;
          }
      style>
      

      1.20.自定义指令

      需求1:自定义v-big指令 功能和v-text类似但是会把数值放大10倍
      vue2+vue3天禹老师版复习笔记_第15张图片

      <body>
          <div id="root">
              当前的num值是:<span v-text="num">span><br><br>
              num值放大10倍:<span v-big="num">span><br><br>
              <button @click="num++">点击num加1button>
          div>
      body>
      
      <script>
          Vue.config.productionTip = false
      
          const vm = new Vue({
              el: '#root',
              data: {
                  num: 5
              },
              directives: {
                  //定义指令的第一种方式,函数式
                  /* 
                  调用时机:
                      1.指令与元素绑定成功时
                      2.指令所在模板被重新解析时
                  */
                  big(element,binding){ //big: function(element,binding){} 完整写法
                      //element:指令所在真实DOM
                      //bingding:binding.value常用
                      element.innerText = binding.value * 10
                  }
              }
          })
      script>
      

      需求2:自定义v-fbind指令 功能与v-bind类似但可以默认获取焦点

      <body>
          <div id="root">
              <input type="text" v-bind:value="num"><br><br>
              <input type="text" v-fbind="num"><br><br>
              <button @click="num++">点击num加1button>
          div>
      body>
      
      <script>
          Vue.config.productionTip = false
      
          const vm = new Vue({
              el: '#root',
              data: {
                  num: 5
              },
              directives: {
                  //自定义指令的第二种方式:对象式
                  fbind:{
                      bind(ele,binding){
                          //指令和元素成功绑定时调用
                          ele.value = binding.value
                      },
                      inserted(ele,binding){
                          //指令所在元素被插入页面时调用
                          ele.focus()
                      },
                      update(ele,binding){
                          //指令所在模板被重新解析时调用
                          ele.value = binding.value
                          ele.focus()
                      }
                  }
              }
          })
      script>
      

      自定义指令总结:

      ​ 1.局部指令:定义在配置对象的directives中

      ​ 2.全局指令:Vue.directive(‘指令名’,配置对象)或Vue.directive(‘指令名’,回调函数)

      ​ 3.定义指令时不加v- 但使用指令时要加v-

      ​ 4.指令名如果是多个单词用-隔开,不要写成驼峰式

      ​ 5.directives中的函数中的this指向Window

      1.21.生命周期

      1.生命周期函数又叫生命周期钩子

      2.生命周期钩子就是Vue在关键的时刻帮我们调用的特殊名称的函数

      3.生命周期函数的名字不可更改

      4.生命周期函数中的this指向vm实例对象或组件实例

      5.vm.$destory() 完全销毁vm实例

      vue2+vue3天禹老师版复习笔记_第16张图片

      生命周期钩子 说明
      beforeCreate 数据代理被创建之前
      created 数据代理被创建之后
      beforeMount 真实DOM被挂在到页面之前
      mounted(常用) 真实DOM被挂在到页面之后
      beforeUpdate _data数据发生变化之前
      updated _data数据发生变化之后
      beforeDestroy(常用) vm对象被销毁之前
      destroyed vm对象被销毁之后

      template属性使用

      <body>
          <div id="root">
      		
          div>
      body>
      
      <script>
          Vue.config.productionTip = false
      
          const vm = new Vue({
              el: '#root',
              //不能用