用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Objective-Arts/lens-dist --skill js-perf命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | js-perf |
| description | JS performance patterns |
Applying Addy Osmani's JavaScript performance optimization philosophy, design patterns, and web vitals expertise to frontend development. Use when optimizing page load times, implementing lazy loading, improving Core Web Vitals scores, applying JavaScript design patterns, optimizing images, implementing code splitting, or when performance budgets are discussed. Not for backend optimization, database queries, or non-JavaScript performance work.
Performance is User Experience: Every millisecond of delay costs engagement. Measure real user metrics, not synthetic benchmarks.
Progressive Enhancement: Start with the minimal viable experience, enhance with JavaScript. Never require JS for core content.
Ship Less JavaScript: The fastest code is the code you don't ship. Question every dependency, every abstraction, every byte.
Prioritize Above-the-Fold Content
<!-- Critical CSS inline -->
<style>/* Above-fold styles only */</style>
<!-- Preload critical resources -->
<link rel="preload" href="critical.js" as="script">
<link rel="preload" href="hero.webp" as="image">
<!-- Defer non-critical -->
<script defer src="analytics.js"></script>
Resource Hints
<!-- DNS prefetch for third-party domains -->
<link rel="dns-prefetch" href="//cdn.example.com">
<!-- Preconnect for critical third-parties -->
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
<!-- Prefetch for likely next navigation -->
<link rel="prefetch" href="/likely-next-page.html">
<!-- Modulepreload for ES modules -->
<link rel="modulepreload" href="/critical-module.js">
Route-Based Splitting (React)
// Lazy load routes
const Dashboard = React.lazy(() => import('./Dashboard'));
const Settings = React.lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<Skeleton />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
Magic Comments for Webpack
// Named chunks for debugging
import(/* webpackChunkName: "lodash" */ 'lodash');
// Prefetch - load during idle time
import(/* webpackPrefetch: true */ './HeavyComponent');
// Preload - load in parallel with parent
import(/* webpackPreload: true */ './CriticalComponent');
Modern Formats with Fallbacks
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero" loading="lazy" decoding="async">
</picture>
Responsive Images
<img
srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
src="medium.jpg"
alt="Responsive image"
loading="lazy"
decoding="async"
>
Native Lazy Loading
<!-- Lazy load below-fold images -->
<img src="photo.jpg" loading="lazy" alt="...">
<!-- Eager load above-fold (LCP candidates) -->
<img src="hero.jpg" loading="eager" fetchpriority="high" alt="...">
Largest Contentful Paint (LCP) < 2.5s
First Input Delay (FID) < 100ms / Interaction to Next Paint (INP) < 200ms
// Break up long tasks
function processLargeArray(items) {
const CHUNK_SIZE = 100;
let index = 0;
function processChunk() {
const chunk = items.slice(index, index + CHUNK_SIZE);
chunk.forEach(processItem);
index += CHUNK_SIZE;
if (index < items.length) {
requestIdleCallback(processChunk);
}
}
processChunk();
}
Cumulative Layout Shift (CLS) < 0.1
/* Reserve space for dynamic content */
.image-container {
aspect-ratio: 16 / 9;
width: 100%;
}
/* Avoid inserting content above existing */
.ad-slot {
min-height: 250px;
}
Module Pattern (Encapsulation)
const ShoppingCart = (() => {
let items = [];
const calculateTotal = () => items.reduce((sum, item) => sum + item.price, 0);
return {
add(item) { items.push(item); },
remove(id) { items = items.filter(i => i.id !== id); },
getTotal() { return calculateTotal(); },
getItems() { return [...items]; }
};
})();
Observer Pattern (Pub/Sub)
class EventBus {
#subscribers = new Map();
subscribe(event, callback) {
if (!this.#subscribers.has(event)) {
this.#subscribers.set(event, new Set());
}
this.#subscribers.get(event).add(callback);
return () => this.#subscribers.get(event).delete(callback);
}
publish(event, data) {
this.#subscribers.get(event)?.forEach(cb => cb(data));
}
}
Debounce & Throttle
const debounce = (fn, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
};
const throttle = (fn, limit) => {
let inThrottle;
return (...args) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
};
Memoization
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
Tree Shaking Best Practices
// BAD - imports entire library
import _ from 'lodash';
_.debounce(fn, 100);
// GOOD - imports only what's needed
import debounce from 'lodash/debounce';
debounce(fn, 100);
// BEST - use native or minimal alternatives
function debounce(fn, delay) { /* ... */ }
Performance Budgets
// webpack.config.js
module.exports = {
performance: {
maxAssetSize: 250000, // 250 KB
maxEntrypointSize: 250000,
hints: 'error' // Fail build if exceeded
}
};
Real User Monitoring
import { onCLS, onFID, onLCP, onINP, onTTFB } from 'web-vitals';
function sendToAnalytics({ name, value, id }) {
navigator.sendBeacon('/analytics', JSON.stringify({ name, value, id }));
}
onCLS(sendToAnalytics);
onLCP(sendToAnalytics);
onINP(sendToAnalytics);