1、谈一下你对MVVM的理解
MVVM 是 Model-View-ViewModel
的缩写。
Model
代表数据模型,也可以在Model
中定义数据修改和操作的业务逻辑。View
代表UI 组件,它负责将数据模型转化成UI 展现出来。ViewModel
监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View
和Model
的对象,连接Model
和View
。
在MVVM架构下,
View
和Model
之间并没有直接的联系,而是通过ViewModel
进行交互,Model
和ViewModel
之间的交互是双向的, 因此View
数据的变化会同步到Model
中,而Model
数据的变化也会立即反应到View 上
2、请说一下Vue响应式数据的原理
- 就是利用
Object.defineProperty
数据劫持 和观察者模式实现的 - 在数据初始化的时候吧data里面的数据进行转换,因为
Object.defineProperty
里面有一个get和set方法 get可以返回被劫持的属性的结果,而set可以修改被劫持的属性的结果。 - 每个数据创建一个Dep被观察者,用到数据的地方就被称为观察者
watcher
- 对模板进行编译compiler提取里面所有的需要数据的地方变成
watcher
把watch加入到对应的Dep的观察者列表中 - 当data中的数据发生改变的时候,由于被劫持了 所以vue的内部是知道数据改变了,然后就调用对应的dep去通知对应的观察者列表中的所有观察者,观察者得到通知后立刻去更新视图
3、vue中如何检测数组变化
使用函数劫持的方式,重写了数组的方法,Vue 将 data 中的数组,进行了原型链重写。指向了重新定义的数组原型方法,这样当调用数组API的时候,可以通知依赖更新。如果数组中包含着引用类型。会对数组中的引用类型再次进行监控。
4、为何vue采用异步渲染
如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能考虑,Vue会在本轮数据更新后,再去异步更新试图。
5、nextTick的实现原理
nextTick
主要使用了宏任务和微任务,定义了一个异步的方法,多次调用nextTick
方法会将方法存入队列中,通过这个异步方法清空当前队列,所以这个nextTick
方法就是异步方法。
6、Vue中computed的特点
默认的computed
也是一个watcher
是具备缓存的,当依赖属性发生变化时才会更新试图
7、Watch中的deep:true是如何实现的
当用户指定了watch中的deep属性为true时,如果当前监控值是数组类型,会对对象中的每一项进行求值,此时会将当前的watcher
存入到对应属性的依赖中,这样数组中对象发生变化时也会通知数据更新
8、vue的生命周期
什么时候被调用:
- beforeCreate:在实例初始化之后,数据观测(data observer)之前被调用。
- created:实例已经创建完成之后被调用,在这一步,实例已经完成以下配置:数据观测(data obwerver),属性和方法的运算,watch/event事件回调。这里没有#el。
- beforeMount:在挂载开始之前被调用,相关的render函数首次被调用。
- mounted:el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。
- beforeUpdate:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。
- updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
- beforeDestroy:实例销毁之前调用,在这一步,实例仍然完全可用。
- destroyed:vue实例销毁后调用,调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用。
生命周期内部可以做什么事:
- created:实例已经创建完成,因为他是最早触发的原因可以进行一些数据资源的请求。
- mounted:实例已经挂载完成,可以进行一些DOM操作
- beforeUpdate:可以在这个钩子中进一步的更改状态,这个不会触发附加的重渲染过程
- updated:可以执行依赖于DOM的操作,然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用
- beforeDestroy:可以执行一些优化操作,清空定时器,解除绑定事件
9、ajax请求放在哪个生命周期合适呢?
- 在created的时候,视图中的dom并没有渲染出来,所以此时如果直接去操作dom节点,无法找到相关的元素。
- 在mounted中,由于此时dom已经渲染出来了,所以可以直接操作dom节点
一般情况下都放到mounted中,保证逻辑的统一性,因为生命周期是同步执行的,ajax是异步执行的
10、何时需要用到 beforeDestroy
- 可能在当前页面里使用了
$on
方法,那需要在组件销毁前解绑 - 清除自己定义的定时器
- 解除事件的绑定
scroll mousemove
11、vue中的v-if和v-show的区别
- v-if如果条件不成立不会渲染当前指令所在节点的dom元素
- v-show只是切换当前dom的显示隐藏
12、为什么v-for和v-if不能连用
v-for会比v-if的优先级高一些,如果连用的话,会把v-if给每个元素都添加,会造成性能问题
13、用vnode来描述一个dom结构
虚拟几点就是用一个对象来来描述真实的dom元素。
function c(tag,data,...children){
let key = data.key;
delete data.key;
children = children.map(child => {
if(typeof child === 'object'){
return child;
}else{
return vnode(undefined,undefined,undefined,undefined,child)
}
})
return vnode(tag,data,key,children)
}
function vnode(tag,data,key,children,text){
return {tag,data,key,children,text}
}
14、简述vue中diff算法原理
- 先同级比较,在比较子节点
- 先判断一方有儿子一方没儿子的情况
- 比较都有儿子的情况
- 递归比较子节点
15、v-for中为什么要用key
key主要是用来做dom diff的,例如只是将列表的顺序调换位置,可以通过key比对出来,只会做一个移动的操作,并不会有销毁的操作。v-for尽量不使用索引做为key,因为索引删除之前和之后是一样的,删之前是0,1,2,删之后变成0,1。
16、组件中data为什么是一个函数
同一个组件被复用多次,会创建多个实例,这些实例用的都是同一个构造函数,如果data是一个对象的话,那么所有组件都共享了同一个对象,为了保证组件的数据独立性要求每个组件必须通过data函数返回一个对象作为组件的状态
为什么new Vue({data:{}})可以是一个对象,不是一个函数,因为它不会复用
17、Vue中的事件绑定原理
Vue中的事件绑定分为两种,一种是原生的事件绑定,还有一种是组件的事件绑定。
- 原生dom事件绑定,采用的是
addEventListener
实现 - 组件事件绑定采用
$on
方法
18、v-model中的实现原理及如何自定义v-model
组件的v-model
是value+input
方法的语法糖
编译时,不同的标签解析出的内容不一样例如checkbox等
19、Vue中v-html会导致哪些问题
- 可能会导致XSS攻击
- v-html会替换掉标签内部的子元素
20、Vue父子组件生命周期调用顺序
- 组件的调用顺序都是先父后子,渲染完成的顺序是先子后父
- 组件的销毁操作是先父后子,销毁完成的顺序是先子后父
渲染顺序:父
beforeCreate
->父created
->父beforeMount
->子beforeCreate
->子created
->子beforeMount
->子mounted
->父mounted
更新顺序:父beforeUpdate
->子beforeUpdate
->子updated
->父updated
销毁顺序:父beforeDestroy
->子beforeDestroy
->子destroy
->父destroy
21、Vue组件如何通信
-
父子之间通信 父->子通过props、子->父通过
$on、$emit
-
获取父子实例的方式
$parent、$childeren
-
在父组件中提供数据子组件进行消费Provide、inject
-
Ref获取实例的方式调用组件的属性或者方法
-
Event、Bus
实现跨组件通信 -
Vues状态管理实现通信
22、为什么要用异步组件
如果组件功能多,打包出来的体积会变大,我们可以采用异步组件的方式来加载组件。主要依赖import()
这个语法,可以实现文件的分割加载。
23、什么是作用域插槽
- 插槽:
- 创建组件虚拟节点时,会将组件的儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿子进行分类
{a:[vnode],b[vnode]}
。 - 渲染组件时,会拿对应的slot属性的节点进行替换操作。(插槽的作用域为父组件)
- 作用域插槽
- 作用域插槽在解析的时候,不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。(插槽的作用域为子组件)
24、谈谈你对keep-alive的了解
keep-alive可以实现组件的缓存,当组件切换时不会对当前组件进行卸载,常用的两个属性include、exclude
,2个生命周期activated、deactivated
25、Vue中常见的性能优化
-
不要将所有数据都放在data中,data中的数据都会增加
getter
和setter
,会收集对应的watch
-
vue在
v-for
时给每项元素绑定事件需要用事件代理 -
SPA页面使用
keep-alive
缓存组件 -
拆分组件(提高复用性,增加代码可维护性,减少不必要的渲染)
-
v-if
当值为false时,内部指令不会执行,具有阻断功能,很多情况下使用v-if代替v-show
-
key保证唯一性(默认vue会采用就地复用策略)
-
Object.freeze
冻结数据 -
合理使用路由懒加载、异步组件
-
数据持久化的问题(防抖、节流)