

前端面试Vue 高频原理篇+详细解答,还有105道vue面试题集合
source link: https://blog.csdn.net/weixin_52546522/article/details/117404903
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前端面试Vue 高频原理篇+详细解答,还有105道vue面试题集合
小编在群里看到一句话,说三件套加vue就可以找到实习,三件套估计说的是HTML+JavaScript+CSS,但还有这好事?这可不得赶紧把vue面试题安排上。实不实习不重要,主要是想学习哈哈哈哈(手动狗头)。
也有小伙伴私信小编有没有vue的面试题,看来之前的前端面试套餐:Vue面试题总结+JavaScript前端经典面试题+100道 CSS 面试题满足不了大家呀,小编又整理了一套vue面试题集合,需要的小伙伴直接点击这里喔
1.老生常谈之, MPA/SPA 的理解,优缺点是什么?
MPA
多页面应用。
- 构成:有多个页面
html
构成, - 跳转方式:页面的跳转是从一个页面到另一个页面
- 刷新的方式:全页面刷新
- 页面数据跳转:依赖
URL/cookie/localStorage
- 跳转后的资源
会重新加载
- 优点:对 SEO 比较友好,开发难度低一点。
SPA
单页面应用
- 页面组成:由一个外壳页面包裹,多个页面(组件)片段组成
- 跳转方式:在外壳页面中跳转,将片段页面(组件)显示或隐藏
- 刷新方式:页面片段的局部刷新
- 页面的数据跳转:组件间的传值比较容易
- 跳转后的资源
不会重新加载
- 缺点:对 SEO 搜索不太友好需要单独做配置,开发难度高一点需要专门的开发框架
iframe 实际上是
MPA
,但是可以实现SPA
的一些效果,但是本身由不少问题。
2.老生常谈之,为什么需要有这些 MVC/MVVM 模式?谈谈你对 MVC,MVVM 模式的区别,
目的:借鉴后端的思想,职责划分和分层
- Vue, React 不是真正意义上的 MVVM 更不是 MVC,两者核心只处理视图层
view
。
MVC模式
单向的数据,用户的每一步操作都需要重新请求数据库来修改视图层的渲染,形成一个单向的闭环。比如
jQuery+underscore+backbone
。
- M:
model
数据存放层 - V:
view
:视图层 页面 - C:
controller
:控制器 js 逻辑层。
controller
控制层将数据层model层
的数据处理后显示在视图层view层
,同样视图层view层
接收用户的指令也可以通过控制层controller
,作用到数据层model
。所以MVC的缺点是视图层不能和数据层直接交互。
MVVM模式
隐藏了
controller
控制层,直接操控View
视图层和Model
数据层。
- M:model 数据模型
- V: view 视图模板
- VM:view-model 视图数据模板(vue处理的层,vue 中的definedProperty 就是处理 VM 层的逻辑)
双向的数据绑定:
model
数据模型层通过数据绑定Data Bindings
直接影响视图层View
,同时视图层view
通过监听Dom Listener
也可以改变数据模型层model
。
- 数据绑定和DOM事件监听就是
viewModel
层Vue
主要做的事。也就是说:只要将数据模型层Model
的数据挂载到ViewModel
层Vue
就可以实现双向的数据绑定。 - 加上
vuex/redux
可以作为vue和react
的model
数据层。
var vm = new Vue()
复制代码
vm 就是
view-model
数据模型层,data:就是vmview-model
层所代理的数据。
- 综上两者的区别:MVC 的视图层和数据层交互需要通过控制层
controller
属于单向链接。MVVM 隐藏了控制层controller
,让视图层和数据层可以直接交互 属于双向连接。
3. 说一下对 Vue 中响应式数据的理解
小tip:响应式数据指的是数据发生了变化,视图可以更新就是响应式的数据
vue
中实现了一个definedReactive
方法,方法内部借用Object.definedProperty()
给每一个属性都添加了get/set
的属性。definedReactive
只能监控到最外层的对象,对于内层的对象需要递归劫持数据。- 数组则是重写的7个
push pop shift unshift reverse sort splice
来给数组做数据拦截,因为这几个方法会改变原数组
// src\core\observer\index.js
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
// 准备给属性添加一个 dep 来依赖收集 Watcher 用于更新视图。
const dep = new Dep()
// some code
// observe() 用来观察值的类型,如果是属性也是对象就递归,为每个属性都加上`get/set`
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// 这里取数据时依赖收集
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
// childOb 是对对像进行收集依赖
if (childOb) {
childOb.dep.depend()
//这里对数组和内部的数组进行递归收集依赖,这里数组的 key 和 value 都有dep。
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
// 属性发生改变,这里会通知 watcher 更新视图
}
})
}
复制代码
上面的 Dep(类) 是用来干嘛的?答:用来收集渲染的
Watcher
,Watcher
又是一个啥东西?答:watcher
是一个类,用于更新视图的
4. Vue 是怎么检测数组的变化的?
- vue 没有对数组的每一项用
definedProperty()
来数据拦截,而是通过重写数组的方法push pop shift unshift reverse sort splice
。 - 手动调用 notify,通知 render watcher,执行 update
- 数组中如果有对象类型(
对象和数组
)的话会进行数据拦截。 - 所以通过修改数组下标和数组长度是不会进行数据拦截的,也就不会有响应式变化。例如
arr[0] = 1, arr.length = 2
都不会有响应式
// src\core\observer\array.js
const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse']
methodsToPatch.forEach(function (method) {
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 新增的类型再次观察
if (inserted) ob.observeArray(inserted)
// 手动调用 notify 派发更新
ob.dep.notify()
return result
})
})
复制代码
5.Vue 是怎样依赖收集的?(dep 和 Watcher 是什么关系)
tip:
Dep
是一个用来负责收集Watcher
的类,Watcher
是一个封装了渲染视图逻辑的类,用于派发更新的。需要注意的是Watcher 是不能直接更新视图的还需要结合Vnode经过patch()中的diff算法才可以生成真正的DOM
- 每一个属性都有自己的
dep
属性,来存放依赖的Watcher
,属性发生变化后会通知Watcher
去更新。 - 在用户获取(
getter
) 数据时 Vue 给每一个属性都添加了dep
属性来(collect as Dependency)收集Watcher
。在用户setting
设置属性值时dep.notify()
通知收集的Watcher
重新渲染。详情见上面的defineReactive()
Dep依赖收集类
其和Watcher类
是多对多双向存储的关系- 每一个属性都可以有多个
Watcher 类
,因为属性可能在不同的组件中被使用。 - 同时一个
Watcher 类
也可以对应多个属性。
6. Vue 中的模板编译
Vue中模板编译:其实就是将
template
转化成render
函数。说白了就是将真实的DOM(模板)
编译成虚拟dom(Vnode)
- 第一步是将
template 模板
字符串转换成ast 语法树
(parser 解析器),这里使用了大量的正则来匹配标签的名称,属性,文本等。 - 第二步是对 AST 进行静态节点
static
标记,主要用来做虚拟 DOM 的渲染优化(optimize优化器),这里会遍历出所有的子节点也做静态标记 - 第三步是 使用
ast语法树
重新生成render 函数
代码字符串 code。(codeGen 代码生成器)
为什么要静态标记节点,如果是静态节点(没有绑定数据,前后不需要发生变化的节点)那么后续就不需要 diff 算法来作比较。
7. 生命周期钩子实现原理
- vue 中的生命周期钩子只是一个回调函数,在创建组件实例化的过程中会调用对应的钩子执行。
- 使用
Vue.mixin({})
混入的钩子或生命周期中定义了多个函数,vue 内部会调用mergeHook()
对钩子进行合并放入到队列中依次执行
// src\core\util\options.js
function mergeHook (
parentVal: ?Array<Function>,
childVal: ?Function | ?Array<Function>
): ?Array<Function> {
const res = childVal
? parentVal
? parentVal.concat(childVal) // 合并
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
return res
? dedupeHooks(res)
: res
}
复制代码
8.老生常谈之 vue 生命周期有哪些,一般在哪里发送请求?
beforeCreate
: 刚开始初始化 vue 实例,在数据观测observer
之前调用,还没有创建data/methods
等属性created
: vue 实例初始化结束,所有的属性已经创建。beforeMount
: 在 vue 挂载数据到页面上之前,触发这个钩子,render 函数此时被触发。mounted
: el 被 创建的vm.$el
替换,vue 初始化的数据已经挂载到页面之上,这里可以访问到真实的 DOM。一般会在这里请求数据。beforeUpdate
: 数据更新时调用,也就是在虚拟 dom 重新渲染之前。updated
: 数据变化导致虚拟 dom 发生重新渲染之后发生。beforeDestroy
: 实例销毁之前调用该钩子,此时实例还在。vm.$destroy
触发两个方法。destroyed
: Vue 实例销毁之后调用。所有的事件监听都会被接触。
请求数据要看具体的业务需求决定在哪里发送
ajax
9.Vue.mixin({})的使用场景和原理
- 使用场景:用于抽离一个公共的业务逻辑实现复用。
- 实现原理:调用
mergeOptions()
方法采用策略模式针对不同的属性合并。混入的数据和组件的数据有冲突就采用组件本身的。 Vue.mixin({})
缺陷,1.可能会导致混入的属性名和组件属性名发生命名冲突;2. 数据依赖的来源问题
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
// some code
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
}
// 递归遍历合并组件和混入的属性
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
复制代码
10.老生常谈之 vue 组件中的data 为什么必须是一个函数?
- 这和 js 本身机制相关,
data
函数中返回的对象引用地址不同,就能保证不同组件之间的数据不相互污染。 Vue.mixin()
中如果混入data
属性,那么data
也必须是一个函数。因为Vue.mixin()
也可以多处使用。- 实例中
data
可以是一个对象也可以是一个函数,因为我们一个页面一般只初始化一个Vue实例(单例)
11. 老生常谈之 vue 中 vm.$nextTick(cb)实现原理和场景
- 场景:
在 dom 更新循环结束后调用,用于获取更新后的 dom 数据
- 实现原理:
vm.$nextTick(cb)
是一个异步的方法为了兼容性做了很多降级处理依次有promise.then,MutationObserver,setImmediate,setTimeout
。在数据修改后不会马上更新视图,而是经过set
方法 notify 通知Watcher
更新,将需要更新的Watcher
放入到一个异步队列中,nexTick
的回调函数就放在Watcher
的后面,等待主线程中同步代码执行借宿然后依次清空队列中,所以vm.nextTick(callback)
是在dom
更新结束后执行的。
上面将对列中
Watcher
依次清空就是vue 异步批量更新的原理
。提一个小思考:为什么不直接使用setTimeout
代替?因为setTimeout
是一个宏任务,宏任务多性能也会差。
12.老生常谈之 watch 和 computed 区别
computed
内部就是根据Object.definedProperty()
实现的computed
具备缓存功能,依赖的值不发生变化,就不会重新计算。watch
是监控值的变化,值发生变化时会执行对应的回调函数。computed
和watch
都是基于Watcher类
来执行的。
computed
缓存功能依靠一个变量dirty
,表示值是不是脏的默认是true
,取值后是false
,再次取值时dirty
还是false
直接将还是上一次的取值返回。
// src\core\instance\state.js computed 取值函数
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) { // 判断值是不是脏 dirty
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
// src\core\instance\state.js watch 实现
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
): Function {
const vm: Component = this
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true
// 实例化 watcher
const watcher = new Watcher(vm, expOrFn, cb, options)
if (options.immediate) {
const info = `callback for immediate watcher "${watcher.expression}"`
pushTarget()
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
popTarget()
}
return function unwatchFn () {
watcher.teardown()
}
}
Vue面试题集合
面试光看这12道题怎么够呢?小编把vue面试题整理成了一个集合共有105道题目,不多但经典哦。
- vue.js的两个核心是什么?
- vue 的双向绑定的原理是什么?
- vue生命周期钩子函数有哪些?
- 请问 v-if 和 v-show 有什么区别?
- vue常用的修饰符
- nextTick
- 什么是vue生命周期
- 数据响应(数据劫持)
- virtual dom 原理实现
- Proxy 相比于 defineProperty 的优势
- vue中 key 值的作用
- Vue 组件中 data 为什么必须是函数?
- v-for 与 v-if 的优先级
- 说出至少 4 种 vue 当中的指令和它的用法
- vue中子组件调用父组件的方法
- vue中父组件调用子组件的方法
- vue页面级组件之间传值
- 说说vue的动态组件。
- keep-alive内置组件的作用
- 递归组件的用法
- 怎么定义vue-router的动态路由?怎么获取传过来的值?
- vue-router有哪几种路由守卫?
- $route和 $router的区别是什么?
- vue-router响应路由参数的变化
- vue-router 传参
- 不用Vuex会带来什么问题?
- vuex有哪几种属性?
- vuex的State特性是?
- vuex的Getter特性是?
完整版的vue集合PDF资料直接点击这里就可以领取了哦,前端面试套餐也是点击这领取,也能联系小编,说不定你想要的我这正有呢。
无论是几件套加vue还是什么,前端找工作该学的知识点、面试该问到的面试题一个不会落下,加油小伙伴们!喜欢这篇文章的留下你们的 “点赞+评论”,给小编充充能量。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK