1. Vue基础
Vue (读音 /vjuː/) 是一套用于构建用户界面的渐进式 JavaScript框架。
它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型; 帮助你高效地开发用户界面,无论任务是简单还是复杂;
1.1 Vue.js 的特点
- 易用: Vuejs是一个渐进式的框架,相比于其它框架,它更简单,易学,上手快。
- 灵活: (渐进式)不断繁荣的生态系统,可以在一个库和一套完整框架之间自如伸缩。
- 高效: 20kB min+gzip 运行大小;超快虚拟 DOM;最省心的优化。
- 双向绑定:开发效率高。
- 基于组件的代码共享
- Web项目工程化,增加可读性、可维护性
1.2 什么是 MVVM?
MVC和MVVM都是一种软件的体系结构
- MVC是Model – View –Controller的简称,是在前期被使用非常框架的架构模式,比如iOS、前 端;
- MVVM是Model-View-ViewModel的简称,是目前非常流行的架构模式; 通常情况下,我们也经常称Vue是一个MVVM的框架。
- Vue官方说明,Vue虽然并没有完全遵守MVVM的模型,但是整个设计是受到它的启发的。
1.3 说说你对 SPA 单⻚面的理解,它的优缺点分别是什么?
SPA( single-page application )仅在 Web ⻚面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦 ⻚面加载完成,SPA 不会因为用户的操作而进行⻚面的重新加载或跳转;取而代之的是利用路由机制实 现 HTML 内容的变换,UI 与用户的交互,避免⻚面的重新加载。
优点:
- 用户体验好、快,内容的改变不需要重新加载整个⻚面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
缺点:
- 初次加载耗时多:为实现单⻚ Web 应用功能及显示效果,需要在加载⻚面的时候将 JavaScript、 CSS 统一加载,部分⻚面按需加载;
- 前进后退路由管理:由于单⻚应用在一个⻚面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的⻚面切换需要自己建立堆栈管理;
- SEO 难度较大:由于所有的内容都在一个⻚面中动态替换显示,所以在 SEO 上其有着天然的弱势。
1.4 数组中的哪些方法会触发视图的更新?
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新,这些被包裹过的方法包括:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
上面的方法会直接修改原来的数组,所以它们会触发视图更新。
但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()、concat() 和 slice(),使用这些方法将不会触发视图更新。
1.5 Vue中v-for的key 有什么作用?
在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。
这个key属性有什么作用呢?
- key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes。
- 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。
- 使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素。
key 是 VNode 的唯一标记,通过这个 key, diff 操作可以更准确、更快速的达到复用节点,更新视图的 目的。复用节点就需要通过移动元素的位置来达到更新的目的。
1.6 computed和method有什么区别? 计算属性和方法:
-
计算属性和方法:
- 都可以通过this来访问
- 都可以对一些数据进行处理和计算
- 对于包含响应式数据计算的逻辑,应该使用计算属性,因为计算属性是有缓存。
-
computed和method的区别
- computed底层会缓存, 性能更高
- 计算属性会基于它们的依赖关系进行缓存;
- 在数据不发生变化时,计算属性是不需要重新计算的
- 但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算
1.7 什么是双向绑定?v-model的本质是什么?
双向绑定:
- 即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化
- v-model 是语法糖,它负责监听用户在表单元素中的输入事件来更新数据
表单元素使用v-model的本质:
- v-bind绑定value属性的值
- v-on绑定input事件监听到函数,函数会获取最新的值赋值到绑定的属性中
<input type="text" :value="message" @input="message = $event.target.value" />
组件使用v-model的本质:
- 将其 value attribute 绑定到一个名叫 modelValue 的 prop 上;
- 在其 input 事件被触发时,将新的值通过自定义的 update:modelValue 事件抛出(发出);
<Counter v-model="appCounter"/>
<!-- 相当于-->
<Counter v-bind:modelValue="appCounter" @update:modelValue="appCounter = $event"/>
1.8 data选项为什么是一个函数而不是对象?
JavaScript中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行 操作,其他实例中的数据也会发生变化。
而在Vue中,我们更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相 互干扰。
所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义。
这样当我们每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空 间,它们各自维护自己的数据,不会干扰其他组件的正常运行。
1.9 Vue data 中某一个属性的值发生改变后,视图会立即同步执行 重新渲染吗?
不会立即同步执行重新渲染。
Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循 环中发生的所有数据变更。
如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必 要的计算和 DOM 操作是非常重要的。
然后,在下一个的事件循环”tick”中,Vue 刷新队列并执行实际(已去重的)工作。
1.10 sass是什么?如何在vue中安装和使用?
sass是一种CSS预处理器语言,除此之外,less、stylus也是常⻅的CSS预处理器语言。 sass安装和使用步骤如下:
- 用npm安装加载程序( sass-loader、 css-loader等加载程序)。
- 在 webpack.config.js中配置sass加载程序。
sass使用和语法详细内容可以参考我的这篇文章《学习Scss看这篇》
1.11 在 Vue. js开发环境下调用API接口,如何避免跨域
- 在vue.config.js中的devServer选项中的proxy中配置反向代理
- 在vite.config.js中的server选项中的proxy中配置反向代理
- 直接后端开发人员配置cors
1.12v-show 与 v-if 有什么区别?
- 控制手段不同
- 编译过程不同
- 编译条件不同
控制手段:v-show
隐藏则是为该元素添加css--display:none
,dom
元素依旧还在。v-if
显示隐藏是将dom
元素整个添加或删除
编译过程:v-if
切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show
只是简单的基于css切换
编译条件:v-if
是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染
v-show
由false
变为true
的时候不会触发组件的生命周期v-if
由false
变为true
的时候,触发组件的beforeCreate
、create
、beforeMount
、mounted
钩子,由true
变为false
的时候触发组件的beforeDestory
、destoryed
方法
性能消耗:v-if
有更高的切换消耗,v-if当条件为false时,其对应的原生压根不会被渲染到DOM中v-show
有更高的初始渲染消耗,v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有存在的,只是通过CSS的display 属性来进行切换;
*
v-show是不支持template;
开发中如何进行选择呢?
- 如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show;
- 如果不会频繁的发生切换,那么使用v-if;
1.13 v-if和v-for一起使用的弊端及解决办法
Vue.js 中使用最多的两个指令就是 v-if 和 v-for ,因此开发者们可能会想要同时使用它们。虽然不 建议这样做,但有时确实是必须的,于是我们想提供有关其工作方式的指南。
-
2..x 版本中在一个元素上同时使用 v-if 和 v-for 时, v-for 会优先作用。
-
3.x 版本中 v-if 总是优先于 v-for 生效。
由于语法上存在歧义,建议避免在同一元素上同时使用两者。
比起在模板层面管理相关逻辑,更好的办法是通过创建计算属性筛选出列表,并以此创建可⻅元素,比如:
- 在v-for的外层或内层包裹一个元素(template)来使用v-if
- 用computed处理筛选出列表
1.14 谈谈你对 keep-alive 的了解?
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:
-
一般结合路由和动态组件一起使用,用于缓存组件。
-
提供 include 和 exclude 属性,两者都支持字符串或正则表达式。
- include 表示只有名称匹配的组件会被缓存。
- exclude 表示任何名称匹配的组件都不会被缓存。
- 其中 exclude 的优先级比 include 高。
-
对应两个钩子函数 activated 和 deactivated 。
- 当组件被激活时,触发钩子函数 activated。
- 当组件被移除时,触发钩子函数 deactivated。
1.15 说说Vue插槽的作用和平时开发中的应用?
插槽的作用:
- 支持在父组件自定义子组件中的个内容
- 让子组件更具有通用性,不必限定死某个内容
插槽平时开发中的应用:
- 在封装组件时,如果组件中的某个内容是动态的或不确定的,就可以使用插槽来代替了。
- 在使用第三方库时,往往会通过使用插槽类自定义第三方组件中的某些内容。
2. Component组件
2.1 父子组件的生命周期顺序
- 加载渲染过程: 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子 created -> 子beforeMount ->子mounted -> 父mounted
- 子组件更新过程:父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
- 父组件更新过程:父beforeUpdate -> 父updated
- 销毁过程:父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
2.2 组件通讯(传值)式有哪些?
- 父传子:子组件通过props来接收父组件传递的属性 xxx 的值
- 子传父:子组件通过emit触发事件传递,父组件通过监听对应的事件来接收数据
- Provide/Inject:父组件提供内容,子或孙组件可以注入父组件提供的内容。
- 组件实例:通过ref来拿到组件的实例,调用实例的属性或方法进行传值。
- 事件总线:可以自己编写EventBus插件来进行通讯,或世界使用第三方的事件总线库。
- 用Vuex/Pinia: 可以使用全局状态管理来进行全局共享数据。
2.3 什么是生命周期函数?Vue组件的生命周期函数有哪些? 生命周期函数:
生命周期函数
- 生命周期函数是一些钩子函数(回调函数),在某个时间会被Vue源码内部进行回调
- 通过对生命周期函数的回调,我们可以知道目前组件正在经历什么阶段
Vue2的生命周期函数:
-
beforeCreate :组件实例在创建之前
-
created: 组件被创建完成
- 可以发送网络请求
- 可以事件监听
- this.$watch()
-
beforeMount : 组件template准备被挂载
-
mounted :组件template已经被挂载
- 可以获取DOM,可以使用DOM
-
beforeUpdate: 准备更新DOM
-
updated: 更新DOM,根据最新数据生成新的VNode,生成新的虚拟DOM,转换为真实的DOM
-
beforeUnmount: 卸载之前
-
unmounted: DOM 元素被卸载完成
- 回收操作(取消事件监听)
3. Composition API
3.1 什么是Composition API 和 Options API?
Composition API:
- Composition API 是一组 API,允许我们使用导入的函数而不是声明选项来编写 Vue 组件。它是一 个涵盖以下 API 的总称:Reactivity API、Lifecycle Hooks、Dependency Injection等等
- 使用Composition API编写组件时可以根据逻辑功能来组织代码。比如可以把一个功能所用到的 API 放在一起,这样可以让代码高内聚和低耦合,进而提高了代码的逻辑的复用性。
- 在 Vue 3 中,它也主要与script setup语法一起使用。
Options API:
- 在对应的属性中编写对应的功能模块, 比如data定义数据、methods中定义方法、computed中定 义计算属性、watch中监听属性改变,也包括生命周期钩子
- 弊端: 当我们实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中,当组件变得复 杂,导致对应属性的列表也会增⻓,这可能会导致组件难以阅读和理解
3.2 Composition API和之Options API有什么区别?
在逻辑组织和逻辑复用方面,Composition API是优于Options API。 Composition API几乎是函数,会有更好的类型推断,对于TS的支持更友好。 Composition API对 tree-shaking 友好,代码也更容易压缩。
Composition API中⻅不到this的使用,减少了this指向不明的情况。 Composition API用起来稍微复杂一点,而Options API就非常简单、易于使用。
3.3 说说Vue3中setup函数的作用?
在Vue3中, setup() 函数充当了组件编写Composition API 的入口点。
-
setup函数参数主要有两个参数:
-
第一个参数:props , 父组件传递过来的属性会被放到props对象中
-
第二个参数:context, 它里面包含三个属性
- attrs:所有的非prop的attribute;
- slots:父组件传递过来的插槽;
- emit:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可 以通过 this.$emit发出事件)
-
-
可以在setup中可以定义响应式数据、方法、计算属性、侦听器等等。
-
可以通过setup的返回值来替代data选项,让数据可以直接在template中使用。
3.4 ref和reactive有什么区别?开发中如何选择?
-
ref和reactive都是响应式的API,都可以用来定义响应式的数据。
-
ref可以包裹任意数据类型,reactive只能包裹复杂数据类型,比如对象、数组。
-
ref返回一个ref对象,在script中取值需要通过value属性,但是在模板中使用会进行解包不需要调 用value。
-
reactive包裹的是复杂数据类型,直接取里面的属性即可。
-
ref几乎可以应用在任何场景,而且包含reactive适合的场景
-
reactive的应用场景比较受限,第一:值比较固定,第二:值与值之间是有联系的。
-
开发中尽量选择ref
3.5 Composition API常⻅的几个函数与用法?
-
ref
- 包裹任意类型的值,将包裹的值加入响应式
-
reactive
- 包裹复杂类型的值,将包裹的值加入响应式
-
computed
-
把一些复杂逻辑用computed进行包裹,如同Options API中的计算属性一样
-
computed会自动收集相关依赖,当依赖发生变化时,会自动进行更新
-
-
生命周期
-
Vue3中想要在beforeCreate和created中做的事,直接在setup中做即可
-
Vue3的其他的生命周期函数都要在前面加一个on,然后需要在vue中主动引入
-
-
watch
- watch可以监听单个数据源,也可以监听多个数据源
- watch是懒执行,第一次是不会执行的,除非你为其提供第三个参数中的immediate属性为 true
- watch只有等到监听的数据源发生了变化后,才会执行第二个参数(回调)
- watch可以获取监听数据源的前后变化的值
- 侦听多个数据源的时候,第一个参数是数组类型
-
watchEffect
-
watchEffect会自动收集依赖,收集的依赖是第一个参数,也就是回调函数中有哪些东⻄是加 入响应式的
-
如果这个值加入了响应式就会被收集起来,当被收集的值发生了变化,就会重新执行这个回 调函数
-
watchEffect第一次执行是在DOM挂载前执行的,所以如果你想在第一次执行时拿到DOM元素
-
需要传入第二个参数,第二个参数是一个对象,让其flush属性的值为post即可
-
-
toRefs
- 对reactive进行解构后就失去了响应式的效果,因为reactive返回的是一个Proxy对象
- 对Proxy对象进行解构,拿到的是纯净的值,所以没有了响应式的效果
- 如果想要对reactive进行解构,需要对其包裹一个toRefs
- 这么做相当于为reactive中的每一个值包裹了一个ref
3.6 Vue3中的watch和watchEffect有什么区别?
- watch和watchEffect都用用来侦听响应式数据的变化,watch可以侦听指定的源,默认第一次不会
执行,watchEffect虽不能指定侦听的源,但是会自动收集依赖,并默认会先执行一次。
-
watch
- watch可以监听单个数据源,也可以监听多个数据源
- watch是懒执行,第一次是不会执行的,除非你为其提供第三个参数中的immediate属性为true
- watch只有等到监听的数据源发生了变化后,才会执行第二个参数(回调)
- watch可以获取监听数据源的前后变化的值
- 侦听多个数据源的时候,第一个参数是数组类型
-
watchEffect
- watchEffect会自动收集依赖,收集的依赖是第一个参数,也就是回调函数中有哪些东⻄是加入响应式的
- 如果这个值加入了响应式就会被收集起来,当被收集的值发生了变化,就会重新执行这个回调函数
- watchEffect第一次执行是在DOM挂载前执行的,所以如果你想在第一次执行时拿到DOM元素
- 需要传入第二个参数,第二个参数是一个对象,让其flush属性的值为post即可
3.7 说说Vue3中script setup语法糖常⻅用法?
script setup 是在单文件组件中使用 Composition API 的编译时语法糖,相比与之前的setup函数写 法,它具有更多的优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和抛出事件。
- 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
- 更好的IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。
1.script setup
-
当使用 script setup 的时候,任何在 script setup 声明的顶层绑定都能在模板中直接使用
- 声明的顶层绑定:包括变量,函数声明,以及 import 引入的内容
-
响应式数据需要通过ref、reactive来创建
-
在script setup中导入的组件可以直接使用
2.defineProps
- 在script setup语法糖中必须使用 defineProps API来声明props,它具备完整的类型推断并且在
<script setup>
中是直接可用的(不需要额外导入)。
3.defineEmits
- 在script setup语法糖中必须使用 defineEmits API来声明 emits,它具备完整的类型推断并且在
<script setup>
中是直接可用的(不需要额外导入)。
4.defineExpose
- 获取组件的实例可以通过ref来获取,接着组件挂载完成后可通过value拿到组件实例。
- 当拿到组件实例后,默认是不可以访问这个实例中的方法和属性,因为默认没暴露任何方法和属性。
- 因此在Vue3组件中可以用defineExpose API来暴露方法和属性给外部访问。
- defineExpose 也是不需要导入,直接使用即可