2024年前端最新面试题-vue3(完结于24年8月13)

前言

Vue 官推消息,Vue 2 已于 2023年12月31日 停止维护(EOL)。

Vue 2.0 发布于 2016 年,迄今为止已有7年时间。Vue 2.0 的发布是其成为主流框架过程中的一个重要里程碑。

然而随着 2020 年 9 月份发布的 Vue 3 及其生态系统的日渐成熟,Vue 2.0 也渐渐退出了我们的视线,众多开发者们也在逐渐向 Vue3 靠拢。

早在2023年初,众多公司的面试重点就已经开始向Vue3倾斜。

2024有跳槽打算的朋友,拿下Vue3是重中之重。

正文

什么是 MVVC

  • MVC全名是 Model View Controller,时模型 - 视图 - 控制器的缩写,一种软件设计典范。
    • Model(模型):是用于处理应用程序数据逻辑部分。通常模型对象负责在数据库中存取数据。
    • View(视图):是应用程序中处理数据显示的本分。通常视图是依据模型数据创建的。
    • Controller(控制器):是应用程序处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

image.png

  • MVC的思想:一句话描述就是Controller负责将Model的数据用View显示出来,换句话说就是在Controller里面把Model的数据赋值给View。

什么是 MVVM

  • MVVM新增了VM类。
    • ViewModel层:做了两件事达到了数据的双向绑定,一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。 实现的方式时:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转换成后端的数据。实现的方式是:DOM事件监听。

image.png

  • MVVM与MVC最大的区别就是:实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再手动操作Dom元素来改变View的显示。 而是改变属性后该属性对应的View层显示会自动改变(对应Vue数据驱动的思想)
  • 整体看来,MVVM比MVC精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作DOM元素。因为在MVVM中,View不知道Model的存在,Model和ViewModel也察觉不到View,这种低耦合模式提高代码的可重用性。
  • 注意:Vue并没有完全遵循MVVM的思想,这一点官网自己也有声明。

image.png

  • 那么问题来了,为什么官方要说Vue没有完全遵循MVVM思想呢?
  • 严格的MVVVM要求View不能和Model直接通信,而Vue提供了$refs这个属性,让Model可以直接操作View,违反了这一规定,所以是Vue没有完全遵循MVVM。

什么是 SPA

  • 单页面应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个 HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。
  • 一些应用在前端需要具有丰富的交互性、较深的会话和复杂的状态逻辑。Vue 不仅控制整个页面,还负责处理抓取新数据,并在无需重新加载的前提下处理页面切换。

什么是SFC

  • Vue 的单文件组件 (即 *.vue 文件,英文 Single-File Component,简称 SFC) 是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板<template>、逻辑<script>与样式<style>封装在单个文件中。
  • 使用 SFC 的优点:
    1. 使用熟悉的 HTML、CSS 和 JavaScript 语法编写模块化的组件
    2. 让本来就强相关的关注点自然内聚
    3. 预编译模板,避免运行时的编译开销
    4. 组件作用域的 CSS
    5. 在使用组合式 API 时语法更简单
    6. 通过交叉分析模板和逻辑代码能进行更多编译时优化
    7. 更好的 IDE 支持,提供自动补全和对模板中表达式的类型检查
    8. 开箱即用的模块热更新 (HMR) 支持
  • SFC 的主要场景:
    1. 单页面应用 (SPA)
    2. 静态站点生成 (SSG)
    3. 任何值得引入构建步骤以获得更好的开发体验 (DX) 的项目

为什么 data 选项是一个函数

  • 组件的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一分新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
  • 而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。

Vue 组件通讯(传值)有哪些方式

  • 组件通信常用方式有以下几种
    1. props / $emit:适用 父子组件通信
      • 父组件向子组件传递数据是通过 prop 传递
      • 子组件传递数据给父组件是通过 $emit 触发事件来做到
    2. ref 与 $parent:适用 父子组件通信
      • ref,如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素
      • ref,如果用在子组件上,引用就指向组件实例
      • $parent:访问访问父组件的属性或方法
    3. EventBus ($emit / $on):适用于 父子、隔代、兄弟组件通信
    4. $attrs:适用于 隔代组件通信
      • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。
      • 当一个组件没有声明任何 prop时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind=“$attrs” 传入内部组件。
    5. provide / inject:适用于 隔代组件通信
      • 祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。
      • provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系
    6. $root:适用于 隔代组件通信 访问根组件中的属性或方法,是根组件,不是父组件。
    7. Vuex 或 Pinia:适用于 父子、隔代、兄弟组件通信
  • 根据组件之间关系讨论组件通信最为清晰有效
    1. 父子组件:props / $emit / $parent / ref
    2. 兄弟组件:$parent / eventbus / vuex
    3. 跨层级关系:eventbus / vuex / provide+inject / $attrs / $root

Vue 的生命周期方法有哪些

  1. beforeCreate 在组件实例初始化完成之后立即调用。
    • 会在实例初始化完成、props 解析之后、data() 和 computed 等选项处理之前立即调用。
    • 在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问。
  2. created 在组件实例处理完所有与状态相关的选项后调用。
    • 在这一步,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。
    • 然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。
  3. beforeMount 在组件被挂载之前调用。
    • 组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。
    • 它即将首次执行 DOM 渲染过程。
  4. mounted 在组件被挂载之后调用。
    • 在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom节点。
  5. beforeUpdate 在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用。
    • 这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。
  6. updated 在组件因为一个响应式状态变更而更新其 DOM 树之后调用。
    • 父组件的更新钩子将在其子组件的更新钩子之后调用。
    • 这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的。
    • 如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。
  7. **beforeUnmount **实例被卸载之前调用。
    • 在这一步,实例仍然完全可用。
    • 我们可以在这时进行 善后收尾工作,比如清除定时器。
  8. unmounted 实例卸载后调用。
    • 调用后,Vue实例指示的东西都会卸载,所有的事件监听器会被移除,所有的子实例也会被卸载。
  9. activated 若组件实例是<KeepAlive>缓存树的一部分,当组件被插入到 DOM 中时调用。
  10. deactivated 若组件实例是<KeepAlive>缓存树的一部分,当组件从 DOM 中被移除时调用。

异步请求在哪一步发起?

  • 可以在钩子函数 created、beforeMount、mounted 中进行异步请求,因为在这三个钩子函数中,data已经创建,可以将服务器端返回的数据进行赋值。
  • 如果异步请求不需要依赖 DOM,推荐加载 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
    • 能更快获取到服务端数据,减少页面loading时间;
    • ssr 不支持 beforeMount、mounted 钩子函数,所以放在 created 中有助于一致性。

如何理解 Vue 的单项数据流

  • 数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样可以防止从子组件意外改变父组件的状态,从而导致你的应用的数据流向难以理解。
  • 注意:在子组件直接用 v-model 绑定父组件传过来的 props 这样是不规范的写法,开发环境会报警告。
  • 如果实在要改变父组件的 props 值可以再data里面定义一个变量,并用 prop 的值初始化它,之后用$emit 通知父组件去修改。

如何理解 Vue 的双向数据绑定

  • Vue.js 3中的双向数据绑定可以通过使用v-model指令来实现。在Vue.js 2及之前版本中,我们需要手动编写事件处理函数来更新视图或模型,而在Vue.js 3中,这些工作已经被自动化了。
  • 当我们将v-model应用于表单元素(如输入框、复选框等)时,Vue会为该元素创建一个名为"value"的属性,并且还会为其添加相关的事件监听器。
  • 当用户与表单交互时,比如修改文本内容或切换复选框状态,Vue会自动触发对应的事件,然后更新绑定到该元素上的数据。同样地,当我们在JavaScript中直接修改绑定的数据时,也能立即反映在视图上。
  • 双向数据绑定的原理,参考下一题:Vue3响应式原理

Vue3的响应式原理是什么

  • vue3采用数据代理+数据劫持+发布订阅模式的方法。
  • 在初始化vue实例时,通过Proxy进行数据代理,用Proxy对象来代理目标对象,并且对目标对象中的所有属性动态地进行数据劫持,动态地指定一个getter、setter,并通过Reflect操作对象内部数据。
  • 当Proxy对象属性或Proxy数组元素发生变化时,会触发Proxy属性的setter方法,然后通过Reflect操作目标对象属性,同时触发它Dep实例的notify方法进行依赖分发,通知所有依赖的Watcher实例执行内部回调函数

介绍一下 Vue 的虚拟 DOM

  1. 虚拟dom顾名思义就是虚拟的dom对象,它本身就是一个JavaScript对象,只不过它是通过不同的属性去描述一个视图结构,相比于真实dom只保留了核心属性,进而使后续操作更加快速;
  2. 通过引入vdom我们可以获得如下好处:
    • 将真实元素节点抽象成VNode,有效减少直接操作dom次数,从而提高程序性能;
    • 直接操作 dom 是有限制的,比如:diff、clone等操作,一个真实元素上有许多的内容,如果直接对其进行diff操作,会去额外diff一些没有必要的内容;同样的,如果需要进行clone,那么需要将其全部内容进行复制,这也是没必要的。但是,如果将这些操作转移到 JavaScript 对象上,那么就会变得简单了;
    • 操作 dom 是比较昂贵的,频繁的dom操作容易引起页面的重绘和回流,但是通过抽象VNode进行中间处理,可以有效减少直接操作dom的次数,从而减少页面重绘和回流;
    • 方便实现跨平台
      • 同一VNode节点可以渲染成不同平台上的对应的内容,比如:渲染在浏览器是dom元素节点,渲染在Native(iOS、Android)变为对应的控件、可以实现SSR(服务端渲染)、渲染到WebGL中等等;
      • Vue3中允许开发者基于VNode实现自定义渲染器(renderer),以便于针对不同平台进行渲染;
  3. vdom如何生成,又如何成为真实dom?以及在diff中的作用
    • 在vue中我们常常会为组件编写模板template,这个模板会被编译器compiler编译为渲染函数(render function),在接下来的挂载(mount)过程中会调用render函数,返回的对象就是虚拟dom。但它们还不是真正的dom,会在后续的patch过程中进一步转化为真实dom:

  • 挂载过程结束后,vue程序进入更新流程。如果某些响应式数据发生变化,将会引起组件重新render,此时就会生成新的vdom,和上一次的渲染结果diff就能得到变化的地方,从而转换为最小量的dom操作,高效更新视图。

介绍一下 Vue 的 diff 算法

  • DOM操作是非常昂贵的,因此我们需要尽量地减少DOM操作。这就需要找出本次DOM必须更新的节点来更新,其他的不更新,这个找出的过程,就需要应用 diff 算法
    • vue的diff算法是平级比较,不考虑跨级比较的情况。
    • 内部采用深度递归的方式+双指针(头尾都加指针)的方式进行比较。
  1. Vue 中的 diff 算法称为 patching 算法,它由 Snabbdom 修改而来,虚拟 DOM 要想转化为真实 DOM 就需要通过 patch 方法转换
  2. 最初 Vue1.x 视图中每个依赖均有更新函数对应,可以做到精准更新,因此并不需要虚拟 DOM 和 patching 算法支持,但是这样粒度过细导致 Vue1.x 无法承载较大应用;Vue 2.x 中为了降低 Watcher 粒度,每个组件只有一个 Watcher 与之对应,此时就需要引入 patching 算法才能精确找到发生变化的地方并高效更新
  3. vue 中 diff 执行的时刻是组件内响应式数据变更触发实例执行其更新函数时,更新函数会再次执行 render 函数获得最新的虚拟 DOM,然后执行 patch 函数,并传入新旧两次虚拟 DOM,通过比对两者找到变化的地方,最后将其转化为对应的 DOM 操作
  4. patch 过程是一个递归过程,遵循深度优先、同层比较的策略;以 vue3 的 patch 为例:
    1. 首先判断两个节点是否为相同同类节点,不同则删除重新创建
    2. 如果双方都是文本则更新文本内容
    3. 如果双方都是元素节点则递归更新子元素,同时更新元素属性
    4. 更新子节点时又分了几种情况
    5. 新的子节点是文本,老的子节点是数组则清空,并设置文本;
    6. 新的子节点是文本,老的子节点是文本则直接更新文本;
    7. 新的子节点是数组,老的子节点是文本则清空文本,并创建新子节点数组中的子元素;
    8. 新的子节点是数组,老的子节点也是数组,那么比较两组子节点,更新细节 blabla
    9. vue3中引入的更新策略:静态节点标记等

Vue中key的作用

  • key是为Vue中Vnode的唯一标识,通过这个key,我们的diff操作可以更准确、更快速。
    • 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。
  • 更准确:因为带key就不是就地复用了,在sameNode函数a.key === b.key对比中可以避免就地复用的情况。所以更加准确。
  • 更快速:利用key的唯一性生成map对象来获取对应节点,比遍历方式块。

为什么列表操作中,操作数据建议传id,而不是索引

  • 在列表渲染中,如果复杂数据中有异步数据或开销比较大的数据计算或DOM更新,vue会先在虚拟DOM中合并操作,从而导致数据更新和真实DOM存在不同步现象,此时DOM中获取的索引和数据的索引如果无法实时对应,就会有很大的隐患(数据更新了,但是DOM没更新)

v-if 和 v-show 的区别

  • 规则
    • v-show隐藏规则是为该元素添加css属性display:none,dom元素依旧还在。
    • v-if显示隐藏是将dom元素整个添加或删除
  • 编译过程:
    • v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;
    • v-show只是简单的基于css切换
  • 编译条件:
    • v-if是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染
  • 生命周期:
    • v-showfalse变为true的时候不会触发组件的生命周期
    • v-iffalse变为true的时候,触发组件的beforeCreatecreatebeforeMountmounted钩子,由true变为false的时候触发组件的beforeDestorydestoryed方法
  • 性能消耗:
    • v-if有更高的切换消耗;
    • v-show有更高的初始渲染消耗
  • 使用场景:
    • v-ifv-show都能控制dom元素在页面的显示
    • v-if相比v-show开销更大的(直接操作dom节点增加与删除)
    • 如果需要非常频繁地切换,则使用v-show较好
    • 如果在运行时条件很少改变,则使用v-if较好

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

  1. 在Vue2中
    • 因为v-for的优先级比v-if的优先级高,所以每次渲染时都会先将列表渲染出来,再通过条件判断进行显示隐藏,所以将v-ifv-for用在一起会特别消耗性能
  2. 在Vue3中
    • v-ifv-for的优先级更高。这意味着v-if的条件将无法访问到v-for作用域内定义的变量别名
  3. 可以通过计算属性computed提前过滤掉不符合条件的项

计算属性(computed) 和 方法(methods) 的区别

  1. 调用方式:
    • computed 是属性调用,在定义函数时以属性的形式调用,
    • methods 是方法调用,在定义函数时以方法的形式调用,要加()
  2. 缓存功能:
    • computed是计算属性,是有缓存的,当它的依赖属性改变的时候,才会进行重新计算,如果数据没有改变,它是不会运行的。
    • methods是方法,没有缓存功能,只要你调用一次,不管数据是否改变,它都是执行。
  3. 所以,同等条件下,computedmethods 性能好一点,避免重复执行。

计算属性(computed) 和 侦听器(watch) 的区别

  • computed是计算属性,依赖其它属性计算值,并且computed的值有缓存,只有当计算值变化才会返回内容,他可以设置gettersetter
  • watch监听到值的变化就会执行回调,在回调中可以进行一系列的操作。
  • 计算属性一般用在模板渲染中,某个值是依赖其它响应对象甚至是计算属性而来;
  • 而侦听属性适用于观测某个值的变化去完成一段复杂的业务逻辑。

Vue中如何监控某个属性值的变化

  • Vue3中使用watch方法来监听响应式数据的变化,并在数据变化时执行回调函数。
import { watch } from 'vue'

const state = reactive({
  count: 0
})

watch(() => state.count, (newValue, oldValue) => {
  console.log(newValue, oldValue)
})

v-model 是如何实现的,语法糖实际是什么

  • v-model本质上就是语法糖,在使用v-model后既绑定了数据,又添加了一个@input事件监听
    • <input v-model='message' />
    • 等价于
    • <input :value='message' @input='message = $event.target.value'>
  • 当在input元素中使用v-model实现双数据绑定,其实就是在输入的时候触发元素的input事件,通过这个语法糖,也能够实现父子组件数据的双向绑定
  • 如何实现
    1. 作用在表单元素上:
      • 动态绑定了 inputvalue 指向了 messgae 变量
      • 并且在触发 input 事件的时候去动态把 message 设置为目标值:
<input v-model='message' />
//  等同于
<input 
	:value="message" 
	@input="message=$event.target.value"
>
  1. 作用在组件上:
  • 在自定义组件中,v-model 默认会利用名为 valueprop 和名为 input 的事件
  • 本质是一个父子组件通信的语法糖,通过prop$.emit实现。 因此父组件 v-model 语法糖本质上可以修改为:
<child :value="message"  @input="function(e){message = e}"></child>
  • 在组件的实现中,可以通过 v-model 属性来配置子组件接收的prop名称,以及派发的事件名称。

Vuex是什么?

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  • 但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
    • 多个视图依赖于同一状态。
    • 来自不同视图的行为需要变更同一状态。
  • 对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
  • 对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
  • 因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
  • 通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。

什么情况下应该使用 Vuex?

  • Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
  • 如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

路由传值的方式有哪几种

【注意】

  1. 在选项式API中,可以通过this关键字直接获取router对象,如:this.$routerthis.$route
  2. 在setup中,需要先引入useRouteuseRouter方法,创建routerouter对象,如:
    import { useRoute } from "vue-router";
    const route = useRoute();
    

1. 路由路径参数(Route Params)(动态路由)

  1. 在路由定义中使用动态路径参数来传递参数。例如,定义一个带有参数的路由:
    {
      path: '/user/:id',
      component: User
    }
    
  2. 在组件中可以通过$route.params来获取参数值:
    console.log(route.params.id);
    
  3. 优缺点
    • 优点:参数直接嵌入在URL中,易于理解和传递;适合传递单个参数。
    • 缺点:不适合传递多个参数;参数必须在路由定义中提前声明。
    • 使用场景:传递单个参数,例如用户ID、商品ID等。

2. 查询参数(Query Params):

  1. 在URL中使用查询字符串来传递参数。例如,定义一个带有查询参数的路由:
    {
      path: '/user',
      component: User
    }
    
  2. 在组件中可以通过$route.query来获取参数值:
    console.log(route.query.id);
    
  3. 优缺点
    • 优点:参数直接嵌入在URL中,易于理解和传递;适合传递多个参数。
    • 缺点:参数较长时,URL可能会变得冗长。
    • 使用场景:传递多个参数,例如搜索条件、过滤条件等。

路由元信息(Route Meta)

  1. 在路由定义中使用元信息来传递参数。例如,定义一个带有元信息的路由:
    {
      path: '/user',
      component: User,
      meta: {
        id: 123
      }
    }
    
  2. 在组件中可以通过$route.meta来获取参数值:
    console.log(route.meta.id);
    
  3. 优缺点
    • 优点:参数可以在路由定义中灵活设置;适合传递静态参数。
    • 缺点:参数无法直接嵌入在URL中,不适合传递动态参数。
    • 使用场景:传递静态参数,例如页面标题、页面权限等。

命名路由(Named Routes)

  1. 在路由定义中给每个路由设置一个唯一的名称,然后通过名称来传递参数。例如,定义一个带有命名路由的路由:
    {
      path: '/user',
      name: 'user',
      component: User
    }
    
  2. 在组件中可以通过$router.push或的to属性来传递参数:
    router.push({ name: 'user', params: { id: 123 } })
    
  3. 优缺点
    • 优点:参数通过路由名称传递,不依赖具体的URL;适合传递动态参数。
    • 缺点:需要提前定义路由名称。
    • 使用场景:传递动态参数,并且不依赖具体的URL。

Props传递参数

  1. 在路由定义中使用props属性来传递参数。例如,定义一个带有props的路由:
    {
      path: '/user',
      component: User,
      props: true
    }
    
  2. 在组件中可以通过props来接收参数:
    props: ['id']
    
  3. 优缺点
    • 优点:参数通过组件的props属性传递,易于理解和传递;适合传递组件需要的数据。
    • 缺点:需要在路由定义中设置props为true,并在组件中接收props。
    • 使用场景:传递组件需要的数据。

Vuex状态管理

  1. 使用Vuex来管理应用程序的状态,并在不同组件之间共享参数。
  2. 通过在Vuex store中定义和更新参数,然后在组件中使用mapState或mapGetters来获取参数的值。
  3. 优缺点:
    • 优点:参数通过Vuex store管理,可以在整个应用程序中共享和更新;适合共享状态和数据。
    • 缺点:需要引入Vuex并设置store。
    • 使用场景:共享状态和数据,例如用户登录状态、购物车数据等。

$route 和 $router 的区别

  1. $router是VueRouter的一个实例,是当前项目全局的路由对象,主要用来操作路由,他包含了所有的路由,包括路由的跳转方法,钩子函数等
  2. $route是一个跳转的路由对象(路由信息对象),是一个局部的路由对象,表示当前路由,主要用来获取当前路由信息
  3. $router的常用方法
    // 跳转方法,会产生历史记录
    this.$router.push("/login");
    // 跳转方法,不会产生提示记录
    this.$router.replace("/login");
    // 跳转方法,同:window.history.go()
    // 参数:正:前进,负:后退,0:刷新
    this.$router.go(-1)
    
  4. $route的常用属性
    // 字符串,等于当前路由对象的路径,会被解析为绝对路径,如/home/ews
    this.$route.path
    // 对象,包含路由中的动态片段和全匹配片段的键值对,不会拼接到路由的url后面
    this.$route.params
    // 对象,包含路由中查询参数的键值对。会拼接到路由url后面
    this.$route.query
    // 路由规则所属的路由器
    this.$route.router
    // 当前路由的名字,如果没有使用具体路径,则名字为空 
    this.$route.name
    

vue-router 有哪几种路由守卫

Vue Router 提供了多种路由守卫(Navigation Guards),用于在路由切换过程中进行拦截和控制,以满足不同的业务需求。这些路由守卫主要分为以下三种类型:

  1. 全局路由守卫
    • 全局路由守卫是在路由实例上直接定义的钩子函数,它们会对所有路由进行生效。全局路由守卫主要包括:
      • 全局前置守卫(beforeEach):在路由即将改变前调用,可以通过 next 函数控制路由的跳转行为。它是最常用的路由守卫之一,用于执行如权限验证、页面跳转前的准备工作等。
      • 全局解析守卫(beforeResolve):在路由解析之后被调用,但在 DOM 更新之前。它同样可以通过 next 函数控制路由的跳转行为,但通常用于在路由解析之后进行某些操作,如修改路由元信息等。
      • 全局后置守卫(afterEach):在路由跳转完成后被调用,无论是成功跳转还是失败。它不接受 next 函数,因此不能改变路由的跳转行为,但可以用于执行一些收尾工作,如页面滚动、埋点等。
  2. 路由独享守卫
    • 路由独享守卫是在单个路由配置中定义的钩子函数,它只对该路由的实例生效。路由独享守卫只有一个:
      • beforeEnter:在路由独享守卫中,可以定义进入该路由前的守卫逻辑。与全局前置守卫类似,它也接受 to、from 和 next 三个参数,并可以通过 next 函数控制路由的跳转行为。
  3. 组件内守卫
    • 组件内守卫是在组件内部定义的钩子函数,它们只在路由组件内部生效。组件内守卫主要包括:
      • beforeRouteEnter:在路由进入该组件的对应匹配被 confirm 前调用,此时组件实例还未被创建,无法直接访问组件实例。如果需要访问组件实例,可以通过给 next 函数传递一个回调来实现。
      • beforeRouteUpdate(Vue 2.2+):在当前路由改变,但是该组件被复用时调用。例如,对于带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,就会调用该守卫。
      • beforeRouteLeave:在导航离开该组件的对应路由时调用,可以用来提示用户保存修改等。

Vue.$nextTick

  • 等待下一次 DOM 更新时才会执行的方法。
  • 如果在修改数据之后立即执行这个方法,并使用await等待返回的Promise结束,就可以获取更新后的 DOM内容。
  • Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOM操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新

Vue 实例挂载过程中发生了什么

  1. 初始化阶段
    • 创建Vue实例:通过createApp方法创建一个新的Vue应用实例。这个方法接受一个根组件的选项对象作为参数。
    • 设置选项:在这个阶段,Vue会设置实例的选项,包括datamethodscomputedwatch等,并将它们代理到Vue实例上,以便在组件的模板和逻辑中直接使用。
    • 内部设置:Vue还会进行一些内部设置,如初始化响应式系统、事件监听等。
  2. 模板编译阶段
    • 模板预编译:如果Vue实例中配置了template选项或使用了.vue文件中的模板,Vue会在挂载前将模板编译成渲染函数(render function)。这个过程是将模板中的指令和插值表达式转换为JavaScript代码,以便在后续阶段中生成虚拟DOM。
    • 无模板情况:如果没有提供template选项,Vue会将挂载目标(el属性指定的DOM元素)的内容作为模板进行编译。
  3. 挂载阶段
    • 调用mount方法:通过调用Vue实例的mount方法(或自动触发,如果在创建实例时指定了el属性),Vue开始将实例挂载到DOM上。
    • 创建虚拟DOM:渲染函数被执行,生成一个描述将要被渲染的虚拟DOM树的VNode对象。
    • 渲染和挂载:Vue使用一个内部的渲染函数(如patch函数)将虚拟DOM渲染成真实的DOM,并将其挂载到指定的DOM元素上。这个过程会首次执行组件的渲染函数,并创建响应式数据和组件更新函数之间的依赖关系(即依赖收集)。
  4. 生命周期钩子调用
    • beforeMount:在挂载开始之前被调用,此时模板编译已经结束,但尚未将编译后的内容挂载到真实DOM上。
    • mounted:在挂载结束后被调用,此时Vue实例已经渲染成真实的DOM,并挂载到了指定的元素上。在这个阶段,可以访问和操作DOM。
  5. 响应式更新
    • 建立更新机制:在挂载过程中,Vue会建立一个更新机制,用于在响应式数据发生变化时重新渲染组件。这个机制通常包括一个或多个渲染Watcher对象,它们会在数据变化时触发组件的重新渲染。
    • 依赖收集:在首次渲染时,Vue会收集组件内部响应式数据的依赖关系,以便在数据变化时能够找到并更新相应的组件。

Vue 的模版编译原理

Vue的模板编译原理是指将Vue的模板(通常是以HTML为基础的模板字符串或.vue文件中的模板部分)转换为渲染函数(render function)的过程。这个过程允许Vue在运行时高效地生成和更新DOM。以下是Vue模板编译原理的详细步骤:

  1. 解析(Parsing)
    • 作用:将模板字符串解析成抽象语法树(AST)。AST是一种树形结构,用于表示代码的抽象语法结构,便于后续的处理和转换。
    • 过程
      • Vue的编译器会遍历模板字符串,根据模板中的标签、属性、指令等信息生成对应的AST节点。
      • 每个节点都包含类型(如标签、文本、表达式等)、名称(如标签名)、属性(如HTML属性、Vue指令)等信息。
  2. 优化(Optimization)
    • 作用:对AST进行优化处理,以减少运行时的性能开销。
    • 过程
      • 静态节点提升:标记并缓存那些内容不会变化的静态节点,这些节点在后续的渲染过程中不需要重新计算或渲染。
      • 静态属性提升:对于静态属性(即不会随数据变化而变化的属性),Vue会进行特殊处理,以减少DOM操作的开销。
      • 其他优化:如事件监听器的优化、指令的优化等。
  3. 代码生成(Code Generation)
    • 作用:将优化后的AST转换为可执行的JavaScript代码,即渲染函数。
    • 过程
      • 遍历AST,根据节点的类型生成相应的JavaScript代码片段。
      • 这些代码片段最终会被拼接成一个完整的渲染函数,该函数接收一个createElement函数作为参数,用于创建虚拟DOM节点。
      • 渲染函数的主要作用是接收组件的数据并返回虚拟DOM,用于描述组件的结构和内容。
  4. 渲染(Rendering)
    • 作用:通过执行渲染函数生成虚拟DOM,并将其映射到实际的DOM上。
    • 过程
      • 当组件的数据发生变化时,Vue会重新执行渲染函数生成新的虚拟DOM。
      • Vue使用高效的算法(如虚拟DOM的diff算法)来比较新旧虚拟DOM的差异。
      • 根据差异结果,Vue只更新实际DOM中需要变化的部分,从而实现高效的DOM更新。

总结:Vue的模板编译原理可以概括为以下几个步骤:解析模板生成AST、优化AST、将AST转换为渲染函数的JavaScript代码、通过渲染函数生成虚拟DOM并更新实际DOM。这个过程实现了Vue的响应式数据绑定和组件化开发,使得开发者可以更加高效、灵活地编写Web应用。
需要注意的是,模板编译是在构建时(对于服务端渲染或预编译)或组件实例化时(对于客户端渲染)完成的,而不是在运行时。这样可以避免运行时解析和编译模板的性能损耗,提升应用的性能。

computed 和 watch 的区别

**computed(计算属性)watch(侦听器)**是Vue.js中两种常用的响应式数据处理方式,它们之间存在明显的区别,主要体现在以下几个方面:

  1. 返回值
    • computed:有返回值。computed属性基于它们的依赖进行缓存,只有当依赖发生变化时,才会重新计算并返回新的值。
    • watch:监听的函数不需要返回值。watch更主动地监听某个属性的变化,并在变化时执行相应的操作。
  2. 缓存机制
    • computed:支持缓存。computed属性会缓存计算结果,在依赖不变时直接返回缓存值,从而提高性能。
    • watch:不支持缓存。只要监听的数据发生变化,就会触发相应的操作,无论这些操作是否重复或可优化。
  3. 异步支持
    • computed:不支持异步。当computed内有异步操作时,是无法监听数据变化的。computed属性更适合处理同步的、依赖多个数据源的计算逻辑。
    • watch:支持异步操作。watch可以在数据变化时执行异步操作,如发送请求、设置定时器等。
  4. 监听时机
    • computed:在组件创建时立即监听其依赖的数据。一旦依赖的数据发生变化,computed属性就会重新计算。
    • watch:默认在第一次加载时不监听数据变化(除非设置了immediate: true)。它更侧重于在数据变化后执行某些操作。
  5. 使用场景
    • computed:适用于多个数据影响一个数据的场景,如购物车商品结算时,需要根据商品单价和数量计算总价。computed属性可以自动追踪依赖的数据变化,并重新计算得到新的值。
    • watch:适用于一个数据变化影响多个数据的场景,如搜索框输入时,需要根据输入内容过滤数据列表。watch可以监听输入内容的变化,并执行相应的过滤操作。
  6. 深度监听
    • computed:自动处理深度依赖。如果computed属性依赖的对象内部属性发生变化,computed属性也会重新计算。
    • watch:默认情况下不监听对象内部属性的变化(浅监听)。如果需要监听对象内部属性的变化,需要设置deep: true(但请注意,深度监听会消耗更多的性能)。

综上所述,computedwatch在Vue.js中各有其适用场景和优势。在选择使用哪种方式时,应根据具体的需求和场景来决定。

keep-alive 是什么

keep-alive是Vue.js中的一个内置组件,其主要功能是对组件进行缓存,而不是在组件不活动时销毁它们。这个组件的主要作用是在组件切换时保留组件的状态,避免重新渲染和销毁组件,从而提升应用的性能。

  1. 主要特点和作用

    • 缓存组件实例:当组件被<keep-alive>包裹时,其状态(包括数据和DOM)将会被保留,而不是在组件不活动时被销毁。这样,当组件再次被访问时,可以直接使用之前的状态,而不需要重新创建和初始化。
    • 提升性能:通过避免不必要的组件重新渲染和初始化,<keep-alive>可以显著减少应用的加载时间和性能消耗,从而提高用户体验。
    • 支持条件缓存:<keep-alive>提供了includeexclude属性,允许开发者根据组件的name属性来指定哪些组件应该被缓存,哪些不应该。这使得缓存策略更加灵活和可控。
  2. 使用场景

    • <keep-alive>通常用于那些需要频繁切换但又希望保持状态的组件,如标签页、对话框等。通过缓存这些组件,可以在用户切换回这些组件时迅速恢复之前的状态,提高应用的响应速度和流畅度。
  3. 注意事项

    • 组件的key属性:<keep-alive>组件会根据组件的key属性来判断是否缓存组件。因此,在切换组件时,需要确保每个组件都具有唯一的key值,以便正确地缓存和复用组件状态。
    • 内存占用:由于<keep-alive>会缓存组件的状态,如果应用中有大量的组件被缓存,可能会占用较多的内存。因此,需要根据应用的需求和设备的性能来合理使用<keep-alive>,避免不必要的内存消耗。
    • 生命周期钩子的影响:使用<keep-alive>组件可能会影响组件的生命周期钩子函数的执行时机。被缓存的组件可能不会触发常规的创建和销毁钩子函数,而是通过activateddeactivated钩子函数来控制组件的激活和停用。

什么是mixin,如何使用

Mixin在Vue.js中是一种非常灵活的代码复用机制,它允许开发者将组件的共同逻辑提取到一个或多个mixin对象中,然后在需要使用这些逻辑的组件中通过mixins选项进行引入。这样可以在多个组件间共享代码,提高代码的复用性和可维护性。

Mixin的基本概念
Mixin本质上是一个JavaScript对象,它可以包含Vue组件中的任意选项,如datamethodscomputedcomponentsdirectiveshooks等。当组件使用mixin时,mixin对象的所有选项将被“混合”进入该组件本身的选项中。

Mixin的使用方式
Mixin的使用可以分为局部混入和全局混入两种方式。

  1. 局部混入
    • 局部混入是指在单个组件或几个组件中通过mixins选项使用mixin对象。这种方式不会影响其他未使用该mixin的组件。
    • 定义mixin对象:在JavaScript文件中定义一个mixin对象,该对象包含要复用的逻辑和选项。
    • 在组件中引入并使用mixin:在需要使用mixin的组件中,通过mixins选项引入mixin对象。
  2. 全局混入
    • 全局混入则是通过Vue.mixin()方法将mixin对象注册到Vue的全局范围内,这样会影响之后创建的每一个Vue实例。
    • 定义mixin对象:同局部混入,首先定义一个mixin对象。
    • 全局注册mixin:在Vue应用创建之前,通过Vue.mixin()方法将mixin对象注册为全局mixin。

注意事项

  1. 选项合并:当组件和mixin对象含有同名选项时,这些选项将以Vue的合并策略进行合并。例如,data和`methods`````等选项将进行递归合并,而生命周期钩子函数将合并为一个数组,mixin中的钩子将先于组件自身的钩子执行。
  2. 可维护性:虽然mixin可以提高代码的复用性,但过多的使用也可能导致代码的可维护性变差。因此,在使用mixin时应当慎重考虑,并确保mixin的职责单一,避免在mixin中编写过于复杂的逻辑。
  3. 全局混入的影响:全局混入会影响每一个Vue实例,因此在使用全局混入时需要特别小心,确保不会引入意外的副作用。

Vue 中的修饰符有哪些

Vue中常用的修饰符有以下几种:

  1. .prevent
    • 阻止默认事件的触发,例如表单提交、超链接跳转等。
  2. .stop
    • 阻止事件冒泡,在事件处理函数中调用 event.stopPropagation() 方法来实现。
  3. .capture
    • 捕获模式,在事件处理过程中先触发祖先元素的事件处理函数,然后再触发子孙元素的事件处理函数。
  4. .self
    • 限制事件只能在绑定该事件的元素上触发,而不是在其子元素上触发。
  5. .once
    • 只触发一次事件处理函数。
  6. .passive
    • 声明事件处理函数永远不会调用 event.preventDefault() 方法,以提高页面滚动的性能。
  7. .keyCode / .keyAlias
    • 监听按键的特定键码或别名,例如.enter、.esc等。
  8. .native
    • 监听组件根元素的原生事件,而不是组件内部的事件。

$forceUpdate的原理

  • $forceUpdate 是Vue.js中的一个实例方法,用于强制组件重新渲染。它的原理是触发组件的更新流程,将组件的虚拟 DOM 树重新生成,并对比新旧虚拟 DOM 树的差异,最终将变化的部分更新到真实的 DOM 上。
    具体的实现流程如下:
    1. $forceUpdate 方法被调用后,会触发组件的更新标记(dirty);
    2. Vue.js 会异步执行更新队列(update queue),对所有标记为 dirty 的组件进行更新;
    3. 对于需要更新的组件,Vue.js 会重新生成其虚拟 DOM 树,并与之前的虚拟 DOM 树进行比较,找出差异(diff);
    4. 对于有差异的部分,Vue.js 会将其更新到真实的 DOM 上,完成组件的重新渲染。

什么是插槽,如何使用

在Vue3中,插槽(Slot)是组件模板中的一个特殊元素,用于将父组件的内容插入到子组件的特定位置。插槽的使用提高了组件的复用性、可维护性和模块化程度。以下是关于Vue3中插槽的详细介绍和使用方法:

一、插槽的类型
Vue3中的插槽主要分为以下几种类型:

  1. 默认插槽:
    • 当没有提供具名插槽时,Vue 3会自动使用默认插槽来插入内容。
    • 在子组件模板中,使用<slot></slot>标签来定义默认插槽。
    • 在父组件中,直接将内容放入<ChildComponent></ChildComponent>之间,这些内容将会被插入到默认插槽的位置。
  2. 具名插槽:
    • 可以给插槽起一个名字,然后在父组件中引用这个名字来插入内容。
    • 在子组件模板中,使用<slot name="插槽名"></slot>来定义具名插槽。
    • 在父组件中,使用<template v-slot:插槽名>或简写为<template #插槽名>来指定要插入的内容。
  3. 作用域插槽:
    • 允许父组件访问子组件中的数据。
    • 子组件通过<slot :数据名="数据值"></slot>向插槽传递数据。
    • 父组件通过v-slot:插槽名="插槽数据"或简写为#插槽名="插槽数据"来接收这些数据,并在插槽内容中使用。
  4. 动态插槽名:
    • 插槽名可以是动态的,这允许根据组件的状态或父组件传递的props来决定使用哪个插槽。
    • 使用v-slot:[动态插槽名]或简写为#[动态插槽名]来定义动态插槽。

二、插槽的使用方法

  1. 定义插槽(在子组件中):
    • 使用<slot></slot>定义默认插槽。
    • 使用<slot name="插槽名"></slot>定义具名插槽。
    • 使用<slot :数据名="数据值"></slot>定义作用域插槽。
  2. 使用插槽(在父组件中):
    • 对于默认插槽,直接在<ChildComponent></ChildComponent>之间放置内容。
    • 对于具名插槽,使用<template v-slot:插槽名>或简写为<template #插槽名>来指定内容。
    • 对于作用域插槽,使用v-slot:插槽名="插槽数据"或简写为#插槽名="插槽数据"来接收子组件传递的数据并定义内容。

三、插槽的应用场景
插槽在Vue3中广泛应用于各种场景,包括但不限于:

  1. 动态内容显示:当需要在组件中显示动态内容时,可以使用插槽来插入不同的内容。
  2. 多样式布局:当组件需要支持多种样式布局时,可以使用插槽来定义不同的布局。
  3. 多语言支持:当组件需要支持多种语言时,可以使用插槽来定义不同的语言文本。
  4. 表单验证:当组件需要支持表单验证时,可以使用插槽来定义验证错误信息的位置。
  5. 嵌套组件:当需要将一个组件嵌套到另一个组件中时,可以使用插槽来定义嵌套的位置。

通过使用插槽,Vue3使得组件的开发更加灵活、可维护和可扩展。

Vue Router中的常用路由模式和原理

Vue Router是Vue.js的官方路由管理器,它和Vue.js深度集成,让构建单页面应用(SPA)变得易如反掌。Vue Router中的常用路由模式主要包括hash模式、history模式和abstract模式,每种模式都有其特定的应用场景和原理。

  1. Hash模式

    • 原理:

      • Hash模式是利用URL中的hash值(即#及其后的字符)来实现前端路由的。
      • 当hash值变化时,浏览器不会向服务器发送请求,而是会触发hashchange事件。
      • Vue Router可以监听这个hashchange事件,然后根据hash值的变化来渲染对应的组件。
    • 优点:

      • 兼容性好,可以兼容到IE8。
      • 不会因为hash值的变化而重新加载页面。
      • 无需服务器进行配合。
    • 缺点:

      • URL中包含了#,看起来不够美观。
      • 对于需要锚点功能的需求,可能会与路由机制发生冲突。
      • 重定向操作时,后端无法获取到URL的完整路径。
  2. History模式

    • 原理:
      • History模式利用HTML5 History API(主要是pushStatereplaceState方法)来实现URL的跳转,而不会重新加载页面。
      • 当调用这些API时,虽然URL改变了,但浏览器不会立即向服务器发送请求,而是将新的URL添加到浏览器的历史记录中。
      • 当用户点击浏览器的后退或前进按钮时,会触发popstate事件,Vue Router可以监听这个事件来实现组件的渲染。
    • 优点:
      • 符合URL地址规范,没有#,看起来更美观。
      • 后端可以获取到完整的路由信息。
      • 可以使用history.state获取完整的路由信息。
      • 可以修改历史记录,并且不会立即向后端发起请求。
    • 缺点:
      • 兼容性只到IE10。
      • 当URL路径不存在时,如果服务器没有正确配置,会报404错误。
      • 改变URL路径后,如果浏览器刷新页面,会重新请求资源,因此需要服务器进行配合。
  3. Abstract模式

    • 原理:
      • Abstract模式主要用于那些不支持HTML5 History API和hash的JavaScript环境,比如Node.js环境下的Vue SSR(服务器端渲染)。
      • 在这种模式下,Vue Router只会改变应用的内存状态,而不会改变URL。
    • 应用场景:
      • 当你的Vue应用运行在非浏览器环境(如Node.js)中时,需要使用Abstract模式。

总结
Vue Router的三种路由模式各有优缺点,适用于不同的场景。在选择路由模式时,需要根据项目的具体需求和目标环境来进行选择。默认情况下,Vue Router会使用hash模式,因为它具有良好的兼容性和简单易用的特点。但如果你希望URL看起来更美观,或者你的项目已经支持HTML5 History API,那么history模式可能是一个更好的选择。而abstract模式则主要适用于非浏览器环境。

页面刷新后Vuex 状态丢失怎么解决

页面刷新后Vuex状态丢失的问题,通常是因为Vuex的状态数据是保存在运行内存中的,当页面刷新时,Vue实例会重新加载,导致Vuex中的数据被重置为初始状态。为了解决这个问题,可以采取以下几种方法:

  1. 使用浏览器的本地存储
    • localStorage
      • 特点:数据会永久存储在浏览器中,除非被主动删除。
      • 适用场景:适合存储不需要频繁更改的数据,如用户配置信息等。
      • 实现方式:在Vuex的mutationaction中,监听数据的变化,将数据同步到localStorage中。页面刷新时,从localStorage中读取数据并恢复到Vuex中。
    • sessionStorage
      • 特点:数据存储在当前会话的tab页中,关闭当前tab页或浏览器窗口后数据会消失。
      • 适用场景:适合存储临时数据,如用户会话信息等。
      • 实现方式:与localStorage类似,但数据仅在当前会话中有效。
    • cookie
      • 特点:数据根据设置的有效时间存储,但通常不推荐用于存储大量数据,因为存在大小限制且不易读取。
      • 适用场景:适用于需要跨会话保持的少量数据,如用户登录状态等。
      • 实现方式:在Vuex的mutationaction中设置cookie,页面刷新时从cookie中读取数据。
  2. 使用Vuex持久化插件
    • vuex-persistedstate
      • 特点:这是一个Vuex插件,可以将Vuex的状态数据持久化到浏览器的localStoragesessionStorage中。
      • 优势:简化了持久化存储的实现过程,减少了手动同步和恢复的代码量。
      • 实现方式:
        • 安装vuex-persistedstate插件。
        • 在创建Vuex store时,将插件添加到plugins数组中,并配置存储选项(如使用localStorage还是sessionStorage)。
        • 插件会自动在状态变化时同步数据到本地存储,并在应用初始化时从本地存储中恢复状态。
  3. 将数据存储到后端服务器
    • 适用场景:当数据量较大或需要多个设备之间共享数据时,可以将数据存储到后端服务器中。
    • 实现方式:
      • 在Vuex的action中,使用Ajax、Fetch API等技术将数据发送到后端服务器进行存储。
      • 页面刷新时,从后端服务器重新获取数据并恢复到Vuex中。
    • 注意事项
      • 敏感信息处理:对于敏感信息(如用户登录信息),不应直接存储在浏览器的本地存储中,而应采用更安全的方式来保存,如使用HTTP Secure(HTTPS)协议和加密存储。
      • 性能考虑:频繁地将大量数据存储在本地存储中可能会影响性能。建议只存储必要的状态数据,并避免在每次状态变化时都进行存储操作。
      • 兼容性考虑:本地存储在某些浏览器或环境中可能不可用。在使用本地存储时,需要考虑兼容性问题,并提供适当的降级方案。

综上所述,解决Vuex页面刷新后状态丢失的问题,可以根据具体需求和场景选择合适的方法。对于小型应用或数据量不大的情况,使用浏览器的本地存储可能是一个简单有效的选择;而对于大型应用或需要复杂数据管理的情况,使用后端存储或Vuex持久化插件可能更为合适。

关于 Vue SSR 的理解

Vue SSR(Server Side Rendering,服务器端渲染)是一种在服务器端将Vue组件渲染成HTML字符串,然后将这些字符串直接发送给客户端的技术。这种方式相比传统的客户端渲染(Client Side Rendering,CSR)在多个方面有着显著的优势,但同时也带来了一些挑战。以下是对Vue SSR的详细理解:

  1. Vue SSR的工作原理

    • 请求处理:
      • 当用户访问Vue SSR应用时,其浏览器会向服务器发送HTTP请求。
      • 服务器接收到请求后,会执行Vue应用的服务器端代码。
    • Vue实例启动与渲染:
      • 服务器启动Vue实例,并利用vue-server-renderer模块将Vue组件及其依赖关系转化为HTML字符串。
        这个过程通常伴随着数据预取,即在服务器端调用API获取视图所需的数据,并将其注入到组件的上下文中,确保首次渲染时就有完整的内容。
    • HTML文档生成:
      • 服务器端渲染完成后,生成包含所有必需HTML、CSS样式(通常是内联样式以避免FOUC,即无样式内容闪烁)和必要的JavaScript脚本的完整HTML文档。
      • 其中JavaScript脚本是Vue客户端运行所需的bundle,包含了Vue应用的核心逻辑。
    • 响应发送与客户端激活:
      • 浏览器接收到服务器返回的HTML响应后,迅速渲染出首屏内容。
      • 嵌入的JavaScript开始执行,Vue.js在客户端检测到已存在的服务器端渲染的DOM结构,并将其“激活”,即与Vue实例绑定起来,使其成为具有完整Vue功能的客户端应用。
  2. Vue SSR的优势

    • SEO友好:
      • 由于搜索引擎爬虫可以直接抓取到服务端渲染出的完整HTML页面,因此对于需要优化搜索引擎排名的应用来说,SSR是一个更佳的选择。
    • 更快的首屏加载时间:
      • SSR能够减少用户等待页面加载的时间,因为用户接收到的是已经渲染好的HTML页面,这提高了用户的体验。
    • 更好的用户体验:
      • 用户在首次加载页面时能直接看到完整渲染的内容,无需等待JavaScript文件下载和执行。
    • 前后端同构:
      • Vue SSR应用可以被视为同构或通用应用,因为应用程序的大部分代码都可以在服务端和客户端运行。
  3. 使用场景

    • Vue SSR适用于需要优化SEO、提升首屏加载速度或需要在加载之前进行一些特定处理(如路由鉴权、数据预取)的场景。然而,对于一些对首屏加载速度要求不高的应用,或者服务器资源有限的情况,可能需要权衡是否使用SSR。

综上所述,Vue SSR通过结合前后端的能力,既保证了初始加载时的性能体验和SEO优化,又保留了Vue.js在客户端高效更新和交互的特性。然而,它也带来了一定的配置复杂性和服务器负担,因此在选择是否使用SSR时需要根据项目需求和资源情况进行综合考虑。

了解哪些 Vue 的性能优化方法

Vue的性能优化方法多种多样,涵盖了代码层面的优化、组件优化、资源加载优化等多个方面。以下是一些常用的Vue性能优化方法:

  1. 代码层面的优化

    • 合理使用v-ifv-show
      • v-if:适用于在运行时条件很少改变的场景,因为它会确保在切换过程中,元素适当地被销毁和重建。
      • v-show:适用于频繁切换显示/隐藏的场景,因为它只是通过改变元素的CSS属性来控制显示与隐藏,不会销毁和重建元素。
    • 优化v-for的使用
      • 当使用v-for进行列表渲染时,必须为每一项item设置唯一key值,这有助于Vue内部机制精准找到该条列表数据,提高渲染效率。
      • 避免在v-for中同时使用v-if,因为v-for的优先级高于v-if,如果条件判断较多,会影响渲染速度。
    • 合理使用computedwatch
      • computed:适合进行数值计算,并依赖于其他数据。它会自动缓存计算结果,只有在相关响应式数据变化时才会重新计算。
      • watch:适用于数据变化时需要执行异步或开销较大的操作,或者需要对数据变化作出特定响应的场景。
    • 避免在模板中编写复杂逻辑
      • 复杂的逻辑应该在计算属性或方法中处理,以保持模板的简洁和可读性。
  2. 组件优化

    • 组件懒加载
      • 对于非首屏加载的组件,可以采用异步组件或路由懒加载的方式,延迟加载它们,以提高首屏加载速度。
    • 合理使用keep-alive
      • 对于经常切换的组件,可以使用keep-alive缓存组件实例,避免重复创建和销毁,提高渲染性能。
    • 优化组件的data函数
      • 组件的data必须声明为返回一个初始数据对象的函数,以避免多个实例共享同一份数据。
  3. 资源加载优化

    • 路由懒加载
      • 通过路由懒加载,只有在使用到对应的页面时才会加载相关的代码,这可以减少初始加载的代码量,提升网页的加载速度。
    • 图片懒加载
      • 使用vue-lazyload等插件实现图片懒加载,可以优化页面加载速度,减少不必要的资源加载。
    • 使用CDN加速静态资源加载
      • 将静态资源如CSS、JS、图片等放在CDN服务器上,可以加快资源的加载速度。
  4. 其他优化方法

    • 使用节流和防抖
      • 在处理如滚动、窗口大小调整等高频事件时,使用节流(throttle)和防抖(debounce)技术可以减少不必要的计算和DOM操作,提高性能。
    • 避免全局状态污染
      • 合理使用Vuex或Vue 3的Composition API中的reactiveref等,避免全局状态污染和不必要的性能开销。
    • 优化打包配置
      • 使用webpack等打包工具时,合理配置如代码分割、压缩、Tree Shaking等选项,可以减小打包体积,提高加载速度。
    • 利用Vue Devtools进行性能分析
      • Vue Devtools是一个浏览器扩展,可以帮助开发者分析Vue应用的性能瓶颈,进行针对性的优化。

综上所述,Vue的性能优化需要从多个方面入手,包括代码层面的优化、组件优化、资源加载优化等。通过合理的优化手段,可以显著提升Vue应用的性能和用户体验。

Vue3的新特性有哪些

Vue3带来了许多新特性和改进,这些特性不仅提升了Vue的性能,还增强了其开发体验和灵活性。以下是Vue3的一些主要新特性:

  1. 性能提升

    • 更快的渲染速度:Vue3的渲染速度比Vue2.x快了1.2~2倍,这得益于重写的虚拟DOM实现、运行时编译、静态提升与事件侦听器缓存等技术。
    • 更小的体积:Vue3中的核心API都支持tree-shaking,这意味着更多的功能和更小的体积。开发者可以按需打包,只包含自己使用的功能。
  2. Composition API

    • 逻辑复用与代码组织:Composition API的引入,使得Vue3的逻辑复用和代码组织更加方便。它类似于React Hooks,允许开发者将组件的逻辑按照功能进行分割,并在组件之间复用。
    • 更好的TypeScript支持:Vue3使用TypeScript重写,提供了更好的类型推导和类型检测,使得代码更加稳定和安全。
  3. 响应式原理的改变

    • Proxy替代Object.defineProperty:Vue3使用Proxy作为响应式系统的核心,相比Vue2.x中的Object.definePropertyProxy可以直接对整个对象进行劫持,解决了数组和对象的一些特殊变化无法检测的问题,大大提高了响应式监听的性能。
  4. 新增组件

    • Fragment:允许组件有多个根元素,解决了Vue2.x中模板里只能有一个根元素的问题。
    • Teleport:类似于React中的Portal,提供了一种将子节点渲染到存在于父组件以外的DOM节点的方案。
    • Suspense:允许组件在渲染之前进行“等待”,并在等待时显示fallback的内容,这对于异步组件的渲染非常有用。
  5. 其他新特性

    • 全局API的重命名:Vue3中许多全局API的名称被重命名,如Vue.filter变成了Vue.app.filterVue.directive变成了Vue.app.directive等。
    • 移除过滤器(filter):Vue3中移除了过滤器,开发者需要使用计算属性或方法来替代它们。
    • 自定义指令的API变化:Vue3中自定义指令的API发生了变化,需要使用createdbeforeUnmount代替bindunbindupdate方法也被移除,需要使用beforeUpdateupdated代替。
    • setup函数:Vue3中新增了setup函数,它是所有Composition API的展示舞台。在setup函数中,开发者可以定义响应式数据、计算属性、方法等,并通过return将它们暴露给模板使用。
  6. 更好的错误处理和调试

    • Vue3提供了更丰富的错误信息和调试工具,使得开发者可以更容易地定位和解决问题。

综上所述,Vue3的新特性涵盖了性能提升、Composition API、响应式原理的改变、新增组件、全局API的重命名、移除过滤器、自定义指令的API变化、setup函数以及更好的错误处理和调试等方面。这些新特性使得Vue3在开发效率和性能上都有了显著的提升。

createApp做了哪些事情

  • 执行 createApp 首先会创建渲染器,这里要注意的是存在2种渲染器类型,并且它们都是通过延迟创建的,主要目的是当用户只引用reactive响应式框架的时候,方便进行tree-shaking优化。且两种渲染器都是基于 baseCreateRender 方法来实现。
  • baseCreateRender 函数执行后会返回 render 渲染函数和 createApp 方法,其中 render 函数是组件创建、更新和卸载的主要核心逻辑实现。createApp则用于创建应用实例,进行应用实例的初始化。
  • createAppAPI用于生成默认的应用上下文 context,这里定义了应用实例具备的属性和方法,并通过重写扩展 context.app 属性,让用户能够进行对上下文的自定义操作,比如自定义组件、指令、mixin、插件安装等一系列操作。并存在mount方法完成将根组件转为虚拟节点 vNode,并通过render 函数完成对 vNode 的渲染

reactive和ref的区别是什么

reactiveref是Vue 3中用于创建响应式数据的两个重要API,它们之间存在一些关键的区别,主要体现在用途、使用方式和性能影响等方面。

  1. 用途
    • reactive:主要用于创建响应式的对象或数组。当对象的属性或数组的元素发生变化时,能够自动触发视图的更新。reactive会递归地将对象的所有嵌套属性都变成响应式,确保整个对象的变化能够被追踪。
    • ref:主要用于创建响应式的基本数据类型(如数字、字符串、布尔值等)或引用类型(如对象、数组,但此时对象或数组作为整体被引用)。虽然ref也可以用于对象或数组,但通常不推荐这样做,因为ref创建的响应式对象或数组需要通过.value属性来访问和修改其值,这在使用上相对繁琐。然而,当需要存储单个复杂类型的数据(如对象或数组)并希望保持其响应性时,ref是一个可行的选择。
  2. 使用方式
    • reactive:通过reactive函数传入一个对象或数组,返回一个响应式的对象或数组。对该对象或数组的任何非响应式操作(如直接替换整个对象或数组)都需要谨慎处理,因为这样做可能会丢失响应性。
    • ref:通过ref函数传入一个值(可以是基本数据类型或引用类型),返回一个响应式的ref对象。该对象包含一个名为value的属性,用于存储传入的值。访问或修改该值时,需要通过.value属性进行。
  3. 性能影响
    • reactive:由于reactive会递归地将对象的所有嵌套属性都变成响应式,因此在处理大型对象或深层嵌套的对象时,可能会带来一定的性能开销。然而,这种开销通常是可以接受的,因为Vue的响应式系统被设计得非常高效。
    • ref:对于基本数据类型,ref的性能开销几乎可以忽略不计。但是,当使用ref来存储大型对象或数组时,虽然这些对象或数组本身不是响应式的(需要通过.value访问),但额外的.value访问可能会使代码更加繁琐,并且在某些情况下可能会影响可读性。然而,从性能角度来看,refreactive在处理大型对象或数组时的性能差异并不显著。
  4. 总结
    • 选择原则:在Vue 3中,推荐根据数据的类型和用途来选择使用reactive还是ref。对于基本数据类型或需要作为整体引用的复杂类型(如对象或数组),可以使用ref。对于需要频繁访问其属性的对象或数组,以及希望自动追踪其所有嵌套属性变化的场景,应使用reactive
    • 注意事项:无论是使用reactive还是ref,都需要注意保持数据的响应性。特别是当使用reactive时,应避免直接替换整个对象或数组,因为这可能会导致响应性丢失。如果需要替换整个对象或数组,请考虑使用Vue提供的特定方法(如Vue.set,但在Vue 3中更推荐使用reactive配合数组的响应式方法或triggerRef等)来确保响应性不被破坏。

Vue3和Vue2的区别

Vue3和Vue2在多个方面存在显著的区别,这些区别主要体现在性能、API设计、响应式系统、TypeScript支持以及组件结构等方面。以下是对这些区别的详细分析:

  1. 性能提升
    • Vue3:通过重写虚拟DOM的实现和优化编译模板,Vue3实现了更快的渲染速度和更低的内存使用率。据官方数据,Vue3的更新性能比Vue2提高了1.32倍,而服务器端渲染(SSR)速度则提高了23倍。此外,Vue3还通过webpack的tree-shaking功能减少了最终打包的体积,使得应用更加轻量。
    • Vue2:虽然Vue2已经是一个高效的框架,但在处理大量数据或复杂组件时,其性能表现可能不如Vue3。
  2. API设计
    • Vue3:引入了Composition API,这是一种新的API设计方式,允许开发者以更灵活的方式组织组件的逻辑。通过Composition API,开发者可以将组件的功能拆分成更小的、可复用的函数(称为composables),这有助于构建大型应用并保持代码的可维护性。同时,Vue3也保留了Options API,以便与现有的Vue2项目兼容。
    • Vue2:使用Options API,通过分割不同的属性(如data、computed、methods等)来组织组件的逻辑。这种方式在小型项目中可能较为直观,但在大型项目中可能会导致代码难以管理和维护。
  3. 响应式系统
    • Vue3:使用基于ES6的Proxy API来实现响应式系统,这使得Vue3能够检测到更多类型的变化,如属性的添加和删除、数组内部的变化等。Proxy虚拟化目标数据并设置不同的处理程序,通过getters和setters拦截数据,从而无需使用vm.$set来让数据动态响应。
    • Vue2:使用基于Object.defineProperty的响应式系统,这种方式无法检测到对象属性的添加或删除,也无法直接追踪基于数组索引的变化或数组长度的变化。
  4. TypeScript支持
    • Vue3:从一开始就把TypeScript作为一等公民来考虑,提供了更好的TypeScript支持。Vue3的API和类型定义都是基于TypeScript编写的,这为开发大型应用提供了更好的支持。
    • Vue2:在设计时并没有将TypeScript支持作为优先事项,这导致在使用TypeScript进行大型项目开发时会遇到一些难题。
  5. 组件结构
    • Vue3:支持多个根节点(fragments),这意味着组件的模板中可以包含多个根元素。此外,Vue3还引入了Teleport组件,允许开发者将模板内容渲染到DOM中的其他位置。
    • Vue2:不支持多个根节点,每个组件的模板只能有一个根元素。
  6. 生命周期钩子
    • Vue3:生命周期钩子函数有所变化,如beforeCreate和created被替换为setup(作为组件的入口点),而beforeMount、mounted等钩子函数则需要通过on前缀来调用(如onBeforeMount、onMounted)。
    • Vue2:使用传统的生命周期钩子函数,如beforeCreate、created、beforeMount、mounted等。
  7. 模板指令和语法
    • Vue3:对模板指令和语法进行了一些更新和优化,如移除了v-on.native修饰符和keyCode作为v-on的修饰符,并增加了v-slot作为默认的插槽语法。
    • Vue2:使用传统的模板指令和语法。

综上所述,Vue3在性能、API设计、响应式系统、TypeScript支持以及组件结构等方面相比Vue2都有了显著的改进和提升。这些改进使得Vue3成为构建现代Web应用的强大工具。

Vue3的新工具 vite 和 Webpack 有什么区别

  • webpack会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。
  • 而vite是直接启动开发服务器,请求哪个模块再对该模块进行实时编译。
  • 由于现代浏览器本身就支持ES Module,会自动向依赖的Module发出请求。vite充分利用这一点,将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像webpack那样进行打包合并。
  • 由于vite在启动的时候不需要打包,也就意味着不需要分析模块的依赖、不需要编译,因此启动速度非常快。当浏览器请求某个模块时,再根据需要对模块内容进行编译。这种按需动态编译的方式,极大的缩减了编译时间,项目越复杂、模块越多,vite的优势越明显。
  • 在HMR方面,当改动了一个模块后,仅需让浏览器重新请求该模块即可,不像webpack那样需要把该模块的相关依赖模块全部编译一次,效率更高。
  • 当需要打包到生产环境时,vite使用传统的rollup进行打包,因此,vite的主要优势在开发阶段。另外,由于vite利用的是ES Module,因此在代码中不可以使用CommonJS
  • 43
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

杨树林er

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

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

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

打赏作者

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

抵扣说明:

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

余额充值