前端面试-vue系列(面试真题总结)

318 阅读4分钟

vue真题(偏vue2系列)

1、vue2和vue3的双向绑定有什么区别

首先,vue2是有缺陷的

关于对象:他无法检测property的添加个移除。由于vue在初始化实例的时对property执行getter/setter的转化,所以property必须在data对象上存在才能让vue把他转化成响应式。
关于数组:vue不能检测两种改变:一种是直接根据数组的索引去直接改变;第二种是改变数组的长度。Object.defineProperty是不能直接对数组进行监听的,是通过源码对数组的七种方法进行的重写。不能检测是warcher不能检测但是页面是可以的
解决办法的话就是$set()
vue2使用Object.defineproperty对象以及对象的属性进行劫持+发布订阅的模式,只要数据发生变化,就通知更新然后视图更新。
vue3的话是使用proxy实现的响应式,在目标对象之前架设一层拦截,外界对该对象进行访问,都需要经过这层拦截,从而达到过滤和修改
let obj = {
    a: 1,
    b: 2
};
const proxy = new Proxy(obj, {
    get: function(target, prop, receiver) {
        return prop in target ? target[prop] : 0;
    },
    set: function(target, prop, value, receiver) {
        target[prop] = 666;
    }
})
console.log(proxy.a); // 1
console.log(proxy.c); // 0
proxy.a = 10;
console.log(proxy.a); // 666
obj.b = 10;
console.log(proxy.b); // 不是666 而是10 
get和set两个属性的参数都相同,多出来的一个表示新的属性值。下面值没有修改是因为只是劫持了代理对象proxy和原先的obj没有任何关系。

2、vue3引入了一些什么新特性

Performance:性能优化

可以对网页进行一个性能的追踪,分析网页的性能时间。

Tree-shaking support:支持摇树优化

Tree-shaking support带来的体积优化显而易见,之前vue2版本的nexttick、set等函数都是直接挂载在vue全局的,这样的话就有一个问题就是不管你在项目中是否用到,都会打包到项目中。而在vue3中这些api都是通过important进行引入的,这样的话,webpack在打包的时候就能够将不用的api进行剔除。

Composition API:组合API

这个是vue3中新增的最核心之一了,他完美的解决了vue2的mixin的问题,让所有的数据都有了来源,并且能够将模块儿拆分成一个个独立单元,进行高度复用,尤其是对无状态组件更好。

Fragment,Teleport,Suspense:新增的组件

vue2中需要一个根结点或者是引入vue-Fragments,会创建一个虚拟的根结点,vue3的话不需要这个。

Teleport传送门的话,之前vue2是通过portal-vue进行使用,vue3可以直接使用。

Suspense异步组件,会在里面有个插槽,当组件完全加载后才会显示组件的内容,否则显示插槽中的默认的内容。

Better TypeScript support:更好的TypeScript支持
Custom Renderer API:自定义渲染器

3、v-show和v-if的区别

-- v-show是通过css display去控制显示隐藏

-- v-if组件真正的销毁和重建,而不是显示和隐藏

-- 频繁切换状态用v-show,否则用v-if

4、v-for为何要使用key

-- 必须要用key,并且不能是index和random

-- diff算法中通过tag和key来判断,是否是sameNode(相同节点)

-- 减少渲染次数,提升渲染性能

5、描述Vue组件的生命周期(包括父子组件)

单组件:beforeCreate,create,beforeMounted,Mounted,beforeUpdate,updata,beforeDestory,destory

beforeCreate:初始化各个函数之类的;create:vue实例初始化完成;Mounted:网页绘制完成;beforeDestory:用来销毁自定义事件、定时器等

父子组件:

以创建为例子剩下都一样:父beforeCreate --》 子beforeCreate --》 子create -》父create

6、vue组件如何通讯

-- 父子组件 props和this.$emit()

-- 自定义组件 event.on、event.Off、event.$emit

-- vuex

-- 依赖注入 provide、inject

7、描述组件的渲染和更新的过程

概略:初始化对data数据进行响应化处理 --》模版编译,初始化视图 --》监听数据变化(初始化已经响应式处理data的setter和getter)--》模版编译,得到render函数 --》执行render触发touch触发getter后watcher进行依赖收集 --》 触发getter更改的同时会触发setter,会去通知watcher之前是否收集过了 --》收集过了就去触发re-render重新渲染

8、双向数据绑定v-model的实现原理

input元素上会挂载一个input事件,执行这个事件的时候,会把当前input的值给this下的name,value显示的时候显示的是name的这个变量,变量更新后再重新prop进来变量name放在input的value上,然后实现双向数据绑定。

value = this.name -- 》 this.name = $event.target.value -- 》data更新触发re-render

9、对MVVM的理解

view层,即视图展示 --》view-model层,数据处理层,包括生命周期,方法处理等 --》model层,数据存储,data函数

10、computed和watch

-- computed有缓存,data不发生变化则不会重新计算

-- watch深度监听,是有个deep属性,设置后可以深度监听(主要是监听引用类型),但是会很耗费性能(可以配合computed进行监听)

-- watch监听引用类型,拿不到oldval,因为指针赋值的关系,指针没有修改

11、为何组件data必须是一个函数

因为vue的组件实际上是一个class,当使用的时候其实是对这个组件进行了一个实例化,实例化的时候去执行data,如果data不是函数的话,每个组件data都一样了,数据共享污染了;如果是函数,两个data就在闭包之中,不会去相互影响

12、ajax请求应该放在哪个生命周期

mounted之中:因为js是单线程的,ajax是异步去获取数据;放在mounted之前没啥用,请求到也是排队过程中,只会使逻辑更加混乱

13、如何将组件所有的props传递给子组件

$props
<children v-bind="$props"/>

14、如何自己实现v-model

父组件:
<children v-model="value"/>

子组件:
<input type="text" :value="text" @input="$emit('changeValue', $event.target.value)" />

<script>
export default {
	model: {
		prop: text,
		event: changeValue
	},
	props: {
		text: String
	}
}
</script>

15、多个组件有相同的逻辑如何抽离

mixin:将数据进行混入公共使用,如果本身和传入数据有相同的,以本身数据为准;但是有很大缺陷,会陷入mixin地狱,如果传入多个会阅读困难

16、为何要使用异步组件

-- 加载大组件

-- 路由异步加载

17、何时使用keep-alive

-- 缓存组件,不需要重复渲染的时候

-- 多个静态tab页切换的时候

-- 优化性能的时候

18、何时要使用beforeDestory

-- 清楚自定义事件 event.$off,防止内存泄漏

-- 清楚定时器

-- 解绑自定义的DOM事件,比如window scroll等

19、什么是作用域插槽

可以将子组件中的数据传递给使用slot的地方,可以在外层使用

父组件:
<children>
	<template v-slot="slotProps">{{slotProps.web.title}}</template>
<children/>
子组件:
<template>
	<slot :web="web">
		{{web.url}}
	</slot>
</template>
export default {
	data: {
		web: {
			url: 'url链接',
			title: '作用域插槽'
		}
	}
}

20、vuex中的action和mutation有啥区别

-- action可以处理异步,但是mutation不可以

-- mutation做原子操作

-- action可以整合mutaion

21、vue-router原理

-- 稍微复杂点儿的spa,都需要前端路由

hash:

-- hash变化会触发网页跳转,即浏览器的前进与后退

-- hash变化不会刷新网页,

-- hash永远不会提交到server端

主要是通过监听onHashChange事件,来得到新旧路径来进行的页面的渲染

H5 history:

-- 用url规范的路由,但跳转时不刷新页面

-- 主要通过hsitory.pushState和window.onpopstate来实现

页面首次进来刷新页面获取pathName,然后进行路由跳转的时候使用pushState通过定义的页面路径进行跳转,然后通过window.onpopState来监听页面的前进和后退

需要服务端支持,无论切换到哪个页面都是进入index.html(根页面),然后再由这个页面进行路由的切换(由前端控制),否则就会找不到

22、如何配置vue-router异步加载

import异步引入

23、用vnode描述一个dom结构

{
	tag: "div",
	prsop: {className: 'class', style: font-size: 14px;},
	children: [
		{
			...
		}
	]
}

24、vue响应式(data监听核心)

核心api:Object.defineProperty

利用get和set劫持数据进行数据的跟新

监听对象改变:首先会去判断是否是一个对象,如果是就去递归解析,知道全部值解析出来,然后进行赋值;注意,在赋值前也需要递归一次,因为有可能是变更的值是一个对象,然后赋值进行视图更新。这儿的话有个缺点就是对对象属性的操作监听不到,比如新增或者删除属性这种,vue提供了专门的api针对这种情况。
监听数组改变:Object.defineProperty不能监听数组,首先监听数组原型,创建一个新的对象,然后在新对象上对数组的方法进行重写,然后监听数组值进行更新,然后再执行数据的原生方法进行数组的更改。
vue3:proxy兼容性不好:无法poryfill

25、diff算法时间复杂度

-- o(n)

-- o(n^3)优化而来:只比较同级节点;同级节点tag不相同的直接删除重建;tag和key都相同的默认节点相同,不再进行深度比较。

26、简述diff过程

-- 只比较同一层级,不跨级比较

-- tag不相同,直接删除重建,不再深度比较

-- tag和key都相同,则认为是相同节点,不再深度比较

此处vnode都统一表示新的node,oldvnode表示旧的node

h函数接受一个树形结构对象,经过处理后,返回一个包含$e、data、children,text,element等我们需要的vnode对象。得到新旧vnode后,在patch函数中去进行比较,在patch里面执行pre hook,判断第一个函数不是vode类型的话就去创建一个空的vnode关联到这个vnode元素,如何进行的绑定,是因为vode对象里面会有element传下来;

然后进行vnode的比较,如果是sel和key都相同的话,就判断为相同的vnode,然后执行patchnode进行vnode的对比,首先执行一个pre hook(一个生命周期的钩子),设置vnode element(将旧的element赋值给新的进行相应的对比),判断vnode.text是否是undefined,如果是说明新的node的children一般有值,(1)如果新旧都有children: (2)如果新children有,旧children无,旧的text有值就设置为空,然后添加新的children(3)旧children有,新children无,就直接移除children(4)旧的text有值,新的无,直接移除;如果text有值,判断新旧的text是否相同,不相同的话移除旧的children,设置新的text;

如果新旧节点都有children的话执行updataChildren函数:

传入新旧节点进行对比,对比顺序都以前面旧节点后面新节点进行对比,是开始和开始,结束和结束,开始和结束,结束和开始,判断是否相等就是samenode方法就是比较key和sel是否相等,然后根据patchvnode进行处理;如果都没命中,就拿新节点的key和所有的旧节点的key进行比较是否相同,都不相同的话,插入建立新的节点,对应上的话,拿上对应的key节点,判断sel是否相等,不想等的话插入建立新节点,sel和key都想等的话就patchvnode;然后指针累加相遇结束

不相同的话就直接删除重建;

这个就是为什么循环要加key,减少比对过程和销毁重建过程。

27、vue为啥是异步渲染,$nextTick有何用?

-- 异步渲染,合并data修改,减少DOM渲染,提高渲染性能

-- $nextTick是在DOM更新后,触发回调

28、vue的性能优化

-- 合理使用v-if和v-show

-- 合理使用computed

-- v-for中加key,以及避免和v-if同时使用

-- 自定义事件、DOM事件及时销毁

-- 合理使用异步组件

-- 合理使用keep-alive

-- data层级不要太深

-- 使用vue-loader做开发环境的预编译

-- webpack层面的优化

-- 前端通用性能优化(http 缓存,图片懒加载,雪碧图,cdn缓存)

-- 使用ssr