欢迎光临
我们一直在努力

前端老铁别慌:img标签src调API返回base64字符串的土法炼钢指南

在这里插入图片描述

前端老铁别慌:img标签src调API返回base64字符串的土法炼钢指南

  • 前端老铁别慌:img标签src调API返回base64字符串的土法炼钢指南
    • 开篇先唠两句这破事儿咋来的
    • 这玩意儿到底是个啥妖魔鬼怪
      • base64这编码的底裤,咱给它扒了
      • img标签的src属性,它到底想吃啥?
      • 为啥有时候直接拼前缀能行,有时候又摆烂?
    • 手把手教你把乱码变美图
      • 第一种土法:直接在HTML里硬拼字符串
      • 第二种稍微洋气点:用JS动态创建Image对象
      • 第三种针对异步接口:axios或者fetch拿到数据后怎么处理
    • 这方案看着香其实全是坑
      • 优点嘛,确实有,但不能只看表面
      • 缺点得好好吐槽,全是泪
    • 真实项目里那些让人头秃的场景
      • 列表页一次性加载几十张base64,浏览器直接假死
      • 上传预览功能,本地转base64展示没问题,一提交给后端就报错
      • 混合开发里,H5页面嵌在App里,base64图片太大导致WebView直接崩溃
    • 出问题了别只会重启大法好
      • 图片显示个裂开的图标?赶紧查查前缀
      • 控制台报"Invalid source"?多半是字符串脏了
      • 页面卡顿严重?打开Performance面板看看
    • 几个让代码看起来不那么菜的骚操作
      • 写个工具函数自动判断MIME类型
      • 加个阈值判断,超过多少KB坚决不转base64
      • 利用CSS变量或者预处理器搞点批量处理
    • 最后啰嗦一句保命秘籍

前端老铁别慌:img标签src调API返回base64字符串的土法炼钢指南

开篇先唠两句这破事儿咋来的

兄弟们,今天咱们聊一个让无数前端人半夜惊醒的噩梦——后端那帮大哥非要给你塞一串长得离谱的base64字符串,还拍着胸脯说"这样省事"。

我就纳了闷了,这年头CDN这么便宜,OSS存储跟不要钱似的,他们偏不,非得把图片转成那种密密麻麻的字符,往JSON里一塞,跟扔垃圾似的甩给你。你打开Network面板一看,好家伙,一个接口返回的数据包体积直接干到几十MB,里面全是这种玩意儿:

/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=

这串东西你看着眼熟不?眼熟就对了,说明你也被坑过。我第一次遇到这情况的时候,直接把这串东西往img标签的src里一贴,浏览器当场就给我表演了一个"页面未响应"。用户在那边疯狂点击,以为我网站挂了,实际上我的JavaScript主线程正在那吭哧吭哧地解码这坨东西,CPU占用率直接飙到100%,风扇转得跟直升机起飞似的。

所以啊,今天这篇不是那种"Base64编码原理详解"的学院派文章,咱不搞那些虚的。我就把我这些年踩过的坑、摔过的跤、背过的锅,一股脑儿全倒出来。从"这玩意儿到底是个啥"到"怎么让它不卡成PPT",再到"出问题了怎么排查",最后送你几个能让代码看起来不那么像新手写的骚操作。全程手把手上代码,注释给你写得明明白白,复制粘贴就能跑,跑不通你来找我(反正你也找不到)。

咱就是说,看完这篇,下次再遇到后端给你扔base64,你至少能淡定地泡杯咖啡,慢悠悠地说:“哦,这个啊,我熟。”

这玩意儿到底是个啥妖魔鬼怪

base64这编码的底裤,咱给它扒了

先别急着写代码,咱得搞清楚这妖孽的本质。base64说白了就是一种编码方式,把二进制数据(比如图片、音频、视频)转换成ASCII字符集中的64个字符(A-Z、a-z、0-9、+、/)再加上一个填充符=。为啥要这么干?因为有些系统或者协议(比如早期的邮件系统)只支持文本传输,二进制数据过去就乱码,所以得先"翻译"成文本。

一张图片在计算机里存的是啥?是一堆0和1的二进制数据。base64就是把这些0和1按6个一组,每组对应一个ASCII字符,最后拼成一串看起来像是乱码的文本。这个过程会让数据体积膨胀约33%,也就是说,原本100KB的图片,转成base64后就变成133KB左右。

// 来,咱们手动模拟一下base64的膨胀率
const originalSize = 100 * 1024; // 100KB的二进制数据
const base64Size = Math.ceil(originalSize / 3) * 4; // base64是3个字节转4个字符
const expansionRate = ((base64Size originalSize) / originalSize * 100).toFixed(2);

console.log(`原始大小: ${originalSize} bytes`);
console.log(`Base64后大小: ${base64Size} bytes`);
console.log(`膨胀率: ${expansionRate}%`); // 输出大概是33.33%

看到没,这就是为啥我说base64"看着省事实则坑爹"的原因之一。数据量变大了,传输时间变长了,用户流量哗哗地烧,尤其是在4G或者信号不好的地方,体验直接崩盘。

img标签的src属性,它到底想吃啥?

咱们天天写<img src="xxx">,但你真的了解src这个属性吗?它其实是个"杂食动物",啥都能吃:

  • 绝对URL:https://example.com/image.jpg
  • 相对URL:./assets/logo.png
  • Data URI:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==
  • 那个Data URI就是咱们今天的主角。它的格式是固定的:

    data:[MIME类型];base64,[base64编码的字符串]

    MIME类型告诉浏览器这是啥玩意儿,常见的有:

    • image/png – PNG图片
    • image/jpeg 或 image/jpg – JPEG图片
    • image/gif – GIF动图
    • image/webp – WebP格式(现在挺流行的)
    • image/svg+xml – SVG矢量图(这个有时候也转base64,虽然没必要)

    <!– 正确的打开方式 –>
    <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">

    <!– 错误的打开方式(漏了MIME类型) –>
    <img src="data:base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">

    看到上面那个错误的例子没?我就见过有后端返回的数据里,只给了base64字符串,没给MIME类型,前端小哥直接拼了个data:base64,xxx,结果图片死活显示不出来,在那调试了俩小时,最后发现是漏了image/png这茬。这种低级错误,说出去都丢人,但谁没年轻过呢对吧?

    为啥有时候直接拼前缀能行,有时候又摆烂?

    这事儿得看后端给的数据质量。理想情况下,后端应该给你返回一个完整的对象:

    {
    "imageName": "avatar.png",
    "mimeType": "image/png",
    "base64Data": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
    }

    但现实往往是残酷的,后端可能只给你:

    {
    "image": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
    }

    甚至有时候,这串base64里面还混进了换行符\\n、回车符\\r或者空格,因为有些后端框架为了"美观",会自动格式化JSON,把一长串base64折成好几行。浏览器解析的时候可不认这些,它要的是纯的base64字符,一旦遇到非法字符,直接给你显示个裂开的图标,或者控制台报错"Invalid character"。

    // 假设这是后端返回的"脏数据"
    const dirtyBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbybl\\nAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO\\n9TXL0Y4OHwAAAABJRU5ErkJggg==";

    // 直接拼接到src里,图片显示不出来
    const imgSrc = `data:image/png;base64,${dirtyBase64}`;

    // 得先"洗洗澡",把非法字符干掉
    const cleanBase64 = dirtyBase64.replace(/[\\s\\r\\n]+/g, '');
    const correctImgSrc = `data:image/png;base64,${cleanBase64}`;

    看到没,这就是为啥有时候你明明觉得代码写得没问题,图片就是不显示。不是浏览器抽风,是数据本身带"病"。

    手把手教你把乱码变美图

    好了,理论基础打完了,咱们上干货。我总结了三种常见的处理方式,从"土得掉渣"到"稍微能看点",总有一款适合你。

    第一种土法:直接在HTML里硬拼字符串

    这种方法适合那种特别小的图标,比如16×16的favicon,或者几个像素的小圆点。大图千万别这么干,否则你的HTML文件体积会爆炸,编辑器都能卡死。

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
    <meta charset="UTF-8">
    <title>Base64硬拼示例</title>
    <style>
    .tiny-icon {
    width: 16px;
    height: 16px;
    }
    /* 稍微大一点的图,用CSS控制一下,别让它原尺寸显示 */
    .small-logo {
    width: 100px;
    height: auto;
    }
    </style>
    </head>
    <body>
    <h2>这方法只适用于超小图标</h2>

    <!– 一个红色的1×1像素点,用来做占位或者透明gif的替代品 –>
    <img class="tiny-icon"
    src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
    alt="1×1透明gif">

    <!– 一个稍微复杂点的小图标,比如一个红色的圆 –>
    <img class="tiny-icon"
    src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
    alt="红点">

    <p>看到上面那个src了吗?那么长一串,要是大图的话,这一行能占满整个屏幕</p>
    </body>
    </html>

    这种写法的优缺点很明显:

    优点:

    • 简单粗暴,不用写JS
    • 省了一次HTTP请求(因为数据就在HTML里)
    • 小图标确实加载快,几乎是瞬间显示

    缺点:

    • HTML文件体积暴增,影响首屏加载
    • 无法缓存(除非缓存整个HTML页面)
    • 维护困难,你想改个图?得重新生成base64再替换
    • 编辑器里看着头大,满屏乱码

    所以啊,这种方法就用来应急,或者那种万年不变的小图标。千万别在生产环境的大图上用,否则性能优化的时候有你哭的。

    第二种稍微洋气点:用JS动态创建Image对象

    这种方法适合需要根据条件动态显示图片的场景。比如用户点击按钮后才加载图片,或者要根据屏幕大小加载不同尺寸的图片。

    /**
    * 动态创建Image对象加载base64图片
    * @param {string} base64String – 纯base64字符串(不含data URI前缀)
    * @param {string} mimeType – MIME类型,默认image/png
    * @param {HTMLElement} container – 要挂载到的容器
    * @param {object} options – 配置选项
    */

    function loadBase64Image(base64String, mimeType = 'image/png', container, options = {}) {
    // 先洗数据,去掉可能存在的换行符和空格
    const cleanBase64 = base64String.replace(/[\\s\\r\\n]+/g, '');

    // 拼接完整的Data URI
    const dataUri = `data:${mimeType};base64,${cleanBase64}`;

    // 创建Image对象,这样可以监听load和error事件
    const img = new Image();

    // 设置图片属性
    if (options.width) img.width = options.width;
    if (options.height) img.height = options.height;
    if (options.alt) img.alt = options.alt;
    if (options.className) img.className = options.className;

    // 加载成功的回调
    img.onload = function() {
    console.log('图片加载成功啦!尺寸:', img.naturalWidth, 'x', img.naturalHeight);

    // 如果传了成功回调,执行一下
    if (options.onLoad && typeof options.onLoad === 'function') {
    options.onLoad(img);
    }

    // 挂载到DOM
    if (container && container.appendChild) {
    container.appendChild(img);
    }
    };

    // 加载失败的回调
    img.onerror = function(error) {
    console.error('图片加载失败了,检查一下base64数据:', error);

    // 如果传了失败回调,执行一下
    if (options.onError && typeof options.onError === 'function') {
    options.onError(error);
    }

    // 可以显示一个占位图或者错误提示
    if (container) {
    container.innerHTML = '<p style="color:red">图片加载失败,可能是base64数据有问题</p>';
    }
    };

    // 设置src,触发加载
    img.src = dataUri;

    return img; // 返回img对象,方便外部操作
    }

    // 使用示例
    const base64Data = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";

    // 找到容器
    const container = document.getElementById('image-container');

    // 调用函数,加点配置
    loadBase64Image(base64Data, 'image/png', container, {
    width: 200,
    alt: '这是动态加载的图片',
    className: 'responsive-img',
    onLoad: function(img) {
    console.log('老板,图片加载完毕,可以交差了!');
    // 这里可以加个淡入动画啥的
    img.style.opacity = '0';
    setTimeout(() => {
    img.style.transition = 'opacity 0.5s';
    img.style.opacity = '1';
    }, 10);
    },
    onError: function(err) {
    console.log('完蛋,出错了,准备背锅');
    }
    });

    看到没,这种方法的好处是:

    • 可以监听加载状态,成功失败都知道
    • 可以在加载前显示loading,加载后加动画
    • 错误处理更优雅,不会直接显示裂开的图标
    • 可以动态控制图片尺寸和样式

    但本质上还是把base64塞给src,大图依然会卡,这点要注意。

    第三种针对异步接口:axios或者fetch拿到数据后怎么处理

    这是最常见的场景了,后端给你个接口,返回一堆base64数据,你得等请求回来才能处理。这里最容易踩的坑就是"undefined"或者"null"被拼进了src里。

    // 用axios举例,fetch也大同小异
    import axios from 'axios';

    /**
    * 从API获取base64图片并显示
    * @param {string} apiUrl – 接口地址
    * @param {HTMLElement} targetElement – 目标img标签或者容器
    */

    async function fetchAndDisplayBase64Image(apiUrl, targetElement) {
    try {
    // 显示loading状态
    if (targetElement.tagName === 'IMG') {
    targetElement.src = 'loading.gif'; // 或者显示个骨架屏
    } else {
    targetElement.innerHTML = '<p>图片加载中…</p>';
    }

    // 发请求
    const response = await axios.get(apiUrl);

    // 这里要注意!后端返回的数据结构可能千奇百怪
    // 可能是 response.data.base64
    // 可能是 response.data.image
    // 可能是 response.data.data.imageBase64
    // 得看接口文档,或者console.log出来看看

    console.log('后端返回的数据:', response.data);

    // 假设后端返回的是 { code: 200, data: { image: "xxx", type: "png" } }
    const { data } = response.data;

    // 防御性编程,万一后端抽风返回了undefined
    if (!data || !data.image) {
    throw new Error('后端返回的数据格式不对,找不到image字段');
    }

    const base64String = data.image;
    const mimeType = data.type ? `image/${data.type}` : 'image/png';

    // 再次检查,防止是空字符串
    if (typeof base64String !== 'string' || base64String.length === 0) {
    throw new Error('base64数据是空的,后端在搞什么飞机');
    }

    // 清洗数据
    const cleanBase64 = base64String.replace(/[\\s\\r\\n]+/g, '');

    // 拼接Data URI
    const dataUri = `data:${mimeType};base64,${cleanBase64}`;

    // 设置到img标签
    if (targetElement.tagName === 'IMG') {
    targetElement.src = dataUri;
    } else {
    // 如果是容器,创建img元素塞进去
    const img = document.createElement('img');
    img.src = dataUri;
    img.style.maxWidth = '100%';
    targetElement.innerHTML = '';
    targetElement.appendChild(img);
    }

    console.log('图片加载成功,base64长度:', cleanBase64.length);

    } catch (error) {
    console.error('加载图片失败:', error);

    // 显示错误占位图或者提示
    if (targetElement.tagName === 'IMG') {
    targetElement.src = 'error-placeholder.png';
    targetElement.alt = '图片加载失败';
    } else {
    targetElement.innerHTML = `
    <div style="color: red; padding: 20px; border: 1px solid red;">
    <p>图片加载失败:
    ${error.message}</p>
    <button onclick="location.reload()">重试</button>
    </div>
    `
    ;
    }
    }
    }

    // 批量加载多个base64图片(比如列表页)
    async function loadMultipleBase64Images(apiUrls, container) {
    // 创建一个DocumentFragment,减少DOM操作次数
    const fragment = document.createDocumentFragment();

    // 用Promise.allSettled,即使某个失败了也不影响其他的
    const results = await Promise.allSettled(
    apiUrls.map(url => axios.get(url))
    );

    results.forEach((result, index) => {
    const wrapper = document.createElement('div');
    wrapper.className = 'image-item';

    if (result.status === 'fulfilled') {
    const { data } = result.value.data;
    if (data && data.image) {
    const img = document.createElement('img');
    const cleanBase64 = data.image.replace(/[\\s\\r\\n]+/g, '');
    img.src = `data:image/${data.type || 'png'};base64,${cleanBase64}`;
    img.alt = `图片-${index}`;
    img.style.width = '200px';
    img.style.margin = '10px';
    wrapper.appendChild(img);
    } else {
    wrapper.innerHTML = `<p>图片${index}数据格式错误</p>`;
    }
    } else {
    wrapper.innerHTML = `<p>图片${index}加载失败: ${result.reason.message}</p>`;
    }

    fragment.appendChild(wrapper);
    });

    container.appendChild(fragment);
    }

    // 使用示例
    fetchAndDisplayBase64Image('/api/user/avatar', document.getElementById('avatar-img'));

    // 批量加载
    loadMultipleBase64Images(
    ['/api/image/1', '/api/image/2', '/api/image/3'],
    document.getElementById('gallery')
    );

    看到上面那段代码了吗?我写了多少防御性判断?if (!data || !data.image)、if (typeof base64String !== 'string'),这些都是血泪教训啊。后端接口说变就变,今天返回data.image,明天可能就改成data.base64Image,你不做判断,直接data.image.replace(),万一data是undefined,直接报错,页面白屏,用户截图发群里,老板@你,这酸爽…

    还有那个Promise.allSettled,批量加载的时候一定要用,别用Promise.all。后者只要有一个失败,全部凉凉,前者至少能保证其他的图片正常显示。

    这方案看着香其实全是坑

    优点嘛,确实有,但不能只看表面

    base64方案最大的卖点就是"减少HTTP请求"。传统的图片加载是这样的:

  • 浏览器下载HTML
  • 解析到img标签的src
  • 发起HTTP请求去下载图片
  • 下载完成后解码显示
  • 而用base64的话,步骤变成了:

  • 浏览器下载HTML(或者JSON接口数据,里面已经包含了base64)
  • 解析到Data URI,直接解码显示
  • 省掉了那次HTTP请求,对于小图标来说,确实快。因为HTTP请求是有开销的,建立连接、发送请求头、等待响应,这些时间加起来可能比下载一个小图标还长。所以对于那些只有几百字节的图标,base64确实香。

    // 来做个简单的性能对比(伪代码,实际要看Network面板)
    // 方案A:传统URL
    // 请求头开销大约500字节 + 图片本身200字节 = 700字节,外加RTT时间

    // 方案B:base64
    // 图片200字节 -> base64后约267字节,直接包含在HTML里,没有额外请求

    // 结论:小图(<1KB)用base64确实划算
    // 大图(>10KB)就不划算了,base64膨胀33%,而且无法利用浏览器缓存

    缺点得好好吐槽,全是泪

    1. 体积膨胀,流量刺客

    前面说了,base64会让数据膨胀33%。一张1MB的图片,转成base64后就变成1.33MB。用户如果是用流量访问,这多出来的330KB就是真金白银啊。而且这330KB是文本数据,gzip压缩对它的效果不如二进制数据好。

    2. 解码性能,CPU杀手

    浏览器拿到base64字符串后,要先解码成二进制,再解码成图片像素。这个过程是在主线程上进行的,如果图片很大,或者数量很多,会直接阻塞UI,页面卡顿、掉帧,用户以为你网站挂了。

    // 来,咱们写个简单的性能测试
    function testBase64Performance() {
    // 生成一个较大的base64字符串(模拟500KB的图片)
    const size = 500 * 1024; // 500KB
    const binaryData = new Uint8Array(size);
    for (let i = 0; i < size; i++) {
    binaryData[i] = Math.floor(Math.random() * 256);
    }

    // 转成base64(浏览器提供的btoa只能处理字符串,得先转)
    const base64 = btoa(String.fromCharCode.apply(null, binaryData));

    console.log('开始解码测试,base64长度:', base64.length);
    console.time('decodeTime');

    // 创建图片并设置src
    const img = new Image();
    img.onload = () => {
    console.timeEnd('decodeTime'); // 看看用了多久
    console.log('图片尺寸:', img.naturalWidth, 'x', img.naturalHeight);
    };
    img.src = `data:image/jpeg;base64,${base64}`;
    }

    // 在控制台跑一下这个,看看解码500KB的base64要多久
    // 如果超过16ms(一帧的时间),用户就能感知到卡顿

    3. 缓存机制,基本等于没有

    用URL加载的图片,浏览器会自动缓存,下次再访问直接从缓存拿,304都省了。但base64图片的数据是直接写在HTML或者JS里的,除非整个HTML/JS被缓存,否则每次都要重新下载、重新解码。而且就算HTML被缓存了,base64解码的过程每次都要重新来一遍,CPU该烧还是烧。

    4. 内存占用,移动端杀手

    base64字符串是文本,存在JavaScript的堆内存里。一张1MB的图片,base64后1.33MB,解码成Image对象后,还要占用宽高×4字节的内存(RGBA四个通道)。一张1920×1080的图片,解码后就要占用约8MB内存(1920×1080×4)。如果页面里有十几张这样的图,移动端浏览器直接崩溃给你看,尤其是iOS的WebView,内存限制特别严格。

    5. 开发和维护成本

    你想啊,代码里全是这种:

    const imgSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA…(此处省略一万字)…Jggg==";

    这谁看得懂?想改个图,得重新生成base64,找对应的字符串替换,一不小心就改错了。而且代码审查的时候,这坨东西根本没法review,只能相信生成它的人没搞错。

    真实项目里那些让人头秃的场景

    列表页一次性加载几十张base64,浏览器直接假死

    这是我踩过最大的坑,没有之一。当时做的是个电商后台管理系统,商品列表页要显示商品缩略图。后端大哥图省事,直接把图片转成了base64塞在接口里返回。我想着,缩略图嘛,能有多大?结果一测试,列表一页显示50条数据,每条带一张图,页面直接卡死。

    为啥?50张图,每张就算100KB,base64后130KB,总共6.5MB的文本数据。接口返回就要好几秒,然后浏览器解析这6.5MB的JSON,再解码50张图片,主线程直接被占满,页面假死十几秒。用户在那疯狂点击,以为网站挂了,实际上浏览器正在那吭哧吭哧地干活。

    // 错误的示范:一次性渲染所有base64图片
    function renderProductList(products) {
    const container = document.getElementById('product-list');
    let html = '';

    // 千万别这么干!如果products有50个,直接卡死
    products.forEach(product => {
    html += `
    <div class="product-item">
    <img src="data:image/jpeg;base64,
    ${product.base64Image}" alt="${product.name}">
    <h3>
    ${product.name}</h3>
    <p>¥
    ${product.price}</p>
    </div>
    `
    ;
    });

    container.innerHTML = html; // 这一行执行的时候,浏览器要解码所有图片
    }

    // 稍微好点的方案:虚拟滚动 + 懒加载
    function renderProductListSmart(products) {
    const container = document.getElementById('product-list');

    // 只渲染视口内的图片,其他的等滚动到再说
    const viewportHeight = window.innerHeight;
    const itemHeight = 200; // 每个商品卡片高度
    const visibleCount = Math.ceil(viewportHeight / itemHeight) + 2; // 多渲染2个缓冲

    let html = '';
    products.slice(0, visibleCount).forEach((product, index) => {
    html += `
    <div class="product-item" data-index="
    ${index}">
    <!– 先用占位图 –>
    <img src="placeholder.jpg" data-base64="
    ${product.base64Image}" class="lazy-img" alt="${product.name}">
    <h3>
    ${product.name}</h3>
    <p>¥
    ${product.price}</p>
    </div>
    `
    ;
    });

    container.innerHTML = html;

    // 懒加载逻辑:当图片进入视口时再加载base64
    const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
    if (entry.isIntersecting) {
    const img = entry.target;
    const base64 = img.getAttribute('data-base64');
    if (base64) {
    // 延迟一点加载,避免同时解码太多
    setTimeout(() => {
    img.src = `data:image/jpeg;base64,${base64}`;
    img.removeAttribute('data-base64');
    }, Math.random() * 500); // 随机延迟0-500ms,分散解码压力
    }
    observer.unobserve(img);
    }
    });
    });

    document.querySelectorAll('.lazy-img').forEach(img => observer.observe(img));
    }

    看到没,解决方案就是不要一次性渲染所有base64图片。用虚拟滚动(virtual scrolling)只渲染视口内的,或者用Intersection Observer做懒加载,等图片要显示了再解码。而且加了个随机延迟,避免同时解码多张图导致卡顿。

    上传预览功能,本地转base64展示没问题,一提交给后端就报错

    文件上传前的本地预览,用base64很方便。FileReader的readAsDataURL方法直接就能拿到Data URI,塞给img标签就能预览。但这里有个大坑:前端拿到的base64是带data:image/jpeg;base64,前缀的,而后端可能只需要后面的纯base64字符串。

    // 文件上传预览的经典代码
    document.getElementById('file-input').addEventListener('change', function(e) {
    const file = e.target.files[0];
    if (!file) return;

    const reader = new FileReader();

    reader.onload = function(event) {
    // 这里拿到的result是完整的Data URI
    const dataUri = event.target.result;
    console.log('预览用的Data URI:', dataUri);
    // 输出:data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD…

    // 设置到img标签预览,没问题
    document.getElementById('preview').src = dataUri;

    // 但是提交给后端的时候,得把前缀去掉!
    const base64String = dataUri.split(',')[1]; // 取逗号后面的部分

    // 提交给后端
    uploadToServer(base64String);
    };

    reader.readAsDataURL(file);
    });

    async function uploadToServer(base64String) {
    try {
    const response = await fetch('/api/upload', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json'
    },
    body: JSON.stringify({
    image: base64String,
    fileName: 'avatar.jpg'
    })
    });

    const result = await response.json();
    console.log('上传结果:', result);
    } catch (error) {
    console.error('上传失败:', error);
    }
    }

    还有一个坑是文件大小限制。前端转base64的时候,大文件会导致浏览器卡顿。而且后端接收base64数据时,JSON解析也有大小限制,有些服务器配置默认最多接收1MB的JSON,你传个5MB的base64过去,直接413 Payload Too Large。

    // 加个文件大小检查,避免转太大的base64
    function handleFileSelect(file) {
    const MAX_SIZE = 2 * 1024 * 1024; // 2MB限制

    if (file.size > MAX_SIZE) {
    alert('文件太大,请选择2MB以下的图片');
    return;
    }

    // 还可以先压缩一下再转base64
    compressImage(file, 800, 600).then(compressedFile => {
    // 压缩后再转base64,体积会小很多
    return fileToBase64(compressedFile);
    }).then(base64 => {
    // 预览和上传…
    });
    }

    // 图片压缩函数(用canvas实现)
    function compressImage(file, maxWidth, maxHeight) {
    return new Promise((resolve) => {
    const img = new Image();
    const url = URL.createObjectURL(file);

    img.onload = function() {
    URL.revokeObjectURL(url); // 释放内存

    let { width, height } = img;

    // 等比例缩放
    if (width > maxWidth) {
    height = (height * maxWidth) / width;
    width = maxWidth;
    }
    if (height > maxHeight) {
    width = (width * maxHeight) / height;
    height = maxHeight;
    }

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0, width, height);

    // 转成blob,质量0.8的JPEG
    canvas.toBlob((blob) => {
    resolve(new File([blob], file.name, { type: 'image/jpeg' }));
    }, 'image/jpeg', 0.8);
    };

    img.src = url;
    });
    }

    混合开发里,H5页面嵌在App里,base64图片太大导致WebView直接崩溃

    做Hybrid App的老铁肯定懂这个痛。iOS的WKWebView和Android的WebView都有内存限制,尤其是iOS,单个App的内存占用超过一定阈值(比如几百MB),系统直接给你杀掉,用户看到的就是App闪退。

    如果H5页面里用了大量base64图片,内存占用会飙升。因为base64字符串存在JS的堆里,解码后的Image对象又占一份内存,WebView的渲染层还要存一份位图数据,一份图片三份内存,这谁顶得住?

    // 在Hybrid App里的优化方案
    // 1. 尽量用原生图片组件,而不是H5的img标签
    // 2. 如果必须用H5,控制base64图片的数量和大小

    // 内存管理:当图片离开视口时,释放内存
    function setupMemoryManagement() {
    const imageCache = new Map(); // 缓存已经加载的图片

    const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
    const img = entry.target;

    if (entry.isIntersecting) {
    // 进入视口,加载图片
    const base64 = img.getAttribute('data-base64');
    if (base64 && !imageCache.has(img)) {
    img.src = `data:image/jpeg;base64,${base64}`;
    imageCache.set(img, true);
    }
    } else {
    // 离开视口,释放内存(把src设为空或者占位图)
    if (imageCache.has(img)) {
    img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; // 1×1透明gif
    imageCache.delete(img);

    // 强制垃圾回收(虽然JS没有直接GC的API,但这样可以释放引用)
    if (img.dataset) {
    delete img.dataset.base64;
    }
    }
    }
    });
    }, {
    rootMargin: '100px' // 提前100px开始加载,延迟100px释放
    });

    document.querySelectorAll('img[data-base64]').forEach(img => {
    observer.observe(img);
    });
    }

    // 页面不可见时(比如切换到其他Tab),暂停所有图片加载
    document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
    // 页面隐藏,可以暂停一些非关键的图片解码
    console.log('页面隐藏,节省内存');
    } else {
    console.log('页面显示,恢复加载');
    }
    });

    出问题了别只会重启大法好

    图片显示个裂开的图标?赶紧查查前缀

    这是最常见的问题,控制台可能还没报错,但图片就是显示不出来,是个裂开的图标或者空白。这时候先右键检查元素,看看src属性是不是完整的Data URI。

    常见问题:

  • 漏了MIME类型:data:base64,xxx 应该是 data:image/png;base64,xxx
  • MIME类型写错了:后端给的是jpg,你写成了png,有时候浏览器能自动识别,有时候就不行
  • base64字符串不完整:被截断了,或者复制的时候漏了开头结尾
  • // 调试工具:检查base64字符串是否合法
    function validateBase64(base64String, expectedMimeType = 'image/png') {
    const issues = [];

    // 检查是否为空
    if (!base64String || base64String.length === 0) {
    issues.push('base64字符串为空');
    return issues;
    }

    // 检查长度(base64长度应该是4的倍数)
    if (base64String.length % 4 !== 0) {
    issues.push(`长度不是4的倍数,当前长度:${base64String.length}`);
    }

    // 检查是否有非法字符(base64只能包含A-Z a-z 0-9 + / =)
    const invalidChars = base64String.match(/[^A-Za-z0-9+/=]/g);
    if (invalidChars) {
    issues.push(`包含非法字符: ${[new Set(invalidChars)].join(', ')}`);
    }

    // 检查填充符=的位置(只能在末尾,最多两个)
    const paddingMatch = base64String.match(/=+$/);
    const paddingCount = paddingMatch ? paddingMatch[0].length : 0;
    if (paddingCount > 2) {
    issues.push(`填充符=超过2个,当前:${paddingCount}`);
    }
    if (base64String.includes('=') && !base64String.endsWith('=')) {
    issues.push('填充符=不在末尾');
    }

    // 尝试解码(在浏览器环境里)
    try {
    atob(base64String);
    } catch (e) {
    issues.push(`解码失败: ${e.message}`);
    }

    if (issues.length > 0) {
    console.error('base64验证失败:', issues);
    } else {
    console.log('base64格式正确,长度:', base64String.length);
    }

    return issues;
    }

    // 使用
    const problematicBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbybl\\nAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
    validateBase64(problematicBase64); // 会提示包含非法字符(换行符)

    控制台报"Invalid source"?多半是字符串脏了

    如果控制台报错"Failed to load resource: net::ERR_INVALID_URL"或者"Invalid source",多半是base64字符串里混进了换行符、空格或者其他不可见字符。有些后端语言(比如Java)在生成JSON的时候,为了可读性会自动给长字符串加换行,或者有些数据库字段类型是TEXT,存进去的时候没问题,取出来就带了换行。

    // 清洗函数,专治各种脏数据
    function sanitizeBase64(dirtyBase64) {
    if (typeof dirtyBase64 !== 'string') {
    console.warn('传入的不是字符串:', typeof dirtyBase64);
    return '';
    }

    // 第一步:去掉所有空白字符(空格、制表符、换行、回车)
    let clean = dirtyBase64.replace(/\\s+/g, '');

    // 第二步:去掉URL安全base64的替代字符(有些后端会用-和_代替+和/)
    // 如果是标准base64,这步可以省略
    // clean = clean.replace(/-/g, '+').replace(/_/g, '/');

    // 第三步:检查并修复填充符
    const paddingNeeded = 4 (clean.length % 4);
    if (paddingNeeded !== 4) {
    // 长度不是4的倍数,补=
    clean += '='.repeat(paddingNeeded);
    console.warn('base64长度不是4的倍数,已自动补全');
    }

    // 第四步:去掉可能存在的Data URI前缀(如果后端不小心把前缀也塞进来了)
    if (clean.includes('base64,')) {
    clean = clean.split('base64,')[1];
    console.warn('base64字符串包含Data URI前缀,已自动去除');
    }

    return clean;
    }

    // 测试
    const dirty = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbybl\\nAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg== ";
    console.log('清洗前长度:', dirty.length);
    console.log('清洗后长度:', sanitizeBase64(dirty).length);

    页面卡顿严重?打开Performance面板看看

    如果页面卡成PPT,别瞎猜,打开Chrome DevTools的Performance面板,录个性能分析看看。重点看Main线程的CPU占用,如果看到一长条的"Decode Image"或者"Parse HTML",那就是base64解码在搞事情。

    // 优化方案:把解码放到Web Worker里(如果浏览器支持)
    // 注意:Web Worker里不能操作DOM,只能解码后把ImageBitmap传回来

    // worker.js
    self.onmessage = function(e) {
    const { base64, mimeType, id } = e.data;

    // 在Worker里解码base64(这里只是示例,实际解码图片需要更复杂的逻辑)
    // 实际上浏览器解码图片是在渲染进程的主线程,Worker里没法直接解码成ImageBitmap
    // 但我们可以把base64转成Blob,然后用createImageBitmap(这个API可以在Worker里用)

    const byteCharacters = atob(base64);
    const byteNumbers = new Array(byteCharacters.length);
    for (let i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
    }
    const byteArray = new Uint8Array(byteNumbers);
    const blob = new Blob([byteArray], { type: mimeType });

    createImageBitmap(blob).then(imageBitmap => {
    self.postMessage({ id, imageBitmap }, [imageBitmap]);
    });
    };

    // 主线程代码
    const worker = new Worker('worker.js');

    function decodeBase64InWorker(base64, mimeType) {
    return new Promise((resolve) => {
    const id = Date.now() + Math.random();

    worker.onmessage = function(e) {
    if (e.data.id === id) {
    resolve(e.data.imageBitmap);
    }
    };

    worker.postMessage({ base64, mimeType, id });
    });
    }

    // 使用
    decodeBase64InWorker(base64String, 'image/png').then(imageBitmap => {
    // 在Canvas上绘制,避免创建img元素(省内存)
    const canvas = document.createElement('canvas');
    canvas.width = imageBitmap.width;
    canvas.height = imageBitmap.height;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(imageBitmap, 0, 0);

    // 如果非要显示在img标签里,可以转成blob URL(但这样又多了一步)
    canvas.toBlob(blob => {
    const url = URL.createObjectURL(blob);
    document.getElementById('img').src = url;
    });
    });

    说实话,Web Worker这个方案有点过度设计了,大部分场景用不到。但如果你在做图片编辑器那种需要处理大量大图的应用,可以考虑。

    几个让代码看起来不那么菜的骚操作

    写个工具函数自动判断MIME类型

    别硬编码image/png,万一后端返个gif或者webp,你写死了就抓瞎。可以根据base64字符串的特征来判断,或者让后端把MIME类型也返回。

    /**
    * 自动检测base64图片的MIME类型
    * 通过文件头的magic number来判断
    */

    function detectMimeType(base64String) {
    // 取前几个字符解码,看文件头
    const header = atob(base64String.substring(0, 20));
    const bytes = header.split('').map(c => c.charCodeAt(0));

    // 文件签名(magic numbers)
    const signatures = {
    'image/png': [0x89, 0x50, 0x4E, 0x47], // ‰PNG
    'image/jpeg': [0xFF, 0xD8, 0xFF], // ÿØÿ
    'image/gif': [0x47, 0x49, 0x46], // GIF
    'image/webp': [0x52, 0x49, 0x46, 0x46], // RIFF(webp也是RIFF格式)
    'image/bmp': [0x42, 0x4D], // BM
    'image/svg+xml': [0x3C, 0x73, 0x76, 0x67] // <svg(这个不太准,base64编码后可能不是这个)
    };

    for (const [mime, signature] of Object.entries(signatures)) {
    let match = true;
    for (let i = 0; i < signature.length; i++) {
    if (bytes[i] !== signature[i]) {
    match = false;
    break;
    }
    }
    if (match) return mime;
    }

    // 默认返回png
    return 'image/png';
    }

    // 更实用的方案:根据后缀名或者后端返回的类型
    function getMimeTypeFromFilename(filename) {
    const ext = filename.split('.').pop().toLowerCase();
    const mimeTypes = {
    'png': 'image/png',
    'jpg': 'image/jpeg',
    'jpeg': 'image/jpeg',
    'gif': 'image/gif',
    'webp': 'image/webp',
    'svg': 'image/svg+xml',
    'bmp': 'image/bmp',
    'ico': 'image/x-icon'
    };
    return mimeTypes[ext] || 'image/png';
    }

    // 终极工具函数:智能处理各种情况
    function createDataUri(base64String, options = {}) {
    // 清洗数据
    let cleanBase64 = base64String.replace(/[\\s\\r\\n]+/g, '');

    // 如果已经有data URI前缀了,直接返回
    if (cleanBase64.startsWith('data:')) {
    return cleanBase64;
    }

    // 确定MIME类型
    let mimeType = options.mimeType;
    if (!mimeType && options.filename) {
    mimeType = getMimeTypeFromFilename(options.filename);
    }
    if (!mimeType) {
    mimeType = detectMimeType(cleanBase64);
    }

    return `data:${mimeType};base64,${cleanBase64}`;
    }

    // 使用
    const dataUri = createDataUri(base64String, { filename: 'avatar.png' });
    // 或者
    const dataUri2 = createDataUri(base64String, { mimeType: 'image/jpeg' });

    加个阈值判断,超过多少KB坚决不转base64

    前面说了,base64适合小图,大图走CDN。那多大的图算"大图"?一般来说,10KB以下的可以考虑base64,10KB以上的坚决走URL。这个阈值可以根据项目调整,但别超过50KB,否则就是跟自己过不去。

    /**
    * 智能选择图片加载方式
    * @param {string} base64String – base64字符串
    * @param {string} fallbackUrl – 如果base64太大,使用的备用URL
    * @param {number} threshold – 阈值,默认10KB
    */

    function smartImageLoad(base64String, fallbackUrl, threshold = 10 * 1024) {
    // 计算base64大概的原始大小(base64长度*0.75,因为4个字符代表3个字节)
    const estimatedSize = base64String.length * 0.75;

    if (estimatedSize > threshold) {
    console.warn(`图片大小${(estimatedSize/1024).toFixed(2)}KB超过阈值${threshold/1024}KB,建议使用URL`);
    return {
    type: 'url',
    src: fallbackUrl,
    reason: 'too_large'
    };
    }

    const mimeType = detectMimeType(base64String);
    return {
    type: 'base64',
    src: `data:${mimeType};base64,${base64String}`,
    size: estimatedSize
    };
    }

    // 在React组件里使用
    function SmartImage({ base64Data, fallbackUrl, alt, threshold = 10 * 1024 }) {
    const imageInfo = smartImageLoad(base64Data, fallbackUrl, threshold);

    if (imageInfo.type === 'url') {
    // 如果base64太大,显示一个提示,或者直接用URL
    return (
    <div>
    <img src={fallbackUrl} alt={alt} loading="lazy" />
    <small style={{color: 'gray'}}>大图已优化加载</small>
    </div>
    );
    }

    return <img src={imageInfo.src} alt={alt} />;
    }

    利用CSS变量或者预处理器搞点批量处理

    如果你在用Vue或者React,模板里全是data:image/png;base64,xxx这种,看着就烦。可以用CSS变量或者组件化的方式封装一下。

    // SCSS里定义一些常用的base64小图标(比如1×1的透明图、loading图)
    $transparent-gif: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';

    // 使用
    .placeholder {
    background-image: url($transparent-gif);
    }

    // Vue组件封装
    // Base64Image.vue
    <template>
    <img
    :src="dataUri"
    :alt="alt"
    :class="['base64-img', { 'is-loading': loading }]"
    @load="onLoad"
    @error="onError"
    />
    </template>

    <script>
    export default {
    name: 'Base64Image',
    props: {
    base64: {
    type: String,
    required: true
    },
    mimeType: {
    type: String,
    default: 'image/png'
    },
    alt: {
    type: String,
    default: ''
    },
    // 是否自动检测MIME类型
    autoDetect: {
    type: Boolean,
    default: true
    }
    },
    data() {
    return {
    loading: true,
    error: false
    };
    },
    computed: {
    dataUri() {
    let cleanBase64 = this.base64.replace(/[\\s\\r\\n]+/g, '');

    // 如果已经有前缀,直接返回
    if (cleanBase64.startsWith('data:')) {
    return cleanBase64;
    }

    let mime = this.mimeType;
    if (this.autoDetect) {
    // 简单的检测逻辑,实际项目中可以完善
    if (cleanBase64.charAt(0) === '/') mime = 'image/jpeg';
    else if (cleanBase64.charAt(0) === 'i') mime = 'image/png';
    else if (cleanBase64.charAt(0) === 'R') mime = 'image/gif';
    }

    return `data:${mime};base64,${cleanBase64}`;
    }
    },
    methods: {
    onLoad() {
    this.loading = false;
    this.$emit('load');
    },
    onError(e) {
    this.error = true;
    this.loading = false;
    console.error('Base64图片加载失败:', e);
    this.$emit('error', e);
    }
    }
    };
    </script>

    <style scoped>
    .base64-img {
    transition: opacity 0.3s;
    }
    .is-loading {
    opacity: 0;
    background: #f0f0f0;
    }
    </style>

    这样封装后,使用的时候就很清爽了:

    <Base64Image
    :base64="user.avatar"
    mimeType="image/jpeg"
    alt="用户头像"
    @load="handleLoad"
    />

    最后啰嗦一句保命秘籍

    好了,说了这么多,最后总结几句掏心窝子的话:

    1. 别啥图都转base64,那是十年前的玩法

    现在带宽这么便宜,CDN这么普及,WebP格式压缩率这么高,还抱着base64不放干啥?base64只适合那种特别小的图标(<1KB),或者必须内联的场景(比如HTML邮件、单文件离线应用)。其他的,老老实实走URL,让浏览器缓存去干活。

    2. 要是老板非逼着你全转,就把这篇甩给他看

    有些产品经理或者老板,听风就是雨,看到某个文章说"base64减少HTTP请求能优化性能",就要求全站图片转base64。这时候你别硬刚,把这篇转给他,让他看看"减少HTTP请求"的代价是什么。数据说话,把页面加载时间、内存占用、流量消耗的对比数据甩他脸上,告诉他这是拿用户体验在换所谓的"简洁"。

    3. 后端要是坚持给你返base64,让他至少把MIME类型带上

    最烦那种只返一串base64,啥说明都没有的接口。你问他这是啥格式,他说"你试试不就知道了"。试试?我试你个大头鬼!规范的后端接口应该长这样:

    {
    "image": {
    "data": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
    "mimeType": "image/png",
    "size": 167, // 原始字节数
    "filename": "dot.png"
    }
    }

    4. 实在不行就摸鱼吧

    如果这需求改起来是无底洞,后端不配合,老板不理解,用户还天天骂,那…不如早点下班去吃火锅。身体是自己的,bug是改不完的,base64是杀不尽的。留得青山在,不怕没柴烧,明天又是新的一天(和新的bug)。

    行了,就唠到这儿。希望下次你再遇到base64的坑,能淡定地泡杯咖啡,慢悠悠地说:"哦,这个啊,我熟。"然后复制粘贴上面的代码,搞定收工。

    毕竟,咱们前端工程师的终极目标,不就是让代码能跑,让自己能下班嘛。


    (全文完,字数统计:约7200字,应该够你交差了吧?)

    欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。

    推荐:DTcode7的博客首页。 一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!


    专栏系列(点击解锁)学习路线(点击解锁)知识定位
    《微信小程序相关博客》 持续更新中~ 结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等
    《AIGC相关博客》 持续更新中~ AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结
    《HTML网站开发相关》 《前端基础入门三大核心之html相关博客》 前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识
    《前端基础入门三大核心之JS相关博客》 前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心
    《前端基础入门三大核心之CSS相关博客》 介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页
    《canvas绘图相关博客》 Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化
    《Vue实战相关博客》 持续更新中~ 详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅
    《python相关博客》 持续更新中~ Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具
    《sql数据库相关博客》 持续更新中~ SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能
    《算法系列相关博客》 持续更新中~ 算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维
    《IT信息技术相关博客》 持续更新中~ 作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识
    《信息化人员基础技能知识相关博客》 无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方
    《信息化技能面试宝典相关博客》 涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面
    《前端开发习惯与小技巧相关博客》 持续更新中~ 罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等
    《photoshop相关博客》 持续更新中~ 基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结
    日常开发&办公&生产【实用工具】分享相关博客》 持续更新中~ 分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具

    吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤 非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

    在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » 前端老铁别慌:img标签src调API返回base64字符串的土法炼钢指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址