前言
下面总结了vue的一些经典的面试题,希望对正在找工作面试的小伙伴们提供一些帮助,我们废话少说直接进入整体、
简述一下什么是MVVM模型
MVVM,是Model-View-ViewModel的简写,其本质是MVC模型的升级版。其中 Model 代表数据模型,View 代表看到的页面,ViewModel是View和Model之间的桥梁,数据会绑定到ViewModel层并自动将数据渲染到页面中,视图变化的时候会通知ViewModel层更新数据。以前是通过操作DOM来更新视图,现在是数据驱动视图。
请简要描述 Vue 的实现原理以及如何实现响应式数据
答:Vue 中的响应式数据通过双向绑定实现,采用了数据劫持和观察者模式。在初始化时会先对用户的数据进行递归遍历,为每个属性绑定一个 Watcher
,然后与对应的属性建立一个发布订阅关系。当数据变化时,所有依赖该数据的 Watcher 都会被通知更新视图。
为什么组件中的data是一个函数?
在 new Vue() 中,可以是函数也可以是对象,因为根实例只有一个,不会产生数据污染。
在组件中,data 必须为函数,目的是为了防止多个组件实例对象之间共用一个 data,产生数据污染;而采用函数的形式,initData 时会将其作为工厂函数都会返回全新的 data 对象。
请简述 Vue 生命周期中常见的钩子函数
答:Vue 生命钩子分为 8 个,分别是 beforeCreate
、created
、beforeMount
、mounted
、beforeUpdate
、updated
、beforeDestroy
和 destroyed
。具体来说:
beforeCreate
:实例创建前,此时无法访问到组件的选项数据。created
:实例已经创建完成,并且已经完成了数据观测等配置。beforeMount
:模板编译/挂载前。mounted
:模板编译/挂载完成。beforeUpdate
:数据更新但尚未写入 DOM 时调用。updated
:数据更新并已经写入 DOM 时调用。beforeDestroy
:实例销毁前,此时无法访问到组件的选项数据。destroyed
:实例已经销毁。
Vue 中 computed 和 watch 的区别是什么?
答:computed
是计算属性,依赖于函数内部的响应式数据,在这些响应式数据变化时重新求值,常用于复杂的视图中。而 watch
监听一个或多个特定数据的变化,并且在回调函数中执行某些操作,适用于需要异步或需要深度监听的场景。
Vue 实例挂载过程中发生了什么?
挂载过程指的是 app.mount()过程,这是一个初始化过程,整体上做了两件事情:初始化和建立更新机制。
初始化会创建组件实例、初始化组件状态、创建各种响应式数据。
建立更新机制这一步会立即执行一次组件的更新函数,这会首次执行组件渲染函数并执行patch将vnode 转换为 dom; 同时首次执行渲染函数会创建它内部响应式数据和组件更新函数之间的依赖关系,这使得以后数据发生变化时会执行对应的更新函数。
在 Vue 中如何使用自定义指令?
答:Vue 自定义指令可以全局或者局部注册,通过 Vue.directive
方法进行注册。具体步骤如下:
- 注册全局指令:
Vue.directive('my-directive', {
bind(el, binding, vnode) {},
inserted(el, binding, vnode) {},
updated(el, binding, vnode) {},
componentUpdated(el, binding, vnode) {},
unbind(el, binding, vnode) {}
})
- 注册局部指令:
export default {
directives: {
'my-local-directive': {
bind(el, binding, vnode) {},
inserted(el, binding, vnode) {},
updated(el, binding, vnode) {},
componentUpdated(el, binding, vnode) {},
unbind(el, binding, vnode) {}
}
}
}
Vue 中的 key 值的作用是什么?举例说明
答:在 Vue.js 中,key 值的作用主要是为了提高渲染效率和性能。通过唯一标识 VNode,实现精准地判断是否需要更新 DOM,对避免无谓的 DOM 更新、渲染等起到了关键性的作用,进而优化了渲染性能。例如:
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">{{item.name}}</li>
</ul>
</div>
</template>
在这个示例中,通过一个独特的 key 属性,可以更好地区分每个列表项,并防止列表项重复渲染
如何实现组件间的通信?
答:在 Vue 中,组件通信的方式主要有以下几种:
- 父子组件通信:通过 Prop 向子组件传递数据,通过事件监听、$refs 等机制实现子组件向父组件发送数据。
- 兄弟组件通信:使用 EventBus、Vuex 等集中式状态管理工具,或者让共同祖先组件充当中央事件总线。
- 跨级组件通信:使用 provide/inject API 或者 ref 属性等进行跨级绑定数据。
Vue 组件里常见的修饰符包括哪些?分别有什么作用?
答:Vue 中常见的修饰符主要有 .prevent
、.stop
、.capture
、.self
和 .once
等,分别代表了以下功能:
.prevent
:阻止默认事件。.stop
:停止事件冒泡。.capture
:事件捕获,从外向内传播。.self
:只当事件在该元素本身发生时触发。.once
:只触发一次事件。
在 Vue 中如何实现样式的动态绑定?
答:在 Vue 中样式的动态绑定可以通过 v-bind:class
或者 :class
指令实现。具体使用方法如下:
<div :class="{ 'active': isActive }"></div>
<!-- 可缩写为 -->
<div :class="{'active': isActive}"></div>
<!-- 数组语法 -->
<div :class="[isActive ? 'active' : '', errorClass]"></div>
如何实现 Vue 的懒加载(异步组件)?
答:实现 Vue 组件的懒加载可以使用 Webpack 自带的代码分块功能和异步组件技术。在引用组件时,使用 Webpack 的 import()
方法来进行模块动态加载,在 Vue 组件中则使用 Vue.lazy(() => import('./MyComponent.vue'))
来实现异步加载。
Vue.js 2.x 中通过 keep-alive 组件缓存组件实例有什么问题?
答: 在 Vue.js 2.x 中,keep-alive 组件会缓存包裹的组件实例,并在再次渲染时复用该实例。然而,在某些特殊场景下,这种缓存机制可能导致状态不一致、出现未控制的副作用等问题。另外,keep-alive 只能缓存条件渲染和动态组件内部的组件实例,无法缓存函数式组件。
Vue.js 中 created 和 mounted 生命周期的区别是什么?
答:created 生命周期发生在组件实例被创建之后,即在挂载之前。当组件被创建时,此周期钩子会被调用。mounted 生命周期发生在挂载元素到文档流之后,即在组件挂载完成之后。此周期钩子是每个组件中最常用的。主要区别在于 created 钩子不需要 DOM 就可以访问组件实例,而 mounted 钩子只能在组件 DOM 在挂载之后才能被访问。
在 Vue.js 中 v-show 和 v-if 的区别是什么?
答:v-show 会始终渲染元素,在控制显示和隐藏时只是修改了 css display 属性值,因此页面加载完毕后总是保留该元素的空间并占据一定的渲染性能。v-if 初始渲染时不会渲染元素,直到条件为 in,元素渲染才会触发。
Vue.js 中使用 v-for 时为什么建议在组件上使用 key?有没有什么注意点?
答:在 Vue.js 中使用 v-for 时建议为组件节点(或常规元素)添加唯一的 key 属性,因为 key 的作用是帮助判断哪些组件需要重用、更新或删除。如果没有key,那么每次重新渲染组件时都需要销毁已经存在的所有子组件,然后再从头创建新的子组件,这样会造成大量的性能消耗。 注意:在使用 v-for 时,使用变量 key 必须是一个字符串或者 number。
如何在 Vue.js 中监听数据变化?
答:Vue.js 提供了多种方式监听数据变化,包括以下几种:
- Object.defineProperty 和 getter/setter
- $watch 或 watch 选项
- 计算属性 computed
- 监听器 Watcher
Vue.js 中 keep-alive 组件的作用是什么?
答:Vue.js 中 keep-alive 组件主要有两个作用:缓存不活动的组件实例并在需要时重新渲染、提高组件渲染性能。keep-alive 组件可以在内部所包含的子组件呈现过程中使用一些生命周期钩子函数(activated 和 deactivated)进行跟踪,以便定制触发。
如何在 watch 监听器中深层监听对象中的数据变化?
答:Vue.js 默认只会监听对象直接属性值的变化,因此如果需要监听对象深层嵌套属性值的变化,则需要使用 deep 选项设置为 true,如下所示:
data() {
return {
person: {
name: 'Tom',
age: 25,
address: {
city: 'Beijing'
}
}
}
},
watch: {
'person.address': { // 这里双引号正常使用单引号替代即可
handler(newValue, oldValue) {
console.log('address changed');
},
deep: true
}
}
Vue.js 中 nextTick 的作用是什么?
答:nextTick 函数主要的作用是利用 Vue.js 的异步更新队列,在 DOM 更新后再执行指定的操作。常见的场景包括在某个操作之后读取组件新状态或操作更新后刷新数据等。Vue.js 在多个事件循环周期中将改变推迟到下一个更新周期以防止过量渲染浏览器。例如:
//html
<div id="app">
<button @click="updateValue">修改 Value</button>
{{value}}
</div>
//javascript
new Vue({
el: '#app',
data: {
value: 'init'
},
methods: {
updateValue() {
this.value = 'updated'
console.log(this.el.textContent)//输出结果是“init”
this.nextTick(() => {
console.log(this.$el.textContent) // 输出结果是“updated”
})
}
}
})
在这个示例中,当点击按钮时,Vue.js 操作更新 data中value属性的值会更改,并且在模板上呈现出来,但是,直接运行console.log(this.el.textContent)看不到这种情况,这是因为 Vue.js 是创造性地更新和重新绑定 VDOM,并被分成了多个tick。$nextTick()就像 setTimeout(function(){}, 0),我们可以强制它等待 Vue.js 的 tick 过程结束后再执行操作。
Vue Router中的常用路由模式和原理?
hash 模式:
- location.hash的值就是url中 # 后面的东西。它的特点在于:hash虽然出现url中,但不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。
- 可以为hash的改变添加监听事件window.addEventListener("hashchange", funcRef, false),每一次改变hash (window.location.hash),都会在浏览器的访问历史中增加一个记录,利用hash的以上特点,就可以实现前端路由更新视图但不重新请求页面的功能了。 特点:兼容性好但是不美观
history 模式:
利用 HTML5 History Interface 中新增的pushState()和replaceState()方法。
这两个方法应用于浏览器的历史记录栈,在当前已有的back、forward、go 的基础上,他们提供了对历史记录进行修改的功能。
这两个方法有个共同点:当调用他们修改浏览器历史记录栈后,虽然当前url改变了,但浏览器不会刷新页面,这就为单页面应用前端路由“更新视图但不重新请求页面”提供了基础
特点:虽然美观,但是刷新会出现 404 需要后端进行配置
对Vuex的理解?
概念: Vuex 是 Vue 专用的状态管理库,它以全局方式集中管理应用的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
解决的问题: Vuex 主要解决的问题是多组件之间状态共享。利用各种通信方式,虽然也能够实现状态共享,但是往往需要在多个组件之间保持状态的一致性,这种模式很容易出问题,也会使程序逻辑变得复杂。Vuex 通过把组件的共享状态抽取出来,以全局单例模式管理,这样任何组件都能用一致的方式获取和修改状态,响应式的数据也能够保证简洁的单向流动,使代码变得更具结构化且易于维护。
什么时候用: Vuex 并非是必须的,它能够管理状态,但同时也带来更多的概念和框架。如果我们不打算开发大型单页应用或应用里没有大量全局的状态需要维护,完全没有使用Vuex的必要,一个简单的 store 模式就够了。反之,Vuex将是自然而然的选择。
用法: Vuex 将全局状态放入state对象中,它本身是一颗状态树,组件中使用store实例的state访问这些状态;然后用配套的mutation方法修改这些状态,并且只能用mutation修改状态,在组件中调用commit方法提交mutation;如果应用中有异步操作或复杂逻辑组合,需要编写action,执行结束如果有状态修改仍需提交mutation,组件中通过dispatch派发action。最后是模块化,通过modules选项组织拆分出去的各个子模块,在访问状态(state)时需注意添加子模块的名称,如果子模块有设置namespace,那么提交mutation和派发action时还需要额外的命名空间前缀。
关于 Vue SSR的理解?
SSR即服务端渲染(Server Side Render),就是将 Vue 在客户端把标签渲染成 html 的工作放在服务端完成,然后再把 html 直接返回给客户端。
- 优点: 有着更好的 SEO,并且首屏加载速度更快。
- 缺点: 开发条件会受限制,服务器端渲染只支持 beforeCreate 和 created 两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于 Node.js 的运行环境。服务器会有更大的负载需求。
了解哪些Vue的性能优化方法?
- 路由懒加载。有效拆分应用大小,访问时才异步加载。
- keep-alive缓存页面。避免重复创建组件实例,且能保留缓存组件状态。
- v-for遍历避免同时使用v-if。实际上在 Vue 3 中已经是一个错误用法了。
- 长列表性能优化,可采用虚拟列表。
- v-once。不再变化的数据使用v-once。
- 事件销毁。组件销毁后把全局变量和定时器销毁。
- 图片懒加载。
- 第三方插件按需引入。
- 子组件分割。较重的状态组件适合拆分。
- 服务端渲染。
未完待续~~
求点赞求收藏~~