圈复杂度、TypeScript never 类型、图片上传本地预览优化
一、圈复杂度(Cyclomatic Complexity)
1、意义与作用
- 定义:用于量化代码质量的指标,数值直观反映代码复杂程度
- 重要性:
- 复杂度高的代码 → 维护和测试难度大,易引发后续问题
- 复杂度低的代码 → 结构清晰,利于开发人员理解和修改
- 对保证项目的长期稳定性和可扩展性至关重要
- 实际影响:复杂的嵌套分支代码,修改一处可能影响多处,维护成本高
2、合理范围界定
| 复杂度范围 | 代码状态 | 维护影响 |
|---|---|---|
| 1 - 10 | 理想范围,逻辑清晰 | 开发人员能快速理解和维护 |
| 11 - 20 | 可控范围(尽量避免) | 维护成本开始上升 |
| >20 | 风险范围 | 可读性急剧下降,维护成本大幅增加 |
示例说明:
- 圈复杂度为 25 的代码 → 逻辑可能交织复杂,开发人员需花费大量时间梳理
3、计算方式解析
方法一:E - N + 2 公式
- E:流程图中的边数(控制流边)
- N:流程图中的节点数(语句节点)
- 示例:简单顺序执行代码(边数=1,节点数=2) → 1 - 2 + 2 = 1
方法二:区域划分法
- 统计流程图划分平面的区域数量
- 示例:简单顺序代码平面划分区域为 1 → 复杂度为 1
两种方法从不同角度计算,结果一致,方便从不同视角理解代码复杂度
4、应对高复杂度措施
常见优化方法:
- 对象映射代替分支
// 优化前:大量 if-else
if (type === 'A') { /* ... */ }
else if (type === 'B') { /* ... */ }
// 优化后:对象映射
const handlers = {
A: () => { /* ... */ },
B: () => { /* ... */ }
};
handlers[type]?.();- 逻辑封装
- 将复杂逻辑拆分为独立函数/模块
- 单一职责原则,降低函数复杂度
- 使用策略模式/状态模式
- 针对复杂条件分支,采用设计模式重构
5、可在eslint配置
rules: {
// ...
complexity: ['error', 15],
}二、图片上传本地预览优化
data URL 与 base64
1. data URL 概念
data URL 是一种直接在 URL 中嵌入数据的格式,无需额外请求服务器获取资源。
格式:
data:[<媒体类型>][;base64],<数据>媒体类型:如 image/png、image/jpeg、text/javascript,省略则默认 text/plain。
base64:可选,表明数据是 base64 编码的二进制数据。
<数据>:文本数据或 base64 编码后的二进制数据。
2. base64 编码的作用
- 图片、视频等资源的原始格式是二进制数据,无法直接嵌入到文本格式的 URL 中。
- base64 可将二进制数据转换为纯文本字符串,实现二进制数据的文本化传输和存储。
3. data URL 基础示例
示例 1:嵌入纯文本
<!-- 直接在a标签中嵌入文本数据 -->
<a href="data:text/plain,Hello World">点击查看文本</a>点击链接会直接显示 Hello World,无需请求服务器。
示例 2:嵌入 JS 代码
<!-- 直接运行 data URL 中的JS代码 -->
<script src="data:text/javascript,alert('这是 data URL 运行的JS')"></script>页面加载后会直接弹出提示框。
示例 3:嵌入图片(base64 格式)
<!-- 直接展示 base64 编码的图片,无需服务器请求 -->
<img src="data:image/png;base64,iVBORw..." />Blob 实现方案
1、Blob 类型基础认知
Blob(Binary Large Object,二进制大对象)是 JavaScript 中用于存储二进制数据的对象,可理解为 “二进制数据容器”,支持文本、图片、视频等任意二进制格式数据。
它与 base64 的核心区别在于:
- base64: 将二进制数据编码为纯文本字符串(体积会增大约 33%),需嵌入到 URL 中使用。
- Blob: 直接存储二进制原始数据(体积无额外损耗),需通过 URL.createObjectURL() 生成临时 URL 访问。
2、Blob 与 URL.createObjectURL () 配合使用
浏览器提供 URL.createObjectURL(blob) 方法,可基于 Blob 对象生成一个临时的本地 URL(格式如 blob:http://localhost:5500/abc123),该 URL 仅在当前页面会话中有效,关闭页面后自动释放,避免内存占用。
3、Blob 预览的两种实现方案(附代码示例)
方案 1:直接用 URL.createObjectURL 生成 Blob URL(更简洁) 无需读取文件内容,直接基于文件对象生成临时 URL,性能更优(尤其适合大图片)。
<!-- HTML 结构:文件选择框 + 预览图片容器 -->
<input type="file" id="fileInput" accept="image/*">
<img id="previewImg" style="max-width: 300px; margin-top: 20px; display: none;">
<script>
// 1. 获取 DOM 元素
const fileInput = document.getElementById('fileInput');
const previewImg = document.getElementById('previewImg');
// 2. 监听文件选择事件
fileInput.addEventListener('change', function(e) {
// 获取用户选择的第一个图片文件(files 是类数组,存所有选中文件)
const selectedFile = e.target.files[0];
// 校验:是否选择了文件 + 是否为图片类型
if (!selectedFile || !selectedFile.type.startsWith('image/')) {
alert('请选择有效的图片文件!');
previewImg.style.display = 'none'; // 隐藏预览图
return;
}
// 3. 关键步骤:基于文件生成 Blob 临时 URL
// (File 对象是 Blob 的子类,可直接传入 URL.createObjectURL)
const blobUrl = URL.createObjectURL(selectedFile);
// 4. 赋值给 img.src 实现预览
previewImg.src = blobUrl;
previewImg.style.display = 'block';
// 5. 可选:页面卸载前释放 Blob URL,避免内存泄露
window.addEventListener('beforeunload', function() {
URL.revokeObjectURL(blobUrl); // 主动释放临时 URL
});
// 6. 后台异步上传文件(可选,与预览逻辑分离)
// const formData = new FormData();
// formData.append('image', selectedFile);
// fetch('/api/upload', { method: 'POST', body: formData });
});
</script>💡 技术要点
- File对象是Blob的子类,可直接传入URL.createObjectURL()
- 生成的Blob URL只在当前页面会话有效
- 大文件预览时比base64方案性能更好
- 使用后应及时释放内存:URL.revokeObjectURL()
方案 2:用 FileReader 读取文件为 ArrayBuffer 后创建 Blob(更灵活) 先读取文件的二进制数据(ArrayBuffer),再手动创建 Blob 对象,适合需要对二进制数据做额外处理(如裁剪、压缩)的场景。
<input type="file" id="fileInput2" accept="image/*">
<img id="previewImg2" style="max-width: 300px; margin-top: 20px; display: none;">
<script>
const fileInput2 = document.getElementById('fileInput2');
const previewImg2 = document.getElementById('previewImg2');
fileInput2.addEventListener('change', function(e) {
const selectedFile = e.target.files[0];
if (!selectedFile || !selectedFile.type.startsWith('image/')) {
alert('请选择有效的图片文件!');
previewImg2.style.display = 'none';
return;
}
// 1. 创建 FileReader 实例
const reader = new FileReader();
// 2. 读取文件为 ArrayBuffer(二进制原始数据)
reader.readAsArrayBuffer(selectedFile);
// 3. 读取完成后触发 load 事件
reader.addEventListener('load', function(e) {
// e.target.result 是读取到的 ArrayBuffer 数据
const arrayBuffer = e.target.result;
// 4. 基于 ArrayBuffer 创建 Blob 对象(指定 MIME 类型为原图片类型)
const imageBlob = new Blob([arrayBuffer], { type: selectedFile.type });
// 5. 生成 Blob URL 并预览
const blobUrl2 = URL.createObjectURL(imageBlob);
previewImg2.src = blobUrl2;
previewImg2.style.display = 'block';
// 6. 释放资源
window.addEventListener('beforeunload', () => {
URL.revokeObjectURL(blobUrl2);
});
// (可选)此处可对 arrayBuffer 做额外处理,如修改图片像素等
});
// 7. 处理读取错误
reader.addEventListener('error', function() {
alert('图片读取失败,请重试!');
});
});
</script>4、Blob 方案 vs Base64 方案对比(核心差异)
| 对比维度 | Blob 方案 | Base64 方案 |
|---|---|---|
| 数据体积 | 无额外损耗(原始二进制) | 体积增大约 33%(文本编码) |
| 加载性能 | 生成临时 URL 直接加载,性能优 | 需解析长文本编码,大图片加载慢 |
| 内存占用 | 需手动释放 blobUrl,否则暂存内存 | 编码字符串直接嵌入 DOM,长期占用内存 |
| 适用场景 | 大图片预览、需处理二进制数据的场景 | 小图标、简单预览(如头像缩略图) |
| 与上传配合 | 可直接上传原始 File/Blob 对象 | 需解码 Base64 为二进制再上传 |
5、Blob 方案的注意事项
Blob URL 有效期
URL.createObjectURL()生成的 URL 仅在当前页面会话有效。- 跨页面、刷新页面后失效,不可用于持久化存储。
内存释放
- 使用完 Blob URL 后,需调用
URL.revokeObjectURL(blobUrl)主动释放内存。 - 避免页面长期运行导致内存泄露(尤其频繁预览图片时)。
- 使用完 Blob URL 后,需调用
兼容性
- 所有现代浏览器(Chrome、Firefox、Edge、Safari 10+)均支持,无需担心兼容性问题。
