前端面试题总结(vue)

最近面试了几家,总结了一部分面试问题与解析给大家分享一下~


1.vue响应式原理

vue2的响应式原理是通过数据劫持结合发布者订阅模式 通过object.defineProperty来劫持各个属性的getter跟setter,在数据发生改变的时候发送消息给订阅者 触发相应的监听回调

缺点:无法监听对象的新增或者删除属性的变化,无法监听数组下标的变化

defineProperty 是只能针对对象进行操作的,那数组vue是怎么处理的呢?

官网给出的解释是由于javascript的限制,但是其实是可以使用defineProoerty监控数组下标进行处理的 但是考虑性能问题vue2放弃了这种方式

看github也有人去问过尤大 详情请看

https://github.com/vuejs/vue/issues/8562

所以vue中数组的响应式数据是通过对数组原始方法修改,然后对数组中的每项进行监听。

Vue 2 中数组的响应式原理涉及到如何追踪对数组的变动以及如何通知相关的依赖进行更新。在 Vue 2 中,通过使用 Object.defineProperty() 对数组的变异方法进行包装来实现数组的响应式。

具体来说,当你使用像 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 这样的数组变异方法时,Vue 会拦截这些操作,并发送通知给相关的观察者,从而触发相应的更新。

举例说明,当你调用 array.push() 方法向数组中添加新元素时,Vue 会捕获这个操作并通知相关的观察者,然后更新相关的视图。

需要注意的是,这种响应式的实现只能捕获数组变异方法的调用,而不能捕获直接通过下标修改数组元素的操作,比如 array[index] = value 这样的操作。

对于直接通过下标修改数组元素的情况,可以使用 Vue 提供的 $set 方法来手动触发更新。例如:this.$set(array, index, value)。

总的来说,Vue 2 中数组的响应式原理是基于拦截数组变异方法并通知相关观察者进行更新的机制实现的。

vue3的响应式原理

Vue3 是通过 Proxy 对数据实现 getter/setter 代理,从而实现响应式数据,然后在副作用函数中读取响应式数据的时候,就会触发 Proxy 的 getter,在 getter 里面把对当前的副作用函数保存起来,将来对应响应式数据发生更改的话,则把之前保存起来的副作用函数取出来执行。

具体是副作用函数里面读取响应式对象的属性值时,会触发代理对象的 getter,然后在 getter 里面进行一定规则的依赖收集保存操作。

简单代码


// 使用一个全局变量存储被注册的副作用函数 
let activeEffect 
// 注册副作用函数 
function effect(fn) { 
activeEffect = fn fn() 
} 
const obj = new Proxy(data, { 
// getter 拦截读取操作 
get(target, key) { 
// 将副作用函数 activeEffect 添加到存储副作用函数的全局变量 targetMap 中 track(target, key) // 返回读取的属性值 
return Reflect.get(target, key) 
}, 
// setter 拦截设置操作 
set(target, key, val) { 
// 设置属性值 
const result = Reflect.set(target, key, val) 
// 把之前存储的副作用函数取出来并执行 
trigger(target, key) 
return result 
} }) 
// 存储副作用函数的全局变量 
const targetMap = new WeakMap() 
// 在 getter 拦截器内追踪依赖的变化 
function track(target, key) { 
// 没有 activeEffect,直接返回 
if(!activeEffect) return 
// 根据 target 从全局变量 targetMap 中获取 depsMap 
let depsMap = targetMap.get(target) 
if(!depsMap) { 
// 如果 depsMap 不存,那么需要新建一个 Map 并且与 target 关联 
depsMap = new Map() 
targetMap.set(target, depsMap) 
} 
// 再根据 key 从 depsMap 中取得 deps, deps 里面存储的是所有与当前 key 相关联的副作用函数
 let deps = depsMap.get(key) 
if(!deps) { 
// 如果 deps 不存在,那么需要新建一个 Set 并且与 key 关联 
deps = new Set()
 depsMap.set(key, deps)
} 
// 将当前的活动的副作用函数保存起来 
deps.add(activeEffect) 
} 
// 在 setter 拦截器中触发相关依赖
 function trgger(target, key) { 
// 根据 target 从全局变量 targetMap 中取出 depsMap
 const depsMap = targetMap.get(target) 
if(!depsMap) return 
// 根据 key 取出相关联的所有副作用函数 
const effects = depsMap.get(key) 
// 执行所有的副作用函数 
effects && effects.forEach(fn => fn()) 
}

const obj = new Proxy({a:{b:1}},{

get(target, key){

return Reflect.get(target,key)

},

set(target, key, value){

return Reflect.set(target,key,value)

}

})

obj.a.b = 2

以上代码大家可以思考一下,是否会触发get或者set?

答案是不会的,因为上述代码只是使用proxy代理了外层对象 所以不会触发内层的响应式,vue针对深层次做了递归判断是否是引用类型还是基础类型 进行了处理

那vue2跟vue3的响应式原理的区别就是在于

vue3使用了新特性proxy进行对象的代理监听 解决了无法监听对象的新增、删除属性的问题

Vue3 中是怎么监测数组的变化?

Vue3 对数组实现代理时,用于代理普通对象的大部分代码可以继续使用,但由于对数组的操作与对普通对象的操作存在很多的不同,那么也需要对这些不同的操作实现正确的响应式联系或触发响应。这就需要对数组原型上的一些方法进行重写。

比如通过索引为数组设置新的元素,可能会隐式地修改数组的 length 属性的值。同时如果修改数组的 length 属性的值,也可能会间接影响数组中的已有元素。另外用户通过 includes、indexOf 以及 lastIndexOf 等对数组元素进行查找时,可能是使用代理对象进行查找,也有可能使用原始值进行查找,所以我们就需要重写这些数组的查找方法,从而实现用户的需求。原理很简单,当用户使用这些方法查找元素时,先去响应式对象中查找,如果没找到,则再去原始值中查找。

 

Vue3 对数组实现代理时,用于代理普通对象的大部分代码可以继续使用,但由于对数组的操作与对普通对象的操作存在很多的不同,那么也需要对这些不同的操作实现正确的响应式联系或触发响应。这就需要对数组原型上的一些方法进行重写。

比如通过索引为数组设置新的元素,可能会隐式地修改数组的 length 属性的值。同时如果修改数组的 length 属性的值,也可能会间接影响数组中的已有元素。另外用户通过 includes、indexOf 以及 lastIndexOf 等对数组元素进行查找时,可能是使用代理对象进行查找,也有可能使用原始值进行查找,所以我们就需要重写这些数组的查找方法,从而实现用户的需求。原理很简单,当用户使用这些方法查找元素时,先去响应式对象中查找,如果没找到,则再去原始值中查找。

proxy的性能会更好 可以对象嵌套对象 defineproperty会把对象进行一个深层的递归 性能有欠缺 proxy只需要循环第一层 后面的对象在你使用的时候才会执行代理。

 

2.vue的更新是同步还是异步?

 

    •    数据是同步更新,视图是异步更新

    •    因为如果视图更新是同步的,那会导致多次渲染浪费不必要的性能,没必要,内部做了去重(重新更新的值)和防抖(只更新最后一次)

响应式多次更新的问题,比如一个数组 遍历了一千次 每一次要加东西 数组的东西在dom要用 每次更新触发render 同步更新没有意义 不需要更新一次页面也要更新一千次 

3.自定义指令控制组件权限弊端

弊端1 对于没有权限的组件按钮 会不必要的创建dom再销毁 重绘重排 浪费资源

弊端2 对于没权限的组件 仍然会进入生命周期 如果mounted有请求实际会发送请求 有不必要的风险 及消耗服务器资源 组件上暴露的方法属性会缓存虽然没有dom但组件实例仍然存在 

弊端3 代码统一管理相权限组件的方式不够优雅

4.vue的h函数

h() 函数用于辅助创建虚拟 DOM 节点,它是 hypescript 的简称————能生成 HTML (超文本标记语言) 的 JavaScript,它有另外一个名称,叫做 createVnode()。

h()函数接收参数如下:

    •    type:类型参数,必填。内容为字符串或者 Vue 组件定义。

    •    props:props参数,非必填。传递内容是一个对象,对象内容包括了即将创建的节点的属性,例如 id、class、style等,节点的事件监听也是通过 props 参数进行传递,并且以 on 开头,以 onXxx 的格式进行书写,如onInput、onClick 等。

    •    children:子节点,非必填。内容可以是文本、虚拟 DOM 节点和插槽等等。

5.vue插槽原理

当子组件vm实例化时,获取到父组件传入的slot标签内容,存放在vm.$slot 中,默认插槽为vm.slot.default,具名插槽为vm.slot.xx,,xx为插槽的名字,当组件执行渲染函数时,遇到slot标签,使用$slot中的内容进行提换,此时可以为插槽传递数据,若存在数据,则可称插槽为作用域插槽

slot本质上是返回VNode的函数,一般情况下,Vue中的组件要渲染到页面上需要经过template>>render function >> NVode >> DOM过程。组件挂载的本质就是执行渲染函数得到VNode,至于data/props/computed这些属性都是给VNode提供数据来源

6.webpack跟vite的区别

1.开发模式不同 webpack会把所有模块打包为一个bundle。js文件启动

vite开发模式下没有打包步骤 利用了es moudle importe特性只有在真正需要的时候才进行处理

2.更新模式不同 

webpack是全量更新 即使是更改一个小文件 也会重新编译 vite的增量更新只更新修改的文件

3.扩展性不同

webpack的插件生态比vite广泛

4.配置复杂度不同

webpack的配置相对复杂,vite基本是开箱即用

7.闭包

函数内部嵌套函数 内部函数可以使用外部函数的变量

好处:延长变量作用域,避免全局变量污染

缺点:增加内存 使用不当会造成内存泄漏(手动将变量设置为null)

8.浏览器的垃圾回收机制

浏览器为了防止内存泄漏 定期会寻找不再使用的变量 进行释放

1.标记清除

浏览器运行的时候 会给所有变量做一个标记 去除环境中的变量以及被使用的变量

下次垃圾回收机制回收的时候 会把所有带有标记的变量回收

2.引用计数

声明一个变量 将引用类型的值赋值给这个变量 这个引用类型的值使用次数就是1 同一个值又被赋值给另外一个变量 这个引用类型的值引用次数加一 当包含这个引用类型的变量又被赋值给另外一个值之后 这个引用类型的值的引用次数减1 

当引用次数变成0的时候 说明需要结束引用

垃圾回收机制下次回收的时候,就会释放引用次数为0的值占用的内存

9.事件轮询

js是单线程的,执行同步任务的时候,之间推入调用栈中执行,遇到异步任务时,先将异步任务挂起来,直到异步任务有返回再推入执行队列中,当调用栈中同步任务执行完毕后,调用栈再从执行队列中获取异步任务执行,这个循环的过程就叫做事件轮询

宏任务和微任务是对同步任务和异步任务的更细致的划分。js执行时,会先执行宏任务,再执行宏任务下面所有的为任务,之后再开启下一次的宏任务执行

宏任务:script代码,setTimeout,setInterval

微任务:promise.then() process.nextTick()

 

在js中既先执行完所有同步任务,再执行异步任务,而异步任务有异步队列中会先找到微任务,执行完所有微任务后,执行下一个宏任务,每一个宏任务执行完之后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务之后,再继续执行下一个宏任务。

10.浏览器渲染原理

首先输入url之后进行dns解析出ip 然后去查询有没有缓存 有缓存走缓存 没有走请求 建立tcp连接 三次握手 响应文件 浏览器解析 html通过html解析器解析为dom树 css通过css解析器解析为cssom树 js通过js解析器解析js判断是否有dom或者css的更新 如果有会重新调用 html/css解析器重新解析 完成后 cssom树根dom树合并为render树 然后layout布局 计算元素的大小位置 进行渲染

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值