用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Objective-Arts/lens-dist --skill angular-perf命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | angular-perf |
| description | Angular performance and optimization patterns |
| allowed-tools | [] |
Minko Gechev's core belief: Performance is a feature, not an afterthought. Every millisecond of load time and every kilobyte of bundle size affects user experience.
"The fastest code is code that doesn't run. The smallest bundle is the one you don't ship."
Performance optimization in Angular is about:
Every feature module should be lazy loaded unless proven otherwise.
Not this:
// app.module.ts - everything loaded upfront
@NgModule({
imports: [
UsersModule,
AdminModule,
ReportsModule,
SettingsModule // User may never visit settings
]
})
class AppModule {}
This:
// app-routing.module.ts - load on demand
const routes: Routes = [
{ path: 'users', loadChildren: () => import('./users/users.module').then(m => m.UsersModule) },
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) },
{ path: 'reports', loadChildren: () => import('./reports/reports.module').then(m => m.ReportsModule) },
{ path: 'settings', loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule) }
];
With standalone components (Angular 14+):
const routes: Routes = [
{
path: 'dashboard',
loadComponent: () => import('./dashboard.component').then(c => c.DashboardComponent)
}
];
Default change detection checks every component on every event. OnPush checks only when inputs change.
Not this:
@Component({
// Default change detection - checks on every click anywhere
})
class ExpensiveListComponent {
@Input() items: Item[];
}
This:
@Component({
changeDetection: ChangeDetectionStrategy.OnPush // Only checks when items reference changes
})
class ExpensiveListComponent {
@Input() items: Item[];
}
Rules for OnPush:
@Input() must be immutable (new reference on change)async pipe for observables (triggers change detection correctly)ChangeDetectorRef.markForCheck() only when truly neededWithout trackBy, Angular destroys and recreates DOM for every change.
Not this:
<li *ngFor="let user of users">{{ user.name }}</li>
<!-- Entire list re-rendered when any user changes -->
This:
<li *ngFor="let user of users; trackBy: trackByUserId">{{ user.name }}</li>
<!-- Only changed items re-rendered -->
trackByUserId(index: number, user: User): string {
return user.id;
}
Manual subscriptions leak memory. Async pipe handles cleanup.
Not this:
class UserComponent implements OnInit, OnDestroy {
user: User;
private subscription: Subscription;
ngOnInit() {
this.subscription = this.userService.getUser().subscribe(u => this.user = u);
}
ngOnDestroy() {
this.subscription.unsubscribe(); // Easy to forget
}
}
This:
class UserComponent {
user$ = this.userService.getUser();
constructor(private userService: UserService) {}
}
<div *ngIf="user$ | async as user">{{ user.name }}</div>
Set budgets, enforce them, analyze violations.
angular.json:
{
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
]
}
Analyze bundles:
ng build --stats-json
npx webpack-bundle-analyzer dist/stats.json
Don't just lazy load—preload intelligently.
// Preload all modules after initial load
@NgModule({
imports: [RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules
})]
})
// Or custom strategy - preload based on user behavior
@Injectable()
class CustomPreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
return route.data?.['preload'] ? load() : of(null);
}
}
Never render thousands of DOM nodes. Use virtual scrolling.
import { ScrollingModule } from '@angular/cdk/scrolling';
@Component({
template: `
<cdk-virtual-scroll-viewport itemSize="50" class="viewport">
<div *cdkVirtualFor="let item of items" class="item">
{{ item.name }}
</div>
</cdk-virtual-scroll-viewport>
`
})
class LargeListComponent {
items = Array.from({ length: 10000 }, (_, i) => ({ name: `Item ${i}` }));
}
Offload CPU-intensive work to avoid blocking the main thread.
// app.worker.ts
addEventListener('message', ({ data }) => {
const result = heavyComputation(data);
postMessage(result);
});
// component.ts
if (typeof Worker !== 'undefined') {
const worker = new Worker(new URL('./app.worker', import.meta.url));
worker.onmessage = ({ data }) => {
this.result = data;
};
worker.postMessage(this.inputData);
}
Angular's built-in image optimization directive.
import { NgOptimizedImage } from '@angular/common';
@Component({
imports: [NgOptimizedImage],
template: `
<img ngSrc="hero.jpg" width="800" height="600" priority>
<img ngSrc="thumbnail.jpg" width="200" height="150" loading="lazy">
`
})
Benefits:
priorityDeclarative lazy loading in templates.
@defer (on viewport) {
<heavy-component />
} @placeholder {
<lightweight-skeleton />
} @loading (minimum 500ms) {
<spinner />
}
Trigger options:
on viewport - when enters viewporton idle - when browser is idleon interaction - on click/focuson hover - on mouse hoveron timer(500ms) - after delayBefore shipping, ask:
Apply these checks:
ChangeDetectionStrategy.OnPush on componentstrackBy on all *ngForasync pipe instead of manual subscriptionsNgOptimizedImage// Enable Angular DevTools profiler
// In browser: Angular DevTools extension
// Measure change detection
import { enableDebugTools } from '@angular/platform-browser';
enableDebugTools(appRef.components[0]);
// Then in console: ng.profiler.timeChangeDetection()
// Trace what triggers change detection
constructor(private ngZone: NgZone) {
ngZone.onStable.subscribe(() => console.log('CD cycle complete'));
}
Use a different skill when:
angular-core (DI, testability)design-patternsclaritytypescript (TypeScript)Minko Gechev is the Angular performance skill—use it for optimization, lazy loading, and runtime efficiency.
"Measure first. Optimize what matters. Ship less JavaScript." — Minko Gechev