2022年面试题之vue
1. 谈谈对MVVM的理解
MVVM分为Model、View、ViewMode三者。
Model:代表数据模型,数据和业务逻辑都在Model层中定义;
View:代表UI视图,负责数据的展示;
ViewModel:负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;
Model和View并无直接关联,而是通过ViewModel来进行关联的,Model和ViewModel之间有着数据双向绑定的关系,因此当Model中的数据发生变化时会触发View层的刷新,View中由于用户交互操作而发生改变的数据也会在Model中同步。这种模式实现了Model和View的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作dom。
2. MVC的理解
M即model模型是指模型表示业务规则
V即View视图是指用户看到并与之交互的界面
C即controller控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。
3. vue的计算属性和watch的区别
computed 属性的结果会被缓存,只有它依赖的响应式属性变化时才会重新计算。主要当作属性来使用;
watch 一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;
computed watch 使用场景区别:
computed 适合 多个数据变化影响一个数据
watch 适合一个数据的变动影响多个数据或者复杂的运算
4.兄弟组件之间的通信
① 让父组件允当两个子组件之间的中间件(中继);
② 使用EventBus(事件总线),它允许两个子组件之间直接通讯,而不需要涉及父组件。
5. vue 通信方式
①父传子props
②子传父$emit
③兄弟组件传值eventBus
④父组件使用子组件的数据和方法$refs
⑤子组件使用父组件的数和方法$parent
⑥路由传值
⑦Vuex传值
6. vue中v-for的key作用
key 的作用主要是为了实现高效的更新虚拟 DOM,提高性能。
其原理是vue在patch的过程中通过key可以精准的判断两个节点是否是同一个,
从而避免频繁的更新dom,使得整个patch过程更加高效,减少DOM操作量,提高性能。
key的值只能是字符串、数字类型。key值具有唯一性,建议把数据项id属性值作为key值(因为id具有唯一性)
7.使用index作为key会有什么问题吗
仅用于渲染列表作为展示,使用index作为key没有问题
若对数据进行添加或者删除等破坏顺序的操作,会产生没必要的真实dom更新,页面效果没问题,但是效率低。
<!--比如这里使用index作为key值,在界面上是可以正常展示的,更新也没问题; 但是假如你突然删除了index=2的数据,那vue双向绑定更新dom操作,是不是key也得随之更新了;这种方式比起 你使用array中的item.id唯一标识作为key值是不是慢了,因为key的标识更改了--> <div v-for="(item,index) in array" :key="index"> <span>{{item.name}}-{{item.id}}</span> </div>
8. v-if和v-for哪个的优先级比较高
在vue3中,v-if具有比v-for更高的优先级
在vue2中,v-for 比 v-if 的优先级高。dom渲染的时候优先遍历列表中的数据,再通过v-if 移动容器元素。
9. v-if 和 v-for 两个要怎么使用才能优化性能呢
① 在外层嵌套template(页面渲染不生成dom节点),在这一层进行 v-if 判断,然后在内部进行 v-for 循环
<template v-if="isShow"> <div v-for="item in array" :key="item.id" ></div> </template>
② 如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项
<div v-for="(item,index) in items" :key="item.id" > <span>{{item}}</span> </div>
computed: {
items: function() {
return this.array.filter(function (item) {
return item.isShow
})
}
}
10. vue2 和 vue3 的区别
响应式区别: vue2实例初始化时,会去遍历data里所有对象的property属性,并使用Object.defineProperty把这些property全部转为getter/setter,
访问对象的属性时 getter 函数触发并通过 dep.depend() 收集依赖;修改对象属性的值时 setter 函数触发并通过 dep.notify() 通知watcher。每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染
vue3中用ref、reactive来创建响应式数据,ref是RefImpl类的实例,在RefImpl类的get中收集依赖,在set中触发依赖,而 reactive 则通过Proxy实现,
用reactive创建数据后返回的是 proxy 代理对象,之后便可在该代理对象上进行操作。
Proxy在代理对象前架设了一层拦截器来控制对象访问,相比js的Object.defineProperty只提供了对象属性的get、set拦截操作,es6的Proxy提供了13种拦截操作
根节点: vue2 只支持一个根节点,vue3可支持多个根节点
diff算法: vue2会比较每个虚拟节点,而vue3只会比较变化的虚拟节点。
v-if 和 v-for 优先级:vue2中v-for 的优先级会优先于v-if,造成性能浪费。在 vue3中v-if 的优先级优于v-for
vue3 增加了 compstion Api ,vue3增加了setup替代了beforeCreate和 created 生命周期,其他生命周期的名字也发生了改变
11. Object.defineProperty可以对数组中数据进行双向绑定吗?
可以的。Object.defineProperty对数组和对象的表现是一致,并非不能监控数组索引的变化。而vue2中无法通过数组索引来实现响应式数据的自动更新是 vue 本身的设计导致的。那么我们在 vue 中使用数组的原生的方法(push,slice...)却有双向绑定的效果,是因为vue中重写了数组的方法
12. vue 路由模式(问他们之间的区别一般回答实现原理不同就可以通过)
vue 的路由模式共有两种,分别是哈希和 history。
实现原理不同
hash模式的实现原理是通过监听hashChange事件来实现的。history模式是通过调用 history.pushState方法(或者replaceState) 并且 监听popstate事件来实现的。history.pushState会追加历史记录,并更换地址栏地址信息,但是页面不会刷新,需要手动调用地址变化之后的处理函数,并在处理函数内部决定跳转逻辑;监听popstate事件是为了响应浏览器的前进后退功能。
表现不同
hash模式会在地址栏中有#号,而history模式没有;同时由于history模式的实现原理用到H5的新特性,所以它对浏览器的兼容性有要求(IE >= 10)。
13. react-router/vue-router 中是如何实现 hashrouter 的呢
使用window.location.hash属性和window.onhashchange事件。可以监听浏览器hash值的变化,去执行相应的js切换网页hash路由实现的原理:
(1)hash 指的是地址中#号以及后面的字符。称为散列值。
(2)散列值不会随请求发送到服务器端的,所以改变hash,不会重新加载界面。
(3)监听onhashchange事件,hash改变时,可以通过window.location.hash来获取和设置hash值。
(4)location.hash值的变化直接反应在浏览器的地址栏。
14. vue 和 react 的区别
数据流不同:
react是单向数据流模式,vue 是双向绑定的;
响应式不同:
React主要是通过setState()方法来更新状态,状态更新之后,组件也会重新渲染。
vue 会遍历 data 数据对象,使用 Object.definedProperty() 将每个属性都转换为 getter 和 setter,每个Vue组件实例都有一个对应的 watcher 实例,在组件初次渲染的时候会记录组件用到了那些数据,当数据发生改变的时候,会触发 setter 方法,并通知所有依赖这个数据的 watcher 实例调用 update 方法去触发组件的compile 渲染方法,进行渲染数据。
15. keep-alive 是干嘛用的?
有时候我们在搜索页面不小心点了别的页面,返回的时候还想看到我们之前搜索过的数据时,这时候可以用到 keep-alive 标签包裹组件,返回到组件的界面时,上次操作的数据就被缓存下来了
16. vue中v-slot 插槽的几种使用方式
普通插槽,具名插槽,动态插槽
<!--普通插槽--> <template v-solt> </template> <slot></solt> <!--具名插槽:指定插槽的名字,比如插槽的名字叫 content--> <template v-slot:content></template> <slot name='content' ></slot> <!--动态插槽:插槽的名字是个变量,content是data中声明的变量--> <template v-slot:[content]></template> <slot :name='content' ></slot>
17. vue中 .sync 修饰符的作用
.sync 相当于给组件绑定了一个属性(子组件中还是通过props接收),并且约定好了一个自定义事件,@update:属性名,这样一来,子组件只需要通过 $emit(update:属性名,val),即可修改父组件属性,这样一来无论是调用还是维护都十分方便了
<!-- 父组件--> <myModal :show.sync='modalShow' /> <!-- 打包编译后的语句如下:可以看出来 .sync 其实就是一个语法糖 --> <myModal :show='modalShow' @update:show="val => modalShow = val" /> <!-- 子组件可以通过 $emit 触发 update 方法改变 --> <div @click="$emit('update:show', false)"> 关闭 </div>
18. vue中data是一个对象和一个函数时的区别
vue组件可以复用,如果data是一个对象,作用域没有分开,组件之间的 data 就会相互影响。是一个函数的话每个示例都可以维护一份被返回对象的独立拷贝,组件之间的 data 属性值就不会相互影响。
19. vue 中的 nextTick
作用:用于下次dom更新循环结束之后执行的回调函数实现原理:主要是使用了宏任务和微任务,定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空队列
20. 路由的懒加载
路由懒加载是将各个模块分开打包,在用户查看相关模块内容时就直接引入相关模块的打包文件然后进行显示。
使用路由懒加载的写法,只会在进入当前这个路由时才会执行 component,然后在运行 import 编译加载相应的组件。
使用 import 是因为 import 会返回一个 Promise 对象。可以理解懒加载的实现是通过 Promise 的 resolve 机制。
21. vuex 存在哪里?与 localstorage、sessionstorage有啥区别?
区别:vuex存储在内存,localstorage(本地存储)则以文件的方式存储在本地,永久保存(不主动删除,则一直存在);sessionstorage( 会话存储 ) ,临时保存。localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
应用场景:vuex用于组件之间的传值,localstorage,sessionstorage则主要用于不同页面之间的传值。
永久性:当刷新页面(这里的刷新页面指的是 --> F5刷新,属于清除内存了)时vuex存储的值会丢失,sessionstorage页面关闭后就清除掉了,localstorage不会。
注:大家可能觉得用localstorage可以代替vuex, 对于不变的数据确实可以,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage,sessionstorage无法做到
22. vuex 有几个属性值?
五个属性:state、getters、mutations、actions、modules
state: vuex的基本数据,用来存储变量;
getters: 从基本数据(state)派生的数据,相当于state的计算属性;
mutations: 提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个mution 都有一个字符串的事件类型(type)和一个回调函数(handler)。
回调函数就是我们实际进行状态更改的地方,并且它会接受 state作为第一个参数,提交载荷作为第二个参数。
action: 和mution的功能大致相同,不同之处在于 ①Action提交的是mution,而不是直接变更状态,②Action可以包含任意异步操作。
modules: 模块化vuex,可以让每一个模块拥有自己的 state、mutation、action、 getters,使得结构非常清晰,方便管理
新建vue项目testAPP —> 在testApp中建store文件 —> store文件下又有mudoules 文件夹和 getter.js 和 index.js —> store文件下建立user.js
23: vnode在vue中什么时候执行
vnode 译过来为虚拟dom。为何组件要从直接产出 html
变成产出虚拟DOM呢?其原因是虚拟DOM带来了分层设计,它对渲染过程的抽象,使得框架可以渲染到web
(浏览器) 以外的平台,以及能够实现 SSR
等
VNode是如何渲染成真实的DOM节点的呢?VNode渲染器的工作流有两种:mount、patch。
在组件初始化阶段,只有新的VNode,没有旧的VNode,直接将新的VNode挂载成全新的DOM,这个过程就叫做mount。在组件生命周期中,如果想操作DOM节点都是在mounted之后才能获取到节点信息。因此使用Vue.$mount方法挂载组件时,就会将VNode挂载成真实的DOM。渲染器patch工作流就很好理解了。如果旧的
VNode
存在,则会使用新的VNode
与旧的VNode
进行对比,试图以最小的资源开销完成DOM
的更新,这个过程就叫patch。
24. react hooks 与 vue3 composition api 的差别
1. 原理上:react hooks 底层是基于链表实现的,当组件渲染时会顺序地执行所有的 hooks ,每个 hook 的 next 指向下一个 hook。vue hook 只会调用一次,原因在于它对数据的响应是基于 proxy 的,对数据直接代理观察,只要任何地方改动了 data,相关的 function/template 都会被重新计算。
2. 代码执行:react hooks 是在每次组件渲染时运行,vue setup 只会在组件创建时运行一次;vue composition API 的 setup 晚于 beforecreate 早于 create 被调用
3. 声明状态:react hooks 使用 useState hooks 来初始化状态,vue 声明状态主要是 ref 和 reactive,其中 ref 返回一个响应式对象,通过 value 属性访问到,可用于基本类型/对象。reactive 只将对象作为输入,并返回响应式代理(使用了对象解构会失去响应性)
4. 如何跟踪依赖:react hooks 的 useEffect 每次重新渲染后都会执行,可以通过定义依赖的属性/方法来跳过某些 hook 的执行;vue 通过 watch 执行副作用响应状态/属性的改变
5. 访问组件生命周期:react hooks 从思考习惯改变,考虑副作用依赖什么状态;vue 通过 onMounted、onUpdated 和 onBeforeUnmount 函数
6. Refs: React 的 useRef 和 Vue 的 ref 都允许你引用一个子组件 或 要附加到的 DOM 元素
7. Context 和 provide/inject: vue Composition API 中增加了一对用在 setup() 中的 provide 和 inject 函数
25. Vue 路由导航守卫(全局守卫、路由独享守卫、组件内守卫)详解
全局守卫:beforeEach、beforeResolve、afterEach
全局守卫是指路由实例上直接操作的钩子函数,特点是所有路由配置的组件都会触发,直白点就是触发路由就会触发这些钩子函数
路由独享守卫: beforeEnter
路由独享守卫是指在单个路由配置的时候也可以设置的钩子函数
组件守卫:beforeRouteEnter、beforeRouteUpdadte、beforeRouteLeave
组件守卫是指在组件内执行的钩子函数,类似于组件内的生命周期,相当于为配置路由的组件添加的生命周期钩子函数
详细了解: https://blog.csdn.net/weixin_38083836/article/details/113932103
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通