1. 前端如何实现截图?
第一个方法:shift+win+s 浏览器自带 ctrl+shift +s😎
第二个方法:利用canvas:modern-screenshot库即可。*(有一说一 这个截图实现还是坑)
// html
<div class="container">
<tw-el-tooltip content="生成图片" placement="top" hide-after="0">
<div class="config-button" @click="generateImage">
<el-icon
:config="common_system_picture"
color="#898A8C"
></el-icon>
</div>
</tw-el-tooltip>
</div>
// js
import { domToJpeg } from 'modern-screenshot';
const generateImage = async () => {
// 获取要生成图片的dom节点
const node = document.getElementById('service-analyzer-main');
if (!node) {
return;
}
try {
const dataUrl = await domToJpeg(node, {
// 传入配置
scale: 2,
});
// 通过a标签自动下载图片
const a = document.createElement('a');
a.href = dataUrl;
a.download = route.path.split('/').at(-1) + '.jpg';
a.click();
a.remove();
ElMessage.success('图片生成成功,请耐心等待下载');
} catch (error) {
ElMessage.error('图片生成失败');
}
};
2. 当QPS到达峰值,如何处理?
扩容😎 其实这个问题感觉是问优化了一样,又不是。😂
优化资源:
- 压缩和合并CSS、JavaScript文件,减少HTTP请求。
- 优化图片大小和格式,使用现代格式如WebP。
使用CDN:
- 通过内容分发网络(CDN)来分发静态资源,减少服务器负载并加速资源加载。
缓存策略:
- 利用浏览器缓存和HTTP缓存头,减少重复请求。
- 使用Service Workers来实现自定义的缓存策略。
代码分割:
- 使用代码分割技术,如Webpack的动态import(),按需加载代码。
懒加载:
- 对非首屏资源使用懒加载,延迟加载非关键资源。
优化API调用:
- 减少API请求次数,合并多个请求为一个。
- 使用GraphQL代替RESTful API,减少数据传输量。
服务端优化:
- 确保后端服务能够处理高并发请求。
- 使用负载均衡来分散请求到多个服务器。
限流:
- 对用户请求进行限流,防止过多的请求同时到达服务器。
降级服务:
- 在高负载情况下,提供简化版本的服务或功能。
监控和告警:
- 实现监控系统来跟踪QPS和响应时间,设置告警阈值。
使用Web性能API:
- 使用Navigation Timing API或Resource Timing API来分析页面性能瓶颈。
优化数据库查询:
- 优化数据库查询,使用索引,避免复杂的联表查询。
使用缓存数据库:
- 使用Redis或Memcached等缓存数据库来存储热点数据。
异步处理:
- 对于耗时的后端任务,使用异步处理来提高响应速度。
前端性能优化:
- 优化JavaScript执行,避免长时间运行的脚本阻塞UI线程。
使用HTTP/2:
- 利用HTTP/2的多路复用特性,提高资源加载效率。
自动化扩容:
- 实现自动扩容策略,根据负载动态增加资源。
前端错误处理:
- 增强前端错误处理和重试机制,优雅地处理失败情况。
优化DOM操作:
- 减少DOM操作和重绘重排,使用虚拟DOM等技术
3. JS超过Number最大值的数怎么处理?
首先我们得知道:JavaScript中能精确表示的最大整数:即2^53 - 1,或9007199254740991。
考虑是否第三方库:使用如big.js或decimal.js这样的第三方库来处理大数运算。
或者是:将数值转换为字符串,以避免精度问题。这在需要进行数值比较时特别有用。
4. 如何使用一个链接,pc打开是web网站,手机打开是H5网页?
这个想法就很多了。
使用JavaScript检测用户代理(User Agent) : 在服务器端或客户端使用JavaScript来检测访问设备的类型,然后根据设备类型重定向到不同的URL。
```
if (/Mobi|Android/i.test(navigator.userAgent)) {
// 重定向到H5页面
window.location.href = 'https://m.example.com';
} else {
// 重定向到PC网站
window.location.href = 'https://www.example.com';
}
```
使用服务器端重定向: 在服务器端根据HTTP请求中的User-Agent头部来判断访问设备的类型,并进行相应的重定向。
使用响应式设计: 创建一个响应式网站,使用CSS媒体查询来调整不同屏幕尺寸下的布局和样式,这样同一个网站可以在PC和手机上都提供良好的用户体验。
还有其他的就不多提了,相信屏幕面前的大佬有更多好的方法。😉
5.如何保证用户的使用体验?
保证前端用户的使用体验涉及到多个方面,包括性能、可访问性、设计、可用性和维护性。
确保页面加载速度快,优化首屏加载时间。
使网站在各种设备和屏幕尺寸上都能正常显示。
提供直观、一致的用户界面和交互元素。使用语义化的HTML和ARIA属性。
设计清晰的导航结构,帮助用户快速找到所需内容。使用易于阅读的颜色对比和字体大小。
优雅地处理错误,并提供用户友好的错误信息。保护用户数据,使用HTTPS、数据加密和安全的输入处理。
确保网站对搜索引擎友好,提高网站的可见性。定期更新应用,修复已知问题并改进功能。
利用现代前端框架和库来提高开发效率和应用性能。
太多了 ,单个条目涉及的东西就很多🤣
6.如何解决页面请求接口大规模并发的问题?
PS:浏览器发起的请求最大并发数量一般都是6~8个。
说的文艺点:
请求合并:减少单个页面的请求次数,通过合并请求
前端缓存:使用浏览器缓存或Service Workers缓存数据,减少重复请求。
懒加载:对非首屏资源使用懒加载,延迟加载非关键资源。
代码分割:使用代码分割按需加载资源,减少初始加载时间
并发控制:前端控制并发请求的数量,避免同时发送大量请求。
预加载:对即将需要的资源使用预加载,提前请求数据。
使用WebSocket:对于需要实时通信的应用,使用WebSocket减少HTTP请求。
前端错误处理:增强前端错误处理和重试机制,优雅地处理失败情况
使用CDN,HTTP/2: 通过CDN分发静态资源,减少服务器负载并加速资源加载。利用HTTP/2的多路复用特性,提高资源加载效率。
性能监控:监控应用性能,及时发现并解决问题。
日志分析:分析日志来理解请求模式和性能瓶颈。
一般来说 ,需要的话使用Promise.allSettled就可以了。
** p-limit** 并发限制库 直接用啦🤣
7.设计一套全站请求耗时统计工具?
enmm,这个问题对于我是不是太残忍了😒。
获取请求耗时的方法:performance.getEntriesByType("navigation")
PerformanceObsever 监听页面动态资源
const observer = new PerformanceObserver((list, observer) => {
list.getEntries().forEach((entry) => {
console.log(`name : ${entry.name}`);
console.log(`type : ${entry.entryType}`);
console.log(`duration: ${entry.duration}`);
});
});
observer.observe({
entryTypes: ["resource"],
});
const xhrRequest = (method, url) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.send();
});
};
xhrRequest("get", "http://demo");
fetch("http://demo", {
method: "get",
});
屏幕面前的大佬求放过!🤔
8.大文件上传问题
只能说逃不掉的。
逻辑:
- 将大文件分割成多个文件块
- 逐个上传文件块
- 服务端将文件块顺序合并成完整文件
前端:
1.1 切文件(前端)
1.2 判定切片是否完成上传完成(前端)
- 客户端记录切片的上传状态,只需要上传未成功的切片
1.3 断点、错误续传(前端)
- 客户端上传文件时,记录已上传的切片位置
- 下次上传时,根据记录的位置,继续上传
切片方法:
// 使用单独常量保存预设切片大小 1MB
const chunkSize = 1024 * 1024 * 1;
// 文件切片
const createChunks = (file) => {
// 接受一个文件对象,要把这个文件对象切片,返回一个切片数组
const chunks = [];
// 文件大小.slice(开始位置,结束位置)
let start = 0;
let index = 0;
while (start < file.size) {
let curChunk = file.slice(start, start + chunkSize);
chunks.push({
file: curChunk,
uploaded: false,
chunkIndex: index,
});
index++;
start += chunkSize;
}
return chunks;
}
创建hash值:
const getHash = (file) => {
return new Promise((resolve) => {
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
fileReader.onload = function (e) {
let fileMd5 = SparkMD5.ArrayBuffer.hash(e.target.result);
resolve(fileMd5);
}
});
}
上传切片:
// 批量上传切片
const uploadChunks = (chunks, maxRequest = 6) => {
return new Promise((resolve, reject) => {
if (chunks.length == 0) {
resolve([]);
}
let requestSliceArr = []
let start = 0;
while (start < chunks.length) {
requestSliceArr.push(chunks.slice(start, start + maxRequest))
start += maxRequest;
}
let index = 0;
let requestReaults = [];
let requestErrReaults = [];
const request = async () => {
if (index > requestSliceArr.length - 1) {
resolve(requestReaults)
return;
}
let sliceChunks = requestSliceArr[index];
Promise.all(
sliceChunks.map(chunk => uploadHandler(chunk))
).then((res) => {
requestReaults.push(...(Array.isArray(res) ? res : []))
index++;
request()
}).catch((err) => {
requestErrReaults.push(...(Array.isArray(err) ? err : []))
reject(requestErrReaults)
})
}
request()
})
}
上传操作:
// 文件上传
const uploadFile = async (file) => {
// 设置文件名
fileName = file.name;
// 获取文件hash值
fileHash = await getHash(file);
// 获取切片
chunks = createChunks(file);
try {
await uploadChunks(chunks)
} catch (err) {
return {
mag: "文件上传错误",
success: false
}
}
}
9.H5如何解决移动端适配问题?
这个一般用rem或者是媒体查询 ,post-to-css应该也可以。 haha 不过建议用: lib-flexible 库
eg:
/* 2x 像素密度 */
@media screen and (-webkit-device-pixel-ratio:2) {
/* 样式代码 */
}
/* 3x 像素密度 */
@media screen and (-webkit-device-pixel-ratio:3) {
/* 样式代码 */
}
/* 4x 像素密度 */
@media screen and (-webkit-device-pixel-ratio:4) {
/* 样式代码 */
}
webview也需要处理。WebView的表现形式是在系统中通过应用程序包(APK)来提供的。不同版本的Android设备或者不同的ROM版本,其内附带的WebView的版本也不同,
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no,viewport-fit=cover">
<script>
if (/Android [\S\s]+AppleWebkit\/(\d{3})/.test(navigator.userAgent)) { // 特性检测
var scale = parseInt(window.screen.width) / 640; // 计算缩放比例
document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=640,initial-scale=' + scale + ',maximum-scale=' + scale + ',user-scalable=no,viewport-fit=cover'); // 动态设置viewport属性
</script>
默认字体:
(function(){
if (typeof window.orientation != 'undefined') {
var fontSize = 16 * window.innerWidth / 375;
document.documentElement.style.fontSize = fontSize + 'px';
}
})();
注意:以上代码中375是设计稿宽度,16是字体基准大小
IOS同理:
/* iPhone 4/4S */
@media only screen and (min-device-width: 320px) and (max-device-height: 480px) {
/* 样式代码 */
}
/* iPhone 5/5S/5C/SE */
@media only screen and (min-device-width: 320px) and (max-device-height: 568px) {
/* 样式代码 */
}
/* iPhone 6/6S/7/8 */
@media only screen and (min-device-width: 375px) and (max-device-height: 667px) {
/* 样式代码 */
}
/* iPhone 6 Plus/6S Plus/7 Plus/8 Plus */
@media only screen and (min-device-width: 414px) and (max-device-height: 736px) {
/* 样式代码 */
}
/* iPhone X/XS/11 Pro */
@media only screen and (min-device-width: 375px) and (max-device-height: 812px) {
/* 样式代码 */
}
/* iPhone XR/11 */
@media only screen and (min-device-width: 414px) and (max-device-height: 896px) and (-webkit-device-pixel-ratio: 2) {
/* 样式代码 */
}
/* iPhone XS Max/11 Pro Max */
@media only screen and (min-device-width: 414px) and (max-device-height: 896px) and (-webkit-device-pixel-ratio: 3) {
/* 样式代码 */
}
webVIew:
if (/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(navigator.userAgent)) {
/* iOS版本 WebView */
var version = window.navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
var majorVersion = parseInt(version[1], 10);
if (majorVersion >= 10) {
/* iOS 10或以上的WebView */
/* 样式代码 */
} else if (majorVersion >= 8) {
/* iOS 8或以上,iOS 10以下的WebView */
/* 样式代码 */
} else {
/* iOS 8以下的WebView */
/* 样式代码 */
}
}
10.站点一键换肤方案
这个一般用 sass变量配置,css变量+sass变量+data-theme,滤镜filter(),grayscale() 改变图像灰度.
eg:
$--color-primary-bold: #1846D1 !default;
$--color-primary: #2664FD !default;
$--color-primary-light: #4D85FD !default;
$--color-primary-light-1: #9AC0FE !default;
$--color-primary-light-2: #C1DBFF !default;
$--color-primary-lighter: #E8F2FF !default;
// theme-default.scss
/* 默认主题色-合作蓝色 */
[data-theme=default] {
--color-primary: #516BD9;
--color-primary-bold: #3347B6;
--color-primary-light: #6C85E1;
--color-primary-light-1: #C7D6F7;
--color-primary-light-2: #c2d6ff;
--color-primary-lighter: #EFF4FE;
--main-background: linear-gradient(90deg,#4e68d7, #768ff3);
--user-info-content-background-image: url('../../assets/main/top-user-info-bg.png');
--msg-tip-content-background-image: url('../../assets/main/top-user-info-bg.png');
...
}
// theme-orange.scss
// 阳光黄
[data-theme=orange] {
--color-primary: #FF7335;
--color-primary-bold: #fe9d2e;
--color-primary-light: #FECB5D;
--color-primary-light-1: #FFDE8B;
--color-primary-light-2: #fcdaba;
--color-primary-lighter: #FFF3E8;
--main-background: linear-gradient(90deg,#ff7335 2%, #ffa148 100%);
--user-info-content-background-image: url('../../assets/main/top-user-info-bg-1.png');
--msg-tip-content-background-image: url('../../assets/main/top-user-info-bg-1.png');
...
}
- 把主题色的变量作为基础库的变量
$--color-primary-bold: var(--color-primary-bold) !default;
$--color-primary: var(--color-primary) !default;
$--color-primary-light: var(--color-primary-light) !default;
$--color-primary-light-1: var(--color-primary-light-1) !default;
$--color-primary-light-2: var(--color-primary-light-2) !default;
$--color-primary-lighter: var(--color-primary-lighter) !default;
- App.vue指定默认主题色window.document.documentElement.setAttribute('data-theme', 'default')
body {
filter: hue-rotate(45deg);
}
body {
filter: grayscale(1);
}