Skip to content

Vue2

生命周期钩子函数

  • 初始化前后、dom渲染前后、数据更新dom更新前后、组件销毁先后
  • created (可以开始发送ajax请求,获取data内的数据,保存数据)
  • mounted (可以开始操作dom例如初始化echarts)
  1. beforeCreate:data数据初始化之前,组件还没有数据
  2. created: data数据初始化之后,可以获取到组件的数据
  3. beforeMount:DOM渲染之前,DOM还没渲染
  4. mounted:DOM渲染之后,可以操作DOM了
  5. beforeUpdate: 数据更新,DOM更新前
  6. updated: 数据更新,DOM更新后
  7. beforeDestroy: 组件销毁前
  8. destroyed: 组件销毁后

Vue 的父组件和子组件生命周期钩子函数执行顺序

text
1、Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:
  1)加载渲染过程
     父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
  2)子组件更新过程
     父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
  3)父组件更新过程
     父 beforeUpdate -> 父 updated
  4)销毁过程
  父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

响应式原理

  • 双向数据绑定采用了数据劫持来实现,通过Object.defineProperty()来劫持各个属性的 setter 和 getter,从而实现了数据的响应式更新。getter 方法用于获取数据,setter 方法用于监听数据的变化并更新数据。

  • 缺陷是只能对data节点内定义的数据进行数据劫持,不能监听对象的新增属性和删除属性。

  • vue更新数组不能通过索引,可通过pop、push、shift、unshift、splice、sort、reverse来实现。

  • 解决缺陷的方法通过 Vue.set() 或者 this.$set() 来添加新属性,也可以监听数组的变化,需要手动调用

    image-20230830235650813

image-20230830235407649

为什么data是一个函数而不是对象

如果是一个对象的话,组件被多次使用,里面的对象的数据存储的是在一个地址里面的,如果修改数据,其他组件里面的数据也会被修改,造成数据污染;采用函数的形式就不会出现这种情况,每一次创建的时候,函数都会return一个新的对象,因为每个对象的地址不同

Vue3

生命周期钩子函数

  • onMounted:组件渲染完毕:发请求,操作dom,初始化图表
  • onUnmounted:组件卸载后,清除定时器、延时器、事件监听等等
  1. setup:函数是在组件实例被创建之前调用的,它是使用 Composition API 的入口点。

  2. onBeforeMount: 在挂载开始之前被调用。

  3. onMounted: 在挂载完成后被调用。

  4. onBeforeUpdate: 在更新开始之前被调用,这里可以拿到新的 props 和 reactive 数据。

  5. onUpdated: 在更新完成之后被调用。

  6. onBeforeUnmount: 在卸载之前被调用。

  7. onUnmounted: 在卸载之后被调用。

  8. 选项api和组合api名字不一样

响应式原理

vue3使用了Proxy,是对于整个对象的劫持,对象内部要是发生了变化,都会经过外层的proxy无需递归,效率高。对于数组也是如此

image-20230831001543507

父通过ref或者通过$parent获取组件上的数据必须要defineExpose 暴露才能取到

image-20230907214027096

Vue2&Vue3相关

vue2和vue3的区别

  • 性能优化,更快的初始化时间、渲染性能以及更新性能
  • 响应式原理优化
  • 更好的TS支持
  • 组合式API的支持,省去this的指向
  • 多个根节点
  • 生命周期函数变化,可以有多个相同的生命周期,用setup代替了v2的前两个生命周期函数

Vue的优点是什么

  • 响应式数据绑定:当数据发生变化时,视图自动更新,提高了开发效率。
  • 组件化开发:将一个页面拆分成多个组件,每个组件具有自己的逻辑和样式,便于代码的维护和重用。
  • 轻量高效:轻量级的框架,体积小、性能高,加载速度快,能够快速构建响应式的单页面应用。
  • 生态丰富:有大量的第三方库和插件,能够快速地解决各种问题,如数据管理、路由管理、UI 组件库等。
  • 用户群体大:大量的文章、视频和教程,能够帮助开发者快速解决问题和提高开发效率。

v-if和v-for为什么不建议一起用

VUE2中v-for优先级高,所以不建议一起用 VUE3中v-if优先级高,不需要在意了

  • 因为v-for的优先级比v-if高,一起用的情况下会优先进行v-for的遍历,然后在看v-if的布尔值,这样如果布尔值是false的情况下,会先渲染出来,再销毁,就会带来性能方面的浪费,避免这种情况的话可以在外层套个template标签,因为这个标签渲染不会生成dom节点

Object.defineProperty 和 proxy的差异

  • Object.defineProperty 是对于对象属性的劫持, 需要⼀个个递归劫持, 效率低
  • Object.defineProperty 对于数组的更新检测, ⽐较麻烦,需要使用数组API更新才能被检测到
  • proxy是对整个对象的代理。只要访问了对象的属性,就会经过代理,触发相应的操作。
  • proxy对于数组的变化检测更为简单,不需要特殊处理数组的方法。任何修改数组的操作都能被捕获到,包括数组元素的增加、删除和更改。

虚拟DOM|diff算法|vue中的key

  • 虚拟dom是一个用JavaScript对象表示Web页面的概念,通过对比实际DOM的变化来提高页面性能和交互体验,并使代码更加易于管理和维护。

  • vue中的key是使用v-for遍历数据时,key作为vue中对比算法的标识,为了在进行虚拟 DOM 的比对时,方便 Vue 找到对应的节点,在数据修改后,vue会根据新数据生成新的虚拟DOM。对比下来虚拟DOM与真实DOM有差异的话会替换掉真实DOM。虚拟DOM没变的话就直接使用之前的真实DOM。若在旧的虚拟DOM中没有找到与新虚拟DOM相同的key,则会创建一个真实DOM进行渲染。

    总之,key 属性的作用是为了帮助 Vue 进行虚拟 DOM 的比对,减少不必要的 DOM 操作,提高页面渲染效率

    这个key必须是唯一值,若使用数组下标则在数组中插入或者前增会出现错乱的问题。

watch和computed区别

computed能完成的watch都能完成,watch可以写异步代码,computed不行!

  • watch:是一个侦听器,当数据发生变化时,会依赖于依赖于现有的数据的值并派生出新的数据的值,当要监听复杂数据类型时需要开启深度监听。复杂数据类型需要开启深度监听deep:true,还可以添加immediate:true 开启首次监听
  • computed:是计算属性,有缓存,不能和data中的数据重名,比方说动态控制封装的弹窗的标题,可以通过computed来实现。

watch和computed的原理

  • 当Vue组件实例化时,Vue会遍历watch选项中的每个属性,将它们转化为观察者(Watcher)对象,包含了两个主要函数:gettersetter。getter函数用于收集依赖,并添加到依赖收集器中,setter通知依赖收集器中的观察者对象,告诉它们需要更新视图,观察者对象会执行相应的回调函数来更新视图。
  • 当Vue组件实例化时,Vue会遍历computed选项中的每个计算属性,计算属性的值会被缓存起来,只有在其依赖的响应式数据属性发生变化时,才会重新计算。Vue会在计算属性的getter函数执行时进行依赖追踪,记录哪些响应式数据属性被计算属性所依赖。当依赖的响应式数据属性发生变化时,计算属性的缓存会被标记为过期,下次访问计算属性时会重新计算,并将新值缓存起来。计算属性的值是惰性求值的,它只会在需要的时候才计算,并且只计算一次,然后进行缓存,以提高性能。
  • 总结:监听器和计算属性都依赖于Vue.js的响应式数据系统,通过依赖追踪和观察者模式来实现数据变化的监听和响应。这种机制使Vue能够高效地更新视图以及处理复杂的数据依赖关系。

混入

  • vue2中的混入在一个js文件中写和vue export default一样 例如
  • export default{ data(){return{}},created()
  • 使用方法:先导入写的js文件, 再在.vue文件中的export default内写 mixins: [导入的名字],即可直接使用
  • 说明data数据字段,methods内的方法重名,自己高于混入的,同字段名自己覆盖混入
  • 生命周期函数都会执行,执行混入的,执行自己
  • 优点:封装复用
  • 缺点:数据来源不清晰,后期维护性差

组件通信

  • 父传子:冒号传参props接收 父获取子:给子组件ref
  • 子传父:this.$emit('绑定在子组件上的方法名',id); //id是传的参数 ;
  • 子获取父: $parent进行传递 慎用。嵌套组件时需要要多个$parent ,跟写的位置有关
  • 祖先与后代: Provide 与 Injectc
  • **复杂关系:**通过vuex/pinia存放共享的变量
js
#vue3 事件总线简单方法
npm i mitt
#在src创建个bus文件夹,内放个index.js
//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from 'mitt';
const $bus = mitt();
export default $bus;
#在组件内导入使用
import $bus from '@/bus'
$bus.emit('cp1-send', data)#发布
$bus.on('cp1-send', (data) => {#接收
    console.log(data)
})

#vue2 在src创建个bus文件夹,内放个index.js
import Vue from 'vue';
// 创建一个空的 Vue 实例作为事件总线
const EventBus = new Vue();
export default EventBus;
#在组件内导入使用
import EventBus from './EventBus';
EventBus.$emit('send-data', 'Data from Component A');#发布
#接收		监听事件,在组件创建时就开始监听
 mounted() { 
     EventBus.$on('send-data', data => {
         console.log(data)
     });
  }

组件缓存keep-alive

用于缓存不活动的组件实例,以避免在组件切换时反复创建和销毁组件实例,从而提高性能和用户体验。

  • 用他包裹组件时,会缓存不活动的组件实例,而不是销毁,在组件的切换过程中将状态保留到内存中,防止重复渲染DOM,可以减少加载事件及性能消耗,提升用户体验。有两个生命周期函数activated激活状态的生命周期函数 deactivated 失活状态的生命周期钩子函数。
  • 原理是通过created函数将需要缓存的VNode 节点保存在this.catch中,在页面渲染时再从this.catch中取出相应的节点并渲染

路由

  • 本质上是映射关系,根据不同的URL请求,返回对应不同的资源,那么url地址和真实的资源之间就有一种对应的关系就是路由
  • 作用是设定访问路径,并将路径和组件映射起来(就是用于局部刷新页面,不需要请求服务器来切换页面)
  • 静态路由:将不需要权限就能访问的页面称为静态路由 比如登录页 404页面
  • 动态路由:两种:需要权限才能访问的页面
  • params传参:需要在路由页面配置path: '/user/:a/:b',获取通过route.params.a
  • query传参:router.push('/user?id=123');

$nextTick

  • Vue在更新DOM时是异步执行的,当数据变化时,通过nextTick开启个异步更新队列,等数据更新完毕后,再进行更新视图
  • 原理是利用了Promise、MutationObserver、setTimeout,根据环境支持情况选择最合适的异步机制来执行回调更新DOM

.native

  • 某些组件绑定点击事件不成功需要添加.native修饰符,因为组件内部没有做处理,使用修饰符后就作用到了组件的根元素上

vuex是数据集中管理工具

  • State:用来存数据
  • Getter:类似计算属性,基于state内的数据派生出新的数据,当state内依托的数据变了才会重新计算。
  • mutations: 修改共享数据的唯一节点 此方法内的函数的第一个参数是state。
  • Actions:涉及到异步操作,比如发请求要放在actions中,在actions中修改数据需要调用mutations中的方法 action中方法的第一个参数是context,里面有commit方法,通过这个方法修改context.commit('写在mutations中的方法',传的参数)
  • Modules:模块化路由导入的地方。每一个模块的路由必须开启namespaced:true

为什么有了localstorage还要用vuex

  • Vuex 主要用于在应用程序内共享和管理数据,确保数据在各个组件之间一致。
  • localStorage 存的只能是字符串,如果存储的内容不是字符串,还需要转换,有额外的性能消耗。
  • localStorage 里面的数据变化不能进行监控
  • 主要用途就是可以响应式的处理数据

vuex仓库值初始化数据比较复杂时,state可以是函数写法

image-20230901010816954

vuex辅助函数

js
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
export default {
  computed: {
    ...mapState('storeA', ['countA']), // 映射 storeA 中的 countA 到计算属性
    ...mapGetters('storeA', ['doubleCountA']), // 映射 storeA 中的 doubleCountA 到计算属性
  },
  methods: {
    ...mapMutations('storeA', ['incrementA']), // 映射 storeA 中的 incrementA  到方法
    ...mapActions('storeA', ['asyncIncrementA']), // 映射 storeA 中的 asyncIncrementA  到方法
  },
};

pinia与vuex

pinia仓库支持Composition API 风格,更好的支持TS,不再有modules的嵌套结构,也没有命名空间的概念,不需要记住复杂的关系

js
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  function increment() {
    count.value++
  }
  return { count, increment }
})

路由的hash跟history模式

  • hash:兼容性好,url中带有#,可以监听hashchange事件来获取到新旧路由地址,e.newURL,e.oldURL
  • history:没有#,美观,但需要后端支持,url改变会触发popState事件

自定义指令控制图片加载失败时显示默认图片

自定义指令生命周期:

  • bind:在元素与指令绑定时执行,只执行一次。
  • inserted:在元素插入到 DOM 后执行。
  • update:在包含指令的元素更新时执行。
  • componentUpdated:在包含指令的元素及其子元素都更新后执行。

image-20231120152229542

图片懒加载

将图片真实地址存到data-src属性上 ==》 判断图片是否在可视区域 ==》 如果在,就设置图片src

  • 通过IntersectionObserver实例来检测元素是否进入视口,参数是一个函数,函数有两个参数分别是entries和接收IntersectionObserver的实例,entries是多个被观察者元素信息组合成的数组,然后对每一个被观察者信息进行遍历,如果进入视口,那么就将元素的src属性替换为data-src属性,并停止该元素的检测,避免重复加载
  • 先实例化IntersectionObserver,然后再调用observer.observe(el)传入绑定的元素 先声明再使用

image-20230903225849685

注:此处el.src=binding.value与el.src=el.dataset.src相等 因为使用的时候即设置了:data-src又给自定义指令传了值

插槽

  • 用于动态地传递和渲染内容。主要目的是允许你在一个组件中定义可替换的内容,并在组件实例化时动态填充这些内容。

image-20230901001638716

image-20230901001654630