【前端学习|面试题】Vue

1、谈一谈 nextTick 的原理

  Vue 中的 nextTick 方法实现了类似于 JavaScript 中的 nextTick 的异步任务调度机制,它用于在 DOM 更新之后执行回调函数。

  当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。

  nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。

示例

<script setup>
import { ref, nextTick } from 'vue'

const count = ref(0)

async function increment() {
  count.value++

  // DOM 还未更新
  console.log(document.getElementById('counter').textContent) // 0

  // 暂停代码执行,等待Promise
  await nextTick()
  // DOM 此时已经更新
  console.log(document.getElementById('counter').textContent) // 1
}
</script>

<template>
  <button id="counter" @click="increment">{{ count }}</button>
</template>

  通过使用 nextTick,Vue 可以在 DOM 更新之后执行回调函数,这对于在更新后操作 DOM、获取正确的计算属性值等场景非常有用。

  ⭐️需要注意的是,Vue 在不同环境下使用不同的异步任务调度方式,优先使用 Promise,然后是 MutationObserver,最后才是 setTimeout。这样做是为了尽可能地利用最高效和可靠的异步机制来实现 nextTick 的功能。

2、Vue 组件 data 为什么必须是函数?

  Vue 组件中的 data 选项为什么必须是一个函数,而不是一个普通的对象呢?

  这是因为 Vue 组件在创建多个实例时,如果直接使用一个对象作为 data 选项的值,那么所有实例将共享同一个 data 对象,这会导致状态的混乱和难以维护。

  通过将 data 选项定义为一个函数,Vue 能够为每个组件实例返回一个全新的数据对象,确保每个实例都拥有独立的数据状态。这样做的好处是,每个组件实例都可以独立地修改和管理自己的数据,而不会相互影响。

下面是一个示例代码,演示了为什么 data 必须是一个函数:

// 错误的写法,data 是一个对象
Vue.component('counter', {
  data: {
    count: 0
  },
  template: '<div>{{ count }}</div>'
});

// 正确的写法,data 是一个函数
Vue.component('counter', {
  data() {
    return {
      count: 0
    };
  },
  template: '<div>{{ count }}</div>'
});

  在以上示例中,第一个组件的 data 选项是一个对象,而第二个组件的 data 选项是一个函数。

  如果我们使用第一个组件创建多个实例,它们将共享同一个 count 属性,即使在不同的实例中修改 count 的值,最终都会相互影响。

  而如果我们使用第二个组件创建多个实例,每个实例都会拥有自己独立的 count 属性,它们之间互不影响。

  因此,为了保证组件实例之间的数据独立性和可维护性,Vue 要求我们将 data 选项定义为一个函数,每次创建组件实例时都会调用该函数返回一个新的数据对象。

  这样,每个组件实例都可以管理自己的数据,避免了数据共享和状态混乱的问题。

3、vm.$set() 实现原理是什么?

  vm.$set() 是 Vue.js 中用于在组件实例中添加响应式属性的方法。它的实现原理是通过调用 Vue 实例的内部方法 $set 来实现。

  Vue.js 使用了一种叫做"响应式系统"的机制来追踪数据的变化,并在数据发生改变时自动更新相关的视图。在创建 Vue 组件时,Vue 会对组件的数据对象进行劫持,使其成为响应式的。

  当我们使用 vm.$set() 方法向组件实例中添加新的属性时,它会做以下几个步骤:

  1. 首先,$set 方法会检查要添加的属性是否已经存在于组件实例的数据对象中。
  2. 如果属性已经存在,则直接更新属性的值,并触发相应的重新渲染。
  3. 如果属性不存在,则调用 Vue 内部的方法 defineReactive 来将新属性添加到组件实例的数据对象中,并将其设置为响应式的。
  4. 接下来,$set 方法会触发依赖收集,将新属性添加到依赖列表中,以便在属性值发生变化时能够通知相关的视图进行更新。

  通过使用 vm.$set() 方法,我们可以在运行时动态地添加响应式属性,使其能够与 Vue 的响应式系统进行交互,并实现自动更新视图的效果。

// 示例代码,展示了 vm.$set() 的使用
// 假设 vm 是 Vue 组件实例
vm.$set(vm.dataObject, 'newProperty', 'new value');

  上述代码将在 vm 组件实例的 dataObject 对象中添加一个名为 newProperty 的属性,并将其值设置为 'new value'。这个属性将成为响应式的,当其值发生变化时,相关的视图将自动更新。

4、什么是MVVM?

  MVVM(Model-View-ViewModel)是一种软件架构模式,用于开发用户界面(UI)的应用程序。它将应用程序的逻辑分为三个主要组件:模型(Model)、视图(View)和视图模型(ViewModel)。

  • 模型(Model)代表应用程序中的数据和业务逻辑。它是应用程序的数据源,负责处理数据的获取、存储和操作。
  • 视图(View)是用户界面的可见部分。它负责将数据呈现给用户,并接收用户的输入。
  • 视图模型(ViewModel)是连接模型和视图的中间层。它从模型中获取数据,并根据视图的需求对数据进行处理和转换,然后将处理后的数据提供给视图进行显示。视图模型还负责处理视图中的用户输入,并将其转换为模型可以理解的操作。

简单理解:
  M - Model——模型、数据
  V - View——视图、UI
  VM - ViewModel——用于实现双向数据绑定,即视图的改变,会自动反映到 ViewModel 上从而引起 Model 数据的变化,而 Model 数据的变化也会自动反映到 ViewModel 上从而引起 View 视图的重新渲染。

⭐️Vue 是一种实现了 MVVM 思想的框架

5、响应式数据原理(Vue2.x & Vue3.x)

  响应式数据是 Vue 的核心特性之一,它使得当数据状态发生改变时,相关的组件自动进行了重新渲染,从而使视图和模型保持同步。

  在 Vue 2.x 中,Vue 会将 data 中定义的属性添加到一个名为 Observer 的观察者实例中,当属性被访问时,会自动触发它的 get() 方法,将当前的 Watcher 实例添加进该属性的dep实例中(也称作订阅)。当属性被修改时,会触发 set() 方法,set() 中会通知订阅该属性的所有 Watcher 实例以及子组件的 Watcher 实例进行更新。

  而在 Vue 3.x 中,则使用了Proxy对象来进行响应式实现。当对象添加gettersetter,使用时就会被代理,对所有数据访问和修改进行拦截,从而实现自动更新。与 Observer 不同的是,Proxy 是由 JavaScript 引擎内置的功能,而不是通过定义 setter 和 getter 实现。

  总的来说,Vue 2.x 和 Vue 3.x 对于响应式数据的实现原理是有所不同的,但都是通过监听数据的变化,从而实现组件的自动重新渲染。

6、Proxy 与 Object.defineProperty 的优劣对比?

  Proxy 与 Object.defineProperty 在实现响应式数据方面有很大的不同。下面是它们的优缺点对比:

  • Proxy优点:

    • Proxy 可以直接监听对象而不是监听对象的属性,因此可以捕获更多操作,如对象的新增、删除等处理。

    • Proxy 可以代理多层嵌套的对象,不需要对每个内层对象分别设置代理。

    • Proxy 可以监听数组内元素的变化,而 Object.defineProperty 只能通过修改数组原型方法才能实现。

    • Proxy 拥有更多的拦截方法,比如 has、ownKeys、defineProperty、deleteProperty 等。

  • Proxy缺点:

    • Proxy 不兼容低版本浏览器,需要 polyfill 支持。

    • Proxy 不支持 Object 的一些方法,如 Object.keys、Object.values 和 Object.entries 等。

  • Object.defineProperty优点:

    • 兼容性好,支持绝大多数的浏览器。

    • 适合小型项目,比如单页应用。

    • 可以控制属性的读写行为。

  • Object.defineProperty缺点:

    • 只能监听数据的属性变化,而且只能监听已经存在的属性,对于新增或删除的属性无能为力。

    • 对于对象上多层嵌套的属性,需要递归遍历每一个属性并设置 getter 和 setter,比较麻烦。

    • 只能监听数组的变化方法,对数组内部元素的变化需要使用额外的处理方式,比如 Vue 中的 $set。

  综上所述,使用 Proxy 进行数据劫持能够更加自然和高效地实现响应式数据,但缺点是兼容性问题。而 Object.defineProperty 兼容性好,但无法监听对象新增、删除等操作,对于大型复杂项目的数据劫持会有很多的繁琐操作。

7、Vue中组件通信的方式有哪些?

  • Props和事件:通过使用props将数据从父组件传递给子组件,子组件可以通过事件将数据传递回父组件。这是一种单向数据流的方式,适用于父子组件之间的通信。
  • 自定义事件:父组件可以使用$emit方法触发自定义事件,并在子组件中使用$on方法监听这些事件。这样可以在兄弟组件之间进行通信。
  • 事件总线:可以创建一个空的Vue实例作为事件总线,用于在任何组件之间进行通信。可以通过在组件中触发和监听事件来实现通信。
  • Vuex:Vuex是Vue的官方状态管理库,用于管理应用程序的状态。它提供了一个全局的状态存储,可以在不同组件之间共享数据。通过在组件中使用this.$store访问存储的数据,以及使用mutations和actions来修改数据,可以实现组件之间的通信。
  • $refs:通过在组件上使用ref属性,可以在父组件中直接访问子组件实例。这样可以通过子组件的方法和属性进行通信。
  • Provide 和 Inject: 使用provideinject选项可以在祖先组件和后代组件之间建立依赖注入关系,从而共享数据。

8、虚拟DOM

虚拟DOM(Virtual DOM)是一种用于优化前端框架性能的技术,最初由React引入,后来也在其他前端框架中得到广泛应用,包括Vue.js。虚拟DOM是一个抽象的内存数据结构,它代表了真实DOM树的层次结构。

以下是虚拟DOM的工作原理:

  1. 虚拟DOM树: 当页面渲染时,框架会首先创建一个虚拟DOM树,这个树的结构和内容与实际的DOM树一一对应。但虚拟DOM树只存在于内存中。

  2. 状态变化检测: 当应用状态发生变化(如用户交互或数据更新)时,框架会重新生成一个新的虚拟DOM树,表示新的UI状态。

  3. 虚拟DOM比较: 接下来,框架会将新旧虚拟DOM树进行比较,找出两者之间的差异,即哪些部分需要更新。

  4. 差异更新: 框架只会操作实际DOM树中需要更新的部分,而不是整个DOM树。这可以大大减少DOM操作的成本。

  5. 实际DOM更新: 最后,框架将差异应用到实际的DOM树中,使界面与新的状态保持一致。

虚拟DOM的优势包括:

  1. 性能优化: 通过减少实际DOM操作的次数,虚拟DOM可以提高页面渲染性能,减少重绘和重排的开销。

  2. 跨平台: 虚拟DOM不依赖于浏览器特定的API,因此可以在不同平台上使用,如浏览器、服务器和移动应用。

  3. 方便的抽象: 虚拟DOM提供了一种抽象层,使开发人员更容易管理和更新UI状态。

  ⭐️需要注意的是,虚拟DOM并不是适用于所有情况的解决方案。在大多数小型应用中,直接操作实际DOM可能更加高效,因为虚拟DOM本身也会引入一些开销。虚拟DOM的主要价值体现在大规模和复杂的应用中,特别是需要频繁UI更新的情况下,它可以显著提高性能。

9、v-model双向数据绑定原理

  v-model 是Vue.js中用于实现双向数据绑定的指令。它的原理涉及两个主要概念:数据绑定事件监听

以下是 v-model 的原理:

  1. 数据绑定: 当你在Vue.js中使用 v-model 指令时,它会在表单元素(如 <input><textarea><select>)上创建一个双向数据绑定。这意味着,表单元素的值将与Vue组件的数据属性双向同步。

  2. 值的初始化: 当组件首次加载时,v-model 会将表单元素的值初始化为Vue组件数据属性的值。这确保了表单元素和组件数据的初始值一致。

  3. 数据到视图的同步: 当Vue组件数据属性的值发生变化时,v-model 会自动将新的值同步到绑定的表单元素。这使得当你修改组件数据时,表单元素会自动更新以反映新值。

  4. 视图到数据的同步: 当用户在表单元素中输入内容时,v-model 会监听元素上的输入事件(如 inputchange),并将输入的值自动更新到Vue组件的数据属性。这允许用户在表单元素中输入内容,而组件的数据也会跟着更新。

  简而言之,v-model 建立了一个双向通道,使数据和视图之间的同步变得非常容易。这消除了手动更新数据和视图之间的差异的需要,简化了Vue.js应用程序的开发过程。

以下是一个示例,演示了 v-model 的工作原理:

<template>
  <div>
    <input v-model="message" type="text">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "Hello, Vue.js"
    };
  }
};
</script>

  在这个示例中,v-model 绑定了 <input> 元素和 message 数据属性,从而实现了双向数据绑定。当用户在输入框中输入内容时,message 的值会自动更新,反之亦然。

10、为什么v-for的key尽量不要使用index?

  v-for 中的 key 属性用于帮助Vue.js跟踪节点的标识,以便更高效地更新DOM。尽量避免使用索引作为 key 的原因主要有以下几点:

  1. 不稳定的索引: 数组的索引是基于位置的,当你向数组中插入或删除元素时,索引会发生变化。这意味着如果你将索引作为 key,在数据变化时,可能会导致节点的 key 发生变化,从而触发不必要的DOM更新或重新渲染,这可能会导致性能问题。

  2. 可能导致渲染问题: 如果使用索引作为 key,Vue.js 可能会错误地假设两个具有相同索引的元素具有相同的身份。这可能导致渲染问题,特别是在列表中有重复的值时,Vue.js 可能会混淆元素的身份。

  3. 难以维护和调试: 使用索引作为 key 可能会使代码更难以维护和调试,因为在数据变化时,你需要非常小心地处理 key 变化可能引发的问题。

  代替使用索引作为 key,建议使用具有稳定唯一标识的属性或字段作为 key。这可以是一个具有唯一ID的对象属性,或者是数据集合中的其他稳定属性。这样可以确保在数据变化时,每个元素都能够保持相同的 key,从而更好地实现DOM元素的重用和更新。

  总之,避免使用索引作为 key 可以帮助避免一系列潜在的问题,使你的Vue.js应用更加可靠和高效。

11、vue能监听到数组变化的方法有哪些?为什么这些方法能监听到呢?

Vue.js 提供了多种方式来监听数组的变化,使数组的变动可以触发响应式更新。这些方法包括:

  1. 使用数组变异方法(Mutation Methods): 这些方法包括 push()pop()shift()unshift()splice()sort()reverse(),它们能够监听数组的变化是因为 Vue.js 在内部重写了这些方法,添加了响应式的逻辑。当你使用这些方法修改数组时,Vue.js能够检测到变化并触发相应的视图更新。

  2. 使用非变异方法(Non-Mutation Methods): 这些方法包括 filter()concat()slice(),它们并不直接修改原始数组,而是返回一个新的数组,也能够触发响应式更新。Vue.js通过拦截这些方法的调用,使其返回的新数组具有响应式特性。

  3. 使用 vm.$setVue.set 这些方法允许你在数组中的指定位置添加新元素,并触发响应式更新。它们通过将新元素变成响应式对象来实现。

  4. 使用 vm.$forceUpdate 这个方法可以强制重新渲染组件,包括数组的变化。尽管这种方式不是自动的响应式,但它可以用于手动触发更新。

  5. 使用 array.length 直接赋值: 直接修改数组的 length 属性也能触发响应式更新。

  这些方法能够监听数组的变化,是因为 Vue.js 在内部使用了双向数据绑定和响应式数据系统。当数组发生变化时,Vue.js能够检测到这些变化,然后触发相关组件的重新渲染,确保视图与数据的同步。

  ⭐️需要注意的是,如果你使用第三方库或纯JavaScript的方式修改数组,Vue.js可能无法自动检测到变化。在这种情况下,你可以使用 vm.$setVue.set 来通知 Vue.js 响应式系统,或者使用 vm.$forceUpdate 手动触发更新。但最好的做法是尽量使用Vue.js提供的数组方法来修改数组,以确保响应式更新的正确性。

12、v-if和v-for嵌套使用问题

(以下内容来自Vue官网文档

  与Vue2相反的是,在Vue3中,当同时使用时,v-ifv-for 优先级更高。所以并不推荐在一个元素上同时使用这两个指令,

  当它们同时存在于一个节点上时,v-ifv-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名:

<!--
 这会抛出一个错误,因为属性 todo 此时
 没有在该实例上定义
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

在外新包装一层 `<template>` 再在其上使用 `v-for` 可以解决这个问题 (这也更加明显易读):

<template v-for="todo in todos">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

13、Vue的父组件和子组件生命周期钩子执行顺序是什么?

Vue的父组件和子组件生命周期钩子的执行顺序如下:

  1. 父组件的beforeCreate钩子
  2. 父组件的created钩子
  3. 父组件的beforeMount钩子
  4. 子组件的beforeCreate钩子
  5. 子组件的created钩子
  6. 子组件的beforeMount钩子
  7. 子组件的mounted钩子
  8. 父组件的mounted钩子

  在父组件和子组件的生命周期钩子执行过程中,父组件的钩子先于子组件的钩子执行。

14、v-show 和 v-if 有哪些区别?

v-show 和 v-if 是 Vue.js 中用于条件渲染的指令,它们有以下区别:

  1. v-show:v-show 是通过控制元素的 display 样式来决定是否显示元素。当条件为真时,元素会被显示;当条件为假时,元素会被隐藏,但仍然占据着 DOM 空间。
  2. v-if:v-if 是通过添加或移除元素来决定是否显示元素。当条件为真时,元素会被添加到 DOM 中;当条件为假时,元素会被从 DOM 中移除。v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。v-if 也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
  3. 渲染开销v-show渲染开销较大,因为元素始终存在于 DOM 中,只是通过修改 display 样式来控制显示与隐藏;而 v-if渲染开销较小,因为元素的创建和销毁是动态的。
  4. 切换开销v-show切换开销较小,因为元素始终存在于 DOM 中,只是修改 display 样式;而 v-if切换开销较大,因为元素的创建和销毁会触发额外的性能开销。

  综上所述,如果需要频繁切换元素的显示与隐藏,且元素的数量较少,可以使用 v-show;如果需要根据条件动态地添加或移除元素,或者元素的数量较多,可以使用 v-if。

15、vue-router的 hash 模式和 history 模式是如何实现的?

vue-router 的 hash 模式和 history 模式是通过浏览器的 API 实现的。

  • hash 模式:使用浏览器的 window.location.hash 属性来实现。在 hash 模式下,URL 中的路径后面会有一个 # 符号,例如:http://example.com/#/home。当路由发生变化时,会修改 window.location.hash 的值,然后通过监听 hashchange 事件来响应路由的变化。

  • history 模式:使用浏览器的 history.pushStatehistory.replaceState 方法来实现。在 history 模式下,URL 中的路径没有 # 符号,例如:http://example.com/home。当路由发生变化时,会调用 history.pushStatehistory.replaceState 方法来修改 URL,并通过监听 popstate 事件来响应路由的变化。

  ⭐️需要注意的是,使用 history 模式需要服务器的支持,因为在 history 模式下,刷新页面或直接访问某个路由时,需要服务器返回对应的页面,否则会出现 404 错误。

16、Object.defineProperty有哪些缺点?

  1. 无法监听数组的变化:Object.defineProperty 只能监听对象的属性变化,无法监听数组的变化。因此,当数组的 length 属性发生变化或直接修改数组的索引或通过数组的方法(如push、pop等)修改数组时,无法触发属性的getter和setter。

  2. 无法监听新增属性和删除属性:使用 Object.defineProperty 定义的属性是静态的,无法监听新增属性和删除属性的变化。

  3. 无法深度监听:Object.defineProperty 只能监听对象的属性变化,无法监听对象内部属性的变化。如果对象的属性值是对象,需要递归遍历对象的属性才能实现深度监听。

  4. 重复代码:使用 Object.defineProperty 需要编写大量的重复代码来定义每个属性的 get 和 set 方法,增加了代码量和维护成本。

17、你都做过Vue哪些性能优化?

  Vue.js 提供了一些性能优化的最佳实践和工具,以确保你的应用在大规模数据和复杂组件情况下能够高效运行。以下是一些Vue.js性能优化的常见方法:

  1. 使用响应式数据: Vue.js的响应式系统能够有效地追踪数据的变化,从而只更新必要的DOM元素。确保你的数据是响应式的,这将减少不必要的DOM操作。

  2. 使用v-bind:key 在使用v-for迭代渲染元素时,务必提供唯一的key属性,以帮助Vue.js更好地管理DOM元素,减少不必要的重新渲染。

  3. 懒加载组件: 使用Vue.js的异步组件加载功能,以减小初始加载时的包大小。这样只有在需要时才会下载和渲染组件。

  4. 使用v-ifv-else代替v-show 如果某个元素在大多数情况下是隐藏的,使用v-ifv-else而不是v-show,因为v-show只是使用CSS来隐藏元素,而v-if会在条件不满足时完全移除元素。

  5. 避免过多的计算属性: 计算属性是惰性的,但过多的计算属性可能会导致性能问题。确保只计算必要的数据,并避免复杂的计算属性。

  6. 使用v-cloak 如果你的模板在加载时短暂地显示出未编译的Vue.js模板标记,你可以使用v-cloak来防止这种情况。

  7. 使用<keep-alive> 如果你有频繁切换的组件,可以使用<keep-alive>来缓存这些组件,以避免不必要的销毁和重新创建。

  8. 异步更新: 使用this.$nextTick来在DOM更新后执行操作,以确保在正确的时机访问DOM。

  9. 使用Vue Devtools: Vue Devtools是一个强大的工具,可以帮助你分析和优化你的Vue.js应用,包括组件性能和数据流程的可视化分析。

  这些是一些常见的Vue.js性能优化方法,但实际的优化策略可能因应用的具体情况而有所不同。在进行性能优化时,最重要的是使用工具来分析你的应用并识别性能瓶颈,然后有针对性地采取措施来解决问题。

18、Vue修饰符有哪些?

  • 事件修饰符

    • .stop:阻止事件冒泡

    • .prevent:阻止默认事件的触发

    • .self:只有事件在目标元素本身触发时才会触发事件处理函数,不包括子元素

    • .capture:使用事件捕获模式,即从外部元素到目标元素的事件触发顺序

    • .once:事件只触发一次,之后会自动解绑

      … …

  • 按键修饰符

    • .enter:监听回车键(Enter)。
    • .tab:监听 Tab 键。
    • .delete :监听删除键或退格键。
    • .esc:监听 Esc 键。
    • .space:监听空格键。
    • .up:监听上箭头键。
    • .down:监听下箭头键。
    • .left:监听左箭头键。
    • .right:监听右箭头键。
    • .ctrl:监听 Ctrl 键。
    • .alt:监听 Alt 键。
    • .shift:监听 Shift 键。
    • .meta:监听 Meta 键(Windows 键或 Command 键)。
    • 鼠标按键修饰符
      • .left:左键
      • .right:右键
      • .middle:中键
  • .exact 修饰符

    • .exact 修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符。
  • v-model 修饰符

    • .lazy:监听change事件
    • .trim:自动去除用户输入内容中两端的空格
    • .number:输入自动转换为数字(通过parseFloat()处理)
    • 自定义修饰符

19、你有写过自定义指令吗?自定义指令的生命周期(钩子函数)有哪些?

  一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。下面是一个自定义指令的例子,当一个 input 元素被 Vue 插入到 DOM 中后,它会被自动聚焦:

const focus = {
  mounted: (el) => el.focus()
}

export default {
  directives: {
    // 在模板中启用 v-focus
    focus
  }
}
<input v-focus />

自定义指令的生命周期:

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

20、如何让样式当前有效? 如何修改第三方组件样式?

在 Vue 中,可以使用以下方法来使样式当前有效:

  • 在style标签中添加scoped属性

    <style scoped>
     	 /* 修改第三方组件的样式 */
    </style>
    
  • 使用内联样式:可以在 HTML 元素上使用 style 属性来直接设置样式。例如:

    <div style="color: red;">这是红色文字</div>
    
  • 使用动态绑定的类名:可以通过绑定一个动态的类名来改变元素的样式。可以使用 v-bind 或简写的 : 来绑定一个包含类名的表达式。例如:

    <div :class="{ 'red-text': isRed }">这是红色文字</div>
    

      在上述示例中,isRed 是一个在 Vue 实例中定义的变量,如果它的值为 true,则会将类名 red-text 应用到该元素上。

  • 使用计算属性:可以在计算属性中返回一个对象,对象的属性可以作为类名绑定到 HTML 元素上。例如:

    <div :class="textClass">这是红色文字</div>
    
    new Vue({
      data: {
        isRed: true
      },
      computed: {
        textClass: function() {
          return {
            'red-text': this.isRed
          }
        }
      }
    })
    

      在上述示例中,textClass 是一个计算属性,它会根据 isRed 的值返回一个对象,该对象的属性 red-text 会作为类名绑定到元素上。

关于修改第三方组件样式,有以下几种方法:

  • 使用全局样式:可以在全局样式中修改第三方组件的样式。在 Vue 项目中,可以在 App.vuemain.js 中引入全局样式文件,并在其中修改第三方组件的样式。

  • 使用组件级样式:可以在组件的样式中修改第三方组件的样式。在组件的样式中,可以使用选择器选择第三方组件,并修改其样式。

  • 使用深度选择器:如果第三方组件的样式被其他样式覆盖,可以使用深度选择器 >>>/deep/ 来穿透组件样式的作用域,直接修改第三方组件的样式。例如:

    <style scoped>
    	.my-component >>> .third-party-component {
     	 	/* 修改第三方组件的样式 */
    	}
        :deep(.van-search__action) {
        	/* 修改第三方组件的样式 */
    	}
    </style>
    
  • 使用自定义类名或样式绑定:有些第三方组件库提供了自定义类名或样式绑定的API,你可以使用它们来修改组件的样式。具体的方法取决于所使用的组件库和文档。你可以查阅相关文档以了解如何修改特定组件的样式。

  ⭐️请注意,使用深度选择器可能会导致样式的全局污染,因此应谨慎使用。如果第三方组件提供了自定义样式的选项,最好使用它们提供的方式来修改样式,以避免全局样式冲突的问题。

21、vue.$set (obj, key, value) 方法的实现原理

vue.$set(obj, key, value) 方法的实现原理是通过调用 Vue 实例的 $set 方法来实现。

  1. 在 Vue 内部,$set 方法会首先判断对象 obj 是否是一个响应式对象,如果是,则直接调用内部的setter方法进行赋值。如果不是响应式对象,则会判断 obj 是否是一个数组,如果是数组,则调用数组的 splice 方法进行赋值。如果既不是响应式对象也不是数组,则会将 key 和 value 作为属性和值添加到 obj 上。
  2. 在执行赋值操作后,$set 方法会判断 obj 是否是一个响应式对象,如果是,则会调用内部的 observer 对新添加的属性进行监听。如果不是响应式对象,则会调用 Vue 的 defineReactive 方法将新添加的属性转换为响应式属性。

  总结来说,vue.$set(obj, key, value) 方法的实现原理是通过检查对象是否为响应式对象,然后根据 key 的存在与否来更新或添加相应的属性,并确保这些属性是响应式的,以便 Vue.js 可以追踪其变化并更新视图。

22、Vue-Router 的导航守卫和执行流程

导航守卫:

  1. 全局前置守卫beforeEach):在路由切换开始之前调用,可以用来进行全局的权限验证或者其他操作。如果有多个全局前置守卫,它们将按照注册顺序依次执行。
  2. 路由独享守卫beforeEnter):在路由配置中通过 beforeEnter 字段定义,只对当前路由生效。它们会在全局前置守卫之后调用。
  3. 组件内守卫beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave):在组件内部定义的守卫。beforeRouteEnter 在路由进入组件之前调用,beforeRouteUpdate 在组件复用时调用,beforeRouteLeave 在路由离开组件时调用。
  4. 全局解析守卫beforeResolve):在所有全局前置守卫和路由独享守卫之后调用。它们会在异步路由组件被解析之后调用。
  5. 全局后置守卫afterEach):在路由切换完成之后调用,可以用来进行一些清理操作或者其他操作。如果有多个全局后置守卫,它们将按照注册顺序依次执行。

执行流程:

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  5. 在路由配置里调用 beforeEnter(路由独享的守卫)。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫(2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

23、Vue-Router 中的路由模式和区别

Vue-Router 中的路由模式有两种:哈希模式(Hash Mode)和历史模式(History Mode)。

  1. 哈希模式(Hash Mode)是 Vue-Router 默认的路由模式。在哈希模式下,URL 中的路由会被格式化为 /#/ 开头的带有哈希值的 URL。例如,http://example.com/#/home。哈希值的改变不会导致浏览器向服务器发送请求,而是通过监听 hashchange 事件来触发路由的切换。哈希模式的优点是兼容性好,可以在不支持 HTML5 History API 的浏览器中正常工作。

  2. 历史模式(History Mode)使用 HTML5 History API 来管理路由。在历史模式下,URL 中的路由不再带有哈希值,而是使用真实的 URL 地址,例如 http://example.com/home。为了使历史模式在单页应用中正常工作,需要在服务器上进行相应的配置,以确保在用户访问直接路由时返回正确的页面。历史模式的优点是 URL 更加美观,没有哈希值的干扰。

区别:

  1. 哈希模式不需要服务器配置,可以直接在前端使用,而历史模式需要服务器配置支持。
  2. 哈希模式的 URL 中包含 # 符号,不太美观,而历史模式的 URL 更加直观。
  3. 哈希模式的路由不会触发页面刷新,而历史模式会触发页面刷新。
  4. 哈希模式可以兼容老版本浏览器,而历史模式需要浏览器支持 HTML5 的 history API。

24、Vuex 的理解

  Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以一种可预测的方式进行状态的修改和响应。Vuex 的核心概念包括 state、mutations、actions、getters 和 modules。

  1. State(状态):Vuex 使用单一状态树(Single State Tree)来存储应用的所有状态,即将所有组件的状态集中存储在一个可响应的对象中。State 是唯一的,可以通过 this.$store.state 来访问。

  2. Mutations(变更):Mutations 用于修改 State 中的状态。它是一个同步的操作,通过提交(commit)Mutations 来修改状态。Mutations 接收两个参数:state(当前状态)和 payload(载荷,即传递的参数),每个 Mutation 都有一个字符串类型的事件类型和一个回调函数,用于实际修改状态的逻辑,通过 this.$store.commit('mutationName', payload) 来提交 Mutations。

  3. Actions(动作):Actions 用于处理异步操作或者批量的 Mutations。它可以包含任意异步操作,通过提交(dispatch)Actions 来触发。Actions 接收一个 context 对象,包含了与 Mutations 相同的方法和属性,可以通过 this.$store.dispatch('actionName', payload) 来触发 Actions。

  4. Getters(计算属性):Getters 用于对 State 中的状态进行计算和派生。它类似于 Vue 组件中的计算属性,可以根据 State 中的状态进行一些复杂的计算,并返回结果。Getters 可以通过 this.$store.getters.getterName 来获取。

  5. Modules(模块):Modules 允许将 Store 分割成多个模块,每个模块拥有自己的 state、mutations、actions、getters。模块内部的状态和操作可以通过命名空间来隔离和访问。

  通过使用 Vuex,我们可以将应用的状态集中管理,方便状态的共享修改,使得应用的状态变得可追踪、可维护,并且能够更好地处理复杂的状态逻辑。

25、你有对 Vue 项目进行哪些优化?

  1. 代码压缩:可以使用工具(例如UglifyJS)对Vue项目的代码进行压缩和混淆,以减小文件大小和保护代码逻辑。

  2. 懒加载:对于大型的Vue项目,可以将路由和组件进行懒加载,只在需要时才加载相应的代码和资源,以减少初始加载时间和减轻服务器负载。

  3. 图片优化:优化项目中的图片资源,包括压缩图片大小、使用适当的格式(如WebP)使用精灵图和使用图片CDN等,以减少页面加载时间。

  4. 异步组件:将一些耗时的组件拆分为异步组件,并使用Vue的异步组件加载机制,以提高页面的响应速度。

  5. 缓存优化:使用合适的缓存策略,例如使用浏览器缓存、CDN缓存或服务器端缓存,以减少请求次数和提高页面加载速度。

  6. 代码分割:将Vue项目的代码分割成多个小块,按需加载,以提高页面的首次加载速度。

  7. 减少重绘和回流:通过合理的CSS布局和避免频繁的DOM操作,减少浏览器的重绘(Repaint)和回流(Reflow)操作,以提高页面性能。

26、虚拟DOM的优劣如何?

  虚拟DOM(Virtual DOM)是一种在前端开发中常见的技术概念,它的优劣取决于具体的使用场景和需求。

优点:

  1. 性能优化:虚拟DOM可以通过批量更新和高效的DOM diff算法,减少真实DOM操作的次数,从而提高页面的性能和响应速度。
  2. 跨平台兼容:虚拟DOM可以在不同的平台上运行,例如浏览器、移动端和服务器端。这种跨平台的能力使得开发者可以共享大部分代码,并提高开发效率。
  3. 简化复杂性:虚拟DOM提供了一种抽象层,使得开发者可以以声明性的方式描述用户界面的状态和交互,而无需直接操作底层的DOM API,从而简化了开发过程。
  4. 组件化开发:虚拟DOM与组件化开发相结合,可以提供更高层次的抽象和复用性,使得开发者可以轻松构建和维护复杂的用户界面。

缺点:

  1. 内存消耗:虚拟DOM需要在内存中维护一份虚拟DOM树的副本,这可能会导致内存消耗较大,尤其是对于大型的应用程序或页面。
  2. 初始化耗时:虚拟DOM在初始化时需要构建整个虚拟DOM树,并进行初始渲染,这可能会导致一定的初始化耗时。
  3. 学习成本:虚拟DOM需要开发者掌握额外的概念和技术,例如虚拟DOM的 diff算法和更新策略,这可能对于新手来说存在一定的学习曲线。

  综合来说,虚拟DOM在大多数情况下是有益的,它可以提供性能优化、跨平台兼容、简化复杂性和组件化开发等优势。然而,在特定的场景下,如对于简单的应用程序或性能要求极高的场景,直接操作真实的DOM可能更加高效。

27、Vuex和Pinia的区别

Vuex 和 Pinia 都是用于 Vue.js 应用程序的状态管理库,它们有以下几个区别:

  1. API 设计:Vuex 使用对象风格的 API,通过创建一个包含状态、mutations、actions 和 getters 的单一 Store 对象来管理应用的状态。而 Pinia 使用类风格的 API,通过创建一个继承自 Pinia 类的 Store 类来管理状态。

  2. TypeScript 支持:Pinia 是为 TypeScript 设计的,它提供了完整的类型支持,并且能够自动推断状态和操作的类型。而 Vuex 的 TypeScript 支持相对较弱,需要手动添加类型声明。

  3. 性能优化:Pinia 采用了更加现代的响应式机制,使用 Proxy 对象来实现状态的监听和更新,相比于 Vuex 的基于 Object.defineProperty 的实现方式,性能更好。Pinia 还支持局部状态,可以将状态和操作定义在组件内部,避免不必要的全局状态更新。

  4. 插件生态系统:Vuex 有一个丰富的插件生态系统,可以通过插件来扩展 Vuex 的功能,例如调试工具、持久化存储等。而 Pinia 的插件生态系统相对较小,目前的插件支持相对有限。

  5. 生态和社区支持:Vuex 是 Vue.js 官方推荐的状态管理库,拥有庞大的社区和生态系统,有很多第三方插件和工具可供选择。而 Pinia 是一个相对较新的库,生态和社区支持相对较小,插件和工具的选择相对有限。

  总的来说,Vuex 是一个成熟、稳定且功能丰富的状态管理库,适用于大多数Vue项目、大型应用和复杂的状态管理需求。而Pinia则是一个新的、基于Vue 3的状态管理库,Pinia 更加现代化和轻量,提供了更简洁和直观的API设计,并在性能方面进行了优化,适合于 TypeScript 项目和对性能有较高要求的应用。


Vue就先这一期啦!☀️☀️,下期更新React❕ ❕ ❕
个人收集整理、创作不易, 若有帮助🉑, 请帮忙点赞👍➕收藏❤️, 谢谢!✨✨🚀🚀

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FLechazo~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值