2024年前端最新几个关于vue的面试题,2024年最新直面春招

专业技能

一般来说,面试官会根据你的简历内容去提问,但是技术基础还有需要自己去准备分类,形成自己的知识体系的。简单列一下我自己遇到的一些题

最近得空把之前遇到的面试题做了一个整理,包括我本人自己去面试遇到的,还有其他人员去面试遇到的,还有网上刷到的,我都统一的整理了一下,希望对大家有用。

其中包含HTML、CSS、JavaScript、服务端与网络、Vue、浏览器等等

由于文章篇幅有限,仅展示部分内容

nextTick 函数接受到一个 callback 函数的时候,先不去调用它,而是把它 push 到一个全局的 queue 队列中,等待下一个任务队列的时候再一次性的把这个 queue 里的函数依次执行。

这个队列可能是 microTask 队列,也可能是 macroTask 队列,前两个 api 属于微任务队列,后两个 api 属于宏任务队列。

简化实现一个异步合并任务队列:

let pending = false

// 存放需要异步调用的任务

const callbacks = []

function flushCallbacks() {

pending = false

// 循环执行队列

for (let i = 0; i < callbacks.length; i++) {

callbacksi

}

// 清空

callbacks.length = 0

}

function nextTick(cb) {

callbacks.push(cb)

if (!pending) {

pending = true

// 利用Promise的then方法 在下一个微任务队列中把函数全部执行

// 在微任务开始之前 依然可以往callbacks里放入新的回调函数

Promise.resolve().then(flushCallbacks)

}

}

测试一下:

// 第一次调用 then方法已经被调用了 但是 flushCallbacks 还没执行

nextTick(() => ???console.log(1))

// callbacks里push这个函数

nextTick(() => ???console.log(2))

// callbacks里push这个函数

nextTick(() => ???console.log(3))

// 同步函数优先执行

console.log(4)

// 此时调用栈清空了,浏览器开始检查微任务队列,发现了 flushCallbacks 方法,执行。

// 此时 callbacks 里的 3 个函数被依次执行。

// 4

// 1

// 2

// 3

Vue 优点


???虚拟 DOM 把最终的 DOM 操作计算出来并优化,由于这个 DOM 操作属于预处理操作,并没有真实的操作 DOM,所以叫做虚拟 DOM。最后在计算完毕才真正将 DOM 操作提交,将 DOM 操作变化反映到 DOM 树上

看起来说的很厉害,其实也没说到点上。关于虚拟 DOM 的优缺点,直接看 Vue 作者尤雨溪本人的知乎回答,你会对它有进一步的理解:

网上都说操作真实 DOM 慢,但测试结果却比 React 更快,为什么?[2]

???双向数据绑定通过 MVVM 思想实现数据的双向绑定,让开发者不用再操作 dom 对象,有更多的时间去思考业务逻辑

开发者不操作 dom 对象,和双向绑定没太大关系。React 不提供双向绑定,开发者照样不需要操作 dom。双向绑定只是一种语法糖,在表单元素上绑定 value 并且监听 onChange 事件去修改 value 触发响应式更新。

我建议真正想看模板被编译后的原理的同学,可以去尤大开源的 vue-template-explorer[3]  网站输入对应的模板,就会展示出对应的 render 函数。

???运行速度更快,像比较与 react 而言,同样都是操作虚拟 dom,就性能而言,vue 存在很大的优势

为什么快,快在哪里,什么情况下快,有数据支持吗?事实上在初始化数据量不同的场景是不好比较的,React 不需要对数据递归的进行 响应式定义

而在更新的场景下 Vue 可能更快一些,因为 Vue 的更新粒度是组件级别的,而 React 是递归向下的进行 reconcilerReact 引入了 Fiber 架构和异步更新,目的也是为了让这个工作可以分在不同的 时间片 中进行,不要去阻塞用户高优先级的操作。

???Proxy 是 es6 提供的新特性,兼容性不好,所以导致 Vue3 一致没有正式发布让开发者使用

Vue3 没发布不是因为兼容性不好,工作正在有序推进中,新的语法也在不断迭代,并且发布 rfc 征求社区意见。

???Object.defineProperty 的缺点:无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应

事实上可以,并且尤大说只是为了性能的权衡才不去监听[4]。数组下标本质上也就是对象的一个属性。

React 和 Vue 的比较


???React 默认是通过比较引用的方式(diff)进行的,React 不精确监听数据变化。

比较引用和 diff 有什么关系,难道 Vue 就不 diff 了吗。

???Vue2.0 可以通过 props 实现双向绑定,用 vuex 单向数据流的状态管理框架

双向绑定是 v-model 吧,面试的时候不要把双向绑定和响应式数据给搞混。

???Vue 父组件通过 props 向子组件传递数据或回调

Vue 虽然可以传递回调,但是一般来说还是通过 @change 这样的方式去绑定事件吧,这和回调是两套机制。深入的话可以从 Vue 内部实现的 eventEmitter 事件总线机制来回答。

???模板渲染方式不同,Vue 通过 HTML 进行渲染

事实上 Vue 是自己实现了一套模板引擎系统,HTML 可以被利用为模板的而已,你在 .vue 文件里写的 templateHTML 本质上没有关系。

???React 组合不同功能方式是通过 HoC(高阶组件),本质是高阶函数

事实上高阶函数只是社区提出的一种方案被 React 所采纳而已,其他的方案还有 renderProps 和 最近流行的Hook

Vue 也可以利用高阶函数 实现组合和复用。

diff 算法的时间复杂度


???两个数的完全的 diff 算法是一个时间复杂度为 o(n3), ???Vue 进行了优化 O(n3)复杂度的问题转换成 O(n)复杂度的问题(只比较同级不考虑跨级问题)在前端当中,你很少会跨级层级地移动 Dom 元素,所以 Virtual Dom 只会对同一个层级地元素进行对比

听这个描述来说,React 没有对 O(n3) 的复杂度进行优化?事实上 React 和 Vue 都只会对 tag 相同的同级节点进行 diff,如果不同则直接销毁重建,都是 O(n) 的复杂度。

谈谈你对作用域插槽的理解


???单个插槽当子组件模板只有一个没有属性的插槽时, 父组件传入的整个内容片段将插入到插槽所在的 DOM 位置, 并替换掉插槽标签本身。

跟 DOM 没关系,是在虚拟节点树的插槽位置替换。

Vue 中 key 的作用


???如果不加 key,那么 vue 会选择复用节点(Vue 的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的 bug

不加 key 也不一定就会复用,关于 diff 和 key 的使用,建议大家还是找一些非造玩具的文章真正深入的看一下原理。

为什么 Vue 中不要用 index 作为 key?(diff 算法详解)

组件中的 data 为什么是函数


???因为组件是用来复用的,JS 里对象是引用关系,这样作用域没有隔离,而 new Vue 的实例,是不会被复用的,因此不存在引用对象问题

这句话反正我压根没听懂,事实上如果组件里 data 直接写了一个对象的话,那么如果你在模板中多次声明这个组件,组件中的 data 会指向同一个引用。

此时如果在某个组件中对 data 进行修改,会导致其他组件里的 data 也被污染。而如果使用函数的话,每个组件里的 data 会有单独的引用,这个问题就可以避免了。

这个问题我同样举个例子来方便理解,假设我们有这样的一个组件,其中的 data 直接使用了对象而不是函数:

var Counter = {

template: <span @click="count++"></span>

data: {

count: 0

}

}

注意,这里的 Counter.data 仅仅是一个对象而已,它 是一个引用,也就是它是在当前的运行环境下全局唯一的,它真正的值在堆内存中占用了一部分空间。

也就是说,不管利用这份 data 数据创建了多少个组件实例,这个组件实例内部的 data 都指向这一个唯一的对象。

然后我们在模板中调用两次 Counter 组件:

我们从原理出发,先看看它被编译成什么样[5]的 render 函数:

function render() {

with (this) {

return _c(‘div’, [_c(‘Counter’), _c(‘Counter’)], 1)

}

}

每一个 Counter 会被 _c 所调用,也就是 createElement,想象一下 createElement 内部会发生什么,它会直接拿着 Counter 上的 data 这个引用去创建一个组件。也就是所有的 Counter 组件实例上的 data 都指向同一个引用。

此时假如 id 为 a 的 Counter 组件内部调用了 count++,会去对 data 这个引用上的 count 属性赋值,那么此时由于 id 为 b 的 Counter 组件内部也是引用的同一份 data,它也会感觉到变化而更新组件,这就造成了多个组件之间的数据混乱了。

那么如果换成函数的情况呢?每创建一次组件实例就执行一次 data() 函数:

function data() {

return { count: 0 }

}

// 组件a创建一份data

const a = data()

// 组件b创建一份data

const b = data()

a === b // false

是不是一目了然,每个组件拥有了自己的一份全新的 data,再也不会互相污染数据了。

computed 和 watch 有什么区别


???计算属性是基于他们的响应式依赖进行缓存的,只有在依赖发生变化时,才会计算求值,而使用 methods,每次都会执行相应的方法

这也是一个一问就倒的回答,依赖变化是计算属性就重新求值吗?中间经历了什么过程,为什么说 computed 是有缓存值的?随便挑一个点深入问下去就站不住。事实上 computed 会拥有自己的 watcher,它内部有个属性 dirty 开关来决定 computed 的值是需要重新计算还是直接复用之前的值。

以这样的一个例子来说:

computed: {

sum() {

return this.count + 1

}

}

首先明确两个关键字:

dirty 从字面意义来讲就是 的意思,这个开关开启了,就意味着这个数据是脏数据,需要重新求值了拿到最新值。
总结:

  • 函数式编程其实是一种编程思想,它追求更细的粒度,将应用拆分成一组组极小的单元函数,组合调用操作数据流;

  • 它提倡着 纯函数 / 函数复合 / 数据不可变, 谨慎对待函数内的 状态共享 / 依赖外部 / 副作用;

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

Tips:

其实我们很难也不需要在面试过程中去完美地阐述出整套思想,这里也只是浅尝辄止,一些个人理解而已。博主也是初级小菜鸟,停留在表面而已,只求对大家能有所帮助,轻喷🤣;

我个人觉得: 这些编程范式之间,其实并不矛盾,各有各的 优劣势

理解和学习它们的理念与优势,合理地 设计融合,将优秀的软件编程思想用于提升我们应用;

所有设计思想,最终的目标一定是使我们的应用更加 解耦颗粒化、易拓展、易测试、高复用,开发更为高效和安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值