怎么在前端页面中添加水印?
如何封装一个请求,让其多次调用的时候,实际只发起一个请求的时候,返回同一份结果
web网页如何禁止别人移除水印
react中怎么实现下拉菜单场景,要求点击区域外能关闭下拉组件
如何搭建一套灰度系统?
React 如何实现vue 中 keep-alive 的功能?
如何监控前端页面的崩溃?
如何在前端团队快速落地代码规范
前端如何实现即时通讯?
用户访问页面白屏了,原因是啥,如何排查?
如何给自己团队的大型前端项目设计单元测试?
如何做一个前端项目工程的自动化部署,有哪些规范和流程设计?

以上:https://github.com/encode-studio-fe/natural_traffic/wiki/scan_material5
怎么在前端页面中添加水印?
在前端页面中添加水印可以通过以下几种方法实现:
1. 使用 CSS 实现
使用 CSS 伪元素和 background 属性来添加水印。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Watermark Example</title>
<style>
.watermarked {
position: relative;
}
.watermarked::before {
content: "Watermark";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(-30deg);
font-size: 3rem;
color: rgba(0, 0, 0, 0.1);
pointer-events: none;
z-index: 1000;
}
</style>
</head>
<body>
<div class="watermarked">
<!-- Your content here -->
<p>Some content with a watermark.</p>
</div>
</body>
</html>
2. 使用 Canvas
通过在 canvas 上绘制水印来实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas Watermark Example</title>
</head>
<body>
<canvas id="watermarkCanvas" width="600" height="400"></canvas>
<script>
const canvas = document.getElementById('watermarkCanvas');
const ctx = canvas.getContext('2d');
// Draw background
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw watermark text
ctx.font = '48px Arial';
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 6);
ctx.fillText('Watermark', 0, 0);
ctx.restore();
</script>
</body>
</html>
3. 使用 HTML 的 background 属性
将水印作为背景图像设置到页面的某个容器上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Background Watermark Example</title>
<style>
.watermarked {
background-image: url('watermark.png');
background-repeat: no-repeat;
background-size: 200px 100px;
background-position: center;
}
</style>
</head>
<body>
<div class="watermarked">
<!-- Your content here -->
<p>Some content with a watermark.</p>
</div>
</body>
</html>
4. 使用 JavaScript 动态生成水印
通过 JavaScript 在 DOM 中添加水印元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Watermark Example</title>
<style>
.watermark {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: 9999;
text-align: center;
font-size: 3rem;
color: rgba(0, 0, 0, 0.1);
transform: rotate(-30deg);
}
</style>
</head>
<body>
<div id="content">
<!-- Your content here -->
<p>Some content with a watermark.</p>
</div>
<script>
const watermark = document.createElement('div');
watermark.className = 'watermark';
watermark.textContent = 'Watermark';
document.body.appendChild(watermark);
</script>
</body>
</html>
如何封装一个请求,让其多次调用的时候,实际只发起一个请求的时候,返回同一份结果?
封装一个请求使其在多次调用时只发起一次请求,并返回相同结果,通常是通过请求去重(debouncing)来实现的。这种功能对于避免重复的网络请求、提高性能和减少不必要的负载非常有用。
同时,我们需要确保在请求完成之前,对相同请求的重复调用都会共享相同的请求 Promise。避免出现连续发出相同的请求,在第一个请求尚未完成时,那么可能会发出多个请求的情况。
可以通过以下步骤来实现这个功能:
1. 使用一个缓存机制
我们可以使用 JavaScript 对象或 Map 来缓存已经发起的请求,并在 subsequent 请求中返回缓存的结果。缓存的关键是确保相同的请求参数对应同一个缓存条目。
2. 创建请求缓存封装
以下是一个基于 axios 的请求去重的封装示例:
import axios from 'axios';
// 请求缓存
const requestCache = new Map();
async function fetchData(url, params) {
// 生成缓存 key
const cacheKey = `${url}?${new URLSearchParams(params).toString()}`;
// 检查缓存中是否已有数据
if (requestCache.has(cacheKey)) {
return requestCache.get(cacheKey);
}
// 创建请求 Promise
const requestPromise = axios.get(url, { params })
.then(response => {
// 请求成功,存储结果
requestCache.delete(cacheKey); // 请求完成后,移除缓存
return response.data;
})
.catch(error => {
// 请求失败,清除缓存
requestCache.delete(cacheKey);
throw error;
});
// 存储请求 Promise
requestCache.set(cacheKey, requestPromise);
// 返回 Promise
return requestPromise;
}
export default fetchData;
注意事项:
- 缓存请求 Promise:每个请求的 Promise 被缓存到 requestCache 中。后续的相同请求会返回这个缓存的 Promise。
- 请求完成后移除缓存:请求成功或失败后,删除缓存,以防止缓存中的 Promise 长时间存在,避免内存泄漏。
- 请求失败处理:如果请求失败,清理缓存并抛出错误,以便后续调用可以重新发起请求。
3. 使用请求缓存
使用封装好的 fetchData 函数来发起请求。多次调用相同的请求 URL 和参数只会发起一次网络请求,并返回相同的结果。
import fetchData from './fetchData';
// 使用示例
fetchData('https://api.example.com/data', { id: 1 })
.then(data => console.log(data))
.catch(error => console.error(error));
// 再次调用相同的请求
fetchData('https://api.example.com/data', { id: 1 })
.then(data => console.log(data)) // 共享相同的请求结果
.catch(error => console.error(error));
web 网页如何禁止别人移除水印
防止DOM被删除
为了防止水印被删除,可以利用 MutationObserver API 监听 DOM 变化。MutationObserver 可以监控 DOM 树的变化并触发回调函数。回调函数可以用于检测水印是否被移除,并采取相应的措施进行恢复。
以下是一个示例代码,演示了如何使用 MutationObserver 监听 DOM 变化并检测水印的删除:
// 目标节点
const targetNode = document.body;
// 创建 MutationObserver 实例
const observer = new MutationObserver(mutationsList => {
mutationsList.forEach(mutation => {
// 检查是否有子节点被删除
if (mutation.removedNodes.length > 0) {
// 检查被删除的节点是否为水印
// 如果是,则重新插入水印元素
// targetNode.appendChild(watermarkElement);
}
});
});
// 配置 MutationObserver
const config = { childList: true, subtree: true };
// 开始观察目标节点
observer.observe(targetNode, config);
在上述代码中,我们创建了一个 MutationObserver 实例,并通过 observe 方法绑定到目标节点。在回调函数中,使用 mutation.removedNodes 检测子节点删除情况。如果发现水印被删除,可以在此处重新插入水印元素。
需要注意的是,MutationObserver 是现代浏览器的特性,可能不兼容老旧浏览器。因此,实际应用中应考虑浏览器兼容性。
此外,为了确保水印能迅速恢复,可以在检测到水印被删除时立即执行插入操作。
防止DOM被隐藏
除了防止DOM被删除,还要考虑DOM被隐藏的情况。
要检测到水印DOM被设置为 display: none 隐藏,可以通过 MutationObserver 观察元素的属性变化,而不是仅仅关注子节点的删除。监听 attributes 类型的变化,以检测到 display 样式属性的改变。
以下示例展示了如何监控 display 属性的变化:
// 目标节点(假设水印元素是一个特定的节点)
const watermarkElement = document.querySelector('.watermark');
// 创建 MutationObserver 实例
const observer = new MutationObserver(mutationsList => {
mutationsList.forEach(mutation => {
if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
// 检查水印的 display 属性是否被设置为 none
if (getComputedStyle(watermarkElement).display === 'none') {
// 如果水印被隐藏,重新显示水印
watermarkElement.style.display = 'block';
}
}
});
});
// 配置 MutationObserver
const config = { attributes: true, subtree: true, attributeFilter: ['style'] };
// 开始观察目标节点
observer.observe(document.body, config);
说明
- 目标节点 :在代码中,
watermarkElement代表水印元素。请确保选择器正确。 - MutationObserver 实例 :观察属性变化 (
attributes) 和特定的属性style。 - 属性变化检测 :在回调函数中,使用
getComputedStyle检查display属性的值。如果水印被设置为display: none,则将其恢复为display: block。
react 中怎么实现下拉菜单场景,要求点击区域外能关闭下拉组件
涉及以下几个步骤:
- 创建下拉菜单组件
- 监听点击事件
- 判断点击事件是否在下拉菜单外部
步骤说明
1. 创建下拉菜单组件
import React, { useState, useRef, useEffect } from 'react';
const Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const toggleDropdown = () => {
setIsOpen(!isOpen);
};
// 监听点击事件
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
return (
<div ref={dropdownRef}>
<button onClick={toggleDropdown}>
Toggle Dropdown
</button>
{isOpen && (
<div className="dropdown-menu">
<p>Menu Item 1</p>
<p>Menu Item 2</p>
<p>Menu Item 3</p>
</div>
)}
</div>
);
};
export default Dropdown;
2. 监听点击事件
- 在
useEffect钩子中,添加一个全局的mousedown事件监听器,用于检测点击是否发生在下拉菜单外部。 handleClickOutside函数会检查点击事件的目标是否在下拉菜单外部,如果是则关闭下拉菜单。
3. 判断点击事件是否在下拉菜单外部
- 使用
useRef钩子获取下拉菜单组件的引用(dropdownRef)。 handleClickOutside函数中使用dropdownRef.current.contains(event.target)来判断点击的目标是否在下拉菜单的 DOM 结构内。
注意事项
- 确保
ref正确设置在包含下拉菜单的最外层容器上。 - 在组件卸载时移除事件监听器,以避免内存泄漏。
- 在大型应用中,可以考虑使用更复杂的事件处理库或工具来处理全局点击事件。
通过上述步骤,你可以实现点击区域外关闭下拉菜单的功能。
如何搭建一套灰度系统?
搭建一套灰度发布系统涉及多个方面的技术和流程,目的是在发布新版本时,能够逐步、部分地将新功能或改动推送给用户,以降低发布的风险。
以下是搭建的一般步骤和要点:
1. 明确灰度发布的需求和目标
- 降低风险:避免一次性发布导致的全局性错误影响所有用户。
- 收集反馈:逐步推出新功能,观察用户行为和收集反馈,及时进行调整。
- 验证性能和稳定性:在小范围内测试新版本的性能和稳定性。
2. 架构设计
- 服务分层:将应用分成多个服务或模块,每个服务独立发布,方便单独进行灰度发布。
- 支持多版本并存:确保系统能同时运行多个版本的新旧功能,以便不同用户访问到不同版本。
3. 用户分组策略
- 按用户分组:根据用户的特征(如地域、设备类型、用户等级等)或随机分配,决定哪些用户先接收到新版本。
- 流量分配:通过配置逐步增加新版本的流量比例。例如,先让 5% 的用户使用新版本,然后观察反馈,逐步增加到 10%、20% 等。
4. 灰度发布系统的功能
- 流量调度:能够动态调整不同版本的流量占比,通常由一个流量调度模块控制。
- 用户分组管理:可以管理用户分组,并将用户分配到对应的版本。
- 监控和日志收集:实时监控系统的性能指标(如请求响应时间、错误率、资源使用情况等),并收集用户行为日志。
- 自动回滚:当检测到新版本出现问题时,系统可以自动回滚到稳定的旧版本。
- A/B 测试:结合 A/B 测试工具,进行功能对比测试,进一步细化灰度策略。
5. 工具与技术栈
- 负载均衡器:使用负载均衡器(如 Nginx、HAProxy)进行流量分配,决定哪些请求应该分配到新版本。
- 微服务架构:使用 Kubernetes、Docker 等技术支持微服务架构,方便独立部署和灰度发布。
- CI/CD 工具:结合 Jenkins、GitLab CI/CD、GitHub Actions 等工具进行自动化构建、测试和部署。
- 监控系统:使用 Prometheus、Grafana、ELK 等工具进行系统监控和日志分析。
- A/B 测试工具:如 Google Optimize、Optimizely,结合灰度发布进行用户体验的差异化测试。
6. 实施灰度发布
- 部署基础设施:搭建好灰度发布的基础设施,包括流量调度、监控、日志收集等模块。
- 制定灰度策略:根据业务需求,制定详细的灰度发布策略,包括用户分组、流量占比、回滚条件等。
- 逐步推进:从小规模用户开始,逐步扩大灰度范围,并观察各项指标以确保系统稳定。
- 持续监控与反馈:在整个灰度发布期间持续监控系统表现,收集用户反馈,及时作出调整或回滚。
7. 回滚策略
- 快速回滚机制:在发布过程中出现问题时,灰度系统应能快速回滚到上一稳定版本。
- 保持数据一致性:确保新版本的数据格式与旧版本兼容,回滚时不会导致数据丢失或不一致。
8. 总结与优化
- 记录发布过程:详细记录每次灰度发布的过程、问题和解决方案,为后续发布积累经验。
- 优化灰度系统:根据发布过程中的经验,不断优化灰度系统,提升系统的健壮性和可操作性。
React 如何实现 vue 中 keep-alive 的功能?
在 React 中实现类似于 Vue 中 keep-alive 的功能,可以使用组件状态和 React 的生命周期方法来控制组件的挂载和卸载。
以下是一种实现方式:
1. 使用状态管理组件
创建一个 KeepAlive 组件,用于存储和管理被"缓存"的组件。
import React, { useState } from 'react';
// KeepAlive 组件
const KeepAlive = ({ children, name }) => {
const [cache, setCache] = useState({});
// 保存组件
const saveCache = () => {
setCache((prev) => ({
...prev,
[name]: children,
}));
};
// 恢复组件
const getCachedComponent = () => {
return cache[name] || children;
};
// 组件挂载时保存
React.useEffect(() => {
saveCache();
}, [children]);
return <>{getCachedComponent()}</>;
};
// 示例用法
const App = () => {
const [activeComponent, setActiveComponent] = useState('ComponentA');
return (
<div>
<button onClick={() => setActiveComponent('ComponentA')}>Component A</button>
<button onClick={() => setActiveComponent('ComponentB')}>Component B</button>
<KeepAlive name={activeComponent}>
{activeComponent === 'ComponentA' ? <ComponentA /> : <ComponentB />}
</KeepAlive>
</div>
);
};
const ComponentA = () => <div>Component A</div>;
const ComponentB = () => <div>Component B</div>;
export default App;
2. 实现逻辑
- 状态管理 :
KeepAlive组件使用一个状态cache来存储被缓存的组件。 - 保存和恢复:在组件挂载时保存当前子组件到缓存中;每次渲染时,检查缓存并返回之前的组件,避免重新渲染。
- 使用示例 :通过按钮切换
activeComponent的状态,展示不同的组件,同时保留它们的状态。
如何监控前端页面的崩溃?
监控前端页面的崩溃通常涉及捕获和报告 JavaScript 错误、性能问题以及页面状态。
以下是一些常见的方法和工具来实现这些监控:
1. 使用 window.onerror
-
定义 :
window.onerror是一个全局事件处理程序,用于捕获 JavaScript 执行时的错误。 -
实现:
window.onerror = function(message, source, lineno, colno, error) {
// 处理错误信息,例如发送到服务器
console.error('Error captured:', { message, source, lineno, colno, error });
// 可以通过 HTTP 请求将错误信息发送到日志服务器
};
2. 使用 window.addEventListener('unhandledrejection')
-
定义:捕获未处理的 Promise 拒绝(rejections)。
-
实现:
window.addEventListener('unhandledrejection', function(event) {
// 处理 Promise 拒绝,例如发送到服务器
console.error('Unhandled rejection:', event.reason);
// 可以通过 HTTP 请求将错误信息发送到日志服务器
});
3. 使用 try...catch
-
定义 :在可能出现错误的代码块中使用
try...catch捕获异常。 -
实现:
try {
// 可能会抛出错误的代码
} catch (error) {
// 处理错误,例如发送到服务器
console.error('Caught error:', error);
// 可以通过 HTTP 请求将错误信息发送到日志服务器
}
4. 使用错误监控工具
- Sentry:捕获前端错误并提供详细的堆栈跟踪和上下文信息。
- New Relic:提供全面的前端性能监控和错误捕获。
- Rollbar:实时捕获和报告 JavaScript 错误和异常。
- LogRocket:记录用户会话并捕获前端错误。
5. 性能监控和日志
- 浏览器开发者工具:使用 DevTools 监控网络请求、性能和资源使用。
- Web Vitals:跟踪核心 Web Vitals 指标(如 LCP、FID、CLS)来发现性能问题。
- Custom Logging:自定义日志记录功能,将应用程序状态和错误发送到日志服务器。
6. 网络请求监控
- 自定义错误日志:在 JavaScript 错误处理程序中,通过 HTTP 请求将错误信息发送到远程服务器进行存储和分析。
- 日志服务器:维护一个后端日志服务器,用于存储和分析前端错误和崩溃数据。
以上丸整:https://github.com/encode-studio-fe/natural_traffic/wiki/scan_material5
因为篇幅原因,只展示部分内容了,全部写出来太长了,欢迎沟通讨论,以上内容都有对应答案
祝大家求职顺利