前端面试题整理背书

308 阅读19分钟

整理的前端面试题背书,背到就是赚到!

JS基础

数据类型

JS分为基本类型和对象类型。

基本类型有 boolean,null,undefined,number,string,symbol,bigint.

基本类型存储的是值,对象类型存储的是内存地址。

类型判断:

用typeof判断基本类型,判断null使用全等。

判断对象的类型使用 instanceof

数组和对象的循环

1 遍历数组的方法

1.1 map()

1.2 forEach()

1.3 for of

1.4 some()、every()

1.5 filter()

1.6 reduce()、reduceRight()

1.7 find()、findIndex()

1.8 keys()、values()、entries()

2 遍历对象的方法

2.1 for in

2.2 Object.keys()、Object.values()、Object.entries()

2.3 Object.getOwnPropertyNames()

2.4 Object.getOwnPropertySymbols()

2.5 Reflect.ownKeys()

2.6 总结

3 其他遍历方法

3.1 for

3.2 while

3.3 do / while

3.4 for await of

原型、原型链

声明的对象都有共用的属性和方法,对象类型都会有一个prototype属性指向它的原型,直到是 Object 为止。

原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链。

闭包

闭包是指一个函数内有另一个函数,内部函数可以访问到外部函数中的变量,那么这个内部函数就是闭包。

闭包存在的意义就是让我们可以间接访问到函数内部的变量;

作用:实际应用中主要用于封装私有变量,收敛权限等。

缺点:如果频繁使用闭包,内存消耗很大。

this

this是函数运行时自动生成的一个内部对象,指向调用它的对象。

在全局环境下调用,this指向window。

一个对象调用下,this指向对象。

new的时候,this指向new出来的对象。

箭头函数没有this,他的this取决于包裹箭头函数的第一个普通函数的this。

可以通过bind,apply,call方法改变this。

Call和apply的区别,传参的格式不一样,call是字段分开,apply是数组。

深拷贝、浅拷贝

拷贝所有的属性值到一个新的对象时需要用到拷贝。

浅拷贝:可以通过Object.assign()方法和展开运算符来实现浅拷贝,当属性值是对象时,拷贝的是地址。

深拷贝:当我们遇到深层次对象时需要用到深拷贝,可以通过JSON.stringify()方法来实现深拷贝,但是这种方式不支持undefined、函数、symbol和循环引用的情况。

我们也可以使用成熟的工具库,例如lodash或jQuery,都封装了深拷贝的方法。

手写深拷贝的思路:

深拷贝其实就是一个递归浅拷贝的过程。在方法体里,根据属性的类型判断,如果是值类型或者undefined、null直接返回,如果是对象类型,递归遍历对象进行拷贝,如果属性类型是日期,正则,通过new的方式拷贝,如果是函数,通过apply改变this指向,如果是循环引用,可以利用weakMap解决。

es6中使用过什么

let,const声明变量、反引号模板字符串、箭头函数、promise、class、Set、Map、es6的模块化import,export、数组的新方法,map,reduce,filter,find等等

var、let、const的区别

Var在全局作用域下声明变量会导致变量挂载在window上,其他两者不会;

Var存在提升,可以在声明之前使用,let和const因为暂时性死区的原因,不能在声明前使用。

继承

利用原型去继承,在子类的构造函数中通过Parent.call(this)继承父类的属性,然后改变子类的原型为 new Parent()来继承父类的函数。

利用class实现继承,通过extends和构造函数内添加super方法实现继承。

模块化

模块化可以解决命名冲突、提供复用性、提高代码可维护性等;

模块化的几种方式:

立即执行函数(早期使用的);

AMD: 依赖前置,requireJS的;

CMD: 就近依赖,seaJS的;

CommonJS: NodeJS,通过module.exports导出,require导入;

ES6 module: es6的模块化,通过export default导出,import导入;

proxy

proxy代理是es6的,可以用来自定义对象的操作,例如可以用来自定义set和get函数。

vue3就是通过proxy来替换原本的Object.defineProperty来实现数据响应式,在get中收集依赖,在set派发更新,无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好。

缺陷是浏览器兼容性不好。

Map、WeakMap、Object区别

Key的类型不一样:

Object的key只能是string、symbol;

Map的key可以是任意类型;

WeakMap的key只能是Object类型,并且该对象为弱引用,因此多用于解决引用层面。

API也有不同,还有Object是无序的,map是有序的。

generator

函数名前面加星号表示generator函数。

generator最大的特点是可以控制函数的执行。Generator 函数调用和普通函数不同,它会返回一个迭代器。

promise

Promise是一种异步的解决方案,promise有三种状态,pending,resolved,rejected,通过then的方式实现链式调用。

async和await

一个函数加上async就会返回一个promise,await只能配套async使用;

async 就是将函数返回值使用 Promise.resolve() 包裹了下,和 then 中处理返回值一样。

可以解决地域回调,缺点是当多个异步代码之间没有依赖关系时,使用await会导致性能降低;

await原理是它内部实现了generator,generator加上promise的语法糖;

手写promise

首先我们创建了三个常量用于表示状态,对于经常使用的一些值都应该通过常量来管理,便于开发及后期维护;

在函数体内部首先创建了常量 that,赋值this,因为代码可能会异步执行,用于获取正确的 this 对象;

一开始 Promise 的状态应该是 pending;

用value 变量用于保存 resolve 或者 reject 中传入的值;

用resolvedCallbacks 和 rejectedCallbacks 保存 then 中的回调,因为当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用;

Event loop 事件循环

JavaScript是单线程执行的,事件循环就是为了解决单线程运行阻塞的问题。

在JavaScript中,首先执行同步代码,这属于宏任务,

当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行,

执行所有微任务,当执行完所有微任务后,如有必要会渲染页面。

然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数。

微任务包括 process.nextTick 、queueMicrotask、promise.then 、MutationObserver,其中 process.nextTick 为 Node 独有。

宏任务包括 script 、 setTimeout 、setInterval 、setImmediate 、I/O、postMessage、MessageChannel 及 UI rendering。

手写call方法

首先传参 context 为可选参数,如果不传的话默认上下文为 window,

接下来我们通过 Symbol 为 context 创建一个属性,并将值设置为需要调用的函数,

因为 call 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来,

然后调用函数并将对象上的函数删除。

new

在new的过程中,会生成一个对象,链接到原型,绑定this,然后在返回一个新对象。

如何封装一个库函数

通常我们有两种方式,一是定义一个函数,传入不同的参数,定义好相关的属性,根据需求编写满足对应功能的函数;二是定义一个class,在构造函数中确认基本属性和传参,在class内部定义相关函数确认需求所需要的功能API,再通过new的方式调用。

数组去重

1. 利用set

2. 利用for循环和indexOf或者includes

3. 利用filter

4. 利用map

性能优化

性能问题排查:

1. 数据埋点上报;

2. 可以通过控制台的network、performance等调试工具;

Webpack-bundle-analyzer插件分析打包产物;

JS:

减少代码文件的大小

减少书写嵌套函数

使用eslint等代码检查,减少错误代码

传入函数的参数保持类型一致

节流、防抖

加载资源优化:

静态资源的压缩合并、缓存

使用CDN让资源加载更快(就近选择最优节点)

图标可以使用svg格式,webp格式、雪碧图、base64格式,使用css代替一些基础图形。

渲染优化:

CSS放前面,JS放后面

资源预加载

懒加载(图片懒加载,下拉加载更多)

减少DOM查询,对DOM查询做缓存,多个操作尽量合并在一起执行

Webpack性能优化:

优化loader,减少webpack打包时间

代码压缩,按需加载,减少webpack打包体积。

首屏加载性能优化

通过DOMContentLoad或者performance来计算首屏时间

加载慢的原因:

1. 网络延时问题

2. 资源文件体积过大

3. 重复发送请求

4. 加载脚本时渲染内容堵塞

优化方式:

1. 资源加载优化

通过代码压缩、Gzip、图片压缩、代码拆分的方式减少资源大小;

通过HTTP缓存、本地localstorage存储等减少http请求;

通过http缓存、CDN、DNS预解析、http2提高http请求响应速度;

通过按需加载、懒加载、预加载的方式优化资源加载;

2. 页面渲染优化

优化HTML代码,例如减少DOM数量、JS放在底部、CSS放在头部;

优化JS、CSS代码,例如减少重排重绘、降低CSS选择器层级、使用requestAnimationFrame优化动画效果;

webpack优化前端性能

可以利用uglifyJSPlugin来压缩JS代码、compression-webpack-plugin压缩文件、配置image-webpack-loader压缩图片等等;

使用tree shaking删除死代码,减少打包体积;

使用代码分割(code splitting)实现按需加载;

使用CDN加载第三方库;

使用webpack的缓存提高打包效率

使用浏览器缓存提高页面的加载速度;

Vue方向的性能优化

V-if、v-show的正确使用;

Computed和watch的正确使用;

V-for遍历加上key,避免同时使用v-if(建议使用computed动态列表数据);

Object.freeze 方法来冻结一个对象,避免被vue劫持,优化大量数据展示;

及时销毁事件,避免内存泄漏;

图片资源懒加载,vue-lazyload;

路由懒加载;

const Foo = () => import('./Foo.vue')
const router = new VueRouter({
    routes: [
        { path: '/foo', component: Foo }
    ]
})

第三方插件的按需引入;

长列表性能优化,vue-virtual-scroll-list 和 vue-virtual-scroller ;

服务端渲染SSR;

长列表性能优化

1. 将数据分页,利用分页的原理,每次服务器端只返回一定数目的数据,浏览器每次只对一部分进行加载。

2. 使用懒加载的方法,每次加载一部分数据,其余数据当需要使用时再去加载。

3. 虚拟列表,每次只渲染需要视口的部分,可以自己实现也可以使用第三方库,例如vue-virtual-scroll-list 和 vue-virtual-scroller。

节流和防抖

浏览器的resize/scroll等事件在触发时,会不断调用绑定的回调函数,极大地浪费资源,降低前端性能。

节流: n秒内只允许一次,无论在这期间触发多少次,只有一次生效(滚动事件中会发起网络请求,但是我们并不希望用户在滚动过程中一直发起请求,而是隔一段时间发起一次)

实现:可以使用时间戳加定时器实现节流,将当前时间和上一次执行函数的时间对比,如果差值大于设置的等待时间就执行函数。

image.png


防抖: n秒后执行一个事件,如果在n秒内又触发了,则重新计时(有一个按钮点击会触发网络请求,但是我们并不希望每次点击都发起网络请求,而是当用户点击按钮一段时间后没有再次点击的情况才去发起网络请求)

实现:利用定时器实现防抖,设置一个基础定时器,每次调用都清空上一次的定时器,开始新的定时器,延迟执行事件。

image.png

模块化方案

  • 通过书写在不同文件中,使用script标签进行加载

  • CommonJS进行加载(常用于服务端,NodeJS,webpack就使用这种方式,特点是同步的,运行时加载,磁盘读取速度快)

  • AMD(require.js使用这种方式,特点是异步加载,依赖前置,现在不常用)

  • CMD(特点是异步加载,依赖就近,现在不常用)

  • ES6模块(特点是静态编译,目前浏览器端的默认标准)

Js内存

JavaScript引擎具有自动垃圾回收机制,我们需要关注内存泄漏的场景。

例如意外的全局变量、闭包、事件的监听没有移除、缓存等等。

Vue

Mvvm是什么

Mvvm是一种架构设计模式,它通过双向的数据绑定,将 View 和 Model 的同步更新给自动化了。当 Model 发生变化的时候,ViewModel 就会自动更新;ViewModel 变化了,View 也会更新。

Vue双向绑定的原理

vue实现双向绑定的原理主要是采用数据劫持和发布订阅者模式来实现的。通过Object.defineProperty()的API来实现数据响应式,劫持各个属性的setter和getter,在数据变动的时候发布消息给订阅者,触发相应的监听回调。

Vue2针对对象会循环遍历对象中定义的每个属性,都为其设置一个getter和setter;针对数组,会重写一些数组方法,为后续的响应式做好准备,并且对数组中每项数据都会调用observe方法观察。

Vue组件之间的参数传递(组件通信)

组件通信一般分为几种情况,父子通信,兄弟通信,其他通信。

父组件通过props传递数据给子组件,子组件通过$emit发送事件传递数据给父组件。

也可以通过parent或者parent或者children对象来访问组件实例。

兄弟组件可以通过parent.parent.children.name获取相应组件的实例。

其他的可以通过eventbus或者vuex来实现组件通信。

mixins

mixins我们通常用来对组件进行扩展封装,如果多个组件有相同的业务逻辑代码,就可以通过mixins混入代码,比如上拉加载下拉刷新等等。

computed 和 watch 区别

Computed是计算属性,依赖其他属性计算值,并且computed的值有缓存,只有当依赖值变化才会返回内容。

Watch是监听,监听到值的变化就会执行回调。

data为什么是函数

一个组件在多次复用时,如果data是对象的话,就会造成一个组件修改data以后会影响到其他所有组件,所以需要将data写成函数,每次调用会获得新的数据。

在使用new Vue()生成一个根组件的时候,可以写成对象,因为不会复用。

Vue与angular、react的区别

Vue和angular都支持双向绑定,但是实现原理不一样,angular依赖对数据做脏检查来实现双向绑定,vue使用object.defineProperty()对数据进行劫持,通过发布订阅者的模式出发数据和视图的更新。

Vue和react都是基于组件实现的,react所有组件共享一个状态树,vue每个组件都有自己的状态。React使用jsx语法,vue使用模板语法。React使用单向数据流管理数据,vue使用双向绑定来管理数据。React采用virtual dom对渲染出来的结果做脏检查,减少需要操作真实DOM的次数,vue使用模板编译和响应式系统来实现高效渲染性能。

Vuex是什么

Vuex是vue组件之间的状态管理,用来读取的状态放在store中,改变状态的方式是提交mutations,如果是异步操作,应该封装在action中。

keep-alive是什么

keep-alive是 Vue 内置的一个组件,可以缓存组件。

keep-alive包裹动态组件时,会缓存不活动的组件实例,这样在组件切换过程中将状态保留在内存中,防止重复渲染DOM。

结合include和exclude可以指定或排除组件。(vue3现在需要反过来用router-view去包裹keep-alive)

<router-view v-slot="{ Component }">
    <keep-alive>
        <component :is="Componenet"></component>
    </keep-alive>
</router-view>    

原理:

keep-alive内部定义了一个map,缓存创建过的组件实例,它返回的渲染函数内部会查找里面的component组件对应的VNode,如果该组件在map中存在就直接返回它。

Component的is属性是个响应式数据,因此只要他变化,keep-alive的render渲染函数就会重新执行。

Scoped是什么

在style标签中写入scoped,使css只在当前组件中起作用。它的实现原理是,在编译后渲染的样式中加上哈希值,避免污染其他样式。

如果要在scoped下修改子组件的样式,需要使用deep。

V-if和v-show的区别

v-if 按照条件判断是否渲染dom,v-show 是 display 的 block 或 none。

route和router的区别

Route表示当前路由信息对象,包含当前URL路径、参数等等。

Router是vue router的实例对象(层级更高的),包含了控制路由的API,例如push、replace等。

vue中 key 值的作用

在 Vue 中,key 是用于帮助 Vue 识别和跟踪虚拟 DOM 的变化的特殊属性。当 Vue 更新渲染真实 DOM 时,它使用 key 属性来比较新旧节点,并尽可能地复用已存在的真实 DOM 节点,以提高性能。

vue 等单页面应用及其优缺点

优点:Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统。MVVM、数据驱动、组件化、轻量、简洁、高效、快速、模块友好。

缺点:不支持低版本的浏览器,最低只支持到IE9;不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);第一次加载首页耗时相对长一些;不可以使用浏览器的导航按钮需要自行实现前进、后退。

虚拟dom是什么

虚拟dom表示为一个JavaScript 对象,就是以js对象的形式去添加 dom 元素,本质上是优化了diff算法,采用了新旧dom的对比,获取差异的dom,一次性更新到真实dom;它更适合批量级修改dom;循环遍历中设置 key 可以最大的利用节点。

Vue2 push的实现

1.无法检测数组或者对象的新增和删除(api本身限制,vue2通过setset和delete转换为响应式对象)

2.无法检测通过索引改变数组的操作(因为性能问题没有去实现,作者认为性能消耗和带来的用户体验不成正比,通过set方法,劫持对应下标的元素让它变成响应式)

3.无法检测数组长度的变化(api本身限制)

vue2中提供了数组的变异方法(即在原有方法上不变的基础上进行功能扩展)push、pop、shift、unshift、splice、sort、reverse,这些方法定义在原型上,在内部调用了def函数来定义数组的属性或方法,并使用notify()方法触发依赖更新。(数组劫持核心就是重写数组的方法然后重新观测数组里的元素。)

Vue的render函数

在vue中我们使用模板HTML语法组件页面,使用render函数我们可以用js语言来构建dom。因为vue是虚拟dom,所以在拿到template模板时也要转译成VNode的函数,而用render函数构建dom,vue就免去了转译的过程。

Vue的nextTick

vue在更新dom时是异步执行的,当数据发生变化,vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新。如果想要在修改数据之后立刻得到更新后的dom结构,可以使用Vue.nextTick()。

原理:$nextTick()返回一个promise对象,利用事件循环机制,在下次 DOM 更新后执行回调函数,确保回调函数中的操作基于最新的 DOM 状态。

(Vue 采用了基于虚拟 DOM 的异步更新机制,将数据变更和 DOM 更新解耦,以提高性能和渲染效率。Vue 通过响应式数据机制实现了数据的自动追踪和更新。当我们修改 Vue 实例的响应式数据时,Vue 会自动跟踪数据的变化,并在需要的时候触发视图的更新。)

Vue的插槽slot

通常我们在使用一个复用组件时,获取这个组件在不同的地方有少量的更改,这时候我们可以通过slot插槽向组件内部指定位置传递一些模板片段,让子组件在它们的组件中渲染这些片段。

slot可以分为默认插槽、具名插槽、作用域插槽。

slot本质上是返回VNode的函数。

Vue路由中的hash和history的区别

hash:vue默认是hash模式,路径中带有#号,它是利用hashchange事件实现路由切换,优点是兼容性好,缺点是颜值丑;

history:它是利用h5新提供的history API实现的,他的路径没有#号,但是它的兼容性不如hash,并且需要服务端支持,将不存在的路径重定向到入口文件;

Vue2和vue3的区别

Vue3在兼顾vue2的options API的同时还增加了composition API,大大增加了代码的逻辑组织和代码复用能力;

Vue3提供了更好的 TypeScript 支持;

Vue3移除了一些不常用的API,引入了tree-shaking,按需打包,减小打包体积;

Vue3优化了diff算法,减少不必要的更新,提升渲染性能;

Vue3使用proxy实现数据双向绑定,可以监听整个对象; 

Proxy响应式原理

Proxy代理一个对象,它可以重新定义属性的get和set行为。Proxy接受两个参数。第一个参数是所要代理的目标对象,第二个是一个对应的处理函数,该函数将拦截对应的操作。

在get的时候添加依赖,在数据变动的时候发布消息给订阅者,触发相应的监听回调。

动态组件、异步组件

动态组件:通过is属性绑定变量,可以实现切换不同的组件;

异步组件:通过异步的方式动态加载组件,通过使用异步组件,可以分割应用程序的代码,并根据需要按需加载组件,从而提高应用程序的性能;

原理:使用defineAsyncComponent定义异步组件,它返回一个包装了异步加载逻辑的组件选项对象

Webpack

Webpack是一个模块打包工具,它能够很好的管理和打包web开发中所用到的html、JavaScript、css以及各种静态文件,让开发过程更加高效。

对于不同类型的资源,webpack有对应的模块加载器loader,loader的作用就是让webpack拥有了加载和解析非JavaScript文件的能力。

还有plugin可以扩展webpack的功能,让webpack具有更多的灵活性,在webpack运行的生命周期中会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。

实现原理:

webpack打包,最基本的实现方式,是将所有的模块代码放到一个数组里,通过数组ID来引用不同的模块,而webpack引用的时候,主要通过 _webpack_require 的方法引用不同索引的模块。

webpack中的代码压缩和混淆机制:

压缩:删除 Javascript 代码中所有注释、跳格符号、换行符号及无用的空格,缩短变量名称从而压缩 JS 文件大小。并且不同作用域的变量名是可以重复的,类似a,b,c可以反复出现。

混淆:经过编码将变量和函数原命名改为毫无意义的命名,以防止他人窥视和窃取 Javascript 源代码。让我们的代码尽可能的不可读,常见的做法有:分离变量,增加无意义的代码,打乱控制流。

加密:一般使用eval方法,效果和混淆类似。大部分js加密只是对源码进行了字符串的变换,并没有深入到代码语法层面。

loader和插件:

常用Loader包括css-loader、jsx-loader、style-loader、url-loader、file-loader。

常用plugin包括html-webpack-plugin、uglifyjs-webpack-plugin、webpack-parallel-uglify-plugin(多核压缩)、mini-css-extract-plugin(css提取到单独的文件中,支持按需加载)

webpack的构建流程:

1) 初始化参数;

2) 开始编译:初始化compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译;

3) 确定入口:根据配置中的entry找出所有的入口文件;

4) 编译模块;

5) 输出资源,写入到文件系统;

webpack 和 gulp 的区别

1.Gulp侧重于前端开发的 整个过程 的控制管理(像是流水线),更多的是一种工作流。我们可以通过给gulp配置不通的task(通过Gulp中的gulp.task()方法配置,比如启动server、sass/less预编译、文件的合并压缩等等)来让gulp实现不同的功能,从而构建整个前端开发流程。

2.Webpack有人也称之为 模块打包器 ,由此也可以看出Webpack更侧重于模块打包,当然我们可以把开发中的所有资源(图片、js文件、css文件等)都可以看成模块,最初Webpack本身就是为前端JS代码打包而设计的,后来被扩展到其他资源的打包处理。Webpack是通过loader(加载器)和plugins(插件)对资源进行处理的。

浏览器

URL输入到浏览器呈现发生了什么

网络请求加载资源过程:

l 浏览器根据DNS服务器得到域名的ip地址

l 向这个ip的机器发送http请求

l 服务器收到,处理并返回http请求

l 浏览器得到返回内容

浏览器解析和页面渲染过程

l 根据HTML结构生成DOM Tree

l 根据CSS生成CSSOM(Layout Tree)

l 将DOM和CSSOM 整合 形成RenderTree

l 根据RenderTree开始渲染和展示

l 遇到

l 计算元素位置进行页面布局

l 绘制页面,最终在浏览器中呈现

浏览器有哪些对象,怎么获取输入的地址,vue怎么获取输入的地址

浏览器对象:

window,全局作用域,浏览器窗口,window对象有innerWidth和innerHeight属性;

document,表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。

navigator,表示浏览器的信息,最常用的属性包括:

navigator.appName:浏览器名称;

navigator.appVersion:浏览器版本;

navigator.language:浏览器设置的语言;

navigator.platform:操作系统类型;

navigator.userAgent:浏览器设定的User-Agent字符串。

location,表示当前页面的URL信息,可以用 location.href 获取

location.protocol; // 'http'

location.host; // 'www.example.com'

location.port; // '8080'

location.pathname; // '/path/index.html'

location.search; // '?a=1&b=2'

location.hash; // 'TOP'

screen对象表示屏幕的信息,常用的属性有:

screen.width:屏幕宽度,以像素为单位;

screen.height:屏幕高度,以像素为单位;

screen.colorDepth:返回颜色位数,如8、16、24。

history对象保存了浏览器的历史记录,JavaScript可以调用history对象的back()或forward (),相当于用户点击了浏览器的“后退”或“前进”按钮。

vue路径:

完整url可以用 window.location.href

路由路径可以用 this.$route.path

路由路径参数 this.$route.params

浏览器缓存(HTTP缓存策略)

在浏览器中,有以下几种常见的缓存:

1. Service worker缓存:可以拦截网络请求并返回缓存的响应,常用来实现离线访问;

2. 强制缓存:通过设置cache-control和expires等响应头实现,可以让浏览器直接从本地缓存中读取资源而不发起请求;

3. 协商缓存:通过设置last-modified和etag等响应头实现,可以让浏览器发送条件请求,询问资源是否更新,如果服务器返回304(not modified),则直接使用缓存;

4. Web storage缓存:localstorage和sessionstorage存储数据;

这些缓存的优先级如上依次从上到下。

前端跨Tab页面通信

1. Localstorage、indexDB

2. Broadcast channel广播频道

3. SharedWorker

4. Websocket通讯

5. window.postMessage()

对于非同源页面,可以通过嵌入同源iframe作为桥,转换为同源页面通信的方式。

CSS