创建 SVG 图标预览页面:从零实现到解决 CORS 问题

文章来源声明: 原文作者:一曲终散; 来源站点:掘金; 原文链接:https://juejin.cn/post/7690775846018039834; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

适合需要快速自建图标预览工具的开发者。核心价值在于绕过 AJAX、直接复用已注入 DOM 的 SVG symbol,避开 file:// 跨域限制,思路简洁可迁移到类似静态页工具。

创建 SVG 图标预览页面:从零实现到解决 CORS 问题 -----------------------------

问题描述

在项目开发中,我们有一个包含大量 SVG 图标的 icon.js 文件,这些图标以 SVG <symbol> 的形式存储。为了方便开发团队查看和使用这些图标,我们需要创建一个静态 HTML 页面来预览所有图标。

需求分析

  1. 展示所有图标:以网格形式展示 icon.js 中的所有 SVG 图标
  2. 图标预览:每个图标以合适的大小显示,方便查看
  3. 点击复制:点击图标可以复制使用代码,提高开发效率
  4. 统计信息:显示图标总数
  5. 美观的界面:提供良好的用户体验

实现过程

第一步:分析 icon.js 文件结构

首先,我们需要了解 icon.js 文件的工作原理。查看文件内容发现:

!<span>function</span>(<span>e</span>){
  <span>var</span> t,n,d,o,i,a,r=<span>'<svg><symbol id="success" viewBox="0 0 48 48" fill="none">...</symbol>...'</span>;
  
  t=<span>function</span>(<span></span>){
    <span>var</span> e,t,n;
    (n=<span>document</span>.<span>createElement</span>(<span>"div"</span>)).<span>innerHTML</span>=r,
    r=<span>null</span>,
    (t=n.<span>getElementsByTagName</span>(<span>"svg"</span>)[<span>0</span>])&&(
      t.<span>setAttribute</span>(<span>"aria-hidden"</span>,<span>"true"</span>),
      t.<span>style</span>.<span>position</span>=<span>"absolute"</span>,
      t.<span>style</span>.<span>width</span>=<span>0</span>,
      t.<span>style</span>.<span>height</span>=<span>0</span>,
      t.<span>style</span>.<span>overflow</span>=<span>"hidden"</span>,
      e=t,
      (n=<span>document</span>.<span>body</span>).<span>firstChild</span>?
        (t=n.<span>firstChild</span>).<span>parentNode</span>.<span>insertBefore</span>(e,t):
        n.<span>appendChild</span>(e)
    )
  };
  
  <span>document</span>.<span>addEventListener</span>?
    [<span>"complete"</span>,<span>"loaded"</span>,<span>"interactive"</span>].<span>indexOf</span>(<span>document</span>.<span>readyState</span>)>-<span>1</span>?
      <span>setTimeout</span>(t,<span>0</span>):
      (n=<span>function</span>(<span></span>){
        <span>document</span>.<span>removeEventListener</span>(<span>"DOMContentLoaded"</span>,n,!<span>1</span>),
        <span>t</span>()
      },
      <span>document</span>.<span>addEventListener</span>(<span>"DOMContentLoaded"</span>,n,!<span>1</span>)):
    <span>document</span>.<span>attachEvent</span>&&<span>/* IE8 及以下处理 */</span>;
}(<span>window</span>);

关键发现:

  • icon.js 是一个立即执行函数
  • 它将 SVG 内容存储在变量 r 中
  • 通过创建一个临时 div,将 SVG 插入到 DOM 中
  • SVG 被设置为隐藏状态(position: absolute, width: 0, height: 0)
  • 在页面加载完成后自动执行

第二步:创建基础 HTML 结构

创建 public/icon-preview.html 文件,包含基本的 HTML 结构和样式:

<span>html</span>></span>
<span><<span>html</span> <span>lang</span>=<span>"zh-CN"</span>></span>
<span><<span>head</span>></span>
    <span><<span>meta</span> <span>charset</span>=<span>"UTF-8"</span>></span>
    <span><<span>meta</span> <span>name</span>=<span>"viewport"</span> <span>content</span>=<span>"width=device-width, initial-scale=1.0"</span>></span>
    <span><<span>title</span>></span>Icon Preview<span></<span>title</span>></span>
    <span><<span>style</span>></span><span>
        * {
            <span>margin</span>: <span>0</span>;
            <span>padding</span>: <span>0</span>;
            <span>box-sizing</span>: border-box;
        }
        <span>body</span> {
            <span>font-family</span>: -apple-system, BlinkMacSystemFont, <span>'Segoe UI'</span>, Roboto, <span>'Helvetica Neue'</span>, Arial, sans-serif;
            <span>background</span>: <span>#f5f5f5</span>;
            <span>padding</span>: <span>20px</span>;
        }
        <span>.header</span> {
            <span>text-align</span>: center;
            <span>margin-bottom</span>: <span>40px</span>;
        }
        <span>.header</span> <span>h1</span> {
            <span>color</span>: <span>#333</span>;
            <span>font-size</span>: <span>32px</span>;
            <span>margin-bottom</span>: <span>10px</span>;
        }
        <span>.header</span> <span>p</span> {
            <span>color</span>: <span>#666</span>;
            <span>font-size</span>: <span>14px</span>;
        }
        <span>.container</span> {
            <span>max-width</span>: <span>1200px</span>;
            <span>margin</span>: <span>0</span> auto;
        }
        <span>.icon-grid</span> {
            <span>display</span>: grid;
            <span>grid-template-columns</span>: <span>repeat</span>(auto-fill, <span>minmax</span>(<span>150px</span>, <span>1</span>fr));
            <span>gap</span>: <span>20px</span>;
        }
        <span>.icon-card</span> {
            <span>background</span>: white;
            <span>border-radius</span>: <span>8px</span>;
            <span>padding</span>: <span>20px</span>;
            <span>text-align</span>: center;
            <span>box-shadow</span>: <span>0</span> <span>2px</span> <span>8px</span> <span>rgba</span>(<span>0</span>,<span>0</span>,<span>0</span>,<span>0.1</span>);
            <span>transition</span>: transform <span>0.2s</span>, box-shadow <span>0.2s</span>;
            <span>cursor</span>: pointer;
        }
        <span>.icon-card</span><span>:hover</span> {
            <span>transform</span>: <span>translateY</span>(-<span>4px</span>);
            <span>box-shadow</span>: <span>0</span> <span>4px</span> <span>16px</span> <span>rgba</span>(<span>0</span>,<span>0</span>,<span>0</span>,<span>0.15</span>);
        }
        <span>.icon-preview</span> {
            <span>width</span>: <span>64px</span>;
            <span>height</span>: <span>64px</span>;
            <span>margin</span>: <span>0</span> auto <span>12px</span>;
            <span>display</span>: flex;
            <span>align-items</span>: center;
            <span>justify-content</span>: center;
        }
        <span>.icon-preview</span> svg {
            <span>width</span>: <span>100%</span>;
            <span>height</span>: <span>100%</span>;
        }
        <span>.icon-name</span> {
            <span>font-size</span>: <span>13px</span>;
            <span>color</span>: <span>#666</span>;
            <span>word-break</span>: break-all;
        }
        <span>.copy-hint</span> {
            <span>position</span>: fixed;
            <span>bottom</span>: <span>20px</span>;
            <span>right</span>: <span>20px</span>;
            <span>background</span>: <span>#333</span>;
            <span>color</span>: white;
            <span>padding</span>: <span>12px</span> <span>24px</span>;
            <span>border-radius</span>: <span>4px</span>;
            <span>opacity</span>: <span>0</span>;
            <span>transition</span>: opacity <span>0.3s</span>;
            <span>pointer-events</span>: none;
        }
        <span>.copy-hint</span><span>.show</span> {
            <span>opacity</span>: <span>1</span>;
        }
        <span>.stats</span> {
            <span>text-align</span>: center;
            <span>margin-bottom</span>: <span>30px</span>;
            <span>color</span>: <span>#666</span>;
            <span>font-size</span>: <span>14px</span>;
        }
    </span><span></<span>style</span>></span>
<span></<span>head</span>></span>
<span><<span>body</span>></span>
    <span><<span>div</span> <span>class</span>=<span>"header"</span>></span>
        <span><<span>h1</span>></span>Icon Preview<span></<span>h1</span>></span>
        <span><<span>p</span>></span>点击图标复制使用代码<span></<span>p</span>></span>
    <span></<span>div</span>></span>
    <span><<span>div</span> <span>class</span>=<span>"container"</span>></span>
        <span><<span>div</span> <span>class</span>=<span>"stats"</span> <span>id</span>=<span>"stats"</span>></span><span></<span>div</span>></span>
        <span><<span>div</span> <span>class</span>=<span>"icon-grid"</span> <span>id</span>=<span>"iconGrid"</span>></span><span></<span>div</span>></span>
    <span></<span>div</span>></span>
    <span><<span>div</span> <span>class</span>=<span>"copy-hint"</span> <span>id</span>=<span>"copyHint"</span>></span>已复制到剪贴板<span></<span>div</span>></span>
<span></<span>body</span>></span>
<span></<span>html</span>></span>

第三步:实现图标提取和渲染逻辑

添加 JavaScript 代码来提取和渲染图标:

<script src=<span>"icon.js"</span>></script>
<span><span><<span>script</span>></span><span>
    <span>const</span> iconGrid = <span>document</span>.<span>getElementById</span>(<span>'iconGrid'</span>);
    <span>const</span> copyHint = <span>document</span>.<span>getElementById</span>(<span>'copyHint'</span>);
    <span>const</span> stats = <span>document</span>.<span>getElementById</span>(<span>'stats'</span>);

    <span>function</span> <span>extractIconIds</span>(<span></span>) {
        <span>const</span> ids = [];
        <span>const</span> symbols = <span>document</span>.<span>querySelectorAll</span>(<span>'symbol'</span>);
        symbols.<span>forEach</span>(<span><span>symbol</span> =></span> {
            <span>const</span> id = symbol.<span>getAttribute</span>(<span>'id'</span>);
            <span>if</span> (id) {
                ids.<span>push</span>(id);
            }
        });
        <span>return</span> ids;
    }

    <span>function</span> <span>createIconCard</span>(<span>iconId</span>) {
        <span>const</span> card = <span>document</span>.<span>createElement</span>(<span>'div'</span>);
        card.<span>className</span> = <span>'icon-card'</span>;
        
        <span>const</span> preview = <span>document</span>.<span>createElement</span>(<span>'div'</span>);
        preview.<span>className</span> = <span>'icon-preview'</span>;
        preview.<span>innerHTML</span> = <span>`<svg><use href="#<span>${iconId}</span>"></use></svg>`</span>;
        
        <span>const</span> name = <span>document</span>.<span>createElement</span>(<span>'div'</span>);
        name.<span>className</span> = <span>'icon-name'</span>;
        name.<span>textContent</span> = iconId;
        
        card.<span>appendChild</span>(preview);
        card.<span>appendChild</span>(name);
        
        card.<span>addEventListener</span>(<span>'click'</span>, <span>() =></span> {
            <span>const</span> code = <span>`<svg><use href="#<span>${iconId}</span>"></use></svg>`</span>;
            navigator.<span>clipboard</span>.<span>writeText</span>(code).<span>then</span>(<span>() =></span> {
                <span>showCopyHint</span>();
            });
        });
        
        <span>return</span> card;
    }

    <span>function</span> <span>showCopyHint</span>(<span></span>) {
        copyHint.<span>classList</span>.<span>add</span>(<span>'show'</span>);
        <span>setTimeout</span>(<span>() =></span> {
            copyHint.<span>classList</span>.<span>remove</span>(<span>'show'</span>);
        }, <span>2000</span>);
    }

    <span>function</span> <span>init</span>(<span></span>) {
        <span>const</span> iconIds = <span>extractIconIds</span>();
        stats.<span>textContent</span> = <span>`共 <span>${iconIds.length}</span> 个图标`</span>;
        
        iconIds.<span>forEach</span>(<span><span>iconId</span> =></span> {
            <span>const</span> card = <span>createIconCard</span>(iconId);
            iconGrid.<span>appendChild</span>(card);
        });
    }

    <span>init</span>();
</span><span></<span>script</span>></span></span>

第四步:遇到 CORS 问题

直接在浏览器中打开 HTML 文件时,遇到了以下错误:

Access <span>to</span> XMLHttpRequest <span>at</span> <span>'file:///E:/all_projects/HuLa/public/icon.js'</span> 
<span>from</span> origin <span>'null'</span> has been blocked <span>by</span> CORS policy: 
<span>Cross</span> origin requests <span>are</span> <span>only</span> supported <span>for</span> protocol schemes: http, data, 
isolated<span>-</span>app, chrome<span>-</span>extension, chrome, https, chrome<span>-</span>untrusted.

问题原因:

  • 最初尝试使用 XMLHttpRequest 读取 icon.js 文件内容
  • 直接打开 HTML 文件(file:// 协议)时,浏览器出于安全考虑禁止跨域请求
  • 即使是同目录下的文件,也会被浏览器视为跨域

解决方案: 既然 icon.js 会自动将 SVG 插入到 DOM 中,我们不需要通过 AJAX 读取文件,而是直接从 DOM 中提取 <symbol> 元素。

第五步:优化初始化时机

由于 icon.js 是异步加载的,我们需要确保在 icon.js 执行完成后再初始化图标列表:

<span>function</span> <span>init</span>(<span></span>) {
    <span>const</span> iconIds = <span>extractIconIds</span>();
    stats.<span>textContent</span> = <span>`共 <span>${iconIds.length}</span> 个图标`</span>;
    
    iconIds.<span>forEach</span>(<span><span>iconId</span> =></span> {
        <span>const</span> card = <span>createIconCard</span>(iconId);
        iconGrid.<span>appendChild</span>(card);
    });
}

<span>if</span> (<span>document</span>.<span>readyState</span> === <span>'complete'</span>) {
    <span>setTimeout</span>(init, <span>100</span>);
} <span>else</span> {
    <span>window</span>.<span>addEventListener</span>(<span>'load'</span>, <span>() =></span> {
        <span>setTimeout</span>(init, <span>100</span>);
    });
}

关键改进:

  1. 检查 document.readyState 判断页面是否已加载完成
  2. 如果已完成,延迟 100ms 执行初始化
  3. 如果未完成,监听 load 事件后再执行
  4. 100ms 的延迟确保 icon.js 有足够时间将 SVG 插入 DOM

最终代码

完整的 icon-preview.html 文件:

<span>html</span>></span>
<span><<span>html</span> <span>lang</span>=<span>"zh-CN"</span>></span>
<span><<span>head</span>></span>
    <span><<span>meta</span> <span>charset</span>=<span>"UTF-8"</span>></span>
    <span><<span>meta</span> <span>name</span>=<span>"viewport"</span> <span>content</span>=<span>"width=device-width, initial-scale=1.0"</span>></span>
    <span><<span>title</span>></span>Icon Preview<span></<span>title</span>></span>
    <span><<span>style</span>></span><span>
        * {
            <span>margin</span>: <span>0</span>;
            <span>padding</span>: <span>0</span>;
            <span>box-sizing</span>: border-box;
        }
        <span>body</span> {
            <span>font-family</span>: -apple-system, BlinkMacSystemFont, <span>'Segoe UI'</span>, Roboto, <span>'Helvetica Neue'</span>, Arial, sans-serif;
            <span>background</span>: <span>#f5f5f5</span>;
            <span>padding</span>: <span>20px</span>;
        }
        <span>.header</span> {
            <span>text-align</span>: center;
            <span>margin-bottom</span>: <span>40px</span>;
        }
        <span>.header</span> <span>h1</span> {
            <span>color</span>: <span>#333</span>;
            <span>font-size</span>: <span>32px</span>;
            <span>margin-bottom</span>: <span>10px</span>;
        }
        <span>.header</span> <span>p</span> {
            <span>color</span>: <span>#666</span>;
            <span>font-size</span>: <span>14px</span>;
        }
        <span>.container</span> {
            <span>max-width</span>: <span>1200px</span>;
            <span>margin</span>: <span>0</span> auto;
        }
        <span>.icon-grid</span> {
            <span>display</span>: grid;
            <span>grid-template-columns</span>: <span>repeat</span>(auto-fill, <span>minmax</span>(<span>150px</span>, <span>1</span>fr));
            <span>gap</span>: <span>20px</span>;
        }
        <span>.icon-card</span> {
            <span>background</span>: white;
            <span>border-radius</span>: <span>8px</span>;
            <span>padding</span>: <span>20px</span>;
            <span>text-align</span>: center;
            <span>box-shadow</span>: <span>0</span> <span>2px</span> <span>8px</span> <span>rgba</span>(<span>0</span>,<span>0</span>,<span>0</span>,<span>0.1</span>);
            <span>transition</span>: transform <span>0.2s</span>, box-shadow <span>0.2s</span>;
            <span>cursor</span>: pointer;
        }
        <span>.icon-card</span><span>:hover</span> {
            <span>transform</span>: <span>translateY</span>(-<span>4px</span>);
            <span>box-shadow</span>: <span>0</span> <span>4px</span> <span>16px</span> <span>rgba</span>(<span>0</span>,<span>0</span>,<span>0</span>,<span>0.15</span>);
        }
        <span>.icon-preview</span> {
            <span>width</span>: <span>64px</span>;
            <span>height</span>: <span>64px</span>;
            <span>margin</span>: <span>0</span> auto <span>12px</span>;
            <span>display</span>: flex;
            <span>align-items</span>: center;
            <span>justify-content</span>: center;
        }
        <span>.icon-preview</span> svg {
            <span>width</span>: <span>100%</span>;
            <span>height</span>: <span>100%</span>;
        }
        <span>.icon-name</span> {
            <span>font-size</span>: <span>13px</span>;
            <span>color</span>: <span>#666</span>;
            <span>word-break</span>: break-all;
        }
        <span>.copy-hint</span> {
            <span>position</span>: fixed;
            <span>bottom</span>: <span>20px</span>;
            <span>right</span>: <span>20px</span>;
            <span>background</span>: <span>#333</span>;
            <span>color</span>: white;
            <span>padding</span>: <span>12px</span> <span>24px</span>;
            <span>border-radius</span>: <span>4px</span>;
            <span>opacity</span>: <span>0</span>;
            <span>transition</span>: opacity <span>0.3s</span>;
            <span>pointer-events</span>: none;
        }
        <span>.copy-hint</span><span>.show</span> {
            <span>opacity</span>: <span>1</span>;
        }
        <span>.stats</span> {
            <span>text-align</span>: center;
            <span>margin-bottom</span>: <span>30px</span>;
            <span>color</span>: <span>#666</span>;
            <span>font-size</span>: <span>14px</span>;
        }
    </span><span></<span>style</span>></span>
<span></<span>head</span>></span>
<span><<span>body</span>></span>
    <span><<span>div</span> <span>class</span>=<span>"header"</span>></span>
        <span><<span>h1</span>></span>Icon Preview<span></<span>h1</span>></span>
        <span><<span>p</span>></span>点击图标复制使用代码<span></<span>p</span>></span>
    <span></<span>div</span>></span>
    <span><<span>div</span> <span>class</span>=<span>"container"</span>></span>
        <span><<span>div</span> <span>class</span>=<span>"stats"</span> <span>id</span>=<span>"stats"</span>></span><span></<span>div</span>></span>
        <span><<span>div</span> <span>class</span>=<span>"icon-grid"</span> <span>id</span>=<span>"iconGrid"</span>></span><span></<span>div</span>></span>
    <span></<span>div</span>></span>
    <span><<span>div</span> <span>class</span>=<span>"copy-hint"</span> <span>id</span>=<span>"copyHint"</span>></span>已复制到剪贴板<span></<span>div</span>></span>

    <span><<span>script</span> <span>src</span>=<span>"icon.js"</span>></span><span></<span>script</span>></span>
    <span><<span>script</span>></span><span>
        <span>const</span> iconGrid = <span>document</span>.<span>getElementById</span>(<span>'iconGrid'</span>);
        <span>const</span> copyHint = <span>document</span>.<span>getElementById</span>(<span>'copyHint'</span>);
        <span>const</span> stats = <span>document</span>.<span>getElementById</span>(<span>'stats'</span>);

        <span>function</span> <span>extractIconIds</span>(<span></span>) {
            <span>const</span> ids = [];
            <span>const</span> symbols = <span>document</span>.<span>querySelectorAll</span>(<span>'symbol'</span>);
            symbols.<span>forEach</span>(<span><span>symbol</span> =></span> {
                <span>const</span> id = symbol.<span>getAttribute</span>(<span>'id'</span>);
                <span>if</span> (id) {
                    ids.<span>push</span>(id);
                }
            });
            <span>return</span> ids;
        }

        <span>function</span> <span>createIconCard</span>(<span>iconId</span>) {
            <span>const</span> card = <span>document</span>.<span>createElement</span>(<span>'div'</span>);
            card.<span>className</span> = <span>'icon-card'</span>;
            
            <span>const</span> preview = <span>document</span>.<span>createElement</span>(<span>'div'</span>);
            preview.<span>className</span> = <span>'icon-preview'</span>;
            preview.<span>innerHTML</span> = <span>`<svg><use href="#<span>${iconId}</span>"></use></svg>`</span>;
            
            <span>const</span> name = <span>document</span>.<span>createElement</span>(<span>'div'</span>);
            name.<span>className</span> = <span>'icon-name'</span>;
            name.<span>textContent</span> = iconId;
            
            card.<span>appendChild</span>(preview);
            card.<span>appendChild</span>(name);
            
            card.<span>addEventListener</span>(<span>'click'</span>, <span>() =></span> {
                <span>const</span> code = <span>`<svg><use href="#<span>${iconId}</span>"></use></svg>`</span>;
                navigator.<span>clipboard</span>.<span>writeText</span>(code).<span>then</span>(<span>() =></span> {
                    <span>showCopyHint</span>();
                });
            });
            
            <span>return</span> card;
        }

        <span>function</span> <span>showCopyHint</span>(<span></span>) {
            copyHint.<span>classList</span>.<span>add</span>(<span>'show'</span>);
            <span>setTimeout</span>(<span>() =></span> {
                copyHint.<span>classList</span>.<span>remove</span>(<span>'show'</span>);
            }, <span>2000</span>);
        }

        <span>function</span> <span>init</span>(<span></span>) {
            <span>const</span> iconIds = <span>extractIconIds</span>();
            stats.<span>textContent</span> = <span>`共 <span>${iconIds.length}</span> 个图标`</span>;
            
            iconIds.<span>forEach</span>(<span><span>iconId</span> =></span> {
                <span>const</span> card = <span>createIconCard</span>(iconId);
                iconGrid.<span>appendChild</span>(card);
            });
        }

        <span>if</span> (<span>document</span>.<span>readyState</span> === <span>'complete'</span>) {
            <span>setTimeout</span>(init, <span>100</span>);
        } <span>else</span> {
            <span>window</span>.<span>addEventListener</span>(<span>'load'</span>, <span>() =></span> {
                <span>setTimeout</span>(init, <span>100</span>);
            });
        }
    </span><span></<span>script</span>></span>
<span></<span>body</span>></span>
<span></<span>html</span>></span>

关键知识点

SVG Symbol 和 Use 元素

SVG Symbol:

  • <symbol> 元素用于定义可重用的图形模板
  • 类似于组件,可以在页面中多次引用
  • 通常放在一个隐藏的 SVG 容器中

Use 元素:

  • <use> 元素用于引用已定义的 symbol
  • 通过 href 或 xlink:href 属性指定 symbol 的 id
  • 示例:<svg><use href="#icon-name"></use></svg>

优势:

  • 减少代码重复
  • 便于统一管理和修改
  • 提高性能(symbol 只需解析一次)

icon.js 工作原理

icon.js 是一个自包含的 SVG 图标库,具有以下特点:

  1. 立即执行函数:页面加载时自动执行
  2. DOM 注入:将 SVG 插入到页面中
  3. 隐藏显示:SVG 元素被设置为不可见
  4. 按需引用:通过 <use> 元素引用具体的图标

CORS 跨域问题

什么是 CORS:

  • Cross-Origin Resource Sharing(跨域资源共享)
  • 浏览器的安全机制,限制从一个源加载的文档或脚本如何与另一个源的资源进行交互

file:// 协议的限制:

  • 直接打开 HTML 文件时,origin 为 null
  • 浏览器禁止从 null origin 发起 AJAX 请求
  • 即使是同目录下的文件也会被限制

解决方案:

  1. 使用本地服务器(如 http-server、live-server)
  2. 避免使用 AJAX 读取文件
  3. 利用已加载的资源(如本例中的 icon.js)

DOM 操作最佳实践

查询元素:

  • document.querySelectorAll():查询所有匹配的元素
  • document.querySelector():查询第一个匹配的元素

创建元素:

  • document.createElement():创建新元素
  • element.appendChild():添加子元素
  • element.innerHTML:设置 HTML 内容

事件处理:

  • addEventListener():添加事件监听器
  • setTimeout():延迟执行
  • window.load 事件:确保资源加载完成

使用方法

  1. 直接打开:在浏览器中打开 public/icon-preview.html 文件
  2. 查看图标:页面会自动加载并显示所有图标
  3. 复制代码:点击任意图标,自动复制使用代码到剪贴板
  4. 使用图标:在项目中使用复制的代码引用图标

扩展功能建议

如果需要进一步增强功能,可以考虑:

  1. 搜索功能:添加搜索框,快速查找特定图标
  2. 分类显示:按图标类型或用途分类展示
  3. 颜色自定义:提供颜色选择器,实时预览不同颜色的图标
  4. 尺寸调整:允许调整图标预览大小
  5. 导出功能:将选中的图标导出为独立 SVG 文件
  6. 代码生成:生成不同框架的使用代码(Vue、React 等)

总结

通过创建 icon-preview.html,我们成功实现了一个简洁实用的 SVG 图标预览工具。在实现过程中,我们:

  1. 分析了 icon.js 的工作原理,理解了 SVG symbol 的使用方式
  2. 遇到了 CORS 跨域问题,并通过避免 AJAX 请求解决了这个问题
  3. 优化了初始化时机,确保在 icon.js 加载完成后再渲染图标
  4. 提供了良好的用户体验,包括网格布局、悬停效果、点击复制等功能

这个工具不仅方便了开发团队查看和使用图标,也展示了如何在不依赖复杂框架的情况下,使用原生 HTML/CSS/JavaScript 创建实用的工具。

参考资料