【vue2.x】基础知识

Vue 基础知识(2.x)

生命周期

vue生命周期分为四个阶段八个生命周期钩子函数

阶段钩子函数
创建阶段beforeCreate,created
挂载阶段beforeMount,mounted
更新阶段beforeUpdate,updated
销毁阶段beforeDestroy,destroyed
  1. beforeCreate
    在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用。

    在这个阶段,数据是获取不到的,并且真实 dom 元素也是没有渲染出来的

  2. created
    在实例创建完成后被立即调用。
    在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且 $el、property 目前尚不可用。

    在这个阶段,可以访问到数据了,但是页面当中真实dom节点还是没有渲染出来,在这个钩子函数里面,可以进行相关初始化事件的绑定、发送请求操作

  3. beforeMount
    在挂载开始之前被调用。
    相关的 render 函数首次被调用。

    代表 dom 马上就要被渲染出来了,但是却还没有真正的渲染出来,这个钩子函数与 created 钩子函数用法基本一致,可以进行相关初始化事件的绑定、发送请求操作

  4. mounted
    实例被挂载后调用。
    这时 el 被新创建的 vm.$el 替换了。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内。

    挂载阶段的最后一个钩子函数,数据挂载完毕,真实 dom 元素也已经渲染完成了,这个钩子函数内部可以做一些实例化相关的操作

  5. beforeUpdate
    在数据发生改变后,DOM 被更新之前被调用。
    这里适合在现有 DOM 将要被更新之前访问它,比如移除手动添加的事件监听器。

    这个钩子函数初始化时不会执行,当组件挂载完毕的时候,并且当数据改变的时候,才会立马执行,这个钩子函数获取dom的内容是更新之前的内容

  6. updated
    在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。
    当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。

    这个钩子函数获取 dom 的内容是更新之后的内容生成新的虚拟 dom,新的虚拟 dom 与之前的虚拟 dom 进行比对,差异之后,就会进行真实 dom 渲染。在 updated 钩子函数里面就可以获取到因 diff 算法比较差异得出来的真实 dom 渲染了。

  7. beforeDestroy
    实例销毁之前调用。
    在这一步,实例仍然完全可用。

    当组件销毁的时候,就会触发这个钩子函数代表销毁之前,可以做一些善后操作,可以清除一些初始化事件、定时器相关的东西。

  8. destroyed
    实例销毁后调用。
    该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。

    Vue 实例失去活性,完全丧失功能

slot

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ==> 子组件
  2. 分类:默认插槽、具名插槽、作用域插槽
  3. <slot> 元素作为组件模板之中的内容分发插槽。<slot> 元素自身将被替换
  4. 在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slotslot-scope 这两个目前已被废弃但未被移除且仍在文档中的 attribute。

默认插槽

<!-- 子组件 son -->
<template>
  <div class="box">
    <slot>我是默认值,当使用者没有传递具体结构时,我会出现</slot>
  </div>
</template>

<!-- 父组件 -->
<template>
  <div>
    <son> Hello!</son>
  </div>
</template>

具名插槽

给 slot 增加 name 属性即可设置为具名插槽
使用时可用:

  1. v-slot:xxx#xxx,只能添加在<template>上(推荐)
  2. slot="xxx"可以用在一个普通元素上(不推荐,自2.6.0起已被废弃)
<!-- 子组件 son -->
<template>
  <div class="box">
    <div>
      <slot name="header"></slot>
    </div>
    <div>
      <slot name="footer"></slot>
    </div>
  </div>
</template>

<!-- 父组件 -->
<template>
  <div>
    <son>
      <!-- 推荐写法 -->
      <template v-slot:header>
        我是头部
      </template>
      <template #footer>
        我是头部
      </template>

      <!-- 不推荐写法 -->
      <p slot="header">我是头部</p>
    </son>
  </div>
</template>

作用域插槽

让插槽可以访问到子组件中的内容
使用时可用:

  1. v-slot:插槽名="自定义变量名"#插槽名="自定义变量名" (推荐)
  2. slot-scope="自定义变量名" (不推荐,自2.6.0起已被废弃)
<!-- 子组件 son -->
<template>
  <div class="box">
    <slot :info="info" address="123"></slot>
  </div>
</template>

<!-- 父组件 -->
<template>
  <div>
    <!-- 推荐写法一 利用v-slot: -->
    <son v-slot:default="slotProps">
      {{slotProps.info}}
      {{slotProps.address}}
    </son>

    <!-- 推荐写法二 利用# -->
    <son #default="slotProps">
      {{slotProps.info}}
      {{slotProps.address}}
    </son>

    <!-- 推荐写法三 (利用解构,可以给解构值起别名和默认值) -->
    <son v-slot:default="{info:userInfo, address='默人地址'}">
      {{userInfo}}
      {{address}}
    </son>

    <!-- 不推荐写法,自2.6.0后已废弃 -->
    <son slot-scope="slotProps">
      {{slotProps.info}}
      {{slotProps.address}}
    </son>
  </div>
</template>

组件间通信方式

  1. props / $emit
  • props 父 ===> 子
  • $emit 子 ===> 父
  1. EventBus 事件总线
    任意组件间的通信
    // 1. 创建事件总线
    import Vue from 'vue'
    const bus = new Vue()
    Vue.prototype.$bus = bus
    
    // 2. 发布事件
    this.$bus.$emit('eventName', 'hello') 
    
    // 3. 订阅事件
    this.$bus.$on('eventName', (msg)=>{
      console.log(msg) // hello
    })
    
    // 4. 取消订阅
    destoryed(){
      this.$bus.$off() // 取消所有的事件订阅
      this.$bus.$off('eventName') // 取消指定事件名
      this.$bus.$off('eventName',()=>{}) // 取消指定事件名的指定回调
    }
    
  2. vuex
  3. $attrs / $listeners
  • $attrs 父 ===> 所有子孙
    • 从父组件传给自定义子组件的属性,如果没有 prop 接收会自动设置到子组件内部的最外层标签上,如果是 class 和 style 的话,会合并最外层标签的 class 和 style。
    • 如果子组件中不想继承父组件传入的非 prop 属性,可以使用 inheritAttrs 禁用继承,然后通过 v-bind=“$attrs” 把外部传入的 非 prop 属性设置给希望的标签上,但是这不会改变 class 和 style。
      <!-- 父节点 -->
      <template>
        <div>
          <son name="Tom" :age={18} address="123"></son>
        </div>
      </template>
      
      <!-- 子节点 son -->
      
      <template>
        <div>
          <span>name: {{$attrs.name}}</span>
          <span>age: {{age}}</span>
          <span>address: {{$attrs.address}}</span>
          <!-- 孙子节点 使用v-bind="$attrs"可以继续传递父节点的属性 -->
          <grand-son v-bind="$attrs"></grand-son>
        </div>
      </template>
      
      <script>
        export default {
          props: ['age'],
          inheritAttrs: false, // 这里记得加上这个属性,并设置为false
          created(){
            console.log(this.$attrs) // {name: 'Tom', address: '123'}
          }
        }
      </script>
      
  • $listeners 所有子孙 ===> 父
    • 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。
    • 它可以通过 v-on=“$listeners” 传入内部组件——在创建更高层次的组件时非常有用。
    <!-- 父节点 -->
    <template>
      <div>
        <son @changeValue="changeValue" @changeData="changeData"/>
      </div>
    </template>
    
    <!-- 子节点 son -->
    <template>
      <div>
        <input @change="$listeners.changeValue"></input>
        <!-- 孙子节点 使用v-on="$listeners"可以把子节点中所有的事件都传给父节点 -->
        <grand-son v-on="$listeners"></grand-son>
      </div>
    </template>
    
    <!-- 孙子节点 grand-son -->
    <template>
      <button @click="$listeners.changeData('hello')">提交</button>
    </template>
    
  1. provide / inject
    父 ===> 所有子孙
    父组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。
    主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

    provide 和 inject 绑定并不是可响应的。如果传入了一个可监听的对象,那么其对象的 property 还是可响应的。比如传递对象等

    <!-- 父组件 -->
    <div>
      <son></son>
    </div>
    
    <script>
      export default {
        provide(){
          return {
            name: this.name
          }
        }
        data(){
          return {
            name: 'Tom'
          }
        }
      }
    </script>
    
    <!-- 子组件 son -->
    <div>
      <grand-son></grand-son>
    </div>
    
    <!-- 孙子组件 grand-son-->
    <div>
      {{name}}
    </div>
    <script>
      export default {
        inject: ['name']
      }
    </script> 
    

class/style的动态绑定

动态绑定 class

  1. 对象
<div :class="{ 'active': isActive, 'text-red': isRed }"></div>
  1. 数组

    2.1 单纯数组

    <div :class="[isActive, isRed]"></div>
    <script>
    export default {
      data(){
        return {
          isActive: 'active',
          isRed: 'text-red'
        }
      }
    }
    </script>
    

    2.2 三元运算

    <div :class="[isActive ? 'active' : '', isRed ? 'text-red' : '']"></div>
    

动态绑定 style

  1. 对象
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
  1. 数组
<div :style="[baseStyles, overridingStyles]"></div>

vue检测数据变化的原理

参考链接1参考链接2参考链接3

在这里插入图片描述

1. 数据监听器(Observer)

  • 目的:能够对数据对象进行监听,即当读取或者修改数据属性的时候,能够追踪到变化。
  • 实现方式:通过Object.defineProperty()来劫持各个属性的 get 和 set,在数据被读的时候,触发 get 方法,执行 Dep 来收集依赖,也就是收集 Watcher。

    注意事项
    新增属性和删除属性都无法追踪到

2. 依赖收集器(Dep)

  • 目的:当数据发生变化时,会将该数据及其依赖收集起来,当依赖项发生变化时,会通知依赖项,从而通知依赖它的组件。
  • 实现方式:在get中进行依赖收集,在set中通所有依赖去执行更新

3. 依赖(Watcher)

  • watch、computed 这些都是依赖

虚拟 DOM 和 diff 算法

vue 通过模版编译生成虚拟 DOM 树,然后在通过渲染器渲染成真实 DOM,当数据更新时,产生新的虚拟dom 树,如果直接用新的虚拟 DOM 树生成真实 DOM 并不是最优的方法。为了进一步降低找出差异的性能的性能消耗,就要使用 diff 算法

虚拟DOM

是一棵以 JavaScript 对象作为基础的树,每一个节点称为 VNode ,用对象属性来描述节点,实际上它是一层对真实 DOM 的抽象,最终可以通过渲染操作使这棵树映射到真实环境上,简单来说 Virtual DOM 就是一个 Js 对象,用以描述整个文档

优势

  1. 跨平台优势,虚拟 DOM 并不依赖于浏览器平台,可以被应用于跨平台的开发中
  2. 提高性能,由于虚拟 DOM 只更新必要的部分,减少了对真实 DOM 的操作次数,可以大幅提高性能
  3. 开发效率高,通过使用虚拟 DOM,我们可以更加专注于业务逻辑的开发,不需要过多地关心 DOM 操作的细节
  4. 组件化开发,通过将一个页面拆分成多个组件,每个组件都维护自己的虚拟 DOM 树,方便复用和维护

缺点

  1. 学习成本高,虚拟 DOM 需要掌握一定的 Vue 知识和编程技巧,因此学习成本相对较高
  2. 代码量大,由于虚拟 DOM 需要构建抽象 DOM 树,因此会增加一定的代码量和复杂度
  3. 首次渲染效率低,在首次渲染页面时,需要构建完整的虚拟 DOM 树,因此首次渲染的效率相对较低
diff算法

diff 算法是一种对比算法。对比两者是旧虚拟 DOM 和新虚拟 DOM,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,实现精准地更新真实 DOM。

在vue2 中采用双端diff算法,核心是通过新前与旧前新后与旧后新后与旧前新前与旧后暴力比对5 种查找 参考链接

在 vue3 中采用快速diff算法,核心是先进行预处理,再利用source数组,相比双端 diff,快速 Diff 需要处理的边际条件会更少 参考链接

this.$set()

作用

给响应式对象添加新的属性。

如果我们在创建实例以后,再在实例上绑定新属性,vue 是无法进行双向绑定的

<script>
export default {
  data() {
    retrun {
      form: {
        name: 'Tom',
        age: 18
      }
    }
  },
  mounted() {
    // 不会触发响应式
    this.form.sex = '男'
  }
}
</script>

sex 属于 form 的一个新属性,而 vue 的原理是,在创建实例的时候,遍历 data 里的值,监听gettersetter方法,一旦
这些值更新了,就去触发对应的视图更新。而 sex 并不是 vue 实例化的时候拥有的属性,所以我们新增这个属性,vue 并没有对
他的settergetter方法进行监听,因此无法实现双向绑定,所以此时需要使用this.$set(this.form, 'sex', '男')方法进行双向绑定

用法

this.$set(obj, key, value)

  • obj: 要添加属性的目标对象
  • key: 要添加的属性名
  • value: 要添加的属性值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值