1. Vue中如何检测数组的变化?
vue中对数组没有进行defineProperty,而是重写了数组的7 个方法。 分别是:
push
shift
pop
splice
unshift
sort
reverse
因为这些方法都会改变数组本身。
数组里的索引和长度是无法被监控的。
2. Vue中如何进行依赖收集的?
Vue初始化的时候,挂载之后会进行编译。生成renderFunction。
当取值的时候,就会搜集watcher,放到dep里面。
当用户更改值的时候,就会通知watcher,去更新视图。
3. 如何理解Vue中的模板编译原理?
这个问题的核心是如何将template转换成render函数。
将template模块转换成ast语法书 - parserHTML
对静态语法做标记(某些节点不改变)
重新生成代码 - codeGen, 使用with 语法包裹字符串
4. Vue组件生命周期有哪些?
beforeCreate 在实例初始化之后,数据观测observer 和event、watcher事件配置之前被调用
created 实例已经创建完成,在这一步,以下配置被完成
数据观测
属性和方法的运算
watch/ event事件回调
$el尚未生成
beforeMount 在挂载之前被调用,render尚未被调用
mounted el被新创建的vm. $el替换,并挂载到实例上去之后调用
beforeUpdate 数据更新时,被调用,发生在虚拟Dom重新渲染和打补丁之前
update 由于数据更改导致的虚拟Dom重新渲染和打补丁,在这之后调用
beforeDestroy 实例销毁之前调用
destroyed 实例销毁之后调用,调用后Vue实例的所有东西都会被解绑,所有的事件监听会被移除,子实例被销毁,该钩子在服务端渲染期间不被调用
keep- alive(activated & deactivated)
5. Vue的组件data为什么必须是一个函数?
new Vue 是一个单例模式,不会有任何的合并操作,所以根实例不必校验data一定是一个函数。
组件的data必须是一个函数,是为了防止两个组件的数据产生污染。
如果都是对象的话,会在合并的时候,指向同一个地址。
而如果是函数的时候,合并的时候调用,会产生两个空间。
6. 请说明nextTick的原理。
nextTick是一个微任务。
nextTick中的回调是在下次Dom更新循环结束之后执行的延迟回调
可以用于获取更新后的Dom
Vue中的数据更新是异步的,使用nextTick可以保证用户定义的逻辑在更新之后执行
7. computed和watch的区别是什么?
computed和watch都基于watcher来实现的。
computed的属性是具备缓存的,依赖的值不发生变化,对其取值时计算属性方法不会重复执行
watch是监控值的变化,当值发生改变的时候,会调用回调函数
8. Vue.set方法是如何实现的?
vue给对象和数组本身都增加了dep属性
当给对象新增不存在的属性的时候,就会触发对象依赖的watcher去更新
当修改数组索引的时候,就调用数组本身的splice方法去更新数组
9. Vue为什么要用虚拟Dom?
虚拟dom就是用js对象来描述真实Dom,是对真实Dom的抽象
由于直接操作Dom性能低,但是js层的操作效率高,可以将Dom操作转化成对象操作。最终通过diff算法比对差异进行更新Dom
虚拟Dom不依赖真实平台环境,可以实现跨平台
10. Vue的diff算法原理是什么?
Vue的diff算法是平级比较,不考虑跨级比较的情况。内部采用深度递归的方式+ 双指针方式比较
先比较两个节点是不是相同节点
相同节点比较属性,复用老节点
先比较儿子节点,考虑老节点和新节点儿子的情况
优化比较:头头、尾尾、头尾、尾头
比对查找,进行复用
11. 既然vue通过数据劫持可以精准的探测数据变化,为什么还要进行diff检测差异?
响应式数据变化,Vue确实可以在数据变化的时候,响应式系统可以立刻得知。但是如何每个属性都添加watcher的话,性能会非常的差。
粒度过细,会导致更新不精准
所以采用watcher + Diff算法来检测差异。
12. 请说明key的作用和原理
Vue在patch过程中,通过key可以判断两个虚拟节点是否是相同节点。
没有key会导致更新的时候出问题
尽量不要采用索引作为key
13. 谈谈对组件的理解
组件化开发能大幅提高应用开发效率、测试性、复用性
常用的组件化技术:属性、自定义事件、插槽
降低更新范围,值重新渲染变化的组件
高内聚、低耦合、单向数据流
14. 请描述组件的渲染流程
产生组件虚拟节点 - > 创建组件的真实节点 - > 插入到页面
15. 异步组件原理
先渲染异步占位符节点 - > 组件加载完毕后调用forceUpdate强制更新。
16. 函数组件的优势和原理
函数式组件的特性:无状态、无生命周期、无this 。因此性能会高一点。
正常的一个组件是一个类继承了Vue。
函数式组件,就是一个普通的函数。
17. 组件的传值方式有哪些?
props和emit : 父组件向子组件传递数据,通过prop传递。子组件传递数据给父组件是通过emit事件
parent, children获取当前组件的父组件和当前组件的子组件
attrs和listeners 。
父组件通过provide提供,子组件通过inject注入变量
$ref获取实例
eventBus平级组件数据传递
Vuex
18. $attrs是为了解决什么问题出现的?
主要作用是为了实现批量传递数据。
provide/ inject更适合应用在插件中,主要实现跨级数据传递。
19. v-for和v-if哪个优先级更高?
首先,v- for 和v- if 不能在同一个标签中使用。
先处理v- for ,再处理v- if 。
如果同时遇到的时候,应该考虑先用计算属性处理数据,在进行v- for ,可以减少循环次数。
20. Vue的普通Slot以及作用域Slot的区别
普通插槽
普通插槽是渲染后做替换的工作。父组件渲染完毕后,替换子组件的内容。
作用域插槽
作用域插槽可以拿到子组件里面的属性。在子组件中传入属性然后渲染。
21. Vue.use是干什么的?
Vue. use是用来使用插件的。我们可以在插件中扩展全局组件、指令、原型方法等。
会调用install方法将Vue的构建函数默认传入,在插件中可以使用vue,无需依赖vue库
22. vue的修饰符有哪些?
. stop
. prevent
. capture
. self
. once
. passive
. right
. center
. middle
. alt
23. keep-alive平时在哪里使用?原理是什么?
使用keep- alive包裹动态组件时,会对组件进行缓存,避免组件重新创建
使用有两个场景,一个是动态组件,一个是router- view
include 字符串或正则表达式。只有名称匹配的组件会被缓存
exclude 字符串或正则表达式。任何名称匹配的组件都不会被缓存
max 数字、最多可以缓存多少组件实例
24. vue-router有几种钩子函数?执行流程如何?
钩子函数有三种:
全局守卫
路由守卫
组件守卫
25. 谈谈Vue的性能优化有哪些?
编码阶段
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
v- if 和v- for 不能连用
如果需要使用v- for 给每项元素绑定事件时使用事件代理
SPA 页面采用keep- alive缓存组件
在更多的情况下,使用v- if 替代v- show
key保证唯一
使用路由懒加载、异步组件
防抖、节流
第三方模块按需导入
长列表滚动到可视区域动态加载
图片懒加载
SEO 优化
预渲染
服务端渲染SSR
打包优化
压缩代码
Tree Shaking/ Scope Hoisting
使用cdn加载第三方模块
多线程打包happypack
splitChunks抽离公共文件
sourceMap优化
用户体验
骨架屏
PWA
还可以使用缓存 ( 客户端缓存、服务端缓存) 优化、服务端开启gzip压缩等。
数据层级不要过深,合理的设置响应式数据
使用数据时,缓存值的结果,不频繁取值
合理设置key
v- show ( 频繁切换性能高) 和v- if 的合理使用
控制组件的粒度 - > Vue采用组件级别更新
采用函数式组件 - > 函数式组价开销低
采用异步组件 - > 借助webpack的分包策略
使用keep- alive来缓存组件
虚拟滚动、时间分片等策略
打包优化
26. 谈谈你对Vuex的理解?
Vuex是专门为vue提供的全局状态管理系统,用于多个组件中的数据共享、数据缓存。
state ( 单一状态树) getter/ Mutation显示提交更改state
Action类似Mutation,提交Mutation,可以包含任意异步操作。
module ( 当应用变得庞大复杂,拆分store为具体的module模块)
问题:无法持久化。
27. 双向绑定实现原理
当一个Vue实例创建时,Vue会遍历data选项的属性,用 Object. defineProperty 将它们转为 getter/ setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher重新计算,从而致使它关联的组件得以更新。
28. v-model的实现以及它的实现原理吗?
vue中双向绑定是一个指令v- model,可以绑定一个动态值到视图,同时视图中变化能改变该值。v- model是语法糖,默认情况下相于: value和@input。
使用v- model可以减少大量繁琐的事件处理代码,提高开发效率,代码可读性也更好
通常在表单项上使用v- model
原生的表单项可以直接使用v- model,自定义组件上如果要使用它需要在组件内绑定value并处理输入事件
我做过测试,输出包含v- model模板的组件渲染函数,发现它会被转换为value属性的绑定以及一个事件监听,事件回调函数中会做相应变量更新操作,这说明神奇魔法实际上是vue的编译器完成的。
29. 你知道Vue3有哪些新特性吗?它们会带来什么影响?
性能提升
更小巧、更快速
支持自定义渲染器
支持摇树优化:一种在打包时去除无用代码的优化手段
支持Fragments和跨组件渲染
API 变动
模板语法99 % 保持不变
原生支持基于class 的组件,并且无需借助任何编译及各种stage阶段的特性
在设计时也考虑TypeScript的类型推断特性
重写虚拟DOM 可以期待更多的编译时提示来减少运行时的开销
优化插槽生成可以单独渲染父组件和子组件
静态树提升降低渲染成本
基于Proxy的观察者机制节省内存开销
不兼容IE11
检测机制更加全面、精准、高效, 更具可调试式的响应跟踪
30. 实现双向绑定 Proxy 与 Object.defineProperty 相比优劣如何?
Object. definedProperty的作用是劫持一个对象的属性,劫持属性的getter和setter方法,在对象的属性发生变化时进行特定的操作。而 Proxy劫持的是整个对象。
Proxy会返回一个代理对象,我们只需要操作新对象即可,而Object. defineProperty只能遍历对象属性直接修改。
Object. definedProperty不支持数组,更准确的说是不支持数组的各种API ,因为如果仅仅考虑arry[ i] = value 这种情况,是可以劫持 的,但是这种劫持意义不大。而Proxy可以支持数组的各种API 。
尽管Object. defineProperty有诸多缺陷,但是其兼容性要好于Proxy。