ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Iconify 图标离线部署全攻略:内网、离线环境与构建期编译方案

Iconify 图标离线部署全攻略:内网、离线环境与构建期编译方案 直接使用 Iconify 的朋友应该都遇到过这种情况本地开发一切正常图标花里胡哨随便用但一旦把项目部署到内网、政务云、客户机房或者拿到没网的演示环境里页面上的 SVG 图标瞬间变成一个个方框。原因很简单——Iconify 的默认工作方式是从远程 API 动态拉取图标数据网络一断图标自然就没了。我最近正好把一个中后台管理系统从“远程 API 加载图标”彻底改造成“全套离线方案”过程中踩了不少坑也把几种主流思路都试了一遍。这篇文章就把 Iconify 图标集离线使用的完整方案、选型逻辑、实操代码和排查技巧整理出来给同样被内网部署、离线环境、加载速度折磨的朋友一个可以直接抄作业的参考。1. 先搞清楚 Iconify 到底怎么工作的为什么“联网”是它的默认设计1.1 一个组件怎么就能渲染任意图标集Iconify 的设计思路和传统字体图标、SVG Sprite 差别很大。它不要求你把图标文件下载到本地再导入而是把几十个开源图标集Material Design Icons、Tabler、Phosphor、Ant Design Icons 等统一抽象成一套 JSON 数据格式每个图标集对应一个 JSON 文件每个图标在文件里记录的是 SVG 的 path 数据、viewBox、别名等信息。当你写Icon iconmdi:home /时候iconify/vue或iconify/react组件其实做了几件事先检查本地有没有注册过mdi这个集合的数据。如果没有组件会自动向https://api.iconify.design/mdi.json?iconshome发起请求。拿到返回的 JSON 后解析出图标对应的 SVG 数据再渲染成实际的svg节点。也就是说Iconify 默认是一套“云端按需加载”的架构。它的好处很明显可以用统一的组件 API 访问几乎所有开源图标集图标数据不用打进前端 bundle首次只加载用到的图标内存和流量都省。但坏处也藏在这里——一旦 API 不可达组件手里没有本地数据就只能显示空白或占位方块。1.2 远程 API 的天花板内网、慢网、离线场景在我的实际项目里一开始图省事全站直接用的iconify/vue默认配置开发环境跑得飞快。但到了部署阶段问题一个个冒出来客户服务器在内网只有白名单域名能访问外网api.iconify.design根本连不通。页面加载时控制台密密麻麻全是Failed to fetch图标区域全部空白。某次离线演示现场没有网络PPT 和系统都准备好了结果一打开页面顶栏的菜单图标、按钮图标、状态图标全变方框场面一度非常尴尬。即使外网可用跨公网请求第三方 CDN 也存在稳定性隐患。网络一抖图标就闪烁、延后渲染视觉上非常掉价。这些问题本质上都是同一个矛盾Iconify 的“按需加载”默认依赖远程 API而企业在生产环境里往往要求资源可控、网络隔离、加载稳定。“离线的核心难点”不是 Iconify 不能离线用而是怎么把“按需加载”从“远程拉取”改成“本地提供”。1.3 离线使用方案大致有几类思路我梳理下来市面上的离线方案基本可以分成四类构建期编译用unplugin-icons这类工具在打包时把用到的图标编译成组件代码运行时完全不依赖任何 API。运行时本地注册把图标集的 JSON 数据打包进 bundle或在应用启动时通过addCollection/addIcon注册到本地然后组件就直接从本地数据渲染。自建 API 镜像把 Iconify 的 API 逻辑搬到自己的服务器上客户端仍然用远程/本地加载的方式但请求地址指向自己的服务。全量内联打包把整个图标集 JSON 全部注册进应用没有远程请求但 bundle 体积会膨胀。这几类方案各有适用场景后面我会逐个展开给出配置和代码。先看一张对比表方便你心里有数。2. 五种离线方案横向对比先选型再动手2.1 方案总览表方案核心思路运行时是否依赖网络图标动态性打包体积适用场景unplugin-icons 构建期编译打包时把图标编译成组件完全离线低图标名需编译期确定极小只包含用到的图标Vite/Webpack 项目、图标相对固定iconify-icons 按需注册运行时通过 addIcon 注册单图标完全离线中可从固定集合里选小只包含注册的图标Vue/React 中后台图标可控iconify-json 注册整个集合运行时通过 addCollection 注册集合完全离线高集合内任意图标中等看集合大小内网交付、图标变化频繁自建 Iconify API 镜像本地部署 API客户端动态拉取仅局域网不依赖公网高任意图标很小按需拉取多项目共享、微前端、大型中台字体图标(iconfont/Font Awesome)放弃 Iconify改用本地字体完全离线低需重新生成字体很小图标极简单、兼容老浏览器2.2 方案选型决策逻辑这些方案不是非此即彼甚至在同一个项目里可以组合使用。比如主框架用unplugin-icons做构建期编译同时保留一部分运行时注册的 JSON 数据来应对后台动态下发的图标名。我做选型的时候核心考虑三个问题图标是否动态如果图标名完全由后端接口返回比如菜单表里存了mdi:home、fa:user这类字符串那构建期编译方案就不太方便更适合运行时注册或自建 API。网络边界在哪如果整个系统部署在纯内网公网完全不通那要么把数据打包进应用要么在局域网内部署一个 API 服务。团队维护成本图标数量固定且少用按需注册最省事图标集庞大且不断新增自建 API 镜像或全量集合注册更省心。下面我就按方案逐个讲实操代码比较多建议收藏后对着敲一遍。3. 方案一构建期按需编译unplugin-iconsVite 项目首选3.1 安装与配置unplugin-icons是目前在 Vite 项目里体验最好的 Iconify 离线方案。它的原理是在构建时读取iconify/json或本地 SVG 文件把用到的图标编译成独立的组件代码打包进最终产物。运行时不再有任何网络请求也没有自己解析 JSON 的开销。以 Vue 3 Vite 为例先安装依赖npm install -D unplugin-icons iconify/json npm install -D unplugin-auto-import # 可选用于自动注册图标组件然后在vite.config.ts里加插件import { defineConfig } from vite; import vue from vitejs/plugin-vue; import Icons from unplugin-icons/vite; import IconsResolver from unplugin-icons/resolver; import AutoImport from unplugin-auto-import/vite; export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [IconsResolver()], }), Icons({ compiler: vue3, autoInstall: true, scale: 1.2, defaultClass: inline-block, }), ], });这里有几个配置点我单独说明一下autoInstall: true当代码里用到了~icons/mdi/home但本地没有安装iconify-json/mdi时构建工具会自动安装对应的图标集包。团队协作时建议提前装好避免构建时动态安装带来的网络依赖。compiler: vue3根据你的框架选React 项目用reactVue 2 用vue2还有svelte、solid等选项。scale统一控制图标缩放。这是我比较习惯的设置因为 Material Design Icons 默认是 24px 的 viewBox项目中经常需要和其他字号对齐。3.2 组件里的用法配置完成后使用方式很简单。手动引入方式script setup langts import IconMdiHome from ~icons/mdi/home; import IconTablerSettings from ~icons/tabler/settings; /script template div IconMdiHome classtext-red-500 / IconTablerSettings classw-5 h-5 / /div /template配合unplugin-auto-import的 resolver你甚至不需要手动import模板里直接用icon-mdi-home /这种命名风格的组件即可template div icon-mdi-home classtext-red-500 / icon-tabler-settings classw-5 h-5 / /div /template它的命名规则是icon-前缀 图标集名 图标名所有连接符转成驼峰或短横线。我个人更推荐手动 import 的方式因为 IDE 类型提示更明确代码可读性也更好不容易出现“模板里敲错名字但构建不报错”的问题。3.3 动态图标名怎么绕过unplugin-icons最大的局限是“图标名在编译期必须确定”。如果用~icons/${dynamicName}这种动态拼接Vite 会直接报错因为构建工具没法在打包时穷举所有可能的图标名。如果业务里确实存在动态图标名后台返回我一般推荐两种处理方式维护一张本地映射表把后端可能返回的图标名映射到unplugin-icons编译好的组件上。适合图标数量可控的管理后台。结合方案二在应用启动时把动态图标用到的集合通过addCollection注册动态图标用Icon渲染静态图标继续用unplugin-icons编译组件。这也是我最终采用的方式两者互不冲突。动态图标名映射表的示例import IconMdiHome from ~icons/mdi/home; import IconMdiAccount from ~icons/mdi/account; import IconTablerSettings from ~icons/tabler/settings; const iconMap: Recordstring, Component { mdi:home: IconMdiHome, mdi:account: IconMdiAccount, tabler:settings: IconTablerSettings, }; // 渲染时根据 name 动态取组件3.4 需要注意的点unplugin-icons这套方案我用了挺久整体体验很好但有几个细节必须提醒图标文件会被编译成组件所以每个图标实际是一个 Vue/React 组件。如果同一个页面里图标数量特别多上百个组件的实例化开销要比纯 SVG 字符串稍高一点但现代浏览器里的差异几乎感知不到。最好在项目里统一图标尺寸和颜色避免出现“同一个页面里图标大小不一”的情况。我通常封装一个AppIcon.vue组件内部统一处理class和style禁止业务代码直接使用~icons路径。要注意 tree-shaking。unplugin-icons的按需编译本身是 tree-shaking 友好的但如果把图标组件集中导出到一个index.ts并全量 re-export反而可能让打包器把没用到的组件也打进去。最好保持按需 import别做“图标大全”式的集中导出。4. 方案二运行时注册本地 JSON前后端通用4.1 先安装数据源运行时注册的思路是把图标数据提前放到应用里组件渲染时直接从本地取。这是“离线”这个词最直接的理解方式。Iconify生态提供了两种数据包形式iconify-icons/*例如iconify-icons/mdi每个图标是一个独立模块支持 tree-shaking。iconify-json/*例如iconify-json/mdi每个包是完整的图标集合 JSON导入后是整个集合对象。还有一种大而全的iconify/json包含了所有支持的图标集但体积很大直接用会让打包产物非常臃肿所以我一般不在前端应用里直接依赖它只在脚本或服务端用它做数据处理。安装方式# 按单图标引入推荐 npm install iconify-icons/mdi # 或按图标集整体引入 npm install iconify-json/mdi4.2 全局注册与单图标注册如果你用iconify/vue或iconify/react可以直接从包里引入注册方法。全局注册整个图标集合import { addCollection } from iconify/vue; import { icons as mdiIcons } from iconify-json/mdi; addCollection(mdiIcons);注册单个图标import { addIcon } from iconify/vue; import mdiHome from iconify-icons/mdi/home; addIcon(mdi:home, mdiHome);注册完成后模板里照常使用Icon iconmdi:home /但组件不会再发起网络请求直接使用本地注册的数据渲染。这里有个容易踩的坑iconify-json/mdi导入的icons对象本身是一个完整的IconifyJSON对象里面包含prefix、icons、aliases等字段直接用addCollection没问题。而iconify-icons/mdi/home导出的对象是单个图标的IconifyIcon对象必须用addIcon来注册。两者别混用否则组件拿不到正确的数据结构。4.3 维护一个 icons.ts 统一注册很多项目用着用着就乱了有人直接在任意组件里addIcon有人重复注册同一个集合还有人不小心覆盖了已有图标。我建议在项目里建一个src/icons.ts统一管理所有需要注册的图标。我的做法大概是这样import { addCollection, addIcon } from iconify/vue; import { icons as mdiIcons } from iconify-json/mdi; import { icons as tablerIcons } from iconify-json/tabler; import homeIcon from iconify-icons/mdi/home; import settingsIcon from iconify-icons/mdi/cog-outline; // 常用集合整体注册 addCollection(mdiIcons); addCollection(tablerIcons); // 单独补充注册某些图标 addIcon(mdi:home, homeIcon); addIcon(mdi:cog-outline, settingsIcon); export function setupIcons() { // 空函数只在入口调用一次保证模块执行顺序 }然后在main.ts里调用一次import { setupIcons } from ./icons; setupIcons();这样所有图标注册逻辑都在一个文件里后期排查“哪个图标没有注册”“哪个集合重复了”都非常方便。4.4 Vue/React 用法差异Vue 3 项目里iconify/vue的Icon组件支持直接在模板中使用template Icon iconmdi:home width24 height24 / /template script setup langts import { Icon } from iconify/vue; /scriptReact 项目里iconify/react的用法基本一致import { Icon } from iconify/react; export function HomeButton() { return Icon iconmdi:home width{24} height{24} /; }注册方法addCollection、addIcon在iconify/vue和iconify/react里都有 re-export不需要额外安装iconify核心包。4.5 这个方案有什么坑这套方案最大的坑是“一次性注册整个集合”带来的体积膨胀。以iconify-json/mdi为例它包含几千个图标注册后虽然只有用到的图标会被打包器 tree-shake但如果用import { icons } from iconify-json/mdi这种方式导入打包器很难静态分析出哪些图标会被用到导致整个集合被完整注入 bundle。实际体积可能在几百 KB 到几 MB 不等具体看集合大小。我的建议是如果图标集整体不大比如常用的几个集合直接整体注册省心如果图标集特别大尽量用iconify-icons/*单图标引入或者走unplugin-icons编译方案。体积敏感的正式项目一定要在打包后检查一下 dist 目录里图标数据占了多大。5. 方案三自建 Iconify API 镜像内网部署/多项目共享5.1 原理与 addAPIProvider 用法方案三是目前解决“既要保持 Iconify 动态按需加载能力、又不能依赖公网 API”的最优解。思路很简单在自有服务器或局域网内部署一个我们自己的“迷你版” Iconify API客户端通过配置把默认请求地址从api.iconify.design改成自己的服务器地址。Iconify提供了addAPIProvider方法可以自定义 API 地址import { addAPIProvider } from iconify/vue; addAPIProvider(, { host: /iconify, // 相对路径也支持完整地址例如 https://icons.example.com });这里的第一个参数是 provider 名称传空字符串表示默认 provider。配置之后Icon iconmdi:home /组件请求的地址会变成/iconify/mdi.json?iconshome。注意这个host如果是相对路径请求会基于当前站点域名发起如果是完整地址则指向你自己的服务。对于内网部署我通常建议用相对路径这样 Nginx 反代、域名切换都不用改前端代码。5.2 静态文件托管方式最省事的方式是把图标集 JSON 文件直接拷贝到静态服务器上用 Nginx 托管不需要写任何后端代码。具体操作分两步第一步把iconify/json里的 JSON 文件拷贝到你想要托管的目录。可以使用脚本筛选出项目需要的图标集比如只关心mdi、tabler、ant-design就把这几个 JSON 拷出来mkdir -p /data/iconify-json cp node_modules/iconify/json/json/mdi.json /data/iconify-json/ cp node_modules/iconify/json/json/tabler.json /data/iconify-json/ cp node_modules/iconify/json/json/ant-design.json /data/iconify-json/第二步配置 Nginxlocation /iconify/ { alias /data/iconify-json/; add_header Access-Control-Allow-Origin *; }这样前端请求/iconify/mdi.json?iconshome时Nginx 会把mdi.json原样返回。注意静态文件方式不会处理icons查询参数也就是会把整个集合的大 JSON 全部返回再由客户端从中提取需要的图标。流量会多出一些但对内网环境一般无所谓。如果需要更好的响应体量控制可以用一个简单的后端服务做按需过滤下面看代码。5.3 轻量过滤 API 的实现如果你希望请求/iconify/mdi.json?iconshome时只返回home这个图标的数据可以用 Express 写一个轻量 API。核心逻辑是从本地 JSON 文件读取数据根据icons查询参数裁剪出需要的图标字段再返回给客户端。const express require(express); const fs require(fs); const path require(path); const app express(); const JSON_DIR path.join(__dirname, json); // 支持跨域 app.use((req, res, next) { res.setHeader(Access-Control-Allow-Origin, *); next(); }); app.get(/iconify/:prefix.json, (req, res) { const prefix req.params.prefix; const filePath path.join(JSON_DIR, ${prefix}.json); if (!fs.existsSync(filePath)) { return res.status(404).json({ error: Icon set ${prefix} not found }); } const fullData JSON.parse(fs.readFileSync(filePath, utf8)); const iconsParam req.query.icons; // 没有 icons 参数返回全部数据 if (!iconsParam) { return res.json(fullData); } const iconNames String(iconsParam).split(,).filter(Boolean); const result { prefix: fullData.prefix, icons: {}, aliases: {}, }; // 保留公共字段高度、宽度等 if (fullData.width ! undefined) result.width fullData.width; if (fullData.height ! undefined) result.height fullData.height; iconNames.forEach((name) { if (fullData.icons[name]) { result.icons[name] fullData.icons[name]; } if (fullData.aliases fullData.aliases[name]) { result.aliases[name] fullData.aliases[name]; } }); res.json(result); }); app.listen(3000, () { console.log(Iconify local API listening on port 3000); });把这个服务部署到内网前端配置addAPIProvider(, { host: /iconify })就能在不依赖公网的情况下获得和官方 API 几乎一样的按需加载体验。这个方案特别适合中大型项目尤其是多个前端应用共享一套图标服务的时候部署一次所有项目都能受益。5.4 这个方案的适用场景我实际把方案三用在一个多团队协作的中台项目上效果非常稳。为什么最终选择它而不是方案一或方案二因为那个项目里有十几个子应用每个团队的图标使用习惯不一样如果让每个子应用都自己打包图标不仅冗余而且无法统一版本。自建 API 之后所有子应用统一访问一个内网地址图标集合的更新只需要替换服务器上的 JSON 文件不需要每个应用重新发版运维成本明显更低。但这个方案也有门槛你需要有服务器或容器环境能部署 Node 服务或配置 Nginx。如果项目只是单机交付、买家只有一套系统搞服务端反而多余直接走方案一更合适。6. 方案四全量打包内联以及备选字体方案6.1 bundle 内联 JSON 的正确姿势有些场景比较极端整个系统交付给客户客户现场断网也没有企业内部服务器唯一能运行的就是一个静态目录。这种时候把图标数据直接打进前端 bundle 是最稳妥的。全量内联其实也是用addCollection但导入方式要选对。以 Vue 项目为例import { addCollection } from iconify/vue; import mdi from iconify-json/mdi/icons.json; import tabler from iconify-json/tabler/icons.json; addCollection(mdi); addCollection(tabler);注意这里用的是iconify-json/mdi/icons.json这种直接导入 JSON 文件的写法打包器会把整个 JSON 当作静态资源处理不会做 tree-shaking。得到的 bundle 会明显变大mdi 集合可能占几百 KB如果是大集合加上其他集合总增量可能超过 1MB。这种方法最大的问题是体积。Iconify 支持的图标集加起来总数据量是百 MB 级别的前端不可能全部内联。所以实际使用时要克制只内联项目最常用的 2~3 个集合并且想清楚“这些图标真的都需要吗”。6.2 什么时候才建议全量打包我自己的判断标准是静态站点 纯离线 图标使用范围不可控同时又不想起后端服务。比如给客户做演示机、离线展厅大屏、单机版桌面应用这类场景里 bundle 体积多点不是核心矛盾稳定可靠才是第一位。如果体积实在敏感可以退一步把全量 JSON 按集合拆出来放在本地静态目录运行时按需 fetch。这其实就是方案三里“静态文件托管”的前端版不需要服务器缺点是会发出局域网或本地请求纯 file:// 协议下 fetch 可能受限需要注意部署方式。6.3 备选退回到字体图标在某些传统项目里团队可能不太想引入 Iconify 的组件体系只想简单地把图标显示出来。这时候可以放弃 Iconify 本身直接用字体图标。最经典的做法是用iconfont.cn把选好的图标打包成字体文件下载iconfont.ttf、iconfont.woff2等文件放到项目里然后引入 CSSfont-face { font-family: iconfont; src: url(/fonts/iconfont.woff2) format(woff2), url(/fonts/iconfont.ttf) format(truetype); } .iconfont { font-family: iconfont !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; }HTML 里直接用i classiconfont icon-home/i这种方案的好处是极其简单兼容性极好老浏览器完全没问题。坏处也明显图标的颜色、尺寸、动画都受字体限制多色图标基本没法实现SVG 的高级特性渐变、描边、滤镜更是想都别想。如果你只用到少量单色图标而且团队不想引入复杂工具链字体方案是够用的。但如果你已经决定统一用 Iconify我不建议为了“简单”退回字体因为后续图标扩展和视觉定制会非常痛苦。6.4 字体方案和 Iconify 方案的取舍这里展开说下我对字体方案的看法。很多老项目用 Font Awesome 或 iconfont 用了很多年图省事继续沿用。但现代前端项目里图标的显示需求已经不止“显示一个符号”了经常要换颜色、换尺寸、加动画、甚至做多色渐变。字体图标在“多色”和“矢量细节”上天生吃亏Iconify 这种 SVG 数据驱动的方案才是未来的主流。所以我的建议是除非项目极其简单或浏览器兼容性要求极高否则还是先考虑前面几个离线方案字体方案作为最后兜底。7. 常见问题与排查技巧实录7.1 图标显示为方块或空白这是离线场景下最常见的现象。排查顺序我一般从三个层面入手第一打开浏览器控制台看 Network 面板里有没有api.iconify.design或自定义 API 的请求。如果有且状态码是失败或超时说明组件还在尝试远程加载本地没有对应数据。第二确认本地注册是否生效。在代码里打印图标数据import { getIcon } from iconify/vue; const iconData getIcon(mdi:home); console.log(iconData); // 如果是 undefined说明没有注册成功第三检查图标名是否正确。同一个图标可能在不同的图标集里名字不同比如mdi:account在 Tabler 里叫tabler:user。建议去 Iconify 官网搜索确认图标名再复制到项目里。7.2 离线后控制台仍在请求 API这种情况通常是没有正确设置addAPIProvider或者代码执行顺序不对。addAPIProvider必须在注册图标、渲染任何Icon组件之前执行。如果注册放在某个异步模块里而页面已经渲染了Icon组件已经发出了远程请求那就晚了。我习惯把addAPIProvider放在入口文件的第一行甚至比createApp还早import { addAPIProvider } from iconify/vue; addAPIProvider(, { host: /iconify }); // 其他初始化代码 createApp(App).mount(#app);7.3 别名/变体图标渲染不出来Iconify 的图标数据里有很多“别名”。比如一个图标可能是另一个图标的变体名字叫mdi:home-outline实际数据指向mdi:home但有额外的渲染参数。如果只注册了基础图标而没注册别名组件可能无法正确渲染。避免这个问题的方法很简单尽量注册整个集合的 JSONaddCollection因为集合 JSON 里包含了完整的aliases映射关系。如果只用addIcon注册单个图标注意也要把对应的别名注册进去否则遇到变体名称会失败。另外Iconify 支持渲染参数例如iconmdi:home也可以通过fliphorizontal、rotate{90}等属性实现变换。这些参数是组件层面的不依赖本地数据所以离线后依然可用。7.4 bundle 体积失控打包后发现图标数据占了很大体积基本都是因为把整个图标集 JSON 都 import 进来了。解决办法检查是否误用了iconify/json全量集合包换成iconify-json/*或iconify-icons/*。检查是否有集中 re-export 图标组件的文件改为按需 import。如果用的是unplugin-icons确保没有把iconify/json作为全量依赖打包正常配置下只编译用到的图标不该有这个问题。7.5 SSR、微前端等场景注意点SSR/SSG 场景里iconify/vue服务端渲染时不会触发网络请求。如果服务端渲染的 HTML 里已经包含了图标 SVG说明数据在构建时已经可用了如果没有可能是注册数据没有在服务端执行。建议把图标初始化逻辑放到一个公共模块里入口文件、服务端入口、微前端子应用入口都调用一次。微前端场景里如果多个子应用都用了iconify/vue会有重复注册、域名隔离、样式冲突等问题。我建议在主应用统一初始化图标数据和 API Provider所有子应用直接从本地数据渲染避免每个子应用都去注册导致内存浪费。如果子应用里有特殊图标需求可以在子应用内部再追加注册。8. 我个人在实际项目中的选择文章最后分享下我自己的最终落地组合不做什么大而全的总结就说点实际的。我这次的中后台项目最后采用的是“双轨制”静态图标全部走unplugin-icons构建期编译动态图标菜单表里配置的图标名用iconify-json整体注册了几个常用集合同时内网里挂了一个 Node 写的轻量 Iconify API 作为兜底。这样页面里 90% 的图标在构建期就固定下来了加载零延迟剩下 10% 的后台动态图标也能稳定渲染不会因为某个集合没注册出现方框。按这个方案落地后图标相关的问题基本清零。最直观的变化是以前每次打开页面Network 面板里都有一堆图标请求白屏和闪跳都跟这个有关改完之后图标要么在 HTML 里要么在本地注册数据里完全没有任何外部请求体感快了很多。如果你正被 Inner 网部署、离线演示、图标加载慢这些问题困扰我的建议是先别急着全盘推翻从“项目里到底哪些图标是静态的、哪些是动态的”开始梳理挑一个最小范围先试点。离线这件事不需要一步到位把方案拆细了你反而会发现它没有那么难。
返回列表