|
1 | 1 | # ngx-fast-svg |
2 | 2 |
|
3 | | ---- |
| 3 | +Fast SVG's for Angular powered by browser native features with best performance practices and DX in mind. |
4 | 4 |
|
5 | | -#### Fast SVG's for Angular powered by browser native features with best performance practices and DX in mind |
| 5 | +## Why another SVG lib for Angular? |
6 | 6 |
|
7 | | -<!-- toc --> |
| 7 | +Current implementations of SVG handling in the Browser lacks of awareness of performance. |
8 | 8 |
|
9 | | - - [Fast SVG's for Angular powered by browser native features with best performance practices and DX in mind](#fast-svgs-for-angular-powered-by-browser-native-features-with-best-performance-practices-and-dx-in-mind) |
| 9 | +This library covers next aspects that developers should consider for their projects: |
10 | 10 |
|
11 | | -<!-- tocstop --> |
| 11 | +- Image loading performance |
| 12 | +- Initial rendering performance and runtime performance |
| 13 | +- SVG reusability |
| 14 | +- Optimized bundle size |
| 15 | +- SSR |
12 | 16 |
|
13 | | -# Why another SVG lib for Angular? |
| 17 | +## Getting started |
14 | 18 |
|
15 | | -Current implementations of SVG handling in the Browser lacks of awareness of performance. |
| 19 | +### Install |
16 | 20 |
|
17 | | -There are several things not considered in most of the existing projects: |
| 21 | +```bash |
| 22 | +npm install @push-based/ngx-fast-svg --save |
| 23 | +# or |
| 24 | +yarn add @push-based/ngx-fast-svg |
| 25 | +``` |
18 | 26 |
|
19 | | -- SSR |
20 | | -- bundle size |
21 | | -- DOM size |
22 | | -- Lazy loading |
23 | | -- SVG structure reusability |
24 | | -- Initial rendering performance |
25 | | -- Runtime performance of updates |
| 27 | +### Setup |
26 | 28 |
|
27 | | -We will walk through all the different scenarious in detail later. |
28 | | -To get a quick overview we will list a comparison table: |
| 29 | +**app.module.ts** |
29 | 30 |
|
30 | | -| Library | SSR | Lazy loading | Hydration | Reusability of SVG DOM | Optimized render performance | Size | |
31 | | -| ---------------- | ---------- | ---------------- | --------- | ---------------------- | ---------------------------- | -------- | |
32 | | -| ngx-fast-svg | `easy` | browser natively | βοΈ | βοΈ | βοΈ | 1.52 KB | |
33 | | -| font-awesome | `hard` | β | βοΈ | βοΈ | β | 64.75 KB | |
34 | | -| ant | `moderate` | β | βοΈ | βοΈ | β | 24.38 KB | |
35 | | -| material | `easy` | β | βοΈ | βοΈ | β | 16.92 KB | |
36 | | -| angular-svg-icon | `moderate` | β | βοΈ | βοΈ | β | 1.54 KB | |
37 | | -| ionic | `moderate` | β | βοΈ | βοΈ | βοΈ | 1.44 KB | |
| 31 | +```typescript |
| 32 | +// ... |
| 33 | +import { FastSvgModule } from '@ngx-fast-svg'; |
38 | 34 |
|
39 | | -**SSR** |
40 | | -Server Side Rendering is working. The depending on how easy it is to set it up we distinguish between `easy`, `moderate`, `hard`. |
| 35 | +@NgModule({ |
| 36 | + declarations: [AppComponent], |
| 37 | + imports: [ |
| 38 | + FastSvgModule.forRoot({ |
| 39 | + url: (name: string) => `path/to/svg-assets/${name}.svg`, |
| 40 | + }) |
| 41 | + ] |
| 42 | + providers: [], |
| 43 | + bootstrap: [AppComponent] |
| 44 | +}) |
| 45 | +export class AppModule {} |
| 46 | +``` |
41 | 47 |
|
42 | | -**Lazy loading** |
43 | | -We refer to lazy loading as on demand loading of SVG files based on their visibility in the viewport. |
| 48 | +### Usage in the template |
44 | 49 |
|
45 | | -**Hydration** |
46 | | -Is the process of taking over the SSR HTML and state of the app on the client side. |
47 | | -This can happen in a destructive way (deleting all present HTML and regenerate it from JS) on in a non-destructive way (reusing the existing DOM). |
| 50 | +```html |
| 51 | +<fast-svg [name]="svgName" [size]="svgSize"></fast-svg> |
| 52 | +<!-- OR --> |
| 53 | +<fast-svg [name]="svgName" [width]="svgWidth" [height]="svgHeight"></fast-svg> |
| 54 | +``` |
48 | 55 |
|
49 | | -**Reusability of SVG DOM** |
50 | | -Reusability means that we maintain the content of an SVG, meaning its inner DOM structure `g`, `path` or other tags in one place and reuse them in many different places. |
| 56 | +### Advanced usage |
51 | 57 |
|
52 | | -**Optimized render performance** |
53 | | -To display (render) SVGs the browser takes time. We can reduce that time by adding a couple of improvements. |
| 58 | +#### Providing additional options |
54 | 59 |
|
55 | | -# Install |
| 60 | +During setup phase you can provide additional optional settings such as: |
56 | 61 |
|
57 | | -```bash |
58 | | -npm install @push-based/ngx-fast-svg --save |
59 | | -# or |
60 | | -yarn add @push-based/ngx-fast-svg |
| 62 | +```typescript |
| 63 | + defaultSize?: string; |
| 64 | + suspenseSvgString?: string; |
| 65 | + svgLoadStrategy?: Type<SvgLoadStrategy>; |
61 | 66 | ``` |
62 | 67 |
|
63 | | -# Setup |
| 68 | +`svgLoadStrategy` can be any injectable class that has `load` method that accepts url and returns observable string: |
| 69 | + |
| 70 | +```typescript |
| 71 | +@Injectable() |
| 72 | +export abstract class SvgLoadStrategy { |
| 73 | + abstract load(url: string): Observable<string>; |
| 74 | +} |
| 75 | +``` |
64 | 76 |
|
65 | 77 | **app.module.ts** |
66 | 78 |
|
67 | 79 | ```typescript |
68 | 80 | // ... |
69 | | -import { FAST_SVG_PROVIDERS } from './ngx-fast-svg-ssr/movie.icon.provider'; |
70 | | -import { FastSvgModule } from '@ngx-fast-icon'; |
| 81 | +import { FastSvgModule } from '@ngx-fast-svg'; |
| 82 | +import { loaderSvg } from './assets'; |
| 83 | +import { HttpClientFetchStrategy } from './fetch-strategy'; |
71 | 84 |
|
72 | 85 | @NgModule({ |
73 | 86 | declarations: [AppComponent], |
74 | 87 | imports: [ |
75 | | - BrowserModule, |
76 | 88 | FastSvgModule.forRoot({ |
77 | | - |
| 89 | + url: (name: string) => `path/to/svg-assets/${name}.svg`, |
| 90 | + defaultSize: '32', |
| 91 | + suspenseSvgString: loaderSvg, |
| 92 | + svgLoadStrategy: HttpClientFetchStrategy |
78 | 93 | }) |
79 | 94 | ] |
80 | 95 | providers: [], |
81 | 96 | bootstrap: [AppComponent] |
82 | 97 | }) |
83 | | -export class AppModule { |
| 98 | +export class AppModule {} |
| 99 | +``` |
84 | 100 |
|
| 101 | +#### SSR Usage |
| 102 | + |
| 103 | +You can provide your own SSR loading strategy that can look like this: |
| 104 | + |
| 105 | +```typescript |
| 106 | +@Injectable() |
| 107 | +export class SvgLoadStrategySsr implements SvgLoadStrategy { |
| 108 | + load(url: string): Observable<string> { |
| 109 | + const iconPath = join(process.cwd(), 'dist', 'app-name', 'browser', url); |
| 110 | + const iconSVG = readFileSync(iconPath, 'utf8'); |
| 111 | + return of(iconSVG); |
| 112 | + } |
85 | 113 | } |
| 114 | +``` |
86 | 115 |
|
| 116 | +And then just provide it in you server module. |
| 117 | + |
| 118 | +**app.server.module.ts** |
| 119 | + |
| 120 | +```typescript |
| 121 | +@NgModule({ |
| 122 | + declarations: [], |
| 123 | + imports: [ |
| 124 | + AppModule, |
| 125 | + ServerModule, |
| 126 | + ServerTransferStateModule, |
| 127 | + FastSvgModule.forRoot({ |
| 128 | + svgLoadStrategy: SvgLoadStrategySsr, |
| 129 | + url: (name: string) => `assets/svg-icons/${name}.svg`, |
| 130 | + }), |
| 131 | + ], |
| 132 | + providers: [], |
| 133 | + bootstrap: [AppComponent], |
| 134 | +}) |
| 135 | +export class AppServerModule {} |
87 | 136 | ``` |
| 137 | + |
| 138 | +## Features |
| 139 | + |
| 140 | +### :sloth: Lazy loading for SVGs |
| 141 | + |
| 142 | +Lazy loading is referring to loading resources only if they are visible on screen. Like lazy loading imgs. |
| 143 | +It can be implemented natively over loading attribute or over viewportobserver. |
| 144 | +This library supports lazy loading for SVGs using purely browser native features. |
| 145 | + |
| 146 | +- We display an empty SVG at the beginning. Invisible and without dimensions. |
| 147 | +- On View init the size is applied even if no svg is loaded to avoid flickering in dimensions. |
| 148 | +- A suspense svg is displayed at the same time to reduce visual flickering. |
| 149 | +- We use an img element here to leverage the browsers native features: |
| 150 | + - Lazy loading (loading="lazy") to only load the svg that are actually visible |
| 151 | + - The image is styled with display none. this prevents any loading of the resource ever. |
| 152 | + on component bootstrap we decide what we want to do. When we remove display none it performs the browser native behavior. |
| 153 | + |
| 154 | +### :floppy_disk: Caching |
| 155 | + |
| 156 | +We use a DOM caching mechanism to display svg over the 'use' tag and an href attribute. |
| 157 | +When the browser loaded the svg resource we trigger the caching mechanism. |
| 158 | + |
| 159 | +`re-fetch -> cache-hit -> get SVG -> cache in DOM` |
| 160 | + |
| 161 | +Cached SVG elements can be reused in multiple places places and support different stylings. |
| 162 | + |
| 163 | +### :rocket: Optimized for performance |
| 164 | + |
| 165 | +This library leverages best performance practices: |
| 166 | + |
| 167 | +- Component is styled with `content-visiblity: auto;` and `contain: content;`. It makes instances outside of viewport completely excluded from browser style recalculation process. |
| 168 | +- Cache is stored in a tag which is not processed by the browser. |
| 169 | +- We use native browser `fetch` which is not patched by `zone.js` and is on average 2.5 times faster than fetching over `HTTPClient`. |
| 170 | + |
| 171 | +### π€ SSR Support |
| 172 | + |
| 173 | +This library also Supports lazy loading with SSR and http transfer cache. |
| 174 | +If SSR load svgs on server => ends up in DOM cache and ships to the client. |
| 175 | + |
| 176 | +## Comparison |
| 177 | + |
| 178 | +Here's library comparison with other popular SVG solutions. |
| 179 | + |
| 180 | +| Library | SSR | Lazy loading | Optimized render performance | Size | |
| 181 | +| ---------------- | ---------- | ---------------- | ---------------------------- | -------- | |
| 182 | +| ngx-fast-svg | `easy` | browser natively | βοΈ | 1.52 KB | |
| 183 | +| font-awesome | `hard` | β | β | 64.75 KB | |
| 184 | +| ant | `moderate` | β | β | 24.38 KB | |
| 185 | +| material | `easy` | β | β | 16.92 KB | |
| 186 | +| angular-svg-icon | `moderate` | β | β | 1.54 KB | |
| 187 | +| ionic | `moderate` | β | βοΈ | 1.44 KB | |
| 188 | + |
| 189 | +<!-- | Library | SSR | Lazy loading | Hydration | Reusability of SVG DOM | Optimized render performance | Size | |
| 190 | +| ---------------- | ---------- | ---------------- | --------- | ---------------------- | ---------------------------- | -------- | |
| 191 | +| ngx-fast-svg | `easy` | browser natively | βοΈ | βοΈ | βοΈ | 1.52 KB | |
| 192 | +| font-awesome | `hard` | β | βοΈ | βοΈ | β | 64.75 KB | |
| 193 | +| ant | `moderate` | β | βοΈ | βοΈ | β | 24.38 KB | |
| 194 | +| material | `easy` | β | βοΈ | βοΈ | β | 16.92 KB | |
| 195 | +| angular-svg-icon | `moderate` | β | βοΈ | βοΈ | β | 1.54 KB | |
| 196 | +| ionic | `moderate` | β | βοΈ | βοΈ | βοΈ | 1.44 KB | --> |
| 197 | + |
| 198 | +**SSR** |
| 199 | +Server Side Rendering is working. The depending on how easy it is to set it up we distinguish between `easy`, `moderate`, `hard`. |
| 200 | + |
| 201 | +**Lazy loading** |
| 202 | +We refer to lazy loading as on demand loading of SVG files based on their visibility in the viewport. |
| 203 | + |
| 204 | +<!-- **Hydration** |
| 205 | +Is the process of taking over the SSR HTML and state of the app on the client side. |
| 206 | +This can happen in a destructive way (deleting all present HTML and regenerate it from JS) on in a non-destructive way (reusing the existing DOM). |
| 207 | +
|
| 208 | +**Reusability of SVG DOM** |
| 209 | +Reusability means that we maintain the content of an SVG, meaning its inner DOM structure `g`, `path` or other tags in one place and reuse them in many different places. --> |
| 210 | + |
| 211 | +**Optimized render performance** |
| 212 | +To display (render) SVGs the browser takes time. We can reduce that time by adding a couple of improvements. |
0 commit comments