cpp*_*udy 6 icons svg universal server-side-rendering angular
我们有一个Angular 16 Universal项目,我们希望找到使用 SVG 图标的最佳方式。性能对于我们的网络应用程序至关重要。我们不能使用 Icomoon 等字体,因为图标是多色的,并且很难定制和维护。
首先,我们开发了一个Angular 指令,可以在运行时内联图标。我们尝试了以下模式:
仅客户端:当应用程序在浏览器中运行时,通过 HttpClient.get() 内联图标。但是,在加载整个 main.js(包含该指令)之前,不会开始下载图标。这会导致明显的闪烁。
SSR + 客户端:激活 Angular Hydration 后,服务器执行 get 调用来获取图标,客户端不会重复所述调用。这解决了闪烁问题,因为返回的页面已经包含 SVG。但是,我担心在服务器端引入这些请求时会产生瓶颈。
此外,图标目前由资产服务器提供服务,我们希望能够将我们的组件作为库发送给其他团队,以便他们可以重用它们。如果这些团队从不同的主机名 (CORS) 向我们的资产服务器发出请求,这可能会产生问题。因此,提出了一些建议:
在构建时内联 SVG,特别是对于那些关键且必须始终显示的 SVG。这将解决我们指令的潜在问题,但会增加脚本的大小。而且,我还没有找到通过 Webpack 配置它的简单方法。将它们直接粘贴到我们的模板中似乎是一个不可取的解决方案。
使用 asset 文件夹,以便在将我们的库传递给其他团队时将图标包含在 dist 文件夹中。
考虑到所有这些想法,将图标包含为 SVG 的最佳方式是什么?
扩展Eliseo的答案,您可能会考虑一种在应用程序初始化期间预加载 SVG 图标的方法,然后利用Angular 组件内联显示这些图标,从而允许 CSS 自定义。
这将提供另一种方法来实现内联 SVG,而无需深入研究Webpack 配置。
您的 Angular 项目的结构将是:
src/
|-- app/
|-- components/
|-- header/
|-- header.component.html // Template file for the header component
|-- header.component.ts // TypeScript file for the header component
|-- header.component.css // CSS file for the header component
|-- core/
|-- services/
|-- svg.service.ts // Service for fetching and storing SVG icons
|-- shared/
|-- components/
|-- svg-icon/
|-- svg-icon.component.ts // Component for rendering SVG icons inline
|-- modules/
# other modules
|-- app.module.ts // Main application module where SVG_ICON_INITIALIZER is provided
|-- assets/
|-- symbol-defs.svg // SVG file containing symbol definitions for icons
|-- environments/
|-- environment.ts
# environment configuration files
|-- main.ts // Main entry file for the application
|-- index.html // Main HTML file
|-- styles.css // Global styles
|-- # other files
Run Code Online (Sandbox Code Playgroud)
创建一个服务来处理获取和存储 SVG 图标。
并用于APP_INITIALIZER
在应用程序初始化期间预加载 SVG 图标:这应该可以减轻“仅客户端”模式中提到的可察觉的闪烁,并可能缓解“SSR + 客户端”模式中的服务器瓶颈问题。
src/app/core/services/svg.service.ts
:
import { Injectable, APP_INITIALIZER, Provider } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class SvgService {
private svgDefs: string;
constructor(private http: HttpClient) {}
loadSvgIcons(): Promise<void> {
return this.http.get('assets/symbol-defs.svg', { responseType: 'text' })
.toPromise()
.then(svgDefs => {
this.svgDefs = svgDefs;
});
}
getIcon(iconId: string): string {
const match = this.svgDefs.match(new RegExp(`<symbol id="${iconId}"[^>]*>((.|\\n)*)<\\/symbol>`));
return match ? match[0] : '';
}
}
export const SVG_ICON_INITIALIZER: Provider = {
provide: APP_INITIALIZER,
useFactory: (svgService: SvgService) => () => svgService.loadSvgIcons(),
deps: [SvgService],
multi: true,
};
Run Code Online (Sandbox Code Playgroud)
该loadSvgIcons
方法从本地资源文件 ('assets/symbol-defs.svg') 中获取 SVG 图标,并将 SVG 标记存储在属性中svgDefs
。该getIcon(iconId: string)
方法用于从svgDefs
属性中检索特定的 SVG 图标数据。
然后创建一个 Angular 组件来内联渲染 SVG 图标。
src/shared/components/svg-icon/svg-icon.component.ts
:
import { Component, Input } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { SvgService } from './svg.service';
@Component({
selector: 'svg-icon',
template: `<div [innerHTML]="iconSvg"></div>`,
})
export class SvgIconComponent {
@Input() icon: string;
iconSvg: SafeHtml;
constructor(private svgService: SvgService, private sanitizer: DomSanitizer) {}
ngOnChanges() {
const iconSvgString = this.svgService.getIcon(this.icon);
this.iconSvg = this.sanitizer.bypassSecurityTrustHtml(iconSvgString);
}
}
Run Code Online (Sandbox Code Playgroud)
旨在SvgIconComponent
内联渲染 SVG 图标。输入icon
属性用于指定图标 ID。该iconSvg
属性用于存储 SVG 图标字符串。ngOnChanges
生命周期钩子用于在iconSvg
输入icon
属性更改时更新属性。
在您的 Angular模块文件(例如app.module.ts
)中,您可以SVG_ICON_INITIALIZER
从svg.service.ts
文件中导入 并将其添加到providers
数组中,以确保在应用程序初始化期间预加载 SVG 图标。
src/app/app.module.ts
:
// app.module.ts
import { SVG_ICON_INITIALIZER } from './svg.service';
@NgModule({
declarations: [SvgIconComponent],
imports: [/* */],
providers: [SVG_ICON_INITIALIZER],
bootstrap: [/* */]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)
例如,在典型的 Angular 项目中,您可能在目录中或直接在目录下有一个header
目录,其中包含标头组件模板的文件。components
app
header.component.html
在该header.component.html
文件中,您将使用该svg-icon
组件来内联渲染 SVG 图标:
src/app/components/header/header.component.html
(标头组件的模板文件):
<div class="header">
<svg-icon icon="icon-1"></svg-icon>
<!-- other header content -->
</div>
Run Code Online (Sandbox Code Playgroud)
现在 SVG 图标是内联渲染的,您可以根据需要应用 CSS 样式。
svg-icon svg {
fill: currentColor;
}
svg-icon:hover svg {
fill: gold;
}
Run Code Online (Sandbox Code Playgroud)
你得到:
+------------------------+ +-------------------------+ +-------------------+
| Angular Initialization | | SvgService | | SvgIconComponent |
| | | | | |
| APP_INITIALIZER | | loadSvgIcons() | | ngOnChanges() |
| (SVG_ICON_INITIALIZER) | ----> | getIcon(iconId: string) | ----> | render SVG inline |
| | | | | with [innerHTML] |
+------------------------+ +-------------------------+ +-------------------+
|
| Fetch SVG icons
v
+--------------------------+
| External Asset File |
| (assets/symbol-defs.svg) |
+--------------------------+
Run Code Online (Sandbox Code Playgroud)
APP_INITIALIZER
using SVG_ICON_INITIALIZER
)在Angular 的初始化阶段触发该loadSvgIcons()
方法。SvgService
SvgService
从外部资源文件 ( ) 中获取 SVG 图标assets/symbol-defs.svg
。SvgIconComponent
每当输入属性发生变化(触发)时,都会SvgService
通过该方法获取特定的 SVG 图标数据。getIcon(iconId: string)
icon
ngOnChanges()
SvgIconComponent
[innerHTML]
使用 Angular 的绑定将 SVG 标记注入到 DOM 中,在其模板内内联渲染 SVG 图标。通过将 SVG 处理封装在服务和组件中,可以促进组件的可重用性以及与其他团队的共享:您应该能够将库传递给其他团队
注意:这确实使用了本地资源文件(“ assets/symbol-defs.svg
”),该文件在构建过程中与应用程序捆绑在一起。
这很简单,并且避免了运行时获取 SVG 图标的任何网络请求,从而减轻了潜在的 CORS 问题。但是,它不利用外部资产服务器。
在 SVG 图标预计会频繁更改或需要在多个项目之间共享图标的情况下,使用外部资源服务器可能会有所帮助。
您需要更新SvgService
以从资源服务器而不是本地“ assets/symbol-defs.svg
”文件获取 SVG 图标。
src/app/core/services/svg.service.ts
:
@Injectable({ providedIn: 'root' })
export class SvgService {
// previous code
loadSvgIcons(): Promise<void> {
// Update the URL to point to the asset server
return this.http.get('https://assets-server.com/symbol-defs.svg', { responseType: 'text' })
.toPromise()
.then(svgDefs => {
this.svgDefs = svgDefs;
});
}
// rest of the code
}
Run Code Online (Sandbox Code Playgroud)
但您还需要处理从外部资源服务器获取 SVG 图标时可能出现的 CORS 问题。
确保资产服务器配置为允许来自托管 Angular 应用程序的域的跨域请求。这通常可以通过在资产服务器上设置适当的 CORS 标头来完成。
// Example CORS headers on the asset server
Access-Control-Allow-Origin: https://your-angular-app.com
Access-Control-Allow-Methods: GET, OPTIONS
Run Code Online (Sandbox Code Playgroud)
更多信息请参阅“ Angular CORS 指南:修复错误”,来自Saujan Ghimire
您可能还需要实施版本控制和缓存清除策略,以确保在有更新时从资产服务器获取最新版本的 SVG 图标。这可以通过在获取 SVG 图标时将版本查询参数附加到 URL 来完成。
该version
变量可以被硬编码为“ v1
”。每当 SVG 图标有更新时,您都会将此值更新为新版本字符串,例如“ ” v2
、“ v3
”等。URL 中的更改会触发浏览器从服务器获取更新的 SVG 图标,绕过任何缓存版本。
src/environments/environment.ts
:
// src/environments/environment.ts
export const environment = {
production: false,
SVG_ICON_VERSION: 'v1',
// other environment-specific configurations
};
Run Code Online (Sandbox Code Playgroud)
通过将SVG_ICON_VERSION
变量放置在环境文件中,您可以对 SVG 图标进行特定于环境的版本控制。例如,与生产环境相比,您的开发环境中可能有不同版本的 SVG 图标。
然后,您将更新loadSvgIcons()
中的方法,以便SvgService
在获取 SVG 图标时将版本查询参数附加到 URL。
src/app/core/services/svg.service.ts
:
import { environment } from '../../../environments/environment';
@Injectable({ providedIn: 'root' })
export class SvgService {
// previous code
loadSvgIcons(): Promise<void> {
const version = 'v1'; // Update this value whenever the SVG icons are updated
return this.http.get(`https://assets-server.com/symbol-defs.svg?version=${version}`, { responseType: 'text' })
.toPromise()
.then(svgDefs => {
this.svgDefs = svgDefs;
});
}
// rest of the code
}
Run Code Online (Sandbox Code Playgroud)
这将利用外部资产服务器来提供 SVG 图标,从而促进更轻松的跨项目更新和共享图标。然而,它确实在运行时引入了网络请求来获取 SVG 图标,这可能会带来瓶颈。
+--------------------+ +-----------------+ +--------------------+
| Angular | | SvgService | | External Asset |
| Application | | | | Server |
| | | loadSvgIcons() | | |
| 1. Bootstraps | | 1. Fetches SVG | | 1. Serves SVG |
| 2. Calls | | icons from | | icons |
| APP_INITIALIZER | -------> | external server | -------> | 2. Checks CORS |
| (loadSvgIcons) | | with version | | headers |
| 3. Renders | | query param | | 3. Returns SVG |
| components | | 2. Stores SVG | | icons |
| with SVG icons | <------ | icons | <------- | |
+--------------------+ +-----------------+ +--------------------+
Run Code Online (Sandbox Code Playgroud)
APP_INITIALIZER
(触发loadSvgIcons
中的方法SvgService
)。SvgService
发送请求以从外部资源服务器获取 SVG 图标,并将版本查询参数附加到 URL。SvgService
存储获取的 SVG 图标以供以后使用。SvgService
内联显示 SVG 图标。我正在研究这个
APP_INITIALIZER
选项,但如果我是对的,在main.js
完全加载之前,代码根本不会被执行。
这仍然会导致一些明显的闪烁,特别是如果main.js
文件在未来继续增长的话。”
确实,APP_INITIALIZER
令牌用于在应用程序启动时执行函数,但它会在main.js
文件加载后运行。
如果main.js
变大并且需要大量时间来加载,则在获取和显示 SVG 图标之前仍然会存在明显的延迟,这可能会导致闪烁。
为了缓解这种情况,您可以采取以下几种方法:
您可以尝试优化捆绑包大小:减小捆绑包的大小main.js
将导致更快的加载时间。这可以通过代码分割、tree-shaking 和其他捆绑优化技术来实现。
通过服务器端渲染,可以在将 SVG 图标发送到客户端之前将其获取并内联到服务器上的 HTML 中。这将消除闪烁,因为图标已经存在于初始渲染中。
另一种方法是在第一次加载后实现 Service Worker 来缓存 SVG 图标,从而消除后续加载中的网络延迟。Service Worker 可以预加载必要的资源,包括 SVG 图标,确保它们在应用程序加载后立即可用。
如果 SVG 图标托管在内容交付网络 (CDN) 上,则它们的加载速度可能比从单个服务器加载得更快,尤其是在 CDN 针对交付静态资产进行了优化的情况下。
HTTP/2 或 HTTP/3 协议还可以帮助并行加载资源,从而减少总体加载时间。
归档时间: |
|
查看次数: |
420 次 |
最近记录: |