前端最新场景题,不来看一看吗?(一)

107 阅读6分钟

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.jsdecimal.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);
}