本文汇总了2019年针对Vue技术栈的前端面试题,涵盖MVVM架构理解、Vue指令差异、响应式原理机制、组件通信及数据监控方法。同时深入探讨了Vue中对象属性添加、数组删除等常见陷阱,以及SPA首屏加载和网站整体性能优化的具体策略。
毕业后我在合肥一家小型公司任职,由于缺乏资深导师和技术交流氛围,我只能独自探索技术路径。管理层往往只注重愿景而忽视实际发展,导致个人职业前景不明朗。因此,我决定辞职,在新年伊始前往杭州,那里的互联网企业数量远超合肥。
抵达杭州后的三天内,我参加了多家公司的面试,涵盖规模较小、初创及发展较好的企业。本文将整理近期的面试题目进行复盘,鉴于我的技术栈以Vue为主,大部分问题均围绕Vue开发展开。
谈谈你对MVVM开发模式的理解
MVVM架构包含Model、View和ViewModel三个核心部分。
Model层负责定义数据模型及业务逻辑。
View层负责UI视图的展示。
ViewModel负责监听Model中数据的变化并控制视图更新,同时处理用户交互操作。
Model与View之间没有直接关联,而是通过ViewModel建立联系。Model与ViewModel之间存在双向数据绑定:当Model数据改变时触发View刷新,View中用户交互导致的数据变化也会同步至Model。
该模式实现了Model与View的数据自动同步,开发者只需专注于数据维护,无需手动操作DOM。
Vue有哪些指令?
常见的指令包括v-html、v-show、v-if、v-for等。
v-if 和 v-show 有什么区别?
v-show仅通过切换display属性(block或none)控制元素显示,不销毁DOM节点;v-if则控制DOM节点的存在与否。若需频繁切换显示状态,使用v-show性能开销更小;若仅需一次性显示或隐藏,v-if更为合理。
简述Vue的响应式原理
Vue实例创建时,会遍历data选项中的属性,利用Object.defineProperty将其转换为getter/setter,并在内部追踪依赖。当属性被访问或修改时,会通知变化。
每个组件实例拥有对应的watcher程序实例,在组件渲染过程中将属性记录为依赖。当依赖项的setter被调用时,会通知watcher重新计算,从而触发关联组件的更新。

Vue中如何在组件内部实现一个双向数据绑定?
假设存在一个输入框组件,需实现用户输入时同步父组件页面中的数据。
具体思路为:父组件通过props向子组件传值,子组件通过$emit通知父组件修改相应的props值。展示了具体实现。
import Vue from 'vue'
const component = {
props: ['value'],
template: `
<div>
<input type="text" @input="handleInput" :value="value">
</div>
`,
data () {
return {
}
},
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}
new Vue({
components: {
CompOne: component
},
el: '#root',
template: `
<div>
<comp-one :value1="value" @input="value = arguments[0]"></comp-one>
</div>
`,
data () {
return {
value: '123'
}
}
})
如和所示,输入数据时父子组件中的数据会同步改变。


在父组件中,我们执行了两项操作:一是向子组件传入props,二是监听input事件并同步自身的value属性。这两步操作可进一步精简,只需修改父组件代码:。
template: `
<div>
<!--<comp-one :value1="value" @input="value = arguments[0]"></comp-one>-->
<comp-one v-model="value"></comp-one>
</div>
`
v-model实际上会自动完成上述两步操作。
Vue中如何监控某个属性值的变化?
例如需要监控data中obj.a的变化,Vue中监控对象属性变化的方法如下:。
watch: {
obj: {
handler (newValue, oldValue) {
console.log('obj changed')
},
deep: true
}
}
deep属性表示深层遍历,但上述写法会监控obj的所有属性变化,并非我们所需。因此需进行如下修改:。
watch: {
'obj.a': {
handler (newName, oldName) {
console.log('obj.a changed')
}
}
}
另一种方法是通过computed实现,代码如下:。
computed: {
a1 () {
return this.obj.a
}
}
利用计算属性的特性,当依赖改变时,会重新计算出新值。
Vue中给data中的对象属性添加一个新的属性时会发生什么,如何解决?
示例代码如下:。
<template>
<div>
<ul>
<li v-for="value in obj" :key="value">
{{value}}
</li>
</ul>
<button @click="addObjB">添加obj.b</button>
</div>
</template>
<script>
export default {
data () {
return {
obj: {
a: 'obj.a'
}
}
},
methods: {
addObjB () {
this.obj.b = 'obj.b'
console.log(this.obj)
}
}
}
</script>
<style></style>
点击按钮后,显示obj.b已成功添加,但视图并未刷新。


原因在于Vue实例创建时obj.b尚未声明,未被转换为响应式属性,因此不会触发视图更新。此时需使用Vue的全局API $set():。
addObjB () {
// this.obj.b = 'obj.b'
this.$set(this.obj, 'b', 'obj.b')
console.log(this.obj)
}
$set()方法相当于手动将obj.b处理为响应式属性,此时视图也会随之改变:。

delete和Vue.delete删除数组的区别
delete仅将被删除的元素变为empty/undefined,其他元素的键值保持不变。
Vue.delete则直接删除数组元素,改变了数组的键值结构。
var a=[1,2,3,4]
var b=[1,2,3,4]
delete a[1]
console.log(a)
this.$delete(b,1)
console.log(b)


如何优化SPA应用的首屏加载速度慢的问题?
将公用的JS库通过script标签外部引入,减小app.bundel的大小,让浏览器并行下载资源文件,提高下载速度。
在配置路由时,页面和组件使用懒加载方式引入,进一步缩小app.bundel的体积,在调用某个组件时再加载对应的js文件。
添加首屏loading图,提升用户体验。
前端如何优化网站性能?
减少HTTP请求数量。
浏览器与服务器通信主要依赖HTTP,每次握手需花费大量时间。不同浏览器对资源文件并发请求数量有限,一旦达到上限,资源请求将处于等待状态,严重影响性能。因此,减少HTTP请求数量是优化网站性能的关键。
CSS Sprites(CSS精灵):将多张图片合并为一张图片以减少HTTP请求,可通过CSS background属性访问图片内容。此方案还能减少图片总字节数。
合并CSS和JS文件:利用grunt、gulp、webpack等工程化打包工具,在发布前将多个CSS或JS文件合并为一个文件。
采用lazyLoad(懒加载):控制网页内容在初始阶段无需加载,不发送请求,仅在用户操作真正需要时加载内容,从而控制网页资源的一次性请求数量。
控制资源文件加载优先级。
浏览器解析HTML内容时从上至下进行,遇到link或script标签会加载对应链接内容。为第一时间展示页面,需提前加载CSS,避免受JS加载影响。
通常做法是将CSS置于头部,JS置于底部。
利用浏览器缓存。
浏览器缓存将网络资源存储于本地,下次请求该资源时,若资源已存在则无需向服务器重新请求,直接从本地读取。
减少重排(Reflow)。
基本原理:重排是DOM变化影响元素几何属性(宽和高)时,浏览器重新计算几何属性,使渲染树中受影响部分失效,并验证DOM树上其他节点的visibility属性。频繁Reflow会导致CPU使用率急剧上升。
减少Reflow:若需在DOM操作时添加样式,尽量通过增加class属性实现,而非直接通过style操作样式。
减少DOM操作。
图标使用IconFont替换。
网页从输入网址到渲染完成经历了哪些过程?
大致可分为以下7步:
输入网址。
发送到DNS服务器,获取域名对应的web服务器IP地址。
与web服务器建立TCP连接。
浏览器向web服务器发送HTTP请求。
web服务器响应请求,返回指定URL的数据(或错误信息、重定向的新URL地址)。
浏览器下载web服务器返回的数据及解析HTML源文件。
生成DOM树,解析CSS和JS,渲染页面,直至显示完成。
jQuery获取的dom对象和原生的dom对象有何区别?
JS原生获取的DOM是一个对象,jQuery对象则是数组对象,即选择出的元素的数组集合,两者属于不同的对象类型,不等价。
原生DOM对象转jQuery对象:。
var box = document.getElementById('box');
var $box = $(box);
jQuery对象转原生DOM对象:。
var $box = $('#box');
var box = $box[0];
jQuery如何扩展自定义方法
(jQuery.fn.myMethod=function () {
alert('myMethod');
})
// 或者:
(function ($) {
$.fn.extend({
myMethod : function () {
alert('myMethod');
}
})
})(jQuery)
使用方式如下:。
$("#div").myMethod();
目前公司面试问题相对基础,但对于仅追求使用而不研究原理的开发者而言可能具有一定难度。因此,学习不仅需追求广度,更需注重深度。
当前前端领域存在海量的JS库、复杂的兼容性问题以及层出不穷的新知识,令人感到学习压力巨大。