2023年最新的前端面试收集
一、你在项目中做的性能优化的事情有哪些
网络优化
-
DNS预解析
- link标签的rel属性设置dns-prefetch,提前获取域名对应的IP地址
-
使用缓存
-
减轻服务端压力,快速得到数据(强缓存和协商缓存)
-
- 强缓存
- 浏览器在访问某个资源时会判断是否使用本地缓存里已经存在的资源文件,使用本地缓存的话则不会发送请求到服务器,从而达到减轻服务器访问压力的作用,且由于直接从本地缓存读取资源文件,大大提高了加载速度。
- 服务器可以通过在响应头里设置Cache-Control: max-age=31536000,max-age代表缓存时间,单位为秒
- Cache-Control除了max-age外,还可以设置其它属性值: no-cache: 不使用强缓存(但仍会使用协商缓存)。
no-store: 不使用缓存(不使用强缓存也不使用协商缓存),每次都向服务器发送资源请求。
private: 只允许客户端使用缓存,不允许其他代理服务器进行缓存。
public: 客户端和代理服务器都可缓存。
s-maxage: 与max-age类似,区别是s-maxage是设定代理服务器的缓存时间。
- 使用缓存的话,状态码200后面会标明情况。浏览器缓存资源的地方有两个:磁盘缓存(disk cache)和内存缓存(memory cache)。 一般来说,浏览器会将较大的资源缓存到disk cache,而较小的资源则被缓存到memory cache里。内存缓存与磁盘缓存相比,访问速度要更快一些!
- 强缓存除了使用Cache-Control实现之外,还可以使用Expires字段,缺点是依赖本地时间,可以被篡改,Expires是Http1.0规范,Cache-Control是Http1.1规范,
- Cache-control的优先级要高于Expires,如果两者同时设置,会优先使用Cache-control而忽略掉Expires。
-
- 协商缓存
- 在强缓存里,是否使用缓存是由浏览器来确定的,而协商缓存则是由服务器来告诉浏览器是否使用缓存资源,也就是浏览器每一次都要发送请求到服务器询问是否使用缓存
- 浏览器初次请求资源,服务器返回资源,同时生成一个Etag值携带在响应头里返回给浏览器,当浏览器再次请求资源时会在请求头里携带If-None-Match,值是之前服务器返回的Etag的值,服务器收到之后拿该值与资源文件最新的Etag值做对比。
- 如果没有变化则返回304,告诉浏览器继续使用缓存(不返回资源文件)。
- 如果发生变化,则返回200和最新的资源文件给浏览器使用。
- 除了Etag外,还有一个Last-Modified的属性,是一个时间值,它是Http1.0规范的,服务器返回Last-Modified,浏览器请求头对应携带的是If-Modified-since。
- 相比Last-Modified,Etag优先级更高,使用上也更精确一些,因为有时候会存在文件内容并没有改变,但文件的修改时间变更了,Last-Modified不一致所以服务器会重新返回资源文件,实际上还是可以继续使用缓存的。
- 总结
- 强缓存优先级大于协商缓存,即两者同时存在时,如果强缓存开启且在有效期内,则不会走协商缓存。
-强缓存就是浏览器本地根据服务器设置的过期时间来判断是否使用缓存,未过期则从本地缓存里拿资源,已过期则重新请求服务器获取最新资源。 - 协商缓存则是浏览器本地每次都向服务器发起请求,由服务器来告诉浏览器是从缓存里拿资源还是返回最新资源给浏览器使用。
- 强缓存优先级大于协商缓存,即两者同时存在时,如果强缓存开启且在有效期内,则不会走协商缓存。
- 协商缓存
-
-
使用 CDN(内容分发网络)
- 用户与服务器的物理距离对响应时间也有影响。
- 内容分发网络(CDN)是一组分散在不同地理位置的 web 服务器,用来给用户更高效地发送内容。典型地,选择用来发送内容的服务器是基于网络距离的衡量标准的。例如:选跳数(hop)最少的或者响应时间最快的服务器。
-
压缩响应
- 压缩组件通过减少 HTTP 请求产生的响应包的大小,从而降低传输时间的方式来提高性能。从 HTTP1.1 开始,Web 客户端可以通过 HTTP 请求中的 Accept-Encoding 头来标识对压缩的支持(这个请求头会列出一系列的压缩方法)
- 如果 Web 服务器看到请求中的这个头,就会使用客户端列出的方法中的一种来压缩响应。Web 服务器通过响应中的 Content-Encoding 头来告知 Web 客户端使用哪种方法进行的压缩
- 目前许多网站通常会压缩 HTML 文档,脚本和样式表的压缩也是值得的(包括 XML 和 JSON 在内的任何文本响应理论上都值得被压缩)。但是,图片和 PDF 文件不应该被压缩,因为它们本来已经被压缩了。
-
使用多个域名
- Chrome 等现代化浏览器,都会有同域名限制并发下载数的情况,不同的浏览器及版本都不一样,使用不同的域名可以最大化下载线程,但注意保持在 2~4 个域名内,以避免 DNS 查询损耗。
-
避免图片src为空
- 虽然 src 属性为空字符串,但浏览器仍然会向服务器发起一个 HTTP 请求:
- IE 向页面所在的目录发送请求;Safari、Chrome、Firefox 向页面本身发送请求;Opera 不执行任何操作。
页面渲染优化
Webkit 渲染引擎渲染流程:
处理 HTML 并构建 DOM 树
处理 CSS 构建 CSS 规则树(CSSOM)
DOM Tree 和 CSSOM Tree 合成一棵渲染树 Render Tree。
根据渲染树来布局,计算每个节点的位置
调用 GPU 绘制,合成图层,显示在屏幕上
-
避免css阻塞
- css影响renderTree的构建,会阻塞页面的渲染,因此应该尽早(将 CSS 放在 head 标签里)和尽快(启用 CDN 实现静态资源加载速度的优化)的将css资源加载
-
降低css选择器的复杂度
浏览器读取选择器,遵循的原则是从选择器的右边到左边读取。
- 减少嵌套:最多不要超过三层,并且后代选择器的开销较高,慎重使用
- 避免使用通配符,对用到的元素进行匹配即可
- 利用继承,避免重复匹配和定义
- 正确使用类选择器和id选择器
-
避免使用CSS 表达式
- css 表达式会被频繁地计算。
-
避免js阻塞
- js可以修改CSSOM和DOM,因此js会阻塞页面的解析和渲染,并且会等待css资源的加载。也就是说js会抢走渲染引擎的控制权。所以我们需要给js资源添加defer或者async,延迟js脚本的执行。
-
使用外链式的js和css
- 在现实环境中使用外部文件通常会产生较快的页面,因为 JavaScript 和 CSS 有机会被浏览器缓存起来。对于内联的情况,由于 HTML 文档通常不会被配置为可以进行缓存的,所以每次请求 HTML 文档都要下载 JavaScript 和 CSS。所以,如果 JavaScript 和 CSS 在外部文件中,浏览器可以缓存它们,HTML 文档的大小会被减少而不必增加 HTTP 请求数量。
-
使用字体图标 iconfont 代替图片图标
- 图片会增加网络请求次数,从而拖慢页面加载时间
- iconfont可以很好的缩放并且不会添加额外的请求
-
首屏加载优化
- 使用骨架屏或者动画优化用户体验
- 资源按需加载,首页不需要的资源延迟加载
-
减少重绘和回流
- 增加多个节点使用documentFragment:不是真实dom的部分,不会引起重绘和回流
- 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局);opacity 代替 visiability,visiability会触发重绘(paint),但opacity不会。
- 把 DOM 离线后修改,比如:先把 DOM 给 display:none (有一次 Reflow),然后你修改 100 次,然后再把它显示出来
JS优化
- 使用事件委托
- 防抖和节流
- 尽量不要使用JS动画
- css3动画和canvas动画都比JS动画性能好
- 多线程
- 复杂的计算开启webWorker进行计算,避免页面假死
- 计算结果缓存
- 减少运算次数,比如vue中的computed
图片优化
- 雪碧图
- 借助减少http请求次数来进行优化
- 图片懒加载
- 在图片即将进入可视区域的时候进行加载(判断图片进入可视区域)
- IntersectionObserver 的API
- 在图片即将进入可视区域的时候进行加载(判断图片进入可视区域)
javascript
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// console.log('元素进入视口');
} else {
// console.log('元素离开视口');
}
});
});
// 添加监听,targetElement监听的目标元素
observer.observe(targetElement);
// 要取消观察的目标
IntersectionObserver.unobserve(targetElement);
- 使用CSS3代替图片
- 有很多图片使用 CSS 效果(渐变、阴影等)就能画出来,这种情况选择 CSS3 效果更好
- 图片压缩
- 压缩方法有两种,
-
- 通过在线网站进行压缩
-
- 通过 webpack 插件 image-webpack-loader。它是基于 imagemin 这个 Node 库来实现图片压缩的。
- 使用渐进式jpeg
- 使用渐进式jpeg,会提高用户体验 参考文章
- 使用 webp 格式的图片
- webp 是一种新的图片文件格式,它提供了有损压缩和无损压缩两种方式。在相同图片质量下,webp 的体积比 png 和 jpg 更小。
webpack打包优化
-
缩小loader 匹配范围
-
优化loader配置
-
test、include、exclude三个配置项来缩⼩loader的处理范围
-
推荐include
include: path.resolve(__dirname, "./src"),
-
-
抽离css
- 借助mini-css-extract-plugin:本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载
-
代码压缩
- JS代码压缩
- mode:production,使用的是terser-webpack-plugin
- CSS代码压缩
- css-minimizer-webpack-plugin
- Html文件代码压缩
- html-webpack-plugin和html-minifier-terser
- JS代码压缩
-
文件大小压缩
- 对文件的大小进行压缩,减少http传输过程中宽带的损耗
-
图片压缩
- 一般来说在打包之后,一些图片文件的大小是远远要比 js 或者 css 文件要来的大,所以图片压缩较为重要
javascript
module: {
rules: [
{
test: /.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/',
}
},
{
loader: 'image-webpack-loader',
options: {
// 压缩 jpeg 的配置
mozjpeg: {
progressive: true,
quality: 65
},
// 使用 imagemin**-optipng 压缩 png,enable: false 为关闭
optipng: {
enabled: false,
},
// 使用 imagemin-pngquant 压缩 png
pngquant: {
quality: '65-90',
speed: 4
},
// 压缩 gif 的配置
gifsicle: {
interlaced: false,
},
// 开启 webp,会把 jpg 和 png 图片压缩为 webp 格式
webp: {
quality: 75
}
}
}
]
},
]
}
- Tree shaking 去除死代码
- Tree Shaking 是一个术语,在计算机中表示消除死代码,依赖于ES Module的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系)
- 代码分离
- 将代码分离到不同的bundle中,之后我们可以按需加载,或者并行加载这些文件
- 默认情况下,所有的JavaScript代码(业务代码、第三方依赖、暂时没有用到的模块)在首页全部都加载,就会影响首页的加载速度
- 代码分离可以分出更小的bundle,以及控制资源加载优先级,提供代码的加载性能
- 这里通过splitChunksPlugin来实现,该插件webpack已经默认安装和集成,只需要配置即可
- 默认配置中,chunks仅仅针对于异步(async)请求,我们可以设置为initial或者all
splitChunks主要属性有如下:
javascript
module.exports = {
...
optimization:{
splitChunks:{
chunks:"all"
}
}
}
配置如下:
Chunks,对同步代码还是异步代码进行处理
minSize: 拆分包的大小, 至少为minSize,如何包的大小不超过minSize,这个包不会拆分
maxSize: 将大于maxSize的包,拆分为不小于minSize的包
minChunks:被引入的次数,默认是1
- 多线程打包提升打包速度
- happypack
javascript
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'happypack/loader',
options: { id: 'js' }
}
}
]
},
plugins: [
new HappyPack({
id: 'js',
threadPool: happyThreadPool,
loaders: ['babel-loader']
})
]
};
- thread-loader
vue优化
- v-for添加key
- 路由懒加载
-
- import引入
- 第三方插件按需引入
- 合理使用computed和watch
- 缓存依赖
- 实时监听
- v-for的同时避免使用v-if
- destory时销毁事件:比如addEventListener添加的事件、setTimeout、setInterval、bus.$on绑定的监听事件等
react优化
- map循环展示添加key
- 路由懒加载
- 第三方插件按需引入
- 使用scu,memo或者pureComponent避免不必要的渲染
- 合理使用useMemo、memo、useCallback 他们三个的应用场景都是缓存结果,当依赖值没有改变时避免不必要的计算或者渲染。
- useCallback 是针对函数进行"记忆"的,当它依赖项没有发生改变时,那么该函数的引用并不会随着组件的刷新而被重新赋值。当我们觉得一个函数不需要随着组件的更新而更新引用地址的时候,我们就可以使用 useCallback 去修饰它。
- React.memo 是对组件进行 "记忆",当它接收的 props 没有发生改变的时候,那么它将返回上次渲染的结果,不会重新执行函数返回新的渲染结果。
- React.useMemo是针对 值计算 的一种"记忆",当依赖项没有发生改变时,那么无需再去计算,直接使用之前的值,对于组件而言,这带来的一个好处就是,可以减少一些计算,避免一些多余的渲染。当我们遇到一些数据需要在组件内部进行计算的时候,可以考虑一下 React.useMemo
二、webworker中为什么能提升js执行的性能
Web Worker 为 JavaScript 创造了多线程环境,允许 JS 主线程创建 Worker 子线程,将一些任务分配给后者运行。这样做就能充分发挥多核 CPU 主机的优势,让两个线程并行执行
独立线程:每个 Web Worker 运行在自己的线程中,拥有独立的 JavaScript 执行环境,不会阻塞主线程。
消息传递:主线程和 Web Worker 之间通过消息传递进行通信。主线程可以向 Web Worker 发送消息,Web Worker 可以向 主线程发送消息。这种通信是异步的,不会阻塞任何线程。
无 DOM 访问:Web Workers 无法直接访问主线程中的 DOM 元素,因为它们在不同的上下文中运行。
-
使用场景:
- 图像处理和滤镜应用
- 数据分析和计算
- 实时通信和聊天应用
- 大规模数据可视化
- 异步加载资源,如脚本和样式表
-
在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅, Worker 线程会在计算任务完成时,通过特定的通信方式将结果返回给主线程。
-
注意: Worker 子线程是浏览器开的,完全受主线程控制。即 Web Worker 是浏览器提供的,JS 并不提供,JS 还是单线程的
javascript
// 在主线程和myworker内都是通过postMessage()方法发送数据,监听message事件接收数据
// 主线程内
var myWorker = new Worker('work.js');
myWorker.onmessage = function (event) { // 接收来自子线程内部的数据
console.log('Received message ' + event.data);
doSomething();
}
const dataToProcess = [1, 2, 3, 4, 5];
myWorker.postMessage(dataToProcess); // 向`myworker`内部发送数据
// myWorker文件内容,子线程
addEventListener('message', function (e) { // 接收来自主线程数据
const result = processData(e.data); // 调用方法,移交处理数据的逻辑
postMessage(result); // 将处理的好结果返回给主线程
}, false); // false指定事件句柄在冒泡阶段执行
function processData(data) {
// 处理数据的逻辑
return data.map(item => item * 2);
}
微前端为什么选择qiankun框架
-
成熟稳定:qiankun 是由 Ant Group(蚂蚁金服)团队开发和维护的微前端框架,已经在蚂蚁金服内部和众多大型项目中广泛应用并经过验证。它经过了长期的发展和迭代,具有较高的稳定性和可靠性。
-
功能丰富:qiankun 提供了完整的微前端解决方案,包括应用的注册、加载、通信、生命周期管理等功能。它具有独立运行、集成部署、按需加载等特性,可以满足复杂的微前端架构需求。
-
灵活性:qiankun 支持多种前端框架(如 React、Vue、Angular)的应用集成,不限制开发团队使用的具体技术栈。它提供了统一的应用接入和管理方式,使得不同团队开发的应用可以无缝集成,实现共享组件和状态管理等。
-
性能优化:qiankun 在应用加载和通信方面进行了优化,采用了基于浏览器标准的沙箱隔离机制,避免了应用之间的冲突和影响。它支持应用的按需加载,减少了初始加载时的资源开销,提高了整体性能和用户体验。
-
社区支持和生态系统:qiankun 拥有活跃的社区和广泛的生态系统,有大量的文档、教程和示例可供参考。它也得到了开源社区的认可和贡献,可以获得及时的技术支持和问题解答。