总结最近招聘面试时,经常会问的一些关于Vue的问题。
vue的data为什么是个函数?
data是Vue的数据对象,Vue会递归的把data的property转换为setter/getter,让property能相应数据变化。
个人理解:
这个问题主要是针对于Vue2的,对于Vue3采用的方法的组合调用来完成组件的定义。data改为了var data = ref([1, 2, 3])的形式
按照Vue教程来看,教程上确实是写的data是对象,这是因为教程是在HTML中通过new Vue使用的,Vue本身new之后都会是一个新对象,不存在共用对象的问题。
但是在单页面应哟.vue文件中使用时,当一个组件被实例化的时候,会通过data获取数据对象。如果data是一个函数,则调用data函数时会返回一个全新的对象,保证同一个组件被多次实例化后都独立维护自己的数据对象。如果data是一个对象,组件多次实例化后会共享同一套数据对象。vue生命周期有哪些?
beforeCreate:在实例初始化之后,只是基础加载 data和dom都不可用
created:实例已经创建完成之后被调用,data,method等都可用,dom不可用,可以使用nextTick访问dom。一般数据请求都放在这里
beforeMounted:组件挂载之前调用
mounted:组件挂载完成之后调用dom加载完成,双向绑定生效
beoreUpdate:数据更新时调用,虚拟dom重新渲染之前调用
updated:数据更新完成,要避免在这里更新数据,防止出现循环更新
beforeDestory:实例销毁之前调用
destory:实例销毁完成调用
activated:keep-alive专属生命周期,组件被激活时调用
deactivated:组件被销毁时调用computed和watch区别是什么?
computed是计算属性,依赖其他属性进行计算并返回计算结果。具有缓存机制,如果依赖的属性没有变更,则每次都是从缓存中取数据。
watch是监听,可以监听某一个属性的变化并执行自定义的一系列操作。
computed相比于method多了缓存机制,method里面的方法不具有缓存逻辑,每次调用方法都是执行内部的业务逻辑v-if和v-for为什么不建议一起用?
v-if和v-for的优先级不同,v-for的优先级会高于v-if。
可以利用计算属性computed 对list进行计算解析,过滤出需要的内容再进行v-for循环。vue双向绑定方式有哪些?
v-model和.sync修饰符。
v-model是变成value和input的语法糖,主要是通过vue的mode属性结合event事件实现的,一般只能绑定一个属性。
.sync修饰符主要是使用update:函数实现的,可以绑定多个属性。为什么用虚拟dom?
在底层实现上,Vue将模板编译成虚拟dom渲染函数。结合相应系统,vue能智能的算出最少要重新渲染多少组件,并把dom操作次数减到最少。虚拟DOM是什么?有什么优缺点?
由于在浏览器中操作DOM是很昂贵的。频繁操作DOM,会产生一定性能问题。这就是虚拟Dom的产生原因。Vue2的Virtual DOM 借鉴了开源库 snabbdom 的实现。Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点,是对真实DOM的一层抽象。
优点:
(1). 保证性能下限:框架的虚拟DOM需要适配任何上层API可能产生的操作,他的一些DOM操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的DOM操作性能要好很多,因此框架的虚拟DOM至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,既保证性能的下限。
(2). 无需手动操作DOM:我们不需手动去操作DOM,只需要写好 View-Model的 代码逻辑,框架会根据虚拟DOM和数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率。
(3).跨平台:虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器端渲染、weex开发等等。
缺点:
(1). 无法进行极致优化:虽然虚拟DOM + 合理的优化,足以应对大部分应用的性能需要,但在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化。
(2).首次渲染大量DOM时,由于多了一层DOM计算,会比innerHTML插入慢。你都做过哪些 Vue 的性能优化?
* 对象层级不要过深,否则性能就会差。
* 不需要响应式的数据不要放在 data 中(可以使用 Object.freeze() 冻结数据)
* v-if 和 v-show 区分使用场景
* computed 和 watch 区分场景使用
* v-for 遍历必须加 key,key最好是id值,且避免同时使用 v-if
* 大数据列表和表格性能优化 - 虚拟列表 / 虚拟表格
* 防止内部泄露,组件销毁后把全局变量和时间销毁
* 图片懒加载
* 路由懒加载
* 异步路由
* 第三方插件的按需加载
* 适当采用 keep-alive 缓存组件
* 防抖、节流的运用
* 服务端渲染 SSR or 预渲染前端优化主要从那些方面考虑?
* 降低请求量:合并资源,减少HTTP请求数,minify/gzip压缩
加快请求速度:预解析DNS,减少域名数,并行加载
缓存:HTTP协议缓存请求,离线缓存manifest,离线数据缓存localStorage
* 渲染:JS/CSS优化,加载顺序,服务端渲染浏览器运行机制是什么?
(1). 创建DOM树
(2). 构建渲染树,CSS渲染
(3). 布局渲染,每个元素的大小、位置
(4). 绘制渲染树、再画出来
重绘:改变元素的外观属性例如div的color、background-color、等属性发生改变时
重排(回流):元素的规模尺寸、布局、隐藏改变时
代价:耗时,导致浏览器卡慢优先级由高到低: props ==> methods ==> data ==> computed ==> watch
key的作用
key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
(key主要是用来虚拟dom计算。不使用key时,利用响应式系统计算dom操作次数。使用key时,基于key的变化重新排序,并移除key不存在的元素)
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
用在v-for中提供跟踪每个节点的身份,从而重用和重新排序现有元素。
key是一个唯一标识,如果使用index作为key,当数组index变更,比如增删时。index之后的所有dom节点都会更新。作用域插槽的原理
https://blog.csdn.net/qq_44166697/article/details/108274625
slot本质上是返回VNode的函数,一般情况下,Vue中的组件要渲染到页面上需要经过template>>render function >> VNode >> DOM过程。组件挂载的本质就是执行渲染函数得到VNode,至于data/props/computed这些属性都是给VNode提供数据来源
在2.5之前,如果是普通插槽就直接是VNode的形式了,而如果是作用域插槽,由于子组件需要再父组件访问子组件的数据,所以父组件下是一个未执行的函数(slotScopre) => return h('div',slotScope.msg),接受子组件的slotProps参数,在子组件渲染实例时会调用该函数传入数据。
在2.6之后,两者合并,普通插槽也变成一个函数,只是不接受参数了为什么避免在模板或计算属性中访问$refs
$refs只会在组件渲染完成后生效,并且他们不是响应式的。这仅作为一个用于直接操作子组件的”逃生舱“
依赖注入provide。inject问题
它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的 property 是非响应式的为什么数据请求要建议放到created生命周期?
Created在实例创建完成后同步调用。在这一步中,实例已完成对选项的处理,意味着数据侦听、计算属性、方法、事件/侦听器的回调函数已被配置完毕。assets、static、public区别
public:放一些静态资源,比如html的js,打包是会直接打入dist中
相同:assets、static都可以放一些资源。
不同:
(1). assets会经过webpack打包,占用资源更少,static不会,会直接复制到dist文件夹中。
(2). 但在动态绑定中,assets路径的图片会加载失败,因为webpack使用的是commenJS规范,必须使用require才可以
(3). static/目录下的文件都应该是使用绝对路径,assets使用相对路径。
绝对路径:是指文件在硬盘上真正存在的路径。 /desktop/page/..
相对路径:就是相对于自己的目标文件位置。 “.././“
static中建议放一些外部第三方,自己的放到assets,别人的放到static中
18、深拷贝方式
(1)JSON对象
(2)递归
(3)$.extends
// 递归示例
deepClone(obj){
let newObj = Object.create(null)
for (let k of Object.keys(obj)){
newObj[k] = obj[k]
}
return newObj
}
什么是响应式
数据是可以进行观测的,也就是说在读取和设置的时候可以劫持他来做其他操作。响应式和视图没关系,他只是一种机制,一种侦测数据变化的机制。Vue2和Vue3实现双向绑定原理
Vue2:基于Object.defineProperty 修改对象属性的权限标签
Vue.js一个核心思想是数据驱动****。所谓的数据驱动是指视图是由数据驱动的,对视图的修改不会直接操作Dom,而是通过修改数据。Vue.js里面只需要改变数据,Vue.js通过Directives指令去Dom做封装,当数据发生变化,会通知指令去修改对应的Dom,数据驱动Dom的变化,Dom是数据的一种自然的映射。Vue.js还会对View操作做一些监听(Dom Listener),当我们修改视图的时候,vue.js坚定提到这些编号,从而改变数据。这就形成了双向绑定。
官方解释:
当你把一个普通的JS对象传入Vue实例作为data选项,Vue会递归遍历此对象所有的property,并使用Object.defineProperty把这些property全部转换为getter/setter。
这些getter/setter对用户来说是不可见的,但是在内部他们让Vue能够跟踪依赖,在property被访问和修改时通知变更。
每个组件实例都对应一个watcher实例,他会在组件渲染的过程中把“接触”过的数据property记录为依赖,之后当依赖项的setter触发时,会通知watcher,从而改变关联的组件重新渲染。
defineProperty是ES5中的一个无法shim的特性,这也是Vue不支持IE8以下版本的原因。
个人理解:
通过Object.defineProperty递归的为对象增加setter/getter。并通过watcher监听数据变化,并改变关联的组件重新渲染。
Vue3:基于ES6的Proxy 代理整个对象
在vue3中则是使用es6 proxy代理,将对象data进行转化拦截,使用weakMap做弱引用缓存。当触发代理的get时就会调用track,在track里面会把对应的effect收集到targetMap(targetMap就是map数据)
set时会执行trigger,trigger函数如果有获取有对应的effect,就会触发effect。
问题扩展
Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。
一个 Proxy 对象由两个部分组成: target 、 handler 。在通过 Proxy 构造函数生成实例对象时,需要提供这两个参数。 target 即目标对象, handler 是一个对象,声明了代理 target 的指定行为。
Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
vue3优点
Vue3优化diff算法。不再像vue2那样比对所有dom,而采用了block tree的做法。此外重新渲染的算法里也做了改进,利用了闭包来进行缓存。这使得vue3的速度比vue2快了6倍。
- ES6新增内容。
ES6增加了let、const、Map、Set、Symbol、Reflect和Proxy。对于字符串,对象,数组、函数之类的增加了一些方法。增加了Promise对象,async函数、Generator函数等,增加了export导出模块。