本文呈现的问题都是在8月阶段理论面试过程中遇到的,大小公司都有波及,心愿可能助力大家金九银十,也心愿本人可能顺利找到工作。当初里面大环境确实太难了……
文中局部题目提供了参考资料以及本人整顿的相干答案,可能不肯定是最精确的,大家酌情参考。
如波及到其余文章内容援用没有表明出处的,能够私聊补充更新。
Javascript
1.JS根本数据类型有哪些
根本数据类型:
- 字符串(String):示意文本数据,应用引号(单引号或双引号)括起来。例如:"Hello World"。
- 数字(Number):示意数值数据,包含整数和浮点数。例如:10、3.14。
- 布尔值(Boolean):示意逻辑值,只有两个值:true(真)和false(假)。
- null:示意空值,示意一个空对象指针。
- undefined:示意未定义的值,示意变量未被赋值。
- Symbol:示意惟一的标识符,用于对象属性的键。
【扩大】说一下undefined和null的区别
当谈到 JavaScript 中的 undefined 和 null 时,它们是两个不同的值,但都示意短少值。它们之间的区别如下:
- undefined 是示意变量申明但未赋值的默认值。当尝试拜访未初始化的变量时,它的值为 undefined。全局属性 undefined 示意原始值undefined。它是一个 JavaScript 的 原始数据类型 。undefined是全局对象的一个属性。也就是说,它是全局作用域的一个变量。undefined的最后值就是原始数据类型undefined。
- 值 null 特指对象的值未设置。它是 JavaScript 根本类型 之一,在布尔运算中被认为是falsy。它是一个非凡的关键字,示意变量不指向任何对象、数组或函数。值 null 是一个字面量,不像 undefined,它不是全局对象的一个属性。null 是示意短少的标识,批示变量未指向任何对象。把 null 作为尚未创立的对象,兴许更好了解。在 API 中,null 常在返回类型应是一个对象,但没有关联的值的中央应用。
2.const、let和var的区别
【参考资料】
面试官:说说var、let、const之间的区别
3.call、apply和bind的区别
- 三者都能够扭转函数的this对象指向。
- 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window。
- 三者都能够传参,然而apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind能够分为屡次传入。
- bind 是返回绑定this之后的函数,便于稍后调用;apply 、call 则是立刻执行 。
【参考】
彻底弄懂bind,apply,call三者的区别
深刻理解 call, apply, bind 和模仿实现
5分钟带你搞懂 Javascript 中的this(蕴含apply、call、bind)
4.对闭包的了解
概念
一个函数和对其四周状态(lexical environment,词法环境)的援用捆绑在一起(或者说函数被援用突围),这样的组合就是闭包(closure)。也就是说,闭包让你能够在一个内层函数中拜访到其外层函数的作用域。在 JavaScript 中,每当创立一个函数,闭包就会在函数创立的同时被创立进去。
重点的一句:闭包让你能够在一个内层函数中拜访到其外层函数的作用域。
应用场景
- return一个外部函数,读取外部函数的变量;
- 函数作为参数
- IIFE(自执行函数)
- 封装对象的公有对象和公有办法;
5.new 一个构造函数的过程
用new操作符创建对象时产生的事件:
- 创立新对象
- 新对象原型[[prototype]] = 构造函数prototype
- this 指向新对象
- 执行构造函数
- 如果构造函数返回非空对象,就返回这个对象援用,不然返回创立的新对象
函数调用时后面不加new就按一般函数来 执行。加new后对其进行了相应的变动, 按构造函数来执行。
new的具体过程如下:
//例子:function Person(name,age) { this.userName = name; this.userAge = age;}var personl = new Person('LULU',20)
1、创立一个新的空对象。(即实例对象)
obj = {}
2 、设 置 原 型 链
将新对象obj的 __proto__属性指向构造函数的prototype对象。(即所有实例对象通过__proto__能够拜访原型对象。构造函数的原型被其所有实例对象共享。)
obj.__proto__= Person.prototype
3 、将构造函数的this改指向新对象obj并执行函数代码。
(即给实例对象设置属性 userName, userAge并赋值。)
var result = Person.apply(obj,['LULU',20])
4 、如果构造函数中没有人为返回一个对象类型的值,则返回这个新对象obj。否则间接返回那个对象类型值。(个别定义的构造函数中不写返回值。)
if (typeof(result) == 'object') { return result;}else{ return obj; }
【扩大】手动实现一个new操作,参数为构造函数 及其传参
//构造函数function Person(name,age) { this.userName = name; this.userAge = age;}Person.prototype.sayAge = function(){ console.Iog(this.userAge)}// new操作函数newFunfunction newFun() { var obj = {}; Constructor = [].shift.call(arguments); obj.__proto__ = Constructor.prototype; var ret = Constructor.apply(obj, arguments); return typeof ret === 'object' ? ret: obj;};//调用 newFunvar s1 = newFun(Person, 'xiaoyun',20);console.log(s1.userName) //xiaoyunconsole.log(s1.sayAge()) //20
备注:[].shift.call(arguments):删除并返回参数列表arguments中第一个参数,即取得构造函数。arguments残余参数为构数传参。arguments是类数组,没有数组办法shift,可更改shift办法的this指向从而使用到 arguments 上。
6.ES6高阶函数有哪些
ES6中的高阶函数有以下几种:
- map:对数组中的每个元素进行操作,并返回一个新的数组。
- filter:依据指定的条件过滤出数组中的元素,并返回一个新的数组。
- reduce:对数组中的元素进行累加或累积操作,并返回一个后果。
- forEach:对数组中的每个元素进行操作,没有返回值。
- some:判断数组中是否存在满足指定条件的元素,返回布尔值。
- every:判断数组中的所有元素是否都满足指定条件,返回布尔值。
- find:查找数组中满足指定条件的第一个元素,并返回该元素。
- findIndex:查找数组中满足指定条件的第一个元素的索引,并返回该索引。
- sort:对数组中的元素进行排序。
- flat:将多维数组转换为一维数组。
- map、filter、reduce等办法的变种,如flatMap、find、findIndex等。
这些高阶函数能够使代码更简洁、可读性更高,并且提供了一种函数式编程的形式来操作数组。
Typescript
1.TS比照JS的劣势有哪些
CSS
1.说下什么是BFC
BFC到底是什么货色
BFC 全称:Block Formatting Context, 名为 "块级格式化上下文"。
W3C官网解释为:BFC它决定了元素如何对其内容进行定位,以及与其它元素的关系和相互作用,当波及到可视化布局时,Block Formatting Context提供了一个环境,HTML在这个环境中依照肯定的规定进行布局。
简略来说就是,BFC是一个齐全独立的空间(布局环境),让空间里的子元素不会影响到里面的布局。那么怎么应用BFC呢,BFC能够看做是一个CSS元素属性
怎么触发BFC
这里简略列举几个触发BFC应用的CSS属性
- overflow: hidden
- display: inline-block
- position: absolute
- position: fixed
- display: table-cell
- display: flex
【参考】
面试官:请说说什么是BFC?大白话讲清楚
区块格式化上下文
Vue
1.Vue2、3的模版编译渲染过程有哪些区别
2.虚构Dom的劣势是什么?性能肯定更好吗?
虚构 DOM (Virtual DOM )这个概念置信大家都不生疏,从 React 到 Vue ,虚构 DOM 为这两个框架都带来了跨平台的能力(React-Native 和 Weex)
实际上它只是一层对实在DOM的形象,以JavaScript 对象 (VNode 节点) 作为根底的树,用对象的属性来形容节点,最终能够通过一系列操作使这棵树映射到实在环境上
在Javascript对象中,虚构DOM 体现为一个 Object对象。并且起码蕴含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,不同框架对这三个属性的名命可能会有差异
创立虚构DOM就是为了更好将虚构的节点渲染到页面视图中,所以虚构DOM对象的节点与实在DOM的属性一一呼应
长处:
保障性能上限: 框架的虚构 DOM 须要适配任何下层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;然而比起粗犷的 DOM 操作性能要好很多,因而框架的虚构 DOM 至多能够保障在你不须要手动优化的状况下,仍然能够提供还不错的性能,即保障性能的上限; 无需手动操作 DOM: 咱们不再须要手动去操作 DOM,只须要写好 View-Model 的代码逻辑,框架会依据虚构 DOM 和 数据双向绑定,帮咱们以可预期的形式更新视图,极大进步咱们的开发效率;
跨平台: 虚构 DOM 实质上是 JavaScript 对象,而 DOM 与平台强相干,相比之下虚构 DOM 能够进行更不便地跨平台操作,例如服务器渲染、weex 开发等等。
毛病:
无奈进行极致优化: 尽管虚构 DOM + 正当的优化,足以应答绝大部分利用的性能需求,但在一些性能要求极高的利用中虚构 DOM 无奈进行针对性的极致优化。
【参考资料】
面试官:什么是虚构DOM?如何实现一个虚构DOM?说说你的思路
3.Vue2和Vue3的响应式区别
- Vue2中响应式是通过defineProperty实现的
- Vue3中响应式是通过ES6的Proxy实现的
- Vue3中实现响应式数据的办法是ref和reactive
【扩大内容】 盘点 Vue3 与 Vue2 的区别
4.ref和reactive的区别
reactive特点
- reactive的参数个别是对象或者数组,他可能将简单数据类型变为响应式数据。
- reactive的响应式是深层次的,底层实质是将传入的数据转换为Proxy对象
ref特点
- ref的参数个别是根本数据类型,也能够是对象类型
- 如果参数是对象类型,其实底层的实质还是reactive,零碎会主动将ref转换为reactive,例如 ref(1) ===> reactive({value:1})
- 在模板中拜访ref中的数据,零碎会主动帮咱们增加.value,在JS中拜访ref中的数据,须要手动增加.value
- ref的底层原理同reactive一样,都是Proxy
5.Vue的双向数据绑定/响应式原理
【参考资料】
面试官:双向数据绑定是什么
6.Vue2为什么不能监控到数组/对象的属性变更
首先Object.defineProperty是能够监听局部数组的更新动作的,然而因为它是通过遍历的形式来实现,当数组数据量小的时候没有问题,然而当数据量大的时候,解决上会呈现显著的性能问题。也是出于“性能代价和取得的用户体验收益不成正比”的起因,所以Vue2没有去实现监听。Vue3的Proxy的劫持形式完满解决了这个问题。
7.说下Vue中的Slot,应用场景
应用场景
通过插槽能够让用户能够拓展组件,去更好地复用组件和对其做定制化解决
如果父组件在应用到一个复用组件的时候,获取这个组件在不同的中央有大量的更改,如果去重写组件是一件不明智的事件
通过slot插槽向组件外部指定地位传递内容,实现这个复用组件在不同场景的利用
比方布局组件、表格列、下拉选、弹框显示内容等
分类
slot能够分来以下三种:
- 默认插槽
具名插槽
- 子组件用name属性来示意插槽的名字,不传为默认插槽
作用域插槽
子组件在作用域上绑定属性来将子组件的信息传给父组件应用,这些属性会被挂在父组件v-slot承受的对象上。父组件中在应用时通过v-slot:(简写:#)获取子组件的信息,在内容中应用
<!--子组件--><template> <slot name="footer" testProps="子组件的值"> <h3>没传footer插槽</h3></slot></template><!--父组件--><child> <!-- 把v-slot的值指定为作⽤域高低⽂对象 --><template v-slot:default="slotProps"> 来⾃⼦组件数据:{{slotProps.testProps}}</template><template #default="slotProps"> 来⾃⼦组件数据:{{slotProps.testProps}}</template></child>
小结:
- v-slot属性只能在
<template>
上应用,但在只有默认插槽时能够在组件标签上应用 默认插槽名为default,能够省略default间接写v-slot
- 缩写为#时不能不写参数,写成#default
- 能够通过解构获取v-slot={user},还能够重命名v-slot="{user: newName}"和定义默认值v-slot="{user = '默认值'}"
- v-slot属性只能在
【参考资料】
面试官:说说你对slot的了解?slot应用场景有哪些?
8.为什么data 返回一个函数
- vue中组件是用来复用的,为了避免data复用,将其定义为函数。
- vue组件中的data数据都应该是互相隔离,互不影响的,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的中央组件内data数据被扭转时,其余复用中央组件的data数据不受影响,就须要通过data函数返回一个对象作为组件的状态。
- 当咱们将组件中的data写成一个函数,数据以函数返回值模式定义,这样每复用一次组件,就会返回一份新的data,领有本人的作用域,相似于给每个组件实例创立一个公有的数据空间,让各个组件实例保护各自的数据。
- 当咱们组件的date单纯的写成对象模式,这些实例用的是同一个构造函数,因为JavaScript的个性所导致,所有的组件实例共用了一个data,就会造成一个变了全都会变的后果。
9.JSX有理解吗,和template的区别;
10.vue外面针对谬误的生命周期函数应用
11.说下Vuex or Pinia的应用
12.Vue.set的原理
Vue.set是Vue.js提供的一个全局办法,用于在响应式对象上设置响应式属性。它的原理是通过调用Vue实例的外部办法defineReactive来为对象设置新属性,并使其成为响应式属性。
具体原理如下:
- Vue.set办法承受三个参数:对象、属性名和属性值。
- 首先,它会判断对象是否是响应式的,如果不是,则间接返回。
- 而后,它会判断属性是否曾经存在于对象中,如果存在,则间接更新属性的值。
- 如果属性不存在于对象中,它会通过defineReactive办法将属性设置为响应式属性。
- defineReactive办法会为属性创立一个Dep实例对象,用于收集依赖和触发更新。
- 而后,它会应用Object.defineProperty办法将属性设置为响应式,并在getter和setter中触发依赖收集和更新。
- 最初,它会触发属性的更新,告诉相干的Watcher进行视图的更新。
React
1.React中setState什么时候是同步,什么时候是异步
在React中,setState有时是同步的,有时是异步的。
当在React事件处理函数(如onClick)或生命周期办法(如componentDidMount)中调用setState时,setState是异步的。这是因为React会对间断的setState调用进行批处理,以进步性能。
例如,在以下代码中,setState是异步的:
class MyComponent extends React.Component { handleClick() { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 输入的是更新前的值 } render() { return ( <button onClick={this.handleClick.bind(this)}>Click me</button> ); }}
然而,当在setTimeout、Promise、原生事件监听器等异步场景中调用setState时,setState是同步的。
例如,在以下代码中,setState是同步的:
class MyComponent extends React.Component { componentDidMount() { setTimeout(() => { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 输入的是更新后的值 }, 1000); } render() { return ( <div>{this.state.count}</div> ); }}
网络/浏览器
1.说下Http1.0和Http2.0的区别
HTTP 超文本传输协定是位于 TCP/IP 体系结构中的应用层协定,它是万维网数据通信的根底。
HTTP1.1 的缺点
- 高提早 — 队头阻塞(Head-Of-Line Blocking)
- 无状态个性 — 妨碍交互
- 明文传输 — 不安全性
- 不反对服务端推送
http2是HTTP协定的的第二个次要版本,应用于万维网。HTTP/2是HTTP协定自1999年HTTP 1.1公布后的首个更新,次要基于SPDY协定。和HTTP1的次要区别有:
- 1、http2采纳二进制格局而非文本格式。
- 2、http2是齐全多路复用的,而非有序并阻塞的。
- 3、http2采纳了报头压缩,升高了老本。
- 4、http2让服务器能够被动将响应推送到客户端缓存中
HTTP2 的缺点
- TCP 以及 TCP+TLS 建设连贯的延时
- TCP 的队头阻塞并没有彻底解决
- 多路复用导致服务器压力回升
- 多路复用容易 Timeout
HTTP3是一个基于 UDP 协定的 QUIC 协定,它真正“完满”地解决了“队头阻塞”问题。
HTTP3的次要特点
- 改良的拥塞管制、牢靠传输
- 疾速握手
- 集成了 TLS 1.3 加密
- 多路复用
- 连贯迁徙
【参考资料】:
- 一文读懂 HTTP/1HTTP/2HTTP/3
- HTTP2、HTTP3优缺点整顿
2.说下HTML渲染过程
- 构建 DOM树
<!---->
- 将 HTML 解析成许多 Tokens
- 将 Tokens 解析成 Objects
- 将 Objects 组合成为一个 DOM 树
- 构建 CSSOM - CSS对象模型
<!---->
- 解析 CSS 文件,并构建出一个 CSSOM 树(过程相似于 DOM 构建)
- 构建 Render树
<!---->
- 联合 DOM 和 CSSOM 构建出一颗 Render 树
构建过程遵循以下步骤:
- 浏览器从 DOM 树开始,遍历每一个“可见”节点
- 对于每一个"可见"节点,在 CSSOM 上找到匹配的款式并利用
- 生成 Render Tree
- Layout 布局
<!---->
- 计算出元素绝对于 viewport 的绝对地位
<!---->
- Paint 渲染
<!---->
- 将 Render 树转换成像素,显示在屏幕上
残缺流程:
【扩大】JS文件引入对CRP的影响
- 解析 HTML 构建 DOM 时,遇到 JavaScript 会被阻塞;
- JavaScript 执行会被 CSSOM 构建阻塞,也就是说,JavaScript 必须等到 CSS 解析实现后才会执行(这只针对在头部搁置 <style> 和 <link> 的状况,如果放在尾部,浏览器刚开始会应用 User Agent Style 构建 CSSOM)
- 如果应用异步脚本,脚本的网络申请优先级升高,且网络申请期间不阻塞 DOM 构建,直到申请实现才开始执行脚本
应用异步脚本,其实就是通知浏览器几件事:
- 无需阻塞 DOM,在对 Script 执行网络申请期间能够持续构建 DOM,直到拿到 Script 之后再去执行
- 将该 Script 的网络申请优先级升高,缩短响应工夫,确保执行它之前 DOM 和 CSSOM 都构建实现
须要留神如下几点:
- 异步脚本是网络申请期间不阻塞 DOM,拿到脚本之后马上执行,执行时还是会阻塞 DOM,然而因为响应工夫被缩短,此时往往 DOM 曾经构建结束(上面的测验图片将会看到,CSSOM 也曾经构建结束而且页面很快就产生第一次渲染),异步脚本的执行产生在第一次渲染之后
- 只有内部脚本能够应用 async 关键字变成异步,而且留神其与提早脚本(
<script defer />
)的区别,后者是在 Document 被解析结束而 DOMContentLoaded 事件触发之前执行,前者则是在下载结束后执行 - 对于应用 document.createElement 创立的
<script />
,默认就是异步脚本
【扩大】link标签在不同地位对CRP的影响
- link标签在结尾的时候,会引起重绘
- link标签在DOM两头地位时,会阻塞渲染
【扩大】CSS 匹配规定为何从右向左
CSS 匹配就产生在 Render Tree 构建时(Chrome Dev Tools 里叫做 Recalculate Style),此时浏览器构建出了 DOM,而且拿到了 CSS 款式,此时要做的就是把款式跟 DOM 上的节点对应上,浏览器为了进步性能须要做的就是疾速匹配。
首先要明确一点,浏览器此时是给一个"可见"节点找对应的规定,这和 jQuery 选择器不同,后者是应用一个规定去找对应的节点,这样从左到右或者更快。然而对于前者,因为 CSS 的宏大,一个 CSS 文件中或者有上千条规定,而且对于以后节点来说,大多数规定是匹配不上的,到此为止,略微想一下就晓得,如果从右开始匹配(也是从更准确的地位开始),能更快排除不适合的大部分节点,而如果从左开始,只有深刻了才会发现匹配失败,如果大部分规定层级都比拟深,就比拟浪费资源了。
除了下面这点,咱们后面还提到 DOM 构建是"循序渐进的",而且 DOM 不阻塞 Render Tree 构建(只有 CSSOM 阻塞),这样也是为了能让页面更早有元素出现。思考如下状况,如果咱们此时构建的只是局部 DOM,而此时 CSSOM 构建实现,浏览器此时须要构建 Render Tree,如果对每一个节点,找到一条规定进行从左向右匹配,则必须要求其子元素甚至孙子元素都在 DOM 上,但此时 DOM 未构建实现,显然会匹配失败。如果反过来,咱们只须要查找该元素的父元素或先人元素,它们必定在以后的 DOM 中。
【扩大】window.onload和DOMContentLoaded的区别
- onload事件是当页面所有资源(包含DOM,样式表,脚本,图片等资源)全副加载实现后执行
- DOMContentLoaded事件在DOM构造加载实现后即触发,不期待其余资源的加载。更快
【扩大】重绘和重排
概念
重排是什么:从新生成布局。当DOM 的变动影响了元素的几何属性(宽和高)--比方扭转边框宽度或给段落减少文字导致行数减少--浏览器须要从新计算元素的几何属性,同样其余元素的几何属性和地位也会因而受到影响。浏览器会使渲染树中受到影响的局部生效,并从新结构渲染树。这个过程称为重排。
重绘是什么:从新绘制。实现重排后,浏览器会从新绘制受影响的局部到屏幕中。这个过程称为重绘。
产生重排的状况
当页面布局和几何属性扭转时产生“重排”,如下:
- 增加或删除可见的DOM 元素
- 元素地位扭转
- 元素尺寸扭转(包含外边距、内边距、边框厚度、宽度、高度等属性扭转)
- 内容扭转,例如:文本扭转后图片被另一个不同尺寸的图片代替
- 页面渲染器初始化
- 浏览器窗口尺寸扭转
强制刷新队列
获取布局信息的操作会导致列队刷新,以下属性和办法须要返回最新的布局信息,最好防止应用。
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop, scrollLeft, scrollWidth, scrollHeight
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle() (currentStyle in IE)
clientTop:元素上边框的厚度,当没有指定边框厚底时,个别为0。
scrollTop:位于对象最顶端和窗口中可见内容的最顶端之间的间隔,简略地说就是滚动后被暗藏的高度。
offsetTop:获取对象绝对于由offsetParent属性指定的父坐标(css定位的元素或body元素)间隔顶端的高度。
clientHeight:内容可视区域的高度,也就是说页面浏览器中能够看到内容的这个区域的高度,个别是最初一个工具条以下到状态栏以上的这个区域,与页面内容无关。
scrollHeight:IE、Opera 认为 scrollHeight 是网页内容理论高度,能够小于 clientHeight。FF 认为 scrollHeight 是网页内容高度,不过最小值是 clientHeight。
offsetHeight:获取对象绝对于由offsetParent属性指定的父坐标(css定位的元素或body元素)的高度。IE、Opera 认为 offsetHeight = clientHeight + 滚动条 + 边框。FF 认为 offsetHeight 是网页内容理论高度,能够小于clientHeight。offsetHeight在新版本的FF和IE中是一样的,示意网页的高度,与滚动条无关,chrome中不包含滚动条。
Window.getComputedStyle()办法返回一个对象,该对象在利用流动样式表并解析这些值可能蕴含的任何根本计算后报告元素的所有CSS属性的值。 公有的CSS属性值能够通过对象提供的API或通过简略地应用CSS属性名称进行索引来拜访。
缩小重排和重绘
- 合并款式批改
- 批量操作DOM
<!---->
- 脱离规范流的操作有以下3中:
<!---->
- 暗藏元素
- 应用文档碎片
- 拷贝节点
<!---->
- 防止屡次触发布局
- 对于页面中比较复杂的动画,尽量将元素设置为相对定位,操作元素的定位属性,这样只有这一个元素会回流,如果不是定位的话,容易引起其父元素以及子元素的回流。
【参考资料】
- 深刻了解浏览器解析渲染 HTML
- How browsers work
- 浏览器渲染之link标签和script标签对页面的影响
- 重排(reflow)和重绘(repaint)
- 从输出URL到浏览器显示页面过程中都产生了什么
- 讲清楚重排或回流、重绘
3.说下浏览器缓存
【扩大】Last-Modified和Etag的区别?哪个更好一些?
Last-Modified和ETag是HTTP协定中用于缓存验证的两种机制。
- Last-Modified:服务器在响应中增加一个Last-Modified头,该头蕴含了资源的最初批改工夫。客户端在后续申请中能够通过发送If-Modified-Since头,将上次获取到的Last-Modified值发送给服务器进行验证。如果资源的最初批改工夫与客户端提供的工夫雷同,则服务器返回304 Not Modified状态码,示意资源未被批改,客户端能够应用缓存正本。
- ETag:服务器在响应中增加一个ETag头,该头蕴含了资源的惟一标识符(通常是一个哈希值)。客户端在后续申请中能够通过发送If-None-Match头,将上次获取到的ETag值发送给服务器进行验证。如果资源的ETag与客户端提供的值雷同,则服务器返回304 Not Modified状态码,示意资源未被批改,客户端能够应用缓存正本。
两者区别:
- Last-Modified是基于资源的最初批改工夫进行验证,而ETag是基于资源的惟一标识符进行验证。
- Last-Modified的精度是以秒为单位,而ETag没有这个限度,能够更准确地示意资源的变动。
- ETag能够在某些状况下提供更好的性能,因为它不依赖于文件系统的工夫戳,而是依据资源内容生成惟一标识符。比方回滚时Etag更加便捷。
【扩大】日常我的项目中波及到哪些缓存?
- HTML:应用协商缓存。
- CSS&JS&图片:应用强缓存,文件命名带上hash值。
【扩大】webpack打包中的几种hash模式的区别
webpack给咱们提供了三种哈希值计算形式,别离是hash、chunkhash和contenthash。那么这三者有什么区别呢?
- hash:跟整个我的项目的构建相干,构建生成的文件hash值都是一样的,只有我的项目里有文件更改,整个我的项目构建的hash值都会更改。
- chunkhash:依据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的hash值。
- contenthash:由文件内容产生的hash值,内容不同产生的contenthash值也不一样。
显然,咱们是不会应用第一种的。改了一个文件,打包之后,其余文件的hash都变了,缓存天然都生效了。这不是咱们想要的。那chunkhash和contenthash的次要利用场景是什么呢?在理论在我的项目中,咱们个别会把我的项目中的css都抽离出对应的css文件来加以援用。如果咱们应用chunkhash,当咱们改了css代码之后,会发现css文件hash值扭转的同时,js文件的hash值也会扭转。这时候,contenthash就派上用场了。
【参考资料】
- 前端缓存最佳实际
- 彻底弄懂前端缓存
4.说下V8垃圾回收机制?
在JavaScript中,数据类型分为两类,简略类型和援用类型,对于简略类型,内存是保留在栈(stack)空间中,简单数据类型,内存是保留在堆(heap)空间中。
- 根本类型:这些类型在内存中别离占有固定大小的空间,他们的值保留在栈空间,咱们通过按值来拜访的
- 援用类型:援用类型,值大小不固定,栈内存中寄存地址指向堆内存中的对象。是按援用拜访的。
而对于栈的内存空间,只保留简略数据类型的内存,由操作系统主动调配和主动开释。而堆空间中的内存,因为大小不固定,零碎无奈进行主动开释,这个时候就须要JS引擎来手动的开释这些内存。
V8 将堆分为两类新生代和老生代,新生代中寄存的是生存工夫短的对象,老生代中寄存的生存工夫久的对象。
对于这两块区域,V8 别离应用两个不同的垃圾回收器,以便更高效地施行垃圾回收。
- 副垃圾回收器 - Scavenge:次要负责新生代的垃圾回收。
- 主垃圾回收器 - Mark-Sweep & Mark-Compact:次要负责老生代的垃圾回收。
Scavange算法将新生代堆分为两局部,别离叫from-space和to-space,工作形式也很简略,就是将from-space中存活的流动对象复制到to-space中,并将这些对象的内存有序的排列起来,而后将from-space中的非流动对象的内存进行开释,实现之后,将from space 和to space进行调换,这样能够使得新生代中的这两块区域能够反复利用。
简略的形容就是:
- 标记流动对象和非流动对象
- 复制 from space 的流动对象到 to space 并对其进行排序
- 开释 from space 中的非流动对象的内存
- 将 from space 和 to space 角色调换
那么,垃圾回收器是怎么晓得哪些对象是流动对象和非流动对象的呢?
有一个概念叫对象的可达性,示意从初始的根对象(window,global)的指针开始,这个根指针对象被称为根集(root set),从这个根集向下搜寻其子节点,被搜寻到的子节点阐明该节点的援用对象可达,并为其留下标记,而后递归这个搜寻的过程,直到所有子节点都被遍历完结,那么没有被标记的对象节点,阐明该对象没有被任何中央援用,能够证实这是一个须要被开释内存的对象,能够被垃圾回收器回收。
新生代中的对象什么时候变成老生代的对象呢?
在新生代中,还进一步进行了细分,分为nursery子代和intermediate子代两个区域,一个对象第一次分配内存时会被调配到新生代中的nursery子代,如果进过下一次垃圾回收这个对象还存在新生代中,这时候咱们挪动到 intermediate 子代,再通过下一次垃圾回收,如果这个对象还在新生代中,副垃圾回收器会将该对象挪动到老生代中,这个挪动的过程被称为降职。
老生代垃圾回收 - Mark-Sweep & Mark-Compact
新生代空间中的对象满足肯定条件后,降职到老生代空间中,在老生代空间中的对象都曾经至多经验过一次或者屡次的回收所以它们的存活概率会更大,如果这个时候再应用scavenge算法的话,会呈现两个问题:
- scavenge为复制算法,反复复制流动对象会使得效率低下
- scavenge是就义空间来换取工夫效率的算法,而老生代反对的容量交大,会呈现空间资源节约问题
所以在老生代空间中采纳了 Mark-Sweep(标记革除) 和 Mark-Compact(标记整顿) 算法。
Mark-Sweep
Mark-Sweep解决时分为两阶段,标记阶段和清理阶段,看起来与Scavenge相似,不同的是,Scavenge算法是复制流动对象,而因为在老生代中流动对象占大多数,所以Mark-Sweep在标记了流动对象和非流动对象之后,间接把非流动对象革除。
- 标记阶段:对老生代进行第一次扫描,标记流动对象
- 清理阶段:对老生代进行第二次扫描,革除未被标记的对象,即清理非流动对象
Mark-Compact
因为Mark-Sweep实现之后,老生代的内存中产生了很多内存碎片,若不清理这些内存碎片,如果呈现须要调配一个大对象的时候,这时所有的碎片空间都齐全无奈实现调配,就会提前触发垃圾回收,而这次回收其实不是必要的。
为了解决内存碎片问题,Mark-Compact被提出,它是在是在 Mark-Sweep的根底演出进而来的,相比Mark-Sweep,Mark-Compact增加了流动对象整顿阶段,将所有的流动对象往一端挪动,挪动实现后,间接清理掉边界外的内存。
全进展 Stop-The-World
因为垃圾回收是在JS引擎中进行的,而Mark-Compact算法在执行过程中须要挪动对象,而当流动对象较多的时候,它的执行速度不可能很快,为了防止JavaScript应用逻辑和垃圾回收器的内存资源竞争导致的不一致性问题,垃圾回收器会将JavaScript利用暂停,这个过程,被称为全进展(stop-the-world)。
【扩大】如何判断哪个变量导致内存透露的?
在V8的垃圾回收机制中,内存透露通常指的是无奈被垃圾回收器回收的对象。上面是一些判断哪个变量内存透露的常见办法:
- 应用内存剖析工具:能够应用V8提供的内存剖析工具,如Chrome DevTools中的Memory面板,来检测内存透露。这些工具能够显示内存应用状况、对象援用关系等信息,帮忙定位内存透露的对象。
- 监测内存应用状况:通过监测内存应用状况,能够察看变量的内存占用是否随工夫增长,如果是,则可能存在内存透露。能够应用V8提供的内存应用状况API,如process.memoryUsage(),来获取以后过程的内存应用状况。
- 剖析代码逻辑:查看代码中是否存在未开释的资源,比方未敞开的文件句柄、未革除的定时器、未解绑的事件监听器等。这些资源如果没有正确开释,就会导致内存透露。
- 应用堆快照:能够应用V8提供的堆快照工具,如Chrome DevTools中的Heap Snapshot,来捕捉以后堆中的对象快照,并剖析对象的援用关系。通过剖析堆快照,能够找到哪些对象被援用了但实际上不再须要,从而判断是否存在内存透露。
- 监测内存增长:能够在代码中设置定时器,定期检测内存的增长状况。如果发现内存持续增长,可能是因为有对象没有被正确开释,从而导致内存透露。
须要留神的是,判断内存透露并不是一件简略的事件,须要综合思考多个因素。有时候内存增长可能是失常的,例如缓存的对象或者长久化的数据。因而,联合以上办法进行综合剖析,能够更精确地判断哪个变量存在内存透露。
【参考资料】
- 你真的理解垃圾回收机制吗
- 深刻了解谷歌最强V8垃圾回收机制
- Javascript的垃圾回收机制知多少?
- JavaScript中的垃圾回收和内存透露
5.说下浏览器事件循环机制的了解
【扩大】微工作和宏工作
【扩大】如何了解微工作优先于宏工作
从上图咱们就能够晓得,js的事件循环机制规定了
1.优先执行一个宏工作
2.判断是否有微工作(无:继续执行宏工作)
3.有:执行所有的微工作
4.开始下一个宏工作
在js中 script自身就是一个宏工作,所以执行异步队列的时候,会优先执行它的所有微工作,而后执行宏工作
【参考资料】
说下浏览器事件循环机制
「吊打面试官」彻底了解事件循环、宏工作、微工作
齐全了解【宏工作和微工作】
6.两个tab页如何通信
【参考资料】
面试官:你是如何实现浏览器多标签页之间通信的?
JS如何跨标签页通信
7.说下Websocket的了解
【参考资料】
面试官:说说对WebSocket的了解?利用场景?
8.为什么呈现跨域?哪些形式可能解决跨域?CORS是设置哪个申请头?
1.什么是跨域?
跨域全称为Cross-Origin Resource Sharing,意为跨域资源共享,是一种容许以后域(domain)的资源被其余域(domain)的脚本申请拜访的机制,通常因为同源安全策略,浏览器会禁止这种跨域申请。
而咱们所说的跨域问题是因为违反了浏览器的同源安全策略而产生的。
同源是指协定、域名、端口三者雷同,即便两个不同的域名指向同一个ip地址也是非同源。
留神:跨域并不是申请发不进来,申请能收回去,服务端能收到申请并失常返回后果,只是后果被浏览器拦挡了。
2.跨域的解决办法
- jsonp
- cors
- websocket
- postMessage
- document.domain+iframe
- Node代理
- Nginx代理
【参考】
10 种跨域解决方案(附终极计划)
3.CORS是设置哪个申请头
- Origin(起源):批示申请的源地址,即发动申请的网页的地址。例如,Origin: http://example.com
- Access-Control-Request-Method(访问控制申请办法):用于预检申请(OPTIONS申请)中,批示理论申请应用的HTTP办法。例如,Access-Control-Request-Method: GET。
- Access-Control-Request-Headers(访问控制申请头):用于预检申请(OPTIONS申请)中,批示理论申请中会蕴含的额定的自定义申请头。例如,Access-Control-Request-Headers: X-Custom-Header。
- Access-Control-Allow-Origin(容许的起源):用于服务器响应中,批示容许拜访资源的起源。能够是单个的源地址,也能够是通配符(*)示意容许所有起源。例如,Access-Control-Allow-Origin: http://example.com
- Access-Control-Allow-Methods(容许的办法):用于服务器响应中,批示容许的HTTP办法。例如,Access-Control-Allow-Methods: GET, POST, PUT。
- Access-Control-Allow-Headers(容许的头部):用于服务器响应中,批示容许的自定义申请头。例如,Access-Control-Allow-Headers: X-Custom-Header。
- Access-Control-Allow-Credentials(容许凭证):用于服务器响应中,批示是否容许发送凭证(如cookies、HTTP认证或客户端SSL证实)。例如,Access-Control-Allow-Credentials: true。
9.Options预检申请呈现的机会是什么
- 只有跨域的状况下,才会产生预申请
- 与前述简略申请不同,“需预检的申请”要求必须首先应用OPTIONS办法发动一个预检申请到服务器,以获知服务器是否容许该理论申请。“预检申请”的应用,能够防止跨域申请对服务器的用户数据产生未预期的影响。
- withCredentials为true不会产生预申请
- 申请头Content-Type为application/json会产生预申请
- 设置了用户自定义申请头会产生预检申请
- delete办法产生预检申请
预检申请不肯定每一次都会产生
- 这个因为浏览器会对预检申请进行缓存
- 同时通过服务器端设置 Access-Control-Max-Age 字段来设置缓存工夫
- 那么当第一次申请该 URL 时会收回 OPTIONS 申请,浏览器会依据返回的 Access-Control-Max-Age 字段缓存该申请的 OPTIONS 预检申请的响应后果(具体缓存工夫还取决于浏览器的反对的默认最大值,取两者最小值,个别为 10 分钟)。在缓存有效期内,该资源的申请(URL 和 header 字段都雷同的状况下)不会再触发预检。(chrome 关上控制台能够看到,当服务器响应 Access-Control-Max-Age 时只有第一次申请会有预检,前面不会了。留神要开启缓存,去掉 disable cache 勾选。)
- 在 Firefox 中,下限是24小时 (即 86400 秒)。
- 在 Chromium v76 之前, 下限是 10 分钟(即 600 秒)。
- 从 Chromium v76 开始,下限是 2 小时(即 7200 秒)。
- Chromium 同时规定了一个默认值 5 秒。
- 如果值为 -1,示意禁用缓存,则每次申请前都须要应用 OPTIONS 预检申请。
10.Http2.0有哪些新个性
- 二进制分帧
- 首部压缩
- 流量管制
- 多路复用
- 申请优先级
- 服务器推送
【参考】
HTTP----HTTP2.0新个性
11.什么是CDN,它的实现原理
【参考资料】
到底什么是CDN?
CDN原理简略介绍
一文搞懂CDN的技术架构和原理
面试官:如何了解CDN?说说实现原理?
12.TCP三次握手说一下
在TCP/IP协定中,TCP协定提供牢靠的连贯服务,连贯是通过“三次握手”进行初始化的。三次握手的目标是同步连贯单方的序列号和确认号并替换TCP窗口大小信息,实现了三次握手,客户端和服务器端就能够开始传送数据。
第一次握手,是客户端向服务器端发动的,这是用来去发动一个连贯建设的申请,那么这个报文中的SYN位会被标记为:1,所以咱们也常把它叫做一个SYN包;
第二次握手,是由服务器向客户端发动的,是来确认服务器的一个申请链接的,这个报文中咱们的ACK位和SYN位都被标记为:1,所以咱们也把它叫做一个SYN-ACK报文;
第三次握手,同样是由客户端发个服务器端的,这是对服务器的上一个报文的一个确认报文,这个报文中的ACK位被标记为:1,所以咱们也把它叫做一个ACK包。
以上就是TCP的三次握手。
延长-TCP四次离别的含意
当客户端和服务器通过三次握手建设了TCP连贯当前,当数据传送结束,相应的就要断开TCP连贯。那对于TCP的断开连接,这里就有了“四次离别”。
1.第一次离别:客户端发送断开申请
2.第二次离别:服务器收到断开申请,发送批准断开连接的申请
3.第三次离别:服务器发送申请断开连接
4.第四次离别:客户端收到,发送批准断开连接
1.当客户端发送断开申请,只是示意客户端曾经没有数据要发送了,客户端通知服务器,它的数据曾经全副发送结束了,然而,这个时候客户端还是能够承受来自服务器的数据(第一次离别)
2.当服务器收到断开申请时,示意它曾经晓得客户端没有数据发送了并发送批准断开连接的申请,然而服务器还是能够发送数据到客户端的(第二次离别)
3.当服务器发送批准断开连接的申请后,这个时候就示意服务器也没有数据要发送了,就会通知客户端,我也没有数据要发送了(第三次离别)
4.当客户端收到服务器发送申请断开连接后,再去通知服务端我曾经晓得你没有数据要发给我了,批准断开连接申请(第四次离别)
13.UDP协定理解吗
【参考】
UDP协定详解
工程化
1.构建工具应用的哪个?比照一下webpack和vite的差异
webpack core 是一个纯打包工具(对标 Rollup),而 Vite 其实是一个更下层的工具链计划,对标的是 (webpack + 针对 web 的罕用配置 + webpack-dev-server)。
从底层原理上来说,Vite是基于esbuild预构建依赖。而esbuild是采纳go语言编写,因为go语言的操作是纳秒级别,而js是以毫秒计数,所以vite比用js编写的打包器快10-100倍。
webpack启动图:
Vite启动图:
webpack:
剖析依赖=> 编译打包=> 交给本地服务器进行渲染。首先剖析各个模块之间的依赖,而后进行打包,在启动webpack-dev-server,申请服务器时,间接显示打包后果。webpack打包之后存在的问题:随着模块的增多,会造成打出的 bundle 体积过大,进而会造成热更新速度显著拖慢。
vite:
启动服务器=> 申请模块时按需动静编译显示。是先启动开发服务器,申请某个模块时再对该模块进行实时编译,因为古代游览器自身反对ES-Module,所以会主动向依赖的Module发出请求。所以vite就将开发环境下的模块文件作为浏览器的执行文件,而不是像webpack进行打包后交给本地服务器。
Webpack
Webpack 是一个基于打包器的构建工具,同一个入口文件的代码会打包成一个 Bundle 文件。Webpack 长期来的一个痛点是对于大规模利用的利用启动和热更新速度很慢。
当文件产生变动时,整个 JavaScript Bundle 文件会被 Webpack 从新构建,这也是为什么应用 Webpack 的大规模利用在利用启动和热更新时速度很慢的起因。这给进行大规模 JavaScript 利用的开发者造成了很差的开发体验。
Webpack工作流程
Webpack 打包过程:
- 从一个入口文件开始,基于代码文件中的所有 import,export,require 构建依赖树;
- 编译 JS/CSS 等模块;
- 应用算法排序、重写、连贯代码;
- 优化。
开发环境的 Webpack:
- 打包所有代码;
- 启动 webpack-dev-server 托管打包好的代码;
- 启动 websocket 解决热更新 HMR。
利用规模越大,启动和热更新代码越慢。及时启动了热更新,每次代码变更也须要从新生产 Bundle 文件。
Vite
Vite 外围借助了浏览器的原生 ES Modules 和像 esbuild 这样的将代码编译成 native code 的打包工具。
Vite 次要有两方面组成:
- 一个开发服务器,基于 ESM 提供丰盛的内建能力,比方速度快到惊人的模块热更新(HMR);
- 一套构建指令,应用 rollup 进行代码打包,且零配置即可输入用于生产环境的高度优化的动态代码。
Vite 的外围能力和 webpack + webpack-dev-server 类似,然而在开发者体验上有一些晋升:
- 无论我的项目大小有多大,启动利用都只需更少的工夫;
- 无论我的项目大小有多大,HMR(Hot Module Replacing)热更新都能够做到及时响应;
- 按需编译;
- 零配置,开箱即用;
- Esbuild 能力带来的 Typescript/jsx 的原生反对。
大型的 JavaScript 我的项目在开发和生产环境有比拟差的性能体现,往往是因为咱们应用的构建工具没有充沛做到并行处理、内存优化和缓存。
Vite核心理念:Bundless 开发环境构建
浏览器的原生 ES Modules 能力容许在不将代码打包到一起的状况下运行 JavaScript 利用。Vite 的核心理念很简略,就是借助浏览器原生 ES Modules 能力,当浏览器发出请求时,为浏览器按需提供 ES Module 文件,浏览器获取 ES Module 文件会间接执行。
Vite利用启动
Vite 将利用中的模块分为依赖和源码两类,别离进行服务器启动工夫的优化。
- 依赖模块,开发过程中根本不会变动。Vite 对依赖采纳了 esbuild 预构建的形式,esbuild 应用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍;
- 源码模块,是用户本人开发的代码,会常常变动。
Vite 在浏览器申请时按需转换并以原生 ESM 形式提供源码,让浏览器接管了打包程序的局部工作。
Vite工作原理
Vite 通过原生 ES Modules 托管源代码,实质上是让浏览器来接管局部打包器的工作。Vite 只会在浏览器申请产生时,按需将源码转成 ES Modules 格局返回给浏览器,由浏览器加载并执行 ES Modules 文件。
Vite的热更新
在基于 Bundle 构建的构建器中,当一个文件变动时,从新构建整个 Bundle 文件是十分低效的,且随着利用规模的回升,构建速度会直线降落。
传统的构建器尽管提供了热更新的能力,然而也会存在随着利用规模回升,热更新速度显著降落的问题。
Vite 基于 ESM 按需提供源码文件,当一个文件被编辑后,Vite 只会从新编译并提供该文件。因而,无论我的项目规模多大,Vite 的热更新都能够放弃疾速更新。
此外,Vite 正当利用浏览器缓存来减速页面加载,源码模块申请依据 304 Not Modified 进行协商缓存;依赖模块申请通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因而一旦缓存,不会再次申请。
Vite 依靠反对原生 ESM 模块的古代浏览器,极大的升高了利用的启动和从新构建工夫。Vite 实质上是一个在开发环境为浏览器按需提供文件的 Web Server,这些文件蕴含源码模块和在第一次运行时应用 esbuild 预构建的依赖模块。
Vite 和 Webpack 的次要不同在于开发环境下对于源码如何被托管以及反对哪种模块标准。
【参考】:
如果能重来,你要选 Vite 还是 Webpack ?
【打包工具】- Vite 和 webpack 原理、优缺点比照
2.打包构建上做过哪些优化/我的项目构建部署比较慢,怎么来优化
3.动静引入组件的原理是什么,生成的chunk文件和代码的组件引入如何匹配
小程序
1.Taro开发小程序中遇到过哪些兼容性问题
2.当有新的小程序api呈现后,taro没有实现该怎么应用
3.小程序渲染原理有理解吗
【参考资料】
小程序疾速渲染的原理及流程解析
4.小程序滚动穿透如何解决
解决办法:
- 先说H5的,比较简单,只须要通过弹窗的显示状态,来动静设置底层页面最外层View的overflow属性即可。当弹窗显示的时候,document.body.style.overflow = 'hidden'; 当弹窗暗藏的时候,document.body.style.overflow = 'scroll';
- 因为小程序没有DOM构造,下面的计划天然是不起任何作用(就是这么傲娇)。小程序要分两步来解决,先是mask蒙层会滑动穿透的问题,这个能够采纳Taro的catchMove属性 来解决,官网文档上也有说,见下方图片。而后是弹窗外部滚动穿透的问题,采纳的是将弹窗外部最外层View改为ScrollView,光这样改还不行,还要在ScrollView的外层再加个View并且加上catchMove属性。间接在ScrollView下面加catchMove不行,ScrollView不反对这个属性。
注:Taro的CatchMove属性是Taro 3.0.21版本才开始反对
如上操作,Taro 弹窗滚动穿透问题的问题就完满解决了。可同时兼容H5和小程序。
Hybrid混合开发
1.说一下hybrid混合开发中,app端和js端是如何实现通信的?或说下JSBridge的原理
在Hybrid模式下,H5会常常须要应用Native的性能,比方关上二维码扫描、调用原生页面、获取用户信息等,同时Native也须要向Web端发送推送、更新状态等,而JavaScript是运行在独自的JS Context中(Webview容器、JSCore等),与原生有运行环境的隔离,所以须要有一种机制实现Native端和Web端的双向通信,这就是JSBridge:以JavaScript引擎或Webview容器作为媒介,通过协定协定进行通信,实现Native端和Web端双向通信的一种机制。
通过JSBridge,Web端能够调用Native端的Java接口,同样Native端也能够通过JSBridge调用Web端的JavaScript接口,实现彼此的双向调用。
Web端和Native能够类比于Client/Server模式,Web端调用原生接口时就如同Client向Server端发送一个申请相似,JSB在此充当相似于HTTP协定的角色,实现JSBridge次要是两点:
- 将Native端原生接口封装成JavaScript接口
- 将Web端JavaScript接口封装成原生接口
【参考资料】
- 深入浅出JSBridge:从原理到应用
- Hybrid入门及JSBridge原理
性能优化
1.日常开发中性能优化做过哪些
基本思路
整体的逻辑大体如下:
构建工具剖析
- 体积:包体积是否存在杂质,能不能过滤进来?
- 构建速度:构建速度影响开发调试效率和公布相干环境的节奏。
针对体积的剖析伎俩,能够应用webpack-bundle-analyzer来剖析以后我的项目中是否存在反复依赖, 影子依赖, 模块体积差别等等问题,应用交互式可缩放树图可视化的来梳理本人我的项目利用中的问题,而后去找到并且优化它。
针对速度剖析, 能够应用speed-measure-webpack-plugin来剖析以后每个bundle模块打包构建时的工夫数据,别离会蕴含loader, plugin等解决的工夫。依据相干的数据来定点模块,判断是否有优化的空间。
项目分析
- NetWork: 网络面板,用于侦测浏览器资源下载与上传的能耗视图。
- Performance: 性能面板:用于侦测浏览器运行时的性能体现,得出我的项目运行的时序图,同时也能够剖析页面的一些隐式问题,如 (内存透露)。
- Lighthouse: 性能评测(灯塔),基于以后浏览器页面的阶段性加载现状来为用户提供后果剖析数据指标报告。
- 探针: 前端框架如React有相干Profiler的探针API和chrome插件,可能为开发者探测代码编写时的问题,防止反复或者是频繁异样的Repeat rendering。
性能监控平台,为线上的前端进行侦测,从接口调用、首屏性能、流量保活等等一系列的性能指标做度量平台。
如何针对通用问题进行标准化解决???
优化伎俩
构建工具优化
包体积大小
首先,针对包体积大小做一些调整,能做的事件大体上有以下几点:
- 按需加载:针对我的项目中比拟大型的工具类库,如lodash、UI Design、use-hooks等类库,如果只是应用很小的一部分,就须要防止全量加载的状况。
- tree-shaking:通过ESM的个性,能够在构建阶段将我的项目模块中的死代码进行移除,从而保障洁净的构造和最小的文件大小。
- minify:通过webpack相干的插件,能够为代码产物做混同压缩,如JS minify与CSS minify,当我的项目的代码越大的时候,压缩的成果就越显著。
- CDN依赖:对于局部共有依赖能够将其搁置在CDN中来进行加载,可能极大幅度的缩小本地我的项目产物的体积大小,毛病就是无奈进行按需加载,占用浏览器申请资源,具体的收益能够本人衡量。
构建速度
其次就是构建速度了,能够从以下几个方面来着手:
- 长久化缓存:webpack5自身集成,通过cache字段开启即可,webpack4能够通过cache-loader来实现,不过有余的中央是cache-loader仅对loader起作用。(目前该loader看起来曾经暂停保护了)。
- 多过程构建:通过thread-loader能够开启多过程构建,能够进步局部环节的性能效率,比方babel-ast解析等等。webpack4的话则是应用happypack来实现。
- Building:应用基于Rust编写的高性能编译工具来替换默认的babel,比方将babel-loader替换为swc-loader,这个是一个可选的操作,目前市面上少数cli都反对了该选项,以供开发者抉择。
- Dll: dll能够将一些依赖库提前进行打包,可能无效的升高打包类库的次数,只有其批改后才会进行从新的构建,通常用于专用类库的解决。能够应用DllPlugin来配置对应的成果。
- Bundless: Vite是市面上十分炽热的Bundless计划之一,曾经能够看出越来越多的人开始应用它来代替传统Bundle计划,其优良的构建速度, HMR深受好评。
我的项目计划
资源加载
- 懒加载:懒加载我对齐的定义是提早资源的加载,直到齐第一次触达时在进行资源的引入和展现,这项技术被宽泛用于spa-router、图片资源等非阻塞性资源的优化上。对于缩短要害门路渲染十分有帮忙,如各位同学耳熟能详的首屏优化。
- 预加载:预加载的目标就是通过在渲染前事后拿到须要展现的数据,从而缩短要害承流页面出现给用户的渲染工夫,大多数状况下能够分为首页预载和要害页预载两方面。
- 首页预载:Web端提供了相干的pre-load、pre-render、pre-fetch相干API实现。小程序方面也提供了相干冷启动获取数据的能力,例如getBackgroundFetch。对于谋求极致的性能体验的话不妨一试。
- 要害页预载:要害页预载的话就依据本身业务来实现了,参考商城业务中商品详情页、营销会场等页面都是用户比拟相熟的页面。因而,少数时候都会将页面跳转与数据申请独特进行的。
- 接口优化:耗时较久的接口如果阻塞外围链路,能够将其拆成多个粒度小的接口来渐进式的调用以此来保障页面优先加载。反之亦然,如果接口零散占用了太多的申请资源,则能够将其聚合成一个残缺的大接口来获取数据。
- 缓存:浏览器缓存策略大体上分为强缓存和协商缓存。利用好缓存策略能够无效的升高资源的反复加载,从而进步网页的整体加载速度。
网络相干
在这个过程中,会有两个阶段大家耳熟能详,别离是DNS解析和HTTP连贯(三次握手)。它们是这个过程中绝对耗时的两个局部。基于此,能够针对网络来对我的项目做以下一些优化:
- DNS预解析: 理解DNS解析过程的同学都晓得,相当耗时。而浏览器也提供了dns-prefetch的相干操作,用于领先对origin进行解析。
- PreConnect:既然DNS能够预解析,那么申请连贯的话也绝对应的能够领先执行。
- 内容散发网络CDN:通过内容散发网络CDN能够使用户就近获取所须要的内容资源,从而升高网络拥塞,进步用户拜访响应速度和保障拜访资源的命中率。
- HTTP/2:尽可能应用HTTP/2的新个性,它会带来一些意想不到的小惊喜。感兴趣的能够看《HTTP2简介和基于HTTP2的Web优化》
- Gzip:很早之前会开启Gzip的压缩计划来缩小网络理论传输数据的大小,服务器会将申请资源进行压缩后传输给客户端。
交互体验
- 加载态:提到加载态,可能大多数同学第一想到的就是loading,基于loading的实现之上,很多产品都会对局部页面制作骨架组件/骨架屏,它的长处就是表达能力强,相比于简略的加载loading而言,骨架组件可能给用户出现预期的页面与区块的显示成果。
- 虚构列表:虚构列表曾经是近年来陈词滥调的一个优化了,其场景次要用于页面中须要展现大量数据时的一个措施伎俩,如播种地区抉择、有限滚动场景,任何须要重叠数据显示的页面中或多或少都会呈现它的身影。
- 想理解具体的场景和实现过程的能够查阅我之前写的相干文章《百万PV商城实际系列 - 前端长列表渲染优化实战》
浏览器
- 回流和重绘: 回流和重绘是一个生产资源的事件,一旦浏览器产生频繁的回流,会造成浏览器资源的缓和,导致我的项目或多或少呈现卡顿的问题。现如今浏览器尽管曾经为咱们做了Layout相干的合并优化。但还是要从根本上去缩小不必要的状况产生。而解决的计划大多数如下几种:
<!---->
- 代码编写:款式集中扭转,能够无效缩小重排次数,尽可能不要频繁的去获取和写入DOM,须要操作时应提前在内存中筹备好对应的操作元素,从而一次性的将其进行读写。
- 暗藏批改:当波及到DOM元素的频繁操作时,尽可能让其先排出文档流,直到最终操作实现后在将其渲染到页面上,以此来升高回流和重绘的频率。
- GPU合成:对于动画,能够应用transform代替position做一些事件,现如今大部分浏览器都会对transform进行借用GPU资源来做渲染优化。
<!---->
- RAF&RDC:RAF&RDC别离是requestAnimationFrame和requestIdleCallback的简称,正当的应用能够防止频繁逻辑批改导致提早要害事件,如动画和输出响应的交互。一个是针对动画帧成果,一个针对页面执行的逻辑。
- 事件委托:通过事件委托来绑定对应的操作,能够无效的缩小相干内存的耗费。现如今,咱们曾经不在须要太过关注原生事件的解决了,少数状况下框架类库都曾经可能很好的开始工作。
图片
- WebP:webp与png一样,是一种图片格式,因为应用更优的图像数据压缩算法、更小的图片体积因而可能带来更好的访问速度劣势。须要留神的是可能会敌人不反对webp格局的环境问题,这时候就须要进行降级解决避免出现用户无法访问图片的体验。
- 渐进式加载:渐进式是一个过程,渐进式加载的话就是让一张图片从含糊到清晰缓缓展现的一个过程,防止用户因为网络问题导致图片加载失败看到不敌对的体验,实质上的话属于加载状态的一种。和骨架屏区别不大。
- 图片切割:在业务当中,会看到相似商品详情图、宣传海报等相干的图片展现,它自身是一个十分长的图片,且体积因为要保真的起因无奈做一些压缩。因而针对相似的场景就能够抉择图片切割的计划,将长图切割成若干份小图进行展现,搭配之前提到过的一些优化计划能够完满的防止加载渲染问题。
- 图片CDN:应用图片CDN的益处除了能够取得内容散发来进步响应速度外,还能够应用其提供的图片裁切性能动静的定义分辨率大小、清晰度展现等辅助其在页面上的展现。
总结
【参考】
当面试官问我前端能够做的性能优化有哪些
5000字总结面试常问前端我的项目优化到底怎么来做?
面试时我是如何答复“前端性能优化”的
指标-掂量性能和用户体验的规范
2.谬误监控上报怎么做
3.性能监控指标有哪些?首屏工夫如何计算
【扩大】如何优化首屏工夫
- http缓存(强缓存和协商缓存), 性价比最高的一个优化形式。需注意的是,浏览器不缓存XHR接口,本人可依据业务个性动静地抉择缓存策略。比方一个体积很大的接口,但内容变更频率不是很频繁,这种状况就适宜用协商缓存。
- cdn散发(缩小传输间隔)。通过在多台服务器部署雷同的正本,当用户拜访时,dns服务器就近(物理间隔)抉择一台文件服务器并返回它的ip。
- 前端的资源动静加载:
<!---->
- 路由动静加载
- 组件动静加载
- 图片懒加载(offScreen Image),越来越多的浏览器反对原生的懒加载,通过给img标签加上loading="lazy来开启懒加载模式。
<!---->
- 合并申请。这点在串行接口的场景中作用很显著,比方有两个串行的接口A和B,须要先申请A,而后依据接口A的返回后果去申请接口B。如果server和client的物理间隔为D,那么这个串行的场景传输的物理途程为4D。如果合并成一个接口,物理途程可减小为2D。
- 页面应用骨架屏。意思是在首屏加载实现之前,通过渲染一些简略元素进行占位。骨架屏的益处在于能够缩小用户期待时的急躁情绪。这点很无效,在很多成熟的网站都有大量利用。没有骨架屏的话,一个loading图也是能够的。
- 应用ssr渲染。
- service worker:通过sw离线更新缓存的能力,实践上能做到每次拜访页面都无需下载html,且所有动态资源都曾经在本地有缓存。
- 引入http2.0。http2.0比照http1.1,最次要的晋升是传输性能,在接口小而多的时候性能劣势会更加显著。
- 利用好http压缩。即便是最一般的gzip,也能把bootstrap.min.css压缩到原来的17%。可见,压缩的成果非常明显,特地是对于文本类的动态资源。另外,接口也是能压缩的。接口不大的话能够不必压缩,因为性价比低(思考压缩和解压的工夫)。值得注意的是,因为压缩算法的个性,文件越大,压缩成果会越好。
- 利用好script标签的async和defer这两个属性。
- 应用 WebP 代替jpg/png。
【参考】
web的首屏加载工夫
4.如果让你设计一个监控零碎,你会如何设计?
5.单页利用性能指标有FMP,理解吗
【参考】
FP、FCP、FMP、LCP都是什么P?
定位性能指标FMP
6.单页利用首屏优化形式有哪些
【参考资料】
面试官:SPA首屏加载速度慢的怎么解决?
设计模式
1.依赖注入是什么
【参考资料】
依赖注入 in 前端 && Typescript 实现依赖注入
前端架构利器-依赖注入
写给前端同学的依赖注入(DI) 介绍
2.前端框架设计外面常见的设计模式有哪些
- 工厂模式
- 单例模式
- 代理模式
- 策略模式
- 观察者/公布-订阅模式
- 迭代器模式
- 装璜器模式
- 原型模式
【参考资料】
前端须要理解的9种设计模式
平安
1.对称加密和非对称加密
【参考资料】
对称加密和非对称加密
2.网站攻击方式有哪些
【参考资料】
面试官:web常见的攻击方式有哪些?如何进攻?
开放性题
1.前端如何解决并发
比方页面初始化须要10个申请,后端只让每次3个,怎么做
思路拆解:
- urls的长度为0时,results就没有值,此时应该返回空数组
- maxNum大于urls的长度时,应该取的是urls的长度,否则则是取maxNum
- 须要定义一个count计数器来判断是否已全副申请实现
- 因为没有思考申请是否申请胜利,所以申请胜利或报错都应把后果保留在results汇合中
- results中的程序需和urls中的保持一致
// 并发申请函数const concurrencyRequest = (urls, maxNum) => { return new Promise((resolve) => { if (urls.length === 0) { resolve([]); return; } const results = []; let index = 0; // 下一个申请的下标 let count = 0; // 以后申请实现的数量 // 发送申请 async function request() { if (index === urls.length) return; const i = index; // 保留序号,使result和urls绝对应 const url = urls[index]; index++; console.log(url); try { const resp = await fetch(url); // resp 退出到results results[i] = resp; } catch (err) { // err 退出到results results[i] = err; } finally { count++; // 判断是否所有的申请都已实现 if (count === urls.length) { console.log('实现了'); resolve(results); } request(); } } // maxNum和urls.length取最小进行调用 const times = Math.min(maxNum, urls.length); for(let i = 0; i < times; i++) { request(); } })}
function sendRequest(urls, max, callbackFunc) { const REQUEST_MAX = max; const TOTAL_REQUESTS_NUM = urls.length; const blockQueue = []; // 期待排队的那个队列 let currentReqNumber = 0; // 当初申请的数量是 let numberOfRequestsDone = 0; // 曾经申请结束的数量是 const results = new Array(TOTAL_REQUESTS_NUM).fill(false); // 所有申请的返回后果,先初始化上 async function init() { for (let i = 0; i < urls.length; i++) { request(i, urls[i]); } } async function request(index, reqUrl) { // 这个index传过来就是为了对应好哪个申请, // 放在对应的results数组对应地位上的,放弃程序 if (currentReqNumber >= REQUEST_MAX) { await new Promise((resolve) => blockQueue.push(resolve)); // 阻塞队列减少一个 Pending 状态的 Promise, // 进外面排队去吧,不放你进去,不resolve你,你就别想进行上面的申请 } reqHandler(index, reqUrl); // {4} } async function reqHandler(index, reqUrl) { currentReqNumber++; // {5} try { const result = await fetch(reqUrl); results[index] = result; } catch (err) { results[index] = err; } finally { currentReqNumber--; numberOfRequestsDone++; if (blockQueue.length) { // 每实现一个就从阻塞队列里剔除一个 blockQueue[0](); // 将最先进入阻塞队列的 Promise 从 Pending 变为 Fulfilled, // 也就是执行resolve函数了,前面不就能持续进行了嘛 blockQueue.shift(); } if (numberOfRequestsDone === TOTAL_REQUESTS_NUM) { callbackFunc(results); } } } init();}// 测试数据const allRequest = [ "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=1", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=2", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=3", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=4", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=5", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=6", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=7", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=8", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=9", "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=10",];sendRequest(allRequests, 2, (result) => console.log(result));
【参考资料】
对于前端:如何实现并发申请数量管制?
30行代码,高大上解决Promise的多并发问题
2.前端解决大数据会有哪些影响
【参考资料】
前端优化之分分钟学会渲染百万条数据不卡顿
3.比照React和Vue
4.说一下对SEO的理解
【参考】
什么是SEO?如何进行SEO优化?
5.设计离线包计划
从 WebView 初始化到 H5 页面最终渲染的整个过程:
能够看到,一个残缺的WebView加载流程:
- 初始化webview;
- 建设连贯,从后端下载开启WebView须要的相干文件数据包,包含网页中须要申请的文件以及模板Html;
- 加载与承受数据,页面的渲染,并通过实现android和js的交互
目前支流的优化形式次要包含:
- 针对 WebView 初始化:该过程大抵需消耗 70~700ms。当客户端刚启动时,能够先提前初始化一个全局的 WebView 待用并暗藏,当用户拜访了 WebView 时,间接应用这个 WebView 加载对应网页并展现。
- 针对向后端发送接口申请:在客户端初始化 WebView 的同时,间接由 Native 开始网络申请数据,当页面初始化实现后,向 Native 获取其代理申请的数据。
- 针对单页面加载的JS动静拼接 Html:采纳多页面打包, 服务端渲染,以及构建时预渲染等形式。
- 针对加载页面资源的大小:可采纳懒加载等形式,将须要较大资源的局部分离出来,等整体页面渲染实现后再异步申请分离出来的资源,以晋升整体页面加载速度。
离线包的具体思路
默认状况下,咱们先将页面须要的动态资源打包并事后内置到客户端的安装包中,当用户装置利用后,解压资源到本地存储中,当 WebView 加载某个 H5 页面时,拦挡收回的所有 http 申请,查看申请的资源是否在本地存在,如果存在则间接加载本地资源;否则加载网络的资源。
【参考】
Hybrid App 离线包计划实际
一起学跨端技术-离线包
基于 prefetch 的 H5 离线包计划
7.Vue3的Compisition API和React hooks有哪些区别
React Hooks底层是链表,每一个 Hook 的 next 是指向下一个 Hook 的;
Vue对数据的响应是基于 proxy 的,对数据间接代理察看。这种场景下,只有任何一个更改 data 的中央,相干的 function 或者 template 都会被从新计算,因而避开了 React 可能遇到的性能上的问题。
代码执行上:
- Vue Composition API 的 setup() 晚于 beforeCreate 钩子,早于 created 钩子被调用
- React Hooks 会在组件每次渲染时候运行,而 Vue setup() 只在组件创立时运行一次
因为 React Hooks 会屡次运行,所以 render 办法必须恪守某些规定,比方:
不要在循环外部、条件语句中或嵌套函数里调用 Hooks
申明状态上:
React
useState 是 React Hooks 申明状态的次要路径
- 能够向调用中传入一个初始值作为参数
- 如果初始值的计算代价比拟低廉,也能够将其表白为一个函数,就只会在首次渲染时才会被执行
Vue
Vue 应用两个次要的函数来申明状态:ref 和 reactive。
ref() 返回一个反应式对象,其外部值可通过其 value 属性被拜访到。能够将其用于根本类型,也能够用于对象
reactive() 只将一个对象作为其输出并返回一个对其的反应式代理
TODO ……
【参考】
Vue Composition API 和 React Hooks 比照
8.前端灰度怎么做;如果让你设计一个灰度过程的话如何设计,我的项目版本灰度如何来做;如何防止再次发版;
【参考】
前端灰度-前端我的项目如何进行灰度?
你想晓得的前端灰度计划都在这了
9.说下单页利用和多页利用的区别,优缺点
【参考资料】
vue单页面(SPA)和多页面(MPA)的区别(具体答案)
10.管理系统鉴权如何做的
个别咱们来说次要针对RBAC鉴权模式进行管制
页面鉴权用户有没有权限拜访该页面,或者有没有查看通往该页面路由的权限。(组件和路由)
组件: 一.有没有权限拜访页面的时候:通过路由守卫联合后端返回来的token,进行页面跳转之前的鉴权,查看token是否过期并且是否领有该页面的权限。
路由:判断用户是否有权限查看通过指定页面的路由(或菜单或导航): 1.纯前端解决:在写路由表的时候,咱们会在每个路由下加一个meta,而后在meta外面写出能够拜访或查看该路由或页面的角色信息,而后咱们能够通过该meta下的信息应用addrouter管制该路由下的显隐 2.前后端配合解决:每次登陆的时候,都在后端那里获取token的路由表保留到vuex里,在通过addrouter动静渲染token下的路由及导航
UI鉴权:它的颗粒度很细,所以说难度较大,咱们能够通过对立的自定义指令来进行配置。
一般来说UI鉴权指的是按钮鉴权解决UI鉴权 简略的办法是咱们能够获取token下的角色信息,用v-if解决该UI的显隐,然而这种形式毛病很显著,不易于对立治理,所以咱们须要集中封装一个自定义指令,在自定义指令中,集中处理鉴权的逻辑,而后散发在每个鉴权的按钮上。
Node
1.Node用过么,起一个http server的伪代码大略是?
const { createServer } = require('http');const HOST = 'localhost';const PORT = '8080';const server = createServer((req, resp) => { // the first param is status code it returns // and the second param is response header info resp.writeHead(200, { 'Content-Type': 'text/plain' }); console.log('server is working...'); // call end method to tell server that the request has been fulfilled resp.end('hello nodejs http server');});server.listen(PORT, HOST, (error) => { if (error) { console.log('Something wrong: ', error); return; } console.log(`server is listening on http://${HOST}:${PORT} ...`);});
手写局部
1.括号匹配
2.数组排重
3.数组惟一呈现
4.面向对象设计
5.版本号排序
6.函数柯里化
7.数组中有一个数字呈现的次数超过数组长度的一半,请找出这个数字
8.手写单例模式
9.手写疾速排序
10.单向链表的反转取值,通过迭代/递归形式实现
其余
1.git merge 和 git rebase的区别是什么
- git merge会创立一个新的合并提交,保留残缺的提交历史记录,但可能会导致分支历史变得复杂。
- git rebase会将提交复制到指标分支上,并从新利用指标分支上的更改,改写提交历史记录,使分支历史放弃线性,但可能会失落一些提交信息。
【参考资料】:
Merge vs Rebase
面试官:说说你对git rebase 和 git merge的了解?区别?
原文连贯 欢送关注