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