Skip to content

圈复杂度、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、应对高复杂度措施

常见优化方法:

  1. 对象映射代替分支
javascript
// 优化前:大量 if-else
if (type === 'A') { /* ... */ }
else if (type === 'B') { /* ... */ }

// 优化后:对象映射
const handlers = {
  A: () => { /* ... */ },
  B: () => { /* ... */ }
};
handlers[type]?.();
  1. 逻辑封装
  • 将复杂逻辑拆分为独立函数/模块
  • 单一职责原则,降低函数复杂度
  1. 使用策略模式/状态模式
  • 针对复杂条件分支,采用设计模式重构

5、可在eslint配置

ts
rules: {
  // ...
  complexity: ['error', 15],
}

二、图片上传本地预览优化

data URL 与 base64

1. data URL 概念

data URL 是一种直接在 URL 中嵌入数据的格式,无需额外请求服务器获取资源。

格式:

js
data:[<媒体类型>][;base64],<数据>
  • 媒体类型:如 image/png、image/jpeg、text/javascript,省略则默认 text/plain。

  • base64:可选,表明数据是 base64 编码的二进制数据。

  • <数据>:文本数据或 base64 编码后的二进制数据。

2. base64 编码的作用

  • 图片、视频等资源的原始格式是二进制数据,无法直接嵌入到文本格式的 URL 中。
  • base64 可将二进制数据转换为纯文本字符串,实现二进制数据的文本化传输和存储。

3. data URL 基础示例

示例 1:嵌入纯文本

HTML
<!-- 直接在a标签中嵌入文本数据 -->
<a href="data:text/plain,Hello World">点击查看文本</a>

点击链接会直接显示 Hello World,无需请求服务器。

示例 2:嵌入 JS 代码

HTML
<!-- 直接运行 data URL 中的JS代码 -->
<script src="data:text/javascript,alert('这是 data URL 运行的JS')"></script>

页面加载后会直接弹出提示框。

示例 3:嵌入图片(base64 格式)

HTML
<!-- 直接展示 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
<!-- 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>

💡 技术要点

  1. File对象是Blob的子类,可直接传入URL.createObjectURL()
  2. 生成的Blob URL只在当前页面会话有效
  3. 大文件预览时比base64方案性能更好
  4. 使用后应及时释放内存:URL.revokeObjectURL()

方案 2:用 FileReader 读取文件为 ArrayBuffer 后创建 Blob(更灵活) 先读取文件的二进制数据(ArrayBuffer),再手动创建 Blob 对象,适合需要对二进制数据做额外处理(如裁剪、压缩)的场景。

html
<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 方案的注意事项

  1. Blob URL 有效期

    • URL.createObjectURL() 生成的 URL 仅在当前页面会话有效。
    • 跨页面、刷新页面后失效,不可用于持久化存储
  2. 内存释放

    • 使用完 Blob URL 后,需调用 URL.revokeObjectURL(blobUrl) 主动释放内存。
    • 避免页面长期运行导致内存泄露(尤其频繁预览图片时)。
  3. 兼容性

    • 所有现代浏览器(Chrome、Firefox、Edge、Safari 10+)均支持,无需担心兼容性问题。

The future is promising