1. v-if与v-show区别
v-if只有当条件是true才进行渲染,是真正的销毁和重建。
v-show无论条件是否为true都会渲染,只不过是基于css display:none来进行切换。
一般来说,v-if切换开销比较高,而v-show有比较高的初始渲染开销
2. router-link与a有区别吗
router-link其实还是转换成了a标签进行页面跳转。但是router-link利用了缓存机制,只渲染修改的部分。所有尽量使用router-link
3. vue-loader是什么?它的用途有哪些?
是基于webpack的一个的loader,解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理,
selector–将.vue文件解析拆分成一个parts对象,其中分别包含style、script、template
style-compiler–解析style部分
template-compiler 解析template部分
babel-loader-- 解析script部分,并转换为浏览器能识别的普通js
4. 计算属性和watch的区别
计算属性自动监听依赖值得变化,从而动态返回内容。watch是一个过程,在监听的值改变后,可以触发一个回调,并做一些事情。
一般来说,如果只是需要动态值,用计算属性;如果需要监听值的变化后执行逻辑,用watch。
另外:
计算属性是一个对象的时候,会有get和set两个属性。
计算属性跟方法相比,计算属性会有缓存,方法没有;计算属性不能接受参数,方法可以。
5. watch中的deep:true 是如何实现的?为什么computed没有deep:true
如果用户要监听的是个对象,内层的对象就不会被依赖收集,这时候有一个deep属性,要想让收集到内层的对象,就需要将deep设置成true,这时候就会执行traverse这个方法,这个方法里就是做了个数组递归,如果是数组的话,会根据数组的每一项索引取值,进行递归追加依赖,如果是对象会拿的key进行遍历取值,进行递归追加依赖,traverse就是deep:true实现的核心。这样就会把数组或者对象的没一个属性都进行依赖追加进行监听,只有依赖发生变化就会通知视图更新
因为computed是用在模板里的,在模板中的数据会调一个方法JSON.strginify(),放一个对象,默认会对这个对象里的所有属性求值。
6. 为何vue采用异步渲染
如果不采用异步渲染,那么每次更新数据都会对当前组件进行渲染。为了性能考虑,vue会在本轮数据更新后,异步更新视图。(一个组件,更新了很多的数据,每次都要渲染,那效率就太低了)
7. 为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
因为组件会被拿来复用。JS里的对象是引用关系,作用域没有隔离。这样一来就会造成不同组件的data相互影响。而new Vue的实例是不会被复用的,因此也不存在引用对象的问题。
8. vue中key的作用
key 的作用主要是为了更高效的更新虚拟 DOM,提高性能。
举例来说,a、b、c、d、e五个节点,如果更新后变成了a、b、c、z、d、e。如果没有key的话,diff算法会挨个更新元素。给节点添加key后,相当于是给节点添加了唯一标识。diff算法可以正确的识别节点,找到位置插入新的节点。
9. nextTick
作用:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,可以获取更新后的 DOM。
应用场景:需要在视图更新之后,基于新的视图进行操作。
触发时机:在同一事件循环中的数据变化后,DOM完成更新,立即执行Vue.nextTick()的回调。
Vue 在内部对异步队列尝试使用原生的 Promise.then
、MutationObserver
和 setImmediate
,如果执行环境不支持,则会采用 setTimeout(fn, 0)
代替。宏任务耗费的时间是大于微任务的,所以在浏览器支持的情况下,优先使用微任务。如果浏览器不支持微任务,使用宏任务。
10. keep-alive
11. vue的双向绑定原理
分对象和数组
对象的话,利用Object.defineProperty()来重新定义属性,当属性发生变化时,就会通知相关依赖进行更新操作。
数组的话,使用函数劫持的方法,重写了数组的方法
Vue将data中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样调用数组api(7种会改变数组的方法)时,就会通知依赖更新。如果数组中包含引用类型,则会对引用类型再进行监控。
vue3
vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过new Proxy()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
Vue 3.0与Vue 2.0的区别仅是数据劫持的方式由Object.defineProperty更改为Proxy代理,其他代码不变。
12. v-model是如何实现的
13. 单向数据流
vue 组件间传递数据是单向的,即数据总是由父组件传递到子组件,子组件在其内部可以有自己维护的数据,但它无权修改父组件传递给它的数据,当开发者尝试这样做的时候,vue 将会报错。这样做是为了组件间更好的解耦,在开发中可能有多个子组件依赖于父组件的某个数据,假如子组件可以修改父组件数据的话,一个子组件变化会引发所有依赖这个数据的子组件发生变化,所以 vue 不推荐子组件修改父组件的数据,直接修改 props 会抛出警告。
14. 组件通信
(1)props
一般属性,父传子
函数属性,子传父
父子通信比较常用的是,父传子用props,子传父用$emit(方法, 参数)
(2)事件总线,通过一个空的vue实例作为事件总线,用来触发和监听事件。$on
与$emit
可以实现任意组件间通信
(3)vuex
状态管理。最常用,最方便。可以实现任意关系组件的通信。
(4)$parent
,$children
与ref
ref如果在子组件上,就指向子组件实例
15. vuex管理状态的机制
vuex用来管理共享状态
state是共享状态的集合,getters通过操作state获得派生状态,mutations是操作state数据的方法集合,actions让mutations中的方法能够在异步操作中起作用
16. vue实例的生命周期
在beforeCreate 钩子函数调用的时候,是获取不到props 或者data 中的数据的,因为这些数据的初始化都在initState 中。
然后会执行created 钩子函数,在这一步的时候已经可以访问到之前不能访问到的数据,但是这时候组件还没被挂载,所以是看不到的。
接下来会先执行beforeMount 钩子函数,开始创建VDOM,最后执行mounted 钩子,并将VDOM 渲染为真实DOM 并且渲染数据。组件中如果有子组件的话,会递归挂载子组件,只有当所有子组件全部挂载完毕,才会执行根组件的挂载钩子。
再接下来是数据更新时会调用的钩子函数beforeUpdate 和updated,这两个钩子函数没什么好说的,就是分别在数据更新前和更新后会调用。
另外还有keep-alive 独有的生命周期,分别为activated 和deactivated 。用keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行deactivated 钩子函数,命中缓存渲染后会执行actived 钩子函数。
最后就是销毁组件的钩子函数beforeDestroy 和destroyed。前者适合移除事件、定时器等等,否则可能会引起内存泄露的问题。然后进行一系列的销毁操作,如果有子组件的话,也会递归销毁子组件,所有子组件都销毁完毕后才会执行根组件的destroyed 钩子函数。
17. 何时需要使用beforeDestroy
当前页面中使用了$on方法,那就需要在组件销毁前解绑
清除自己定义的定时器
解除事件的绑定scroll, mousemove等
18. Vue模板编译原理
19. 虚拟dom
20. diff算法
21. $.set
我们在data中定义一个对象后,如果给对象增加新的属性。则这个新增加的属性不是响应式的。当数据发生变化,并不会显示到视图。这是因为新添加的属性没有被Object.defineProperty劫持,导致视图不会同步更新。解决办法是使用this.$set(obj, 'name', 'jack')
对于数组,直接通过索引设置元素,例如arr[] = 2
修改数组长度,例如arr.length = 6
vue同样不能监测到数据变化