前言:本文资料来自于web前端面试以及Vue的官方文档,对其中的信息做了更新和归纳。
对vue的理解
一、从历史说起
- 纯前端(静态)
- ASP和JSP (Java+HTML),不太灵活,服务端渲染
- Jquery
- SPA
二、Vue是什么?
- 是一个用于创建用户界面的开源JavaScript框架,也是创建单页应用(SPA)的Web应用框架。
三、Vue核心特性
- 数据驱动(MVVM)模型-视图-视图模型的简写。模型层负责业务逻辑和与服务器的交互;视图层负责将数据模型转化为UI展示出来;视图模型层,用来连接模型和视图。
- 组件化
- 组件化就是将各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,每个
.vue
文件都可以视为一个组件。 - 组件化的优势
- 降低系统的耦合度,在接口不变的情况下,可以替换不同的组件快速开发。
- 调试方便,出现问题时,可以通过移除组件的方式定位问题,正是由于组件之间的低耦合,职责单一。
- 提升可维护性,复用性高,职责单一。
- 组件化就是将各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,每个
- 指令系统:
v-for
,v-if
,v-show
,v-bind
,v-on
,v-model
等。
四、Vue与传统开发的区别
- Vue的界面事件都只操作数据,而Jquery操作DOM。
- Vue的界面变动是根据数据自动绑定出来的,而Jquery操作DOM。
五、Vue和React对比
- 相同点:
- 组件化思想
- 支持服务器端渲染
- 都有虚拟DOM
- 数据驱动视图
- 都有支持native的方案
- 都有自己的构建工具
- 区别:
- 数据流向不同,
React
单向,Vue
双向 - 数据变化的实现原理不同,
React
使用的是不可变数据,而Vue
使用的是可变数据。 - 组件间通信方式不同,
React
使用回调函数,而Vue
中子组件向父组件传递消息有事件和回调函数。
- 数据流向不同,
对SPA单页面应用的理解,优缺点是什么?怎么实现SPA应用?
一、什么是SPA
SPA
是一种网络应用程序或网站的模型,通过动态重写当前页面来与用户交互,避免了页面切换打断用户体验。所有必要的代码都通过单个页面的加载而检索,或者根据需要动态装载适当资源并添加到页面。单页页面在任何时间点都不会重新加载,也不会将控制转移到其他页面。
二、SPA和MPA的区别
- 与SPA相对的就是MPA多页应用,每个页面都是独立的主页面,访问另一个页面时会重新加载文件和代码。
比较项目 单页面应用(SPA) 多页面应用(MPA) 组成 一个主页面和多个页面片段 多个主页面 刷新方式 局部刷新 整页刷新 url模式 哈希模式 历史模式 SEO搜索引擎优化 难实现,可使用SSR方式改善 容易实现 数据传递 容易 通过url、cookie、localStorage等传递 页面切换 速度快,用户体验良好 切换加载资源,速度慢,用户体验差 维护成本 相对容易 相对复杂 - SPA的优缺点
- 优点:
- 具有桌面应用的即时性、网站的可移植性和可访问性
- 用户体验好、快,内容改变不需要重新加载整个页面
- 良好的前后端分离,分工明确
- 缺点:
- 不利于搜索引擎的抓取(SEO)
- 首次渲染速度相对较慢
- 优点:
三、实现一个SPA
- 原理
- 监听地址栏
hash
变化驱动页面变化 - 用
pushsate
记录浏览器的历史,驱动界面发送变化
- 监听地址栏
四、如何给SPA做SEO
- SSR服务器端渲染:通过服务器生成html,再返回给浏览器
- 静态化
- 使用
Phantomjs
针对爬虫处理
v-show和v-if对比
一、相同点:都能控制元素是否显示
二、区别:
- 控制手段不同:
v-show
是为该元素增加了cssdisplay:none
,DOM元素还在。而v-if
是将DOM元素添加或删除。
- 编译过程不同:
v-if
切换有一个局部编译/卸载的过程,过程中会合适地销毁和重建内部的事件监听和子组件;而v-show
只是简单的基于css切换。
- 编译条件不同:
v-if
由false
变为true
时,会触发生命周期钩子
- 性能消耗不同:
v-if
有更高的切换消耗;v-show
有更高的初始渲染消耗;
三、使用场景
- 频繁切换:
v-show
- 在页面渲染前就可以知道是否需要渲染,且渲染后很少切换:
v-if
Vue3生命周期整体流程
beforeCreate
:在组件实例初始化完成后立即调用。created
:在组件实例处理完所有与状态相关的选项后调用。- 当这个钩子被调用时,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。
beforeMount
:挂载前。- 当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。
mounted
:挂载后。- 其自身的 DOM 树已经创建完成并插入了父容器中。
- 所有同步子组件都已经被挂载。
beforeUpdate
:在组件即将因为一个响应式状态变更而更新其DOM树之前调用。updated
:在组件因为一个响应式状态变更而更新其 DOM 树之后调用。beforeUnmount
:卸载前。unmounted
:卸载后。- 组合式API的生命周期勾子,在周期前面加
on
,后面加()
,比如onMounted()
对应着mounted
。
v-if和v-for的优先级
- 首先不推荐同时使用,因为二者优先级不明显。
- 当它们同时存在于一个节点上时,在Vue3中,
v-if
比v-for
优先级更高,而在Vue2中,v-for
比v-if
优先级高。 - 如果要同时用的话,可以在外先包装一层
<template>
再在其上使用v-for
,使他们不在用一节点,可以解决这个问题。
怎么解决SPA首屏加载速度慢?
- 可以通过
服务器端渲染(SSR)
或静态站点生成(SSG)
来缓解。或者单独部署敏感型页面。 - 使用现代的构建工具,可以使用
tree-shake
,仅打包使用到的组件和依赖。 - 如果使用了
Vue Router
,可以使用路由懒加载,当路由被访问的时候才加载对应组件。
Vue中的组件和插件
- 组件是将各种逻辑抽象为一个统一的概念来实现开发的模式,在Vue中,每一个
.vue
文件被视为一个组件 - 插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码。比如vue-router和highlight、i18n等插件。
- 区别:
- 编写形式:
- 组件有标准文件格式,
JS+ template+ style
; - 插件的实现应该暴露一个
install
方法,这个方法的第一个参数是Vue构造器,第二个参数是一个可选的选项对象。
- 组件有标准文件格式,
- 注册形式:
- 组件可以全局注册或局部注册,局部在
<script setup>
下可以直接导入使用; - 插件在
main.js
中通过Vue.use()
进行全局注册。
- 组件可以全局注册或局部注册,局部在
- 使用场景:
- 组件用来构建应用的业务模块,目标是
App.vue
- 插件用来增强技术栈的功能模块,目标是
Vue
本身
- 组件用来构建应用的业务模块,目标是
- 编写形式:
Vue组件间通信方式有哪些?
一、父子组件通信
props
:父传子$emit
:子传父组件v-model
:可以在组件上使用以实现双向绑定。通过使用defineModel()
宏来通信,返回的值是一个ref
。
二、跨层级组件通信
provide
和inject
三、全局状态管理
Vuex
Pinia
(Vuex的升级版)
对双向数据绑定的理解
- v-model 指令实现了数据和视图之间的双向同步,其机制依赖于 Vue 的响应式系统(数据驱动视图)和 DOM 事件监听(视图更新数据)。
- 模型>>视图:Vue3使用
Proxy
对数据对象进行劫持,拦截对数据的访问和修改,进而实现响应式。当修改了响应式状态时,DOM自动更新,Vue会在next tick
更新周期中缓冲所有状态的修改,以确保不管进行了多少次状态修改,每个组件都只会被更新一次。 - 视图>>模型:
v-model
根据所使用的元素自动使用对应的DOM属性和事件组合(input
或change
事件),相关的事件修改值,然后触发响应式系统更新视图。
nextTick( )的理解
nextTick()
是等待下一次DOM更新刷新的工具方法。- 在Vue中更改响应式状态时,最终的DOM更新并不是同步生效的,而是由Vue将他们缓存在一个队列中,直到下一次 “tick” 才一起执行。这样是为了确保每个组件无论发生多少状态变化,都仅执行一次更新,
next tick
是一种性能优化策略。
对Vue的mixin的理解
- 在 Vue 2 中,
mixins
是创建可重用组件逻辑的主要方式。尽管在 Vue 3 中保留了mixins
支持,但对于组件间的逻辑复用,使用组合式 API 的组合式函数是现在更推荐的方式。 mixins
的三个主要的短板:- 不清晰的数据来源:当使用多个
mixin
时,实例上的数据属性来自哪个mixin
变得不清晰,使得追溯实现和理解组件行为变得困难。在组合式函数中,使用ref
+解构模式,解决了这个问题,让属性的来源在消费组件时一目了然。 - 命名空间冲突:多个来自不同作者的
mixin
可能会注册相同的属性名,造成命名冲突。若使用组合式函数,可以通过在解构变量时对变量重命名来避免相同的键名。 - 隐式的跨
mixin
交流:多个mixin
需要依赖共享的属性名来进行相互作用,这使得它们隐式地耦合在一起。而一个组合式函数的返回值可以作为另一个组合式函数的参数被传入,像普通函数一样。
- 不清晰的数据来源:当使用多个
对slot插槽的理解
Vue中key的原理和理解
key
这个特殊的 attribute 主要作为 Vue 的虚拟 DOM 算法提示,在比较新旧节点列表时用于识别 vnode。- 在没有 key 的情况下,Vue 将使用一种最小化元素移动的算法,并尽可能地就地更新/复用相同类型的元素。如果传了 key,则将根据 key 的变化顺序来重新排列元素,并且将始终移除/销毁 key 已经不存在的元素。
怎么缓存当前的组件?缓存后怎么更新?怎么理解keepAlive?
<keepAlive>
是Vue的内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。<!-- 非活跃的组件将会被缓存! --> <KeepAlive><component :is="activeComponent" /> </KeepAlive>
- 通过传入
include
或exclude
prop来包含或者排除缓存项,它们会匹配name
项。 - 通过传入
max
prop来限制可被缓存的最大组件实例数。超过缓存最大数量时,最久未被访问的缓存实例将被销毁。 - 缓存实例的生命周期:新增
onActivated()
和onDeactivated()
- 初次进入组件:
beforeRouteEnter
>beforeCreate
>created
>beforeMount
>mounted
>activated
> … >beforeRouteLeave
>deactivated
- 再次进入组件:
beforeRouteEnter
>activated
> … >beforeRouteLeave
>deactivated
- 初次进入组件:
- 缓存后如何获取数据?可以使用
activated
或者beforeRouteEnter
勾子来获取。
Vue常用的修饰符有哪些?有什么应用场景?
v-on
修饰符.stop
:调用event.stopPropagation()
阻止事件冒泡。.prevent
:调用event.preventDefault()
阻止事件的默认行为。比如点击链接跳转、提交表单刷新、点击右键弹出上下文菜单等。.capture
:在捕获模式添加事件监听器。.self
:只有事件从元素本身发出才触发处理函数。.{keyAlias}
:只在某些按键下触发处理函数。按键修饰符
once
:最多触发一次处理函数。- 鼠标按键修饰符:
.left
、.right
、.middle
.passive
: 通过{ passive: true }
附加一个 DOM 事件,一般用于触摸事件的监听器。
v-bind
修饰符.camel
:将短横线命名的attribute转变为驼峰式命名。.prop
:强制绑定为DOM property.attr
:强制绑定为DOM attribute
v-model
修饰符.lazy
:监听change
事件,而非input
事件。.number
:将输入的合法字符串转为数字。.trim
:移除输入内容两端空格。
Vue的自定义指令
虚拟DOM
- 虚拟 DOM (Virtual DOM,简称 VDOM) 是一种编程概念,意为将目标所需的 UI 通过数据结构“虚拟”地表示出来,保存在内存中,然后将真实的 DOM 与之保持同步。这个概念是由 React 率先开拓,随后被许多不同的框架采用,当然也包括 Vue。
vnode
即一个纯 JavaScript 的对象 (一个“虚拟节点”),它代表着一个<div>
元素。它包含我们创建实际元素所需的所有信息。它还包含更多的子节点,这使它成为虚拟 DOM 树的根节点。- 一个运行时渲染器将会遍历整个虚拟 DOM 树,并据此构建真实的 DOM 树。这个过程被称为挂载 (mount)。
- 如果我们有两份虚拟 DOM 树,渲染器将会有比较地遍历它们,找出它们之间的区别,并应用这其中的变化到真实的 DOM 上。这个过程被称为更新 (patch),又被称为“比对”(diffing) 或“协调”(reconciliation)。
- 虚拟 DOM 带来的主要收益是它让开发者能够灵活、声明式地创建、检查和组合所需 UI 的结构,同时只需把具体的 DOM 操作留给渲染器去处理。
- Vue编译器用来提高虚拟DOM运行时性能的主要优化:
- 静态提升:自动提升完全静态的节点,比对时会跳过它们。
- 更新类型标记(patch flag):用于标记每个元素的更新类型。
- 树结构打平:当组件需要重渲染时,只需要遍历由所有带更新类型标记的后代节点所构成的这个打平的树而非整棵树。
Vue的Diff算法
- 深度优先,只对比同层级的节点
- 比较的过程中,循环从两边向中间收拢
- 比较节点的 类型 和 key,如果有变化,Vue 会进行 最小化更新。
Axios
SSR(服务端渲染)
Vue项目的目录结构,怎么设计的?
- 文件夹和文件夹内部文件的语义一致
- 单一入口/出口
- 就近原则,紧耦合的文件放在一起,且以相对路径引用
- 公共的文件以绝对路径的方式从根目录引用
/src
外的文件不应该被引入
Vue怎么做权限管理?控制到按钮级别的权限怎么做?
一、权限是什么?
- 权限的本质 是通过某种机制对用户的访问范围和操作能力进行约束
- 前端权限归根结底是请求的发起权。请求的发起可能有以下两种形式:
- 页面加载触发。
- 页面上的按钮点击触发。
- 总的来说,所有的请求发起都触发自前端路由或视图
- 路由方面,用户登录后只能看到自己有权访问的导航菜单,也只能访问自己有权访问的路由地址,否则将跳转
4xx
提示页。 - 视图方面,用户只能看到自己有权浏览的内容和有权操作的控件。
- 最后请求控制作为最后防线,越权请求将在前端被拦截。
- 路由方面,用户登录后只能看到自己有权访问的导航菜单,也只能访问自己有权访问的路由地址,否则将跳转
二、具体怎么控制权限
- 前端权限控制可以分为四个方面
- 接口权限控制:一般采用
jwt
(JSON Web Token)的形式来验证,没有通过的话一般返回401
,跳转到登录页面重新登录,登录完拿到token
,将token
存起来,通过axios
请求拦截器进行拦截,每次请求的时候头部携带token
。 - 路由权限控制:在路由标记相应的权限信息,每次路由跳转前做校验。
- 菜单权限控制:可以理解为页面与路由进行解耦。
- 按钮权限控制:可以通过
v-if
进行判断。
- 接口权限控制:一般采用
Vue项目中如何解决跨域?
一、跨域是什么?
- 跨域是浏览器基于同源策略的一种安全手段,同源即:
- 协议相同(如:http)
- 主机相同(host)
- 端口相同(port)
二、如何解决跨域?
- 配置后端支持CORS
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
- JSONP(仅适用于GET请求)
- 使用后端网关或Ngnix代理
- 在生产环境中,可以通过反向代理解决跨域问题。
- 通过Nginx转发请求。
怎么处理Vue项目中的错误?
- 后端接口错误:可以通过
axios
的interceptor
实现网络请求的response
先进行一层拦截 - 代码逻辑错误:
- 全局设置错误处理
app.config.errorHandler = (err, instance, info) => { // 处理错误,例如:报告给一个服务 }
- 错误处理器接收三个参数:错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。
- 全局设置错误处理
- 生命周期勾子错误处理:
onErrorCaptured()
,注册一个钩子,在捕获了后代组件传递的错误时调用。
Vue3与Vue2的区别,有哪些升级?
- 响应式系统升级:由vue2的
Object.defineProperty
替换为Proxy
- 支持对数组和对象新增属性的监听。
- 提升性能,减少了底层实现的限制。
- 性能提升:
- 更快的渲染速度:重写了虚拟DOM,优化了对静态节点的处理。
静态提升
、更新类型标记
、树结构打平
- 更小的包体积。
- 更快的编译。
Tree-shaking
:没用到的功能不会被构建,减少包大小。
- 更快的渲染速度:重写了虚拟DOM,优化了对静态节点的处理。
- Composition API(组合式 API)
- 通过setup()将相关逻辑集中在一起,具有更清晰的逻辑组织。
- 更容易复用和封装逻辑(通过自定义Hooks)。
- 生命勾子变化
- 新增组合式勾子。
destroyed
勾子变为了unmounted
。
- Typescript支持
- 全局API通过app实例进行管理,而Vue2中直接在Vue对象上调用。
- 工具链的更新:
Vue Router
和Pinia
等。