1. 页面访问、渲染速度从哪几个方向优化?
- 异步加载路由(按需加载、懒加载)
- 异步加载组件、插件(模块按功能做最小化拆分便于异步加载和复用)
- 延迟加载
<Image/>资源 - 尽量减少 DOM 访问和操作,减少重绘和回流
- 尽量使用防抖 debounce 和节流 Throttle
- 资源压缩,css js img 压缩体积
- 启用缓存,对不常变动的静态资源进行缓存,强缓存 catch-control,协商缓存 etag
- SSR / SSG / 预渲染
prerander-spa-plugin - DNS 解析慢,开启 DNS 预解析。
<link rel="dns-prefetch" href="//example.com"> - 插件可以使用 CDN 引入,减少打包体积,提高访问速度
- webpack配置优化
- 区分dev和prod配置,避免prod引用无效得插件
- 使用SplitChunksPlugin,cacheGroups配置缓存组,提升打包速度
- 自动拆分chunks,抽离公用代码和第三方插件
node_modules内得插件单独拆到Vendors.chunk,新的chunk可以被共享- 自己写的 utils 工具函数根据引用次数minChunks配置,拆到单独chunk内
- enforceSizeThreshold 设置拆分的体积阈值,超过阈值拆到单独chunk内
- minSize 生成 chunk 的最小体积,避免生成过小的chunk文件
- extract-text-webpack-plugin,把包含CSS的JS文件提取单独CSS文件,支持CSS和SourceMaps按需加载,异步加载,没有重复的编译(性能)
- mini-css-extract-plugin,抽离css及css压缩配置
- terser-webpack-plugin,js压缩
- image-minimizer-webpack-plugin,图片压缩(可选无损压缩和有损压缩)
- webpack@4xx (url-loader) ;webpack@5xx,type:"asset"配置parser.dataUrlCondition.maxSize阀值,超出阀值,自动转为data-url,base64格式。
prerander-spa-plugin,配置页面预渲染,设置单独entry出口文件,提升某个页面的到达时间,利于SEO- 过滤注释,过滤console.log
2. Image 加载的性能优化
- 图片压缩:压缩图片体积
- 图片格式转化:转为base64格式
- 使用矢量图:svg、webP格式
- 减少图片请求:使用雪碧图,减少请求次数
- CDN加速:使用CDN服务加速资源加载
- 延迟加载:只加载在视图内的图片,img 设置loading="lazy"属性
// 设置 loading="lazy" 属性, 在视图内才会加载。兼容问题需要用第三方处理 // srcset 根据不同的屏幕分辨率或设备像素比(DPR)来提供不同尺寸的图片资源 <img src="懒加载图片,默认图.jpg" srcset="小图.jpgg 300w, 默认图.jpg 600w, 大图.jpg 1000w" sizes="(max-width: 600px) 100vw, 50vw" alt="懒加载图片。支持响应式" loading="lazy" /> - 先加载缩略图,再加载高清图
<image src="缩略图.url" data-src="高清图.url" loading="lazy" class="lazy-load" /> <script> document.addEventListener("DOMContentLoaded", function() { // 拿到所有需要高清图需要懒加载的img var lazyImages = [].slice.call(document.querySelectorAll("img.lazy-load")); if ("IntersectionObserver" in window) { // 当图片处于视图窗口内时去处理高清图加载 let lazyImageObserver = new IntersectionObserver(function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { let lazyImage = entry.target; lazyImage.src = lazyImage.dataset.src; lazyImage.classList.remove("lazy-load"); lazyImageObserver.unobserve(lazyImage); } }); }); lazyImages.forEach(function(lazyImage) { lazyImageObserver.observe(lazyImage); }); } else { // 浏览器若不支持IntersectionObserver,则用源src lazyImages.forEach(function(lazyImage) { lazyImage.src = lazyImage.dataset.src; }); } // JavaScript中为加载完成的图片添加`loaded`类 lazyImage.onload = function() { lazyImage.classList.add('loaded'); }; }); </script> // 通过CSS技巧来进一步优化体验,为图片添加平滑过渡效果 <style> img.lazy-load { transition: opacity 0.3s ease-in-out; } img { opacity: 0; will-change: opacity; } img.loaded { opacity: 1; } </style>
3. Web 应用中css js img 加载失败处理
1. JS 加载失败的处理
<script src="js文件.js" onerror="function() { loadScript('备用脚本文件.js') }"></script>
<script>
function loadScript (url) {
const jsflie = document.createElement('script');
jsflie.src = url; // 备用js文件url
document.head.appendChild(jsflie);
};
</script>
2. img 加载失败的处理
<img src="图片地址.jpg"
onerror="this.onerror=null; this.src='备用图片地址.jpg / 默认图片地.jpg';"
alt="图片加载失败提示语"
>
3. CSS 加载失败的处理
- 动态加载css
<link>标签不支持onerror,通过JS动态创建<link>时,可以设置onerror事件监听。const loadCss = (url, errorFn) => { var link = document.createElement('link'); link.type = 'text/css'; link.rel = 'stylesheet'; link.href = url; // 尝试捕捉加载错误 link.onerror = function() { errorFn && errorFn(); }; document.head.appendChild(link); } loadCss('/css地址.css', () => { loadCss('/css备用地址.css') }) - 备用样式
<!-- 默认基础样式 --> <style> body { font-family: Arial, sans-serif; color: #333; background-color: #fff; } </style> <!-- 主样式表 --> <link rel="stylesheet" href="styles.css">
4. SEO 优化如何处理?
SEO(Search Engine Optimization,搜索引擎优化)是提升网站在搜索引擎中排名、增加自然流量。
- SSR 技术层面优化
- 服务端渲染 SSR(如 Next.js / Nuxt.js)
- 预渲染(Prerendering) SPA
- SSG
- 准确的TDK使用:title description keywords
<title>SEO优化指南</title> <meta name="description"> <meta name="keywords" content="SEO优化, 关键词优化, 网站推广, 搜索引擎优化"> - 合理使用语语义化标签
Header / Nav / ASide / Footer / H1 / H2 便于搜索引擎识别,如: <ASide>侧导航</ASide> <H1>SEO标题</H1> <img src="seo-guide.jpg" alt="SEO优化步骤示意图"> - 链接优化
内部链接使用有意义的锚文本,如: <a href="/blog/seo-basics">SEO基础知识</a> 外部链接建议使用 `rel="nofollow"`(如果不想传递权重) <a href="https://example.com" rel="nofollow">第三方资源</a> - 设置robots.txt文件
- 指引搜索引擎更高效地抓取网站内容
- 避免不希望被收录的内容(如后台、测试页面)
- 控制不同搜索引擎的行为(Google、Bing、百度等)
- 响应式的代码也会提升SEO权重
5. 大文件上传方案,切片上传
- 大文件上传时常见问题
- 上传失败率高(网络中断、超时)
- 占用大量内存和带宽
- 无法断点续传
- 切片上传方案流程
- 选择文件,计算文件 Hash(生成
MD5或UUID用于唯一标识文件或断点续传) - 使用
File.slice()将文件切片(每个Chunk大小5MB),每个chunk生成唯一标识 - 并发上传切片,比如设置并发数量为5个
- 记录上传状态,使用
localStorage记录哪些切片已上传 - 上传完成,通知后端合并文件
- 选择文件,计算文件 Hash(生成
- 功能优势
- 断点续传 : 刷新页面后继续传未上传的切片
- 秒传 : 上传前先校验文件 Hash,已存在就跳过
- 进度条 : 显示已上传切片数量 / 总切片数
- 自动重试 : 切片失败后自动重试几次
- Hash 校验 : 上传前计算文件 Hash,用于校验完整性
6. CSS 移动端 1 像素问题
-
补充像素知识
- 物理像素(
设备像素):移动设备出厂时,不同设备自带的不同像素,也称硬件像素; - 逻辑像素(
CSS像素):即css中记录的像素。 - 物理像素和逻辑像素存在比例关系,比例多少与设备相关。
- js 中 window.
devicePixelRatio来获取比例。 - css 中用媒体查询的
-webkit-min-device-pixel-ratio来获取比例。
- 物理像素(
-
出现问题的原因:
- 在部分手机上border=1px无法达到我们想要的效果。这是因为 devicePixelRatio 特性导致,iPhone 的 devicePixelRatio==2 。
-
解决方案:transform: scale(0.5) + 媒体查询
(推荐方案: 很灵活)// 设置 ::after 或 ::befor 实现边线 div::after{ content: ''; width: 100%; border-bottom: 1px solid #000; transform: scaleY(0.5); } /* 2倍屏 */ @media only screen and (-webkit-min-device-pixel-ratio: 2.0) { .border-bottom::after { -webkit-transform: scaleY(0.5); transform: scaleY(0.5); } } /* 3倍屏 */ @media only screen and (-webkit-min-device-pixel-ratio: 3.0) { .border-bottom::after { -webkit-transform: scaleY(0.33); transform: scaleY(0.33); } }
7. Number 超过 JS 最大值如何处理?
- 问题产生背景
- 最大的有限值(最大可表示小数)
Number.MAX_VALUE:≈1.7976931348623157e+308 - 超过这个值的数字会被自动转换为
Infinity。
- 最大的有限值(最大可表示小数)
- 解决方案
- BigInt 函数
// **方式一:在整数字面量后加 `n`** const big = 123456789123456789123456789n // **方式二:使用 `BigInt()` 函数** const big2 = BigInt("9007199254740992");
decimal.js/big.js/bignumber.jsdecimal.js支持加减乘除、取模、幂运算、开方、三角函数、高精度浮点数、四则运算、科学计算、金融计算,支持toJSON()、toString()等格式化输出。big.js更轻量,功能稍少但性能更好, 适合只需要基本加减乘除的场景。bignumber.js功能介于decimal.js和big.js之间,支持数学函数,适合金融系统。const Decimal = require('decimal.js'); let a = new Decimal(123.456789); let b = new Decimal('98765432109876543210987654321.123456789'); let result = a.plus(b); // 加法 console.log(result.toString()); // 输出高精度结果 // ------------------------------------------------------------------------ const Big = require('big.js'); let x = new Big('123.456'); let y = new Big('100'); console.log(x.times(y).toString()); // 12345.6
- BigInt 函数
8. 前端实现截图功能
- 使用HTML5
<canvas>元素的绘图能力,可以将页面指定的 DOM 元素或整个页面元素绘制到画布上,然后从画布中导出图像数据。- 优点:
- 兼容性好,浏览器支持广泛;
- 能够灵活控制截图的内容和样式。
- 缺点:对于复杂的 CSS 样式或包含视频、Canvas 动画等动态内容时,可能无法完全准确地捕捉。
- 优点:
- 第三方库:
html2canvas,读取 DOM 节点及样式并在<canvas>上重建这些节点来生成图像。- 优点:
- 易于使用,不需要深入了解底层技术
- 可以对指定的 DOM 元素或整个页面进行截图
- 支持设置 scale 参数调整输出图像的质量和尺寸,导出PNG或JPEG格式的图片
- 支持大多数 CSS 特性和布局方式
- 缺点:
- 可能存在与某些 CSS 属性或框架不兼容的情况;
html2canvas可能无法准确捕捉到<video>标签的内容或动态的<canvas>内容- 不支持
:before,:after等伪类和伪元素 - 页面中不同源的资源,需要单独设置跨域访问( 跨域资源共享(CORS) 配置 )
- 性能可能不如直接操作 Canvas
- 优点:
9. 实现页面访问的进度条
- 方案一:不依赖框架和库
- 使用
window.performanceAPI 监控页面加载状态 - 使用
window.onload事件,判断页面是否加载完成
- 使用
- 方案二:
useNavigation()+ 手写 css 进度条 (适用:React Router v6.4+)const navigation = useNavigation() // 它可以监听当前的导航状态 navigation.state === "loading" // 正在导航(页面切换中),显示进度条并开始动画 navigation.state === "idle" // 隐藏进度条或完成动画 - 方案三: VUE中使用 router前置守卫和后置守卫
router.beforeEach((to, from, next) => { // 开始进度条动画 }) router.afterEach((to, from, next) => { // 关闭进度条 })
10. 前端水印功能
- 方案一:利用 CSS 伪元素
::before或::after在页面背景层添加水印。<div id="watermark"></div> <style> #watermark { position: fixed; width: 100%; height: 100%; z-index: 9999; /* 确保在其他内容之上 */ pointer-events: none; /* 让水印不会影响用户的交互 */ background: url( "data:image/svg+xml, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" style="transform: rotate(-45deg); font-size: 20px;"> <text x="10" y="50">水印内容文本</text> </svg> ") repeat; </style> - 方案二: 利用
<canvas>元素绘制水印,灵活的样式控制,动态生成水印内容<canvas id="watermarkCanvas" width="300" height="300"></canvas> <script> const createWatermark = (text, containerWidth, containerHeight) => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = containerWidth; canvas.height = containerHeight; ctx.rotate((-45 * Math.PI) / 180); ctx.font = '20pt Arial'; ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'; ctx.textAlign = 'center'; for (let x = -containerWidth; x < containerWidth; x += 100) { for (let y = -containerHeight; y < containerHeight; y += 100) { ctx.fillText(text, x, y); } } return canvas.toDataURL(); } document.body.style.backgroundImage = `url(${createWatermark('水印内容', 500, 500)})`; </script>
11. 实现PC端 & H5端的适配
- 方案一:根据响应式使用不同的css规则:
- 响应式设计:通过 CSS 中的
媒体查询来调整页面布局和样式适应不同的屏幕尺寸 - 弹性网格布局:基于百分比或
flex/grid布局系统,布局可以根据屏幕大小自动调整 - 视口设置:设置视口元标签(
<meta name="viewport" content="width=device-width, initial-scale=1">)。
- 响应式设计:通过 CSS 中的
- 方案二:两套代码,根据不同端跳转不同地址
- 判断当前访问设备是
移动端还是PC端(浏览器 User-Agent(UA) )。 - window.location.href =
isMobile()?"移动端":"PC端";
const isMobile = () => { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } if (isMobile()) { window.location.href = "https://yourdomain.com/mobile"; // render(<MobileApp />); } else { window.location.href = "https://yourdomain.com/pc"; // render(<PcApp />); } - 判断当前访问设备是
12. 如何修改 npm 包
- 方案一:使用 patch-package(推荐)
- 步骤:
- 安装:
npm install patch-package --save-dev - 修改
node_modules中的代码 - 生成补丁:
npx patch-package package-name - 提交补丁文件到 Git
- 团队成员拉取代码后,自动应用补丁
- 安装:
- 优点:修改可提交到版本控制;适合多人协作;不影响原包结构。
- 缺点:只能做小范围修改,临时修改;不适合大规模重构。
- 步骤:
- 方案二:本地修改 node_modules(临时修改)
- 优点: 快速简单
- 缺点: 一旦重新安装依赖,修改会丢失,不适合团队协作或生产环境
- 方案三:Fork 原始仓库 + 自己发布为私有包
- 步骤:
- 在 GitHub 上 Fork 原始 npm 包的仓库。
- 克隆到本地进行修改。
- 修改完成后,打 tag 并发布到自己的 npm 账号下(私有或公开)。
- 在项目中安装你自己的包。
- 优点:完全掌控源码,可持续更新维护
- 缺点:需要维护一个独立的包,后续同步原包更新较麻烦
- 步骤:
13. 前端请求安全,加密
- 使用HTTPS,HTTPS通过SSL/TLS协议加密客户端与服务器之间的通信,防止中间人攻击(MITM),确保传输的数据不会被窃听或篡改
- 限流:对同一IP或User进行访问频率限制,增加验证码、图形验证等
- 数据加密(非对称加密,对称加密,MD5 SHA AES PGP)
- 补充:SHA(Secure Hash Algorithm,安全哈希算法),快速,单向不可逆,微小变化就会改变哈希。
- SHA-1:输出长度:160 位(20 字节)。已被认为不安全
- SHA-2
- SHA-256:输出 256 位(32 字节)。非常流行,广泛应用于区块链(如比特币)、SSL/TLS、数字签名、密码存储(通常结合盐值)等。
- SHA-384:输出 384 位(48 字节)。
- SHA-512:输出 512 位(64 字节)。常用于需要更高安全强度的场景。
14. 接口请求大并发问题怎么处理?
- 请求队列,设置最大请求数量,按队列处理请求的发送
- 防抖/节流,合并不必要的请求,减少并发
- 分页加载,减少请求量,延迟加载不必要的数据