Vue 面试题
Vue
说说对vue的理解
vue是一个基于用户页面的JavaScript库
特点:
数据驱动 => MVVM双向数据绑定结构
组件化 => 减少代码复用、提高可维护性
虚拟DOM
指令系统 => vue可以通过v-的前缀来实现一些操作
常用的指令有
v-if 条件渲染
v-show 控制隐藏
v-on 绑定事件 @简写
v-for 循环渲染
v-bind 属性绑定 :简写
v-model 双向数据绑定
Vue和react的相同和不同
相同点:
组件化操作
虚拟DOM
不同点:
react数据驱动为MVC单项数据流,vue为MVVM双向数据流
react diff算法是计算差异后统一更新,vue是边计算差异,边更新
响应式原理
- vue2
主要由三个部分完成,首先是observer,这个主要是通过object.defineProperty来拦截对象,给每个对象添加geter和seter属性;然后通过watcher来订阅observer的变化,当对象发生改变的时候会通知compiler这个编译器;最后通过compiler编译器完成视图更新。 - vue3
和vue2的部分相同,在vue3中observer是通过proxy拦截对象,可以直接拦截无需遍历。
Diff算法原理
vue中的Diff和虚拟DOM是底层的核心部分
指针
- vue2
Diff算法用来做****新旧虚拟DOM树的比较,最小化的更新视图,而不是全部更新。
在这个比较过程中Diff算法会****通过指针的方式,一层一层的比较,并且更新的操作是比较完毕之后就会更新。 - vue3
和vue2的diff基本一致,不同的是vue3新增静态树提升,这意味着编译时会跳过静态树从而节省性能开销。
flag标记,无需手动添加key也可以监听遍历的节点
Vue中自定义指令的理解,应用场景有哪些?
自定义指令分为****全局自定义指令和局部自定义指令
**全局定义的方式是 **
Vue.directive(‘名称’,
{ bind: ()=>{} ,inserted: ()=>{} },update: ()=>{},unbind: ()=>{}
)**局部自定义指令是 在vue文件中 **
directives:{ “名称”:{} }**应用场景:**表单验证、图片懒加载、无限滚动、右键菜单、复制粘贴等
V2和V3的区别
- **v2 的 **
optionAPI和 v3 的Composition API:
Vue3引入了Composition API,并且支持optionAPI。
可以更好地组织和复用组件逻辑。
与Vue2中的Options API那种面向对象式的更灵活,
Composition API提供了更灵活、可组合和易于测试的方式来编写组件。- TypeScript支持:Vue2对TypeScript的支持并不完全,需要额外的配置和类型声明。而Vue3对TypeScript的支持更加友好,内置了更多的类型声明,并通过改进的响应式系统实现了更好的类型推断。
- 源码大小:Vue3的源码相对较小,压缩后的代码比Vue2更小。这意味着在网络传输和加载时间方面,Vue3更加高效。
- v2的
Object.defineProperty 和 proxy- v3中除去了
filter官方推荐使用监听器watch\watchEffect来进行一些数据过滤的操作
v-once指令,在v2中绑定事件时只能执行一次
v-html,在v2中用来解析数据中的标签,并渲染相应的标签
对于MVVM的理解
MVVM是一种软件架构模式,它将应用程序分为三个核心部分:Model(模型)、View(视图)和ViewModel(视图模型)。它的目标是实现数据与视图之间的解耦,使得开发者能够更好地管理和维护代码。
以下是对MVVM的各个组成部分的简要说明:
- Model(模型):模型代表着应用程序的数据和业务逻辑。它负责处理数据的获取、存储、验证和操作等任务。模型可以是数据库、网络请求返回的数据、或者从其他数据源中获取的数据。
- View(视图):视图是用户界面的可见部分,例如网页上的HTML元素或移动应用程序中的UI控件。视图负责展示数据,并与用户进行交互。在MVVM中,视图应该尽量保持简单和无状态,它主要负责显示来自ViewModel的数据,并将用户的操作转发给ViewModel。
- ViewModel(视图模型):视图模型是连接模型和视图的桥梁。它包含了视图所需展示的数据以及与视图交互的方法和命令。视图模型通过数据绑定机制将模型中的数据与视图中的元素进行绑定,当模型数据改变时,视图会自动更新。
MVVM的关键点在于数据绑定机制,它使得数据的变化能够自动反映到视图中,而不需要手动的DOM操作。这种双向绑定的机制可以减少大量的样板代码,并提高开发效率。
总结起来,MVVM是一种将视图、模型和视图模型分离的架构模式,通过数据绑定实现视图与模型之间的自动同步。它能够提供更好的代码组织和可维护性,同时也降低了开发的复杂度和耦合度。
Vue3中的组合式Api和option Api的区别是什么?
- 组合式API的灵活性:组合式API更加灵活和可组合,可以将相关逻辑划分为多个函数,并根据需要进行组合和复用。相比之下,Options API更加面向对象,需要在同一个选项对象中定义所有的逻辑。
- 组合式API的代码组织方式:组合式API通过逻辑相关的功能组件来组织代码,每个功能组件都可以包含自己的状态、计算属性、方法等。而Options API则是通过选项对象来组织代码,不同的逻辑通过不同的选项来定义。
- 组合式API的可读性:由于组合式API将相关逻辑划分为函数,每个函数只关注一部分逻辑,因此代码更易于阅读和理解。而Options API则将所有的逻辑放在一个对象中,代码可能会变得冗长和复杂。
- 组合式API的类型推断:由于组合式API使用的是函数,Vue3可以更好地推断出函数内部的类型。这使得在使用TypeScript等静态类型检查工具时,能够获得更准确的类型提示和错误检查。
总体来说,组合式API提供了更灵活、可组合和可读性更高的方式来组织和复用代码。它使得组件的逻辑更加清晰,且能够更好地与TypeScript等静态类型检查工具集成。而Options API则更适合简单的组件或者对面向对象编程更熟悉的开发者。
什么是Vue SSR
Vue SSR (Server-Side Rendering) 指的是在服务器端渲染Vue应用程序,生成最初的HTML页面,并将其发送到客户端进行进一步的交互。
传统的Vue应用程序通常在浏览器中运行,使用客户端渲染 (Client-Side Rendering, CSR)。在CSR中,浏览器会下载包含Vue组件的JavaScript文件,并在客户端执行和渲染组件。这种方式可以提供丰富的交互体验,但对于SEO(搜索引擎优化)和首次加载性能等方面存在一些挑战。
相比之下,Vue SSR通过在服务器上预先渲染Vue组件,生成完整的HTML响应。当浏览器请求页面时,服务器直接返回预渲染好的HTML,而无需等待JavaScript文件的加载和执行。这样可以使搜索引擎更好地索引页面内容,并提供更快的首次加载时间,特别是对于慢速网络或低性能设备来说。
Vue SSR的工作原理如下:
- 服务器端接收到HTTP请求,并根据路由匹配相应的Vue组件。
- 服务器创建一个Vue实例,并调用组件的生命周期钩子函数进行数据获取和初始化。
- 当所有数据准备就绪后,服务器使用Vue的渲染器将Vue组件渲染成完整的HTML字符串。
- 服务器将渲染好的HTML字符串作为响应返回给客户端浏览器。
- 客户端浏览器接收到HTML响应,并加载JavaScript文件。一旦Vue实例在浏览器中激活,它会接管静态HTML,并将其转换为动态交互的Vue应用程序。
Vue SSR需要一些特定的配置和服务器环境来支持,例如使用Node.js来运行服务器端代码。同时,由于涉及到前后端渲染的共享逻辑,开发过程中需要注意处理好数据的同步和一致性。
总结来说,Vue SSR是一种通过在服务器上进行预渲染的方式,提供更好的SEO和首次加载性能的解决方案。它在服务器端渲染Vue组件,并将预渲染好的HTML响应发送到客户端浏览器,以实现更好的用户体验和搜索引擎优化。
vue路由的钩子函数有哪些
Vue路由提供了一系列的导航守卫,也称为钩子函数,用于在路由切换过程中执行特定的逻辑。以下是Vue路由的常用钩子函数:
- beforeEach(to, from, next):全局前置守卫,每次路由切换之前都会被调用,可以用于进行全局的身份验证或权限检查等。
- **afterEach(to, from):全局后置守卫,每次路由切换之后都会被调用,没有 **
next函数,无法改变导航。- beforeEnter(to, from, next):路由独享的前置守卫,在单个路由配置中使用,用于对该路由进行特定的身份验证或权限检查等。
- **beforeRouteEnter(to, from, next):组件内的守卫,当路由将要进入某个组件时被调用,允许访问组件实例,但此时还不能访问组件的 **
this对象。- beforeRouteUpdate(to, from, next):组件内的守卫,当路由参数发生变化,但是该组件被复用时调用,允许访问组件实例,并可以通过比较新旧路由参数来做相应的操作。
- beforeRouteLeave(to, from, next):组件内的守卫,当路由将要离开组件时调用,允许访问组件实例,可以在用户离开当前页面前进行一些确认操作。
Vue的路由实现:hash模式 和 history模式原理
Vue的路由实现包括两种模式:hash模式和history模式。
- Hash模式: 在Hash模式下,URL中会带有一个特殊字符“#”,后面跟着路由路径。当浏览器向服务器发送请求时,不会包含这部分hash值。浏览器通过监听
hashchange事件来感知URL的变化,并根据hash值来切换不同的路由组件。**例如,对于URL **
http://example.com/#/home,浏览器只会将http://example.com/发送给服务器,而#/home部分则由浏览器自身处理。
- History模式: 在History模式下,URL没有特殊字符,通过HTML5 History API来管理URL的变化。它使用了
pushState()和replaceState()方法来添加和修改浏览器历史记录条目,并通过popstate事件来感知URL的变化。
V3的设计目标,做了哪些优化?
V3的设计目标是,更小、更快、更完善的ts环境
优化:
- 编译优化
优化diff算法,v3中的diff新增flag标记。当dom树发生变化时比较flag标记- 静态提升
v3将不参与更改的元素,会做静态提升,只会创建一次。下次渲染直接复用
例如<template> <div>你好</div> <p>{{ text }}</p> </template> // 这里div标签就属于不参与更改的元素,v3会做静态提升,将这个node节点保存到render外面。 // 当下次更新视图时,div这个纯html就会在缓存中复用- 事件监听缓存
- SSR优化,将多个静态内容,通过createStaticVNode方法创建。,这个方法会将静态内容innerHtml到页面中
V3中的proxy和v2中的object.defineProperty的区别,有哪些优势
object.defineProperty和proxy都是用来拦截对象,对原对象进行一些增删改查操作
- object.defineProperty
它接收一个对象返回一个新的对象,并且它有存取描述符和数据描述符。
在v2中主要使用了存取描述符,通过get来获取对象的数据,通过set方法来修改对象的数据。
v2中新添加了delete方法用于销毁对象的属性- proxy
proxy也是es6新增的一大特性
它对比object.defineProperty更强大。首先是拦截方式
object.defineProperty只能遍历对象拦截。
proxy可以直接拦截整个对象,并复用对象当中的所有方法。
说说SPA开发模式的首次加载页面慢的原因,怎么解决
原因是:
网络原因、资源文件较大
解决方案:
静态资源的本地缓存、将一些UI组件按需加载
说说Teleport的理解
v3新增的特性,在v2中要实现一个弹出框可能会影响布局。 在v3中新增了teleport组件,它有一个to属性可以将组件中的内容,发送到body标签中,从而不影响布局
<teleport to='body'> <div class='model'> ... ... ... </div> </teleport>
Vue 3 的 Fragments 是什么?
Fragments 是 Vue 3 中的一项新特性,它允许我们在模板中返回多个根元素。这可以让我们更灵活地组织模板结构。
在 Vue 2 中,每个组件的模板都必须有一个根元素。这意味着,如果我们想要在模板中返回多个元素,我们必须将它们包裹在一个根元素内部。
而在 Vue 3 中,我们可以使用 Fragments 来返回多个根元素。这样,我们就不需要再使用一个额外的根元素来包裹多个元素了。
例如:
<template> <div>Item 1</div> <div>Item 2</div> </template>
Vue 3 的 Suspense 是什么?
Suspense 是 Vue 3 中的一项新特性,它允许我们在组件的异步依赖加载完成之前显示一个 fallback 内容。这可以让我们更好地处理异步数据加载的情况。
在 Vue 2 中,我们通常需要在组件内部使用一个状态变量来跟踪数据是否加载完成,并根据这个状态变量来决定是否显示加载中的提示。
而在 Vue 3 中,我们可以使用 Suspense 来简化这个过程。我们只需要将组件包裹在一个 Suspense 元素中,并为 Suspense 元素提供一个 fallback 插槽,用来指定在异步依赖加载完成之前要显示的内容。当异步依赖加载完成后,Suspense 会自动切换到默认插槽中的内容。
<template> <Suspense> <template #default> <AsyncData /> </template> <template #fallback> <div>Loading...</div> </template> </Suspense> </template> <script> import AsyncData from './AsyncData.vue'; export default { components: { AsyncData, }, }; </script>在这个例子中,我们使用了一个 **Suspense 元素来包裹
AsyncData组件。在AsyncData组件的异步依赖加载完成之前**,Suspense 会显示 fallback 插槽中的内容(即“Loading…”)。当异步依赖加载完成后,Suspense 会自动切换到默认插槽中的内容(即AsyncData组件)。
严格模式
严格模式下的一些限制和检查包括:
- 禁止直接修改组件实例的根数据(data):在严格模式下,**只有组件的方法(methods)才能修改组件实例的根数据。如果我们在计算属性(computed)或侦听器(watcher)中试图直接修改根数据,Vue会发出警**告。
- 侦测不到的异步更新警告:在严格模式下,如果我们在异步更新的回调中修改了响应式数据(例如setTimeout或事件处理程序),Vue会发出警告。这是因为Vue无法在异步更新中检测到数据的变化。
- 更严格的模板编译:在严格模式下,Vue的模板编译会更加严格,一些不规范的写法会被禁止。例如,禁止在模板中使用未定义的变量,禁止使用无效的表达式等。
通过开启严格模式,我们可以更早地发现代码中的问题,并且遵循Vue的最佳实践和规范,以提高代码的可维护性和可靠性。我们可以通过在****Vue实例的选项中设置
strict: true来开启严格模式。默认情况下,严格模式是禁用的。
v-if和v-for哪个优先级高
- v2中v-for优先级高
- v3中v-if优先级高
watch和watchEffect的区别
watch用来监听指定的数据。当指定的数据变化时后做一些操作
watchEffect是自动监听,在watchEffect中包含的数据都会被监听到
v3中的TreeShaking的特性
TreeShaking是v3的新特性。
它基于ES6的模块化规范,通过清除多余代码的方式来提高打包速度。
保证代码能够正常运行的前提下,清除多余的无用代码。
优点:
减少打包体积
加快打包速度
vue中常用的修饰符
- 表单修饰符
- number转换数字类型
- trim清除两端空格
- lazy在光标离开时触发
- 事件修饰符
- stop阻止事件冒泡
- prevent阻止默认事件
- self只在自身触发
- once触发一次
- native使根组件的事件定义成子组件的原生事件
- 鼠标修饰符
- left左键
- right右键
- middle中间
- 键盘修饰符
- 支持keycode码
- 支持键盘上的一些功能键等
- enter
- delete
3种Vue3添加公共方法并使用
第一种:使用 app.config.globalProperties 添加
import { createApp } from 'vue'
import axios from 'axios'
const app = createApp({
created() {
console.log(this.$axios)
}
})
app.config.globalProperties.$axios = axiosapp.mount('#root')
在setup中访问(setup中没有this)
<script setup>
import {getCurrentInstance} from 'vue'
const { proxy } = getCurrentInstance();//获取公用方法proxy.$axios,或者use中方法
const {$axios}=proxy
console.log($axios)
</script>
第二种:使用 app.mixin 添加
import { createApp } from 'vue'
import axios from 'axios'
const app = createApp({
created() {
console.log(this.$axios)
}
})
app.mixin({
methods: {
$axios: axios
}
})
app.mount('#root')
第三种:采用 provide, inject 方法
需要注意的是这种方法需要组建 inject 注入进组件才能使用。
import { createApp } from 'vue'
import axios from 'axios'
const app = createApp({
inject: ['$axios'],
created() {
console.log(this.$axios)
}
})
app.provide('$axios', axios)
app.mount('#root')