本系列主要整理博主2023秋招面试情况,此次为朋友的兴业数金前端一面,面试时间为2022-7-13及7-18
单向数据流在 Vue 中实际表现就是:当 Model 中的 data 发生变化的时候会单向修改 View 中的值,而 View 中的值发生变化的时候,Model 不会感知。实际应用就是 v-bind 单向数据。
和单向数据流相比,双向数据绑定就是多了 View 变化会通知到 Model 层。即 MVVM 的具体实现。无论 Model 还是 View 中的值发生变化,都会通过 ViewModel 通知到对方,实现同步。实际应用就是 v-model 双向数据绑定。
单向绑定的优点是相应的可以带来单向数据流,这样做的好处是所有状态变化都可以被记录、跟踪,状态变化通过手动调用通知,源头易追溯,没有“暗箱操作”。同时组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性。缺点则是代码量会相应的上升,数据的流转过程变长,从而出现很多类似的样板代码。同时由于对应用状态独立管理的严格要求(单一的全局store),在处理局部状态较多的场景时(如用户输入交互较多的“富表单型”应用),会显得啰嗦及繁琐。
基本上双向绑定的优缺点就是单向绑定的镜像了。优点是在表单交互较多的场景下,会简化大量业务无关的代码。缺点就是由于都是“暗箱操作”,我们无法追踪局部状态的变化(虽然大部分情况下我们并不关心),潜在的行为太多也增加了出错时 debug 的难度。同时由于组件数据变化来源入口变得可能不止一个,新手玩家很容易将数据流转方向弄得紊乱,如果再缺乏一些“管制”手段,最后就很容易因为一处错误操作造成应用雪崩。
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
一个经典的比喻:
想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应
假设电梯有两种运行策略 debounce 和 throttle,超时设定为15秒,不考虑容量限制
电梯第一个人进来后,15秒后准时运送一次,这是节流
电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖。
相同点:
都可以通过使用 setTimeout 实现
目的都是,降低回调执行频率。节省计算资源
不同点:
函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout和 setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能
函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次
应用场景:
防抖在连续的事件,只需触发一次回调的场景有:
• 搜索框搜索输入。只需用户最后一次输入完,再发送请求
• 手机号、邮箱验证输入检测
• 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
节流在间隔一段时间执行一次回调的场景有:
• 滚动加载,加载更多或滚到底部监听
• 搜索框,搜索联想功能
盒子水平垂直居中的几种方式
(1)dispaly:none 消失+重排+重绘+绑定事件不触发+无过渡;
(2)visibility:hidden 存在+不重排+重绘+绑定事件不触发+有过渡;
(3)opacity:0 存在+不重排+不一定重绘+触发自身绑定+有过渡;
(4)position:absolute 移除视线;
(5)z-index:负值 使用其他元素遮盖;
(6)transform:scale(0,0) 缩放元素为0 存在+绑定事件不触发。
visibility、display、opacity三者区别:
Promise是异步编程的一种解决方案,(大白话就是让这个异步可以写成同步的感觉,向下顺次执行)比传统的解决方案—回调函数和事件—更合理和强大,最主要解决的是死亡的地狱回调
Promise 是一个构造函数,接收一个函数作为参数,返回一个 Promise 实例。一个 Promise 实例有三种状态,分别是 pending、resolved 和 rejected,分别代表了进行中、已成功和已失败。实例的状态只能由 pending 转变 resolved 或者 rejected 状态,并且状态一经改变,就凝固了,无法再被改变了。状态的改变是通过 resolve() 和 reject() 函数来实现的,我们可以在异步操作结束后调用这两个函数改变 Promise 实例的状态,它的原型上定义了一个 then 方法,使用这个 then 方法可以为两个状态的改变注册回调函数。这个回调函数属于微任务,会在本轮事件循环的末尾执行。
promise 能解决什么问题
promise的常用api
Promise.then():得到异步任务的正确结果
Promise.catch():获取异常信息
Promise.finally():无论成功还是失败都会执行
Promise.all():并发处理多个异步任务,所有任务执行完成才能得到结果
Promise.race():并发处理多个异步任务,只要有一个任务完成就能得到结果
promise中race()和all()方法的异同?
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。Promise.race()将里面返回最快的结果返回,不管结果本身是成功状态还是失败状态。
组件化 就是将UI、样式以及其实现的比较完整的功能作为独立的整体,无关业务,无论将这个整体放在哪里去使用,它都具有一样的功能和UI,从而达到复用的效果,这种体系化的思想就是组件化。
组件化和模块化的区别
组件化:从UI界面的角度分析的,把一些可复用的UI元素,抽离为单个组件,便于项目的维护和开发。多个组件可以组合成组件库,方便调用和复用,组件间也可以嵌套,小组件组合成大组件。
模块化:是从代码的角度分析的,把一些可复用的代码,抽离为单个模块,便于项目的维护和开发。可以调用组件来组成模块,多个模块可以组合成业务框架。
为什么要使用组件化和模块化?
• 开发和调试效率高:随着功能越来越多,代码结构会越发复杂,要修改某一个小功能,可能要重新翻阅整个项目的代码,把所有相同的地方都修改一遍,重复劳动浪费时间和人力,效率低;使用组件化,每个相同的功能结构都调用同一个组件,只需要修改这个组件,即可全局修改。
• 可维护性强:便于后期代码查找和维护。
• 避免阻断:模块化是可以独立运行的,如果一个模块产生了bug,不会影响其他模块的调用。
• 版本管理更容易:如果由多人协作开发,可以避免代码覆盖和冲突。
事件循环机制
Webpack详解
1、语法更加简洁、清晰
2、箭头函数不会创建自己的this
3、箭头函数继承而来的this指向永远不变(重要!!深入理解!!)
4、.call()/.apply()/.bind()无法改变箭头函数中this的指向
5、箭头函数不能作为构造函数使用
箭头函数不能被new,是因为箭头函数没有prototype
与内置的[[Construct]]方法,无法完成构造。
6、箭头函数没有自己的arguments
7、箭头函数没有原型prototype
通过设置Webpack proxy实现代理请求后。相当于浏览器与服务端中添加一个代理者
当本地发送请求的时候,代理服务器响应该请求。并将请求转发到目标服务器。目标服务器响应数据后再
将数据这网给代理服务器,最终再由代理服务器将数据响应给本地
跨域问题是在怎么产生的?
因为浏览器有一个安全机制,叫做同源策略。同源呢就是指域名、协议、端口都一致。如果任意一项不一致就是不同源,简单来说就是,你的网页的url和你调用的接口的url不是同一个地方的,浏览器觉得有安全风险,不想让你使用接口的数据。就好像你想去肯德基吃饭,非要点一碗兰州拉面,店员虽然很鄙视你,但是他还是给兰州拉面馆打了电话,问了一下,兰州拉面给你做面送到了肯德基,但是肯德基店员不给你拉面,所以你在肯德基吃不到兰州拉面。这里有一个很反常的操作,一定要注意一下,就是肯德基的店员打电话给兰州拉面馆了。这时,这个店员就相当于是浏览器,肯德基就相当于当前网页,兰州拉面馆就相当于是就接口的服务器,肯德基和兰州拉面不是同一个老板,相当于是不同源,兰州拉面就是你想要的接口数据。
解决跨域的方案:
1、 CORS处理(后端)所有的网站都可以访问这个接口(数据不安全)
2、 JSONP处理(前端),利用script标签的src属性实现跨域(script标签的src属性能够实现天然的跨域)比如拿到百度的一张图片(问题:只能处理get请求)
3、 Nginx 反向代理
4、 使用webpack可以做代理,实现跨域(是目前比较好的跨域处理方案,95%的公司是使用这个来做代理的)
Slot 通俗的理解就是“占坑”,在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中slot位置)
slot什么时候用?
父组件向子组件传递内容
为什么需要slot?
slot 与 props 的区别:通过props属性,父组件可以向子组件传递属性、方法,可是父组件不能通过属性传递带标签的内容、甚至是组件,而插槽可以。
插槽的种类:1.默认插槽2.具名插槽3.作用域插槽(当子组件做循环显示列表 或 某一部分由外部传递进来 时,则使用 作用域插槽)
插槽实现原理:
当子组件vm实例化时,获取到父组件传入slot标签的内容,存放在vm.$ slot中,默认插槽为vm.$ slot.default,具名插槽为vm.$ slot.xxx,xxx为插槽名,当组件执行渲染函数时候,遇到slot标签,使用$ slot(父组件传入slot标签的内容)中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。
export方式导出对象、方法、引用,但是导入的时候不可直接指定对象、方法,需要通过大括号"{}"来指定属性、变量名。
export default导出可不必须指定函数名、类名等,在一个文件或模块中,export、import 可以有多个,export default 仅有一个,export default 向外暴露的成员,可以使用任意变量来接收且import时不需要用大括号嵌套,也不能用大括号嵌套。