最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript性能調(diào)優(yōu)實(shí)戰(zhàn)案例(附完整代碼)

 更新時(shí)間:2026年04月01日 08:16:50   作者:soso1968  
JavaScript性能優(yōu)化是一個(gè)持續(xù)的過(guò)程,需要結(jié)合具體場(chǎng)景靈活運(yùn)用各種策略,下面這篇文章主要介紹了JavaScript性能調(diào)優(yōu)實(shí)戰(zhàn)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

代碼分割(Code Splitting)

1.1 概述

代碼分割是一種將代碼拆分成多個(gè)chunks的技術(shù),可以按需加載,減少初始加載體積,提升首屏加載速度。

1.2 Webpack代碼分割配置

動(dòng)態(tài)導(dǎo)入(Dynamic Import)

// 方式一:使用import()語(yǔ)法
// 原始代碼
import { heavyFunction } from './heavyModule';
// 優(yōu)化后:動(dòng)態(tài)導(dǎo)入
document.getElementById('loadBtn').addEventListener('click', async () => {
  const module = await import('./heavyModule.js');
  module.heavyFunction();
});

路由級(jí)別的代碼分割(React示例)

// App.js - 使用React.lazy和Suspense
import React, { Suspense, lazy } from 'react';
// 動(dòng)態(tài)導(dǎo)入路由組件
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Suspense>
  );
}

Webpack配置

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all', // 對(duì)所有類(lèi)型的chunk進(jìn)行分割
      minSize: 30000, // 模塊最小30KB才分割
      maxSize: 0, // 無(wú)最大限制
      minChunks: 1, // 模塊至少被引用1次
      maxAsyncRequests: 5, // 異步請(qǐng)求最大數(shù)
      maxInitialRequests: 3, // 入口點(diǎn)最大并行請(qǐng)求數(shù)
      automaticNameDelimiter: '~', // chunk名稱(chēng)分隔符
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/, // 匹配node_modules
          priority: -10, // 優(yōu)先級(jí)
          reuseExistingChunk: true, // 復(fù)用已存在的chunk
        },
        default: {
          minChunks: 2, // 模塊至少被引用2次
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].js',
  },
};

1.3 Vite代碼分割

// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'react-vendor': ['react', 'react-dom'],
          'ui-vendor': ['antd', 'lodash'],
          'utils': ['axios', 'dayjs'],
        },
      },
    },
    chunkSizeWarningLimit: 1000,
  },
};

懶加載(Lazy Loading)

2.1 圖片懶加載

原生Intersection Observer API

<!DOCTYPE html>
<html>
<head>
  <style>
    .lazy-image {
      opacity: 0;
      transition: opacity 0.3s;
      min-height: 200px;
      background-color: #f0f0f0;
    }
    .lazy-image.loaded {
      opacity: 1;
    }
  </style>
</head>
<body>
  <img class="lazy-image" data-src="image1.jpg" alt="圖片1">
  <img class="lazy-image" data-src="image2.jpg" alt="圖片2">
  <script>
    class LazyImageLoader {
      constructor(options = {}) {
        this.options = {
          rootMargin: '100px',
          threshold: 0.01,
          ...options
        };
        this.observer = null;
        this.init();
      }
      init() {
        if ('IntersectionObserver' in window) {
          this.observer = new IntersectionObserver(
            this.observerCallback.bind(this),
            this.options
          );
          this.observeElements();
        } else {
          this.fallbackLazyLoad();
        }
      }
      observerCallback(entries) {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            const img = entry.target;
            this.loadImage(img);
            this.observer.unobserve(img);
          }
        });
      }
      loadImage(img) {
        const src = img.dataset.src;
        if (!src) return;
        img.src = src;
        img.onload = () => img.classList.add('loaded');
        img.onerror = () => {
          img.src = 'placeholder.jpg';
          img.classList.add('loaded');
        };
      }
      observeElements() {
        const images = document.querySelectorAll('.lazy-image');
        images.forEach(img => this.observer.observe(img));
      }
      fallbackLazyLoad() {
        const images = document.querySelectorAll('.lazy-image');
        images.forEach(img => this.loadImage(img));
      }
    }
    // 使用
    new LazyImageLoader();
  </script>
</body>
</html>

React圖片懶加載組件

// LazyImage.js
import React, { useState, useRef, useEffect } from 'react';
function LazyImage({ src, alt, className, placeholder }) {
  const [isLoaded, setIsLoaded] = useState(false);
  const [isInView, setIsInView] = useState(false);
  const imgRef = useRef(null);
  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsInView(true);
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );
    if (imgRef.current) {
      observer.observe(imgRef.current);
    }
    return () => observer.disconnect();
  }, []);
  const handleLoad = () => {
    setIsLoaded(true);
  };
  return (
    <div ref={imgRef} className={className}>
      {isInView ? (
        <>
          <img
            src={src}
            alt={alt}
            onLoad={handleLoad}
            style={{ 
              opacity: isLoaded ? 1 : 0,
              transition: 'opacity 0.3s ease-in-out'
            }}
          />
          {!isLoaded && (
            <div 
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                width: '100%',
                height: '100%',
                backgroundColor: '#f0f0f0',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center'
              }}
            >
              {placeholder || '加載中...'}
            </div>
          )}
        </>
      ) : (
        <div style={{ 
          height: '200px', 
          backgroundColor: '#f0f0f0' 
        }}>
          {placeholder || '等待加載...'}
        </div>
      )}
    </div>
  );
}
export default LazyImage;

2.2 組件懶加載

React組件懶加載

// 使用React.lazy
import React, { Suspense } from 'react';
// 預(yù)加載組件
const preloadComponent = (componentImport) => {
  componentImport();
};
// 懶加載組件
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function Dashboard() {
  return (
    <div>
      <Suspense fallback={<LoadingSkeleton />}>
        <HeavyComponent />
      </Suspense>
    </div>
  );
}
// 預(yù)加載觸發(fā)器
function PreloadButton({ onMouseEnter }) {
  return (
    <button
      onMouseEnter={() => {
        // 鼠標(biāo)懸停時(shí)預(yù)加載
        import('./HeavyComponent');
      }}
    >
      查看詳情
    </button>
  );
}

2.3 數(shù)據(jù)懶加載

// 無(wú)限滾動(dòng)加載
class InfiniteScrollLoader {
  constructor(options) {
    this.loading = false;
    this.page = 1;
    this.hasMore = true;
    this.onLoad = options.onLoad;
    this.init();
  }
  init() {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting && !this.loading && this.hasMore) {
          this.loadMore();
        }
      },
      { threshold: 0.1 }
    );
    const sentinel = document.getElementById('scroll-sentinel');
    if (sentinel) observer.observe(sentinel);
  }
  async loadMore() {
    this.loading = true;
    try {
      const data = await this.onLoad(this.page);
      if (data.length === 0) {
        this.hasMore = false;
      } else {
        this.page++;
        this.renderItems(data);
      }
    } catch (error) {
      console.error('加載失敗:', error);
    } finally {
      this.loading = false;
    }
  }
  renderItems(items) {
    // 渲染邏輯
  }
}
// 使用
const loader = new InfiniteScrollLoader({
  onLoad: async (page) => {
    const response = await fetch(`/api/items?page=${page}`);
    return response.json();
  }
});

內(nèi)存泄漏排查

3.1 常見(jiàn)內(nèi)存泄漏場(chǎng)景

3.1.1 未清理的事件監(jiān)聽(tīng)器

// ? 錯(cuò)誤示例 - 內(nèi)存泄漏
class Component {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
    document.addEventListener('click', this.handleClick);
  }
  handleClick() {
    console.log('clicked');
  }
}
// ? 正確示例 - 清理監(jiān)聽(tīng)器
class Component {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
    document.addEventListener('click', this.handleClick);
  }
  handleClick() {
    console.log('clicked');
  }
  destroy() {
    document.removeEventListener('click', this.handleClick);
  }
}
// React中使用useEffect清理
function MyComponent() {
  useEffect(() => {
    const handleClick = () => console.log('clicked');
    document.addEventListener('click', handleClick);
    // 清理函數(shù)
    return () => {
      document.removeEventListener('click', handleClick);
    };
  }, []);
  return <div>Hello</div>;
}

3.1.2 定時(shí)器未清除

// ? 錯(cuò)誤示例
class TimerComponent {
  start() {
    this.timer = setInterval(() => {
      this.update();
    }, 1000);
  }
}
// ? 正確示例
class TimerComponent {
  start() {
    this.timer = setInterval(() => {
      this.update();
    }, 1000);
  }
  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }
}
// React中
function Timer() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('tick');
    }, 1000);
    return () => clearInterval(timer);
  }, []);
  return <div>Timer</div>;
}

3.1.3 閉包導(dǎo)致的內(nèi)存泄漏

// ? 潛在問(wèn)題
function createHandler() {
  const largeData = new Array(1000000).fill('data');
  return function() {
    console.log('handled');
    // 這里訪問(wèn)largeData會(huì)導(dǎo)致其無(wú)法被GC回收
  };
}
// ? 解決方案 - 釋放不需要的引用
function createHandler() {
  const largeData = new Array(1000000).fill('data');
  // 處理完數(shù)據(jù)后立即釋放
  const result = processData(largeData);
  largeData.length = 0; // 清空數(shù)組
  return function() {
    console.log('handled', result);
  };
}

3.2 使用Chrome DevTools檢測(cè)內(nèi)存泄漏

內(nèi)存快照對(duì)比

// 用于測(cè)試內(nèi)存泄漏的代碼
let leakingObjects = [];
function createLeak() {
  for (let i = 0; i < 10000; i++) {
    leakingObjects.push({
      id: i,
      data: new Array(100).fill('x'),
      // 添加到全局window對(duì)象
      window[`temp${i}`] = `data${i}`
    });
  }
}
function cleanLeak() {
  leakingObjects = [];
  // 清理window上的屬性
  for (let key in window) {
    if (key.startsWith('temp')) {
      delete window[key];
    }
  }
}
// 使用步驟:
// 1. 打開(kāi)Chrome DevTools -> Memory
// 2. 拍攝初始快照
// 3. 執(zhí)行 createLeak()
// 4. 拍攝第二個(gè)快照
// 5. 對(duì)比兩個(gè)快照,查看對(duì)象增長(zhǎng)
// 6. 執(zhí)行 cleanLeak()
// 7. 拍攝第三個(gè)快照,確認(rèn)清理效果

堆快照分析

// 測(cè)試DOM節(jié)點(diǎn)泄漏
let detachedNodes = [];
function createDetachedNodes() {
  for (let i = 0; i < 1000; i++) {
    const div = document.createElement('div');
    div.innerHTML = `Node ${i}`;
    // 創(chuàng)建但不掛載到DOM
    detachedNodes.push(div);
  }
}
// 檢測(cè)方法:
// 1. Memory -> Take Heap Snapshot
// 2. 搜索 "Detached DOM tree"
// 3. 查看保留樹(shù)(retainers)找到引用來(lái)源

3.3 內(nèi)存泄漏檢測(cè)工具封裝

class MemoryMonitor {
  constructor() {
    this.initialSize = 0;
    this.snapshots = [];
  }
  async takeSnapshot(name) {
    if (!window.performance || !window.performance.memory) {
      console.warn('性能API不可用');
      return null;
    }
    const snapshot = {
      name,
      timestamp: Date.now(),
      usedJSHeapSize: window.performance.memory.usedJSHeapSize,
      totalJSHeapSize: window.performance.memory.totalJSHeapSize,
      jsHeapSizeLimit: window.performance.memory.jsHeapSizeLimit
    };
    this.snapshots.push(snapshot);
    console.log(`?? ${name}:`, this.formatMemory(snapshot.usedJSHeapSize));
    return snapshot;
  }
  formatMemory(bytes) {
    return (bytes / 1024 / 1024).toFixed(2) + ' MB';
  }
  compareSnapshots(name1, name2) {
    const snap1 = this.snapshots.find(s => s.name === name1);
    const snap2 = this.snapshots.find(s => s.name === name2);
    if (!snap1 || !snap2) {
      console.error('快照不存在');
      return;
    }
    const diff = snap2.usedJSHeapSize - snap1.usedJSHeapSize;
    const sign = diff > 0 ? '+' : '';
    console.log(`?? 內(nèi)存變化: ${sign}${this.formatMemory(diff)}`);
    if (diff > 0) {
      console.warn('?? 內(nèi)存增長(zhǎng),可能存在泄漏');
    }
  }
  printAllSnapshots() {
    console.table(this.snapshots.map(s => ({
      名稱(chēng): s.name,
      時(shí)間: new Date(s.timestamp).toLocaleTimeString(),
      已使用: this.formatMemory(s.usedJSHeapSize),
      總量: this.formatMemory(s.totalJSHeapSize),
      限制: this.formatMemory(s.jsHeapSizeLimit)
    })));
  }
}
// 使用示例
const monitor = new MemoryMonitor();
async function testMemoryLeak() {
  await monitor.takeSnapshot('初始狀態(tài)');
  // 執(zhí)行可能泄漏的操作
  const objects = [];
  for (let i = 0; i < 10000; i++) {
    objects.push({ id: i, data: new Array(100).fill('x') });
  }
  await monitor.takeSnapshot('操作后');
  monitor.compareSnapshots('初始狀態(tài)', '操作后');
  monitor.printAllSnapshots();
}

Chrome DevTools高級(jí)調(diào)試技巧

4.1 Performance面板

性能分析實(shí)戰(zhàn)

// 測(cè)試代碼 - 執(zhí)行耗時(shí)操作
function heavyComputation() {
  const result = [];
  for (let i = 0; i < 100000; i++) {
    result.push(i * i);
  }
  return result;
}
function inefficientSort(arr) {
  // 低效的排序算法 - 冒泡排序
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr;
}
// 使用性能標(biāo)記
performance.mark('start');
const data = heavyComputation();
inefficientSort(data);
performance.mark('end');
performance.measure('總耗時(shí)', 'start', 'end');
const measure = performance.getEntriesByName('總耗時(shí)')[0];
console.log(`執(zhí)行時(shí)間: ${measure.duration.toFixed(2)}ms`);

性能分析步驟

1. 打開(kāi)Chrome DevTools -> Performance
2. 點(diǎn)擊Record按鈕(或按Ctrl+E)
3. 執(zhí)行需要分析的代碼
4. 停止錄制
5. 分析結(jié)果:
   - Main線程: 查看JavaScript執(zhí)行情況
   - Frames: 查看幀率,目標(biāo)60fps(16.6ms/frame)
   - Network: 查看資源加載
   - Flame Chart: 火焰圖,函數(shù)調(diào)用棧

4.2 Sources面板高級(jí)調(diào)試

條件斷點(diǎn)

// 在循環(huán)中設(shè)置條件斷點(diǎn)
function processUsers(users) {
  for (let i = 0; i < users.length; i++) {
    const user = users[i];
    console.log(`Processing user ${i}: ${user.name}`);
    // 設(shè)置條件斷點(diǎn): i === 100
    // 右鍵行號(hào) -> Add conditional breakpoint -> 輸入條件
  }
}

Logpoint(日志點(diǎn))

function calculateTotal(items) {
  let total = 0;
  items.forEach((item, index) => {
    total += item.price;
    // 設(shè)置Logpoint: "Item ${index}: ${item.price}, Total: ${total}"
    // 右鍵行號(hào) -> Add logpoint
    // 不需要中斷,只在Console輸出日志
  });
  return total;
}

DOM斷點(diǎn)

// HTML結(jié)構(gòu)
<div id="container">
  <button id="addBtn">添加元素</button>
  <div id="list"></div>
</div>
<script>
const list = document.getElementById('list');
document.getElementById('addBtn').addEventListener('click', () => {
  const item = document.createElement('div');
  item.textContent = 'New Item';
  list.appendChild(item);
});
</script>
// 使用DOM斷點(diǎn):
// 1. Elements面板右鍵#list元素
// 2. Break on -> Subtree modifications
// 3. 點(diǎn)擊按鈕,自動(dòng)觸發(fā)斷點(diǎn)

黑盒腳本(Blackboxing)

// 對(duì)于第三方庫(kù)代碼,不想單步調(diào)試時(shí)可以黑盒化
// Sources面板 -> 右鍵腳本文件 -> Blackbox script
// 之后調(diào)試時(shí)將跳過(guò)該文件的內(nèi)部細(xì)節(jié)

4.3 Network面板優(yōu)化

資源加載分析

// 模擬API請(qǐng)求
async function fetchUserData() {
  // 添加性能標(biāo)記
  const startMark = 'fetch-start';
  const endMark = 'fetch-end';
  performance.mark(startMark);
  const response = await fetch('/api/users', {
    headers: { 'Content-Type': 'application/json' }
  });
  performance.mark(endMark);
  performance.measure('fetch-user', startMark, endMark);
  const measure = performance.getEntriesByName('fetch-user')[0];
  console.log(`API請(qǐng)求耗時(shí): ${measure.duration}ms`);
  return response.json();
}

緩存策略測(cè)試

// fetch API緩存控制
fetch('/api/data', {
  cache: 'no-cache', // 'default', 'no-store', 'reload', 'no-cache', 'force-cache', 'only-if-cached'
  headers: {
    'Cache-Control': 'max-age=3600',
    'ETag': 'some-etag-value'
  }
});
// Service Worker緩存測(cè)試
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => {
        console.log('SW注冊(cè)成功:', registration);
      })
      .catch(error => {
        console.log('SW注冊(cè)失敗:', error);
      });
  });
}

4.4 Console高級(jí)技巧

console.table數(shù)據(jù)可視化

const users = [
  { id: 1, name: 'Alice', age: 25, role: 'Admin' },
  { id: 2, name: 'Bob', age: 30, role: 'User' },
  { id: 3, name: 'Charlie', age: 35, role: 'Admin' }
];
// 表格化顯示
console.table(users);
// 只顯示特定列
console.table(users, ['name', 'age']);

console.time性能測(cè)量

// 測(cè)量代碼執(zhí)行時(shí)間
console.time('array-sort');
const arr = Array.from({ length: 10000 }, () => Math.random());
arr.sort((a, b) => a - b);
console.timeEnd('array-sort');
// 輸出: array-sort: 5.234ms

console.group分組輸出

function analyzeData(data) {
  console.group('數(shù)據(jù)分析');
  console.log('數(shù)據(jù)總數(shù):', data.length);
  console.group('前5項(xiàng)數(shù)據(jù)');
  console.table(data.slice(0, 5));
  console.groupEnd();
  console.group('統(tǒng)計(jì)信息');
  const avg = data.reduce((sum, val) => sum + val, 0) / data.length;
  console.log('平均值:', avg);
  console.log('最大值:', Math.max(...data));
  console.log('最小值:', Math.min(...data));
  console.groupEnd();
  console.groupEnd();
}

4.5 Lighthouse性能評(píng)分

// 自動(dòng)化Lighthouse測(cè)試
// 安裝: npm install -g lighthouse
// 運(yùn)行: lighthouse https://example.com --view
// 使用Node.js API
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');
async function runLighthouse(url) {
  const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
  const options = {
    logLevel: 'info',
    output: 'json',
    onlyCategories: ['performance'],
    port: chrome.port
  };
  const runnerResult = await lighthouse(url, options);
  await chrome.kill();
  const score = runnerResult.lhr.categories.performance.score * 100;
  console.log(`性能得分: ${score}`);
  // 詳細(xì)報(bào)告
  console.log('FCP:', runnerResult.lhr.audits['first-contentful-paint'].displayValue);
  console.log('LCP:', runnerResult.lhr.audits['largest-contentful-paint'].displayValue);
  console.log('CLS:', runnerResult.lhr.audits['cumulative-layout-shift'].displayValue);
  return runnerResult;
}
// runLighthouse('https://example.com');

4.6 調(diào)試React應(yīng)用

React DevTools

// React Profiler使用
import { Profiler } from 'react';
function onRenderCallback(
  id,              // 組件的標(biāo)識(shí)
  phase,           // 'mount' 或 'update'
  actualDuration,  // 渲染耗時(shí)
  baseDuration,    // 不使用memoization的基礎(chǔ)耗時(shí)
  startTime,       // 開(kāi)始時(shí)間
  commitTime       // 提交時(shí)間
) {
  console.log(`${id} ${phase}:`, actualDuration.toFixed(2) + 'ms');
}
function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <MyComponent />
    </Profiler>
  );
}

性能優(yōu)化檢查清單

// React性能優(yōu)化要點(diǎn)
// 1. 使用React.memo避免不必要的重渲染
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
  return <div>{/* 復(fù)雜渲染 */}</div>;
});
// 2. 使用useMemo緩存計(jì)算結(jié)果
function Component({ items }) {
  const sortedItems = useMemo(() => {
    return [...items].sort((a, b) => a.value - b.value);
  }, [items]);
  return <List items={sortedItems} />;
}
// 3. 使用useCallback緩存回調(diào)函數(shù)
function Parent() {
  const handleClick = useCallback((id) => {
    console.log('Clicked:', id);
  }, []);
  return <Child onClick={handleClick} />;
}
// 4. 虛擬列表優(yōu)化長(zhǎng)列表
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
  return (
    <FixedSizeList
      height={400}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style}>{items[index].name}</div>
      )}
    </FixedSizeList>
  );
}

綜合實(shí)戰(zhàn)案例

案例: 電商商品列表頁(yè)性能優(yōu)化

// goods-list.js - 優(yōu)化前
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function GoodsList() {
  const [goods, setGoods] = useState([]);
  const [loading, setLoading] = useState(false);
  useEffect(() => {
    setLoading(true);
    axios.get('/api/goods')
      .then(res => {
        setGoods(res.data);
        setLoading(false);
      });
  }, []);
  if (loading) return <div>加載中...</div>;
  return (
    <div>
      {goods.map(item => (
        <GoodCard 
          key={item.id}
          title={item.title}
          price={item.price}
          image={item.image}
          description={item.description}
        />
      ))}
    </div>
  );
}
// GoodCard.js - 優(yōu)化前
function GoodCard({ title, price, image, description }) {
  return (
    <div className="card">
      <img src={image} alt={title} />
      <h3>{title}</h3>
      <p>¥{price}</p>
      <p>{description}</p>
    </div>
  );
}
// goods-list-optimized.js - 優(yōu)化后
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { FixedSizeList as List } from 'react-window';
import LazyImage from './LazyImage';
// 懶加載GoodsCard組件
const GoodsCard = React.lazy(() => import('./GoodsCard'));
function GoodsList() {
  const [goods, setGoods] = useState([]);
  const [loading, setLoading] = useState(false);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  // 使用useCallback緩存fetch函數(shù)
  const fetchGoods = useCallback(async (pageNum = 1) => {
    setLoading(true);
    try {
      const res = await axios.get('/api/goods', { 
        params: { page: pageNum, size: 20 } 
      });
      if (pageNum === 1) {
        setGoods(res.data.list);
      } else {
        setGoods(prev => [...prev, ...res.data.list]);
      }
      setHasMore(res.data.hasMore);
    } catch (error) {
      console.error('加載失敗:', error);
    } finally {
      setLoading(false);
    }
  }, []);
  useEffect(() => {
    fetchGoods(1);
  }, [fetchGoods]);
  // 無(wú)限滾動(dòng)加載
  const loadMore = useCallback(() => {
    if (!loading && hasMore) {
      const nextPage = page + 1;
      setPage(nextPage);
      fetchGoods(nextPage);
    }
  }, [loading, hasMore, page, fetchGoods]);
  // 使用useMemo優(yōu)化商品列表
  const memoizedGoods = useMemo(() => goods, [goods]);
  // 行渲染函數(shù)
  const Row = useCallback(({ index, style }) => (
    <div style={style}>
      <GoodsCard item={memoizedGoods[index]} />
    </div>
  ), [memoizedGoods]);
  if (loading && page === 1) return <LoadingSkeleton />;
  return (
    <Suspense fallback={<div>加載組件中...</div>}>
      <List
        height={800}
        itemCount={memoizedGoods.length}
        itemSize={300}
        width="100%"
        onItemsRendered={({ visibleStopIndex }) => {
          // 接近底部時(shí)加載更多
          if (visibleStopIndex >= memoizedGoods.length - 5) {
            loadMore();
          }
        }}
      >
        {Row}
      </List>
      {loading && <div>加載更多...</div>}
    </Suspense>
  );
}
// goods-card-optimized.js
import React, { memo } from 'react';
import LazyImage from './LazyImage';
// 使用React.memo避免不必要的重渲染
const GoodsCard = memo(function GoodsCard({ item }) {
  return (
    <div className="card">
      {/* 使用懶加載圖片 */}
      <LazyImage 
        src={item.image} 
        alt={item.title}
        placeholder="加載中..."
      />
      <h3>{item.title}</h3>
      <p>¥{item.price}</p>
      <p>{item.description}</p>
    </div>
  );
});
export default GoodsCard;

性能對(duì)比測(cè)試

// performance-test.js
import { performance } from 'perf_hooks';
class PerformanceTester {
  constructor(name) {
    this.name = name;
  }
  async test(fn, iterations = 100) {
    const results = [];
    for (let i = 0; i < iterations; i++) {
      const start = performance.now();
      await fn();
      const end = performance.now();
      results.push(end - start);
    }
    const avg = results.reduce((a, b) => a + b, 0) / results.length;
    const min = Math.min(...results);
    const max = Math.max(...results);
    console.log(`\n?? ${this.name} 性能測(cè)試結(jié)果:`);
    console.log(`   平均耗時(shí): ${avg.toFixed(2)}ms`);
    console.log(`   最小耗時(shí): ${min.toFixed(2)}ms`);
    console.log(`   最大耗時(shí): ${max.toFixed(2)}ms`);
    return { avg, min, max };
  }
}
// 使用示例
const tester = new PerformanceTester('商品列表渲染');
// 測(cè)試優(yōu)化前
async function testBefore() {
  // 模擬渲染1000個(gè)商品
  const goods = Array.from({ length: 1000 }, (_, i) => ({
    id: i,
    title: `商品${i}`,
    price: i * 10,
    image: `image${i}.jpg`
  }));
  return goods;
}
// 測(cè)試優(yōu)化后
async function testAfter() {
  // 模擬虛擬列表渲染
  const goods = Array.from({ length: 1000 }, (_, i) => ({
    id: i,
    title: `商品${i}`,
    price: i * 10,
    image: `image${i}.jpg`
  }));
  return goods.slice(0, 20); // 只渲染可見(jiàn)部分
}
// 執(zhí)行測(cè)試
(async () => {
  await tester.test(testBefore);
  await tester.test(testAfter);
})();

最佳實(shí)踐總結(jié)

代碼分割

? 使用動(dòng)態(tài)import()進(jìn)行路由級(jí)別的代碼分割
? 配置splitChunks優(yōu)化第三方庫(kù)打包
? 使用React.lazy()和Suspense實(shí)現(xiàn)組件懶加載
? 避免過(guò)度分割,過(guò)多的chunk會(huì)影響性能

懶加載

? 圖片使用Intersection Observer實(shí)現(xiàn)懶加載
? 長(zhǎng)列表使用虛擬滾動(dòng)技術(shù)
? 非關(guān)鍵功能延遲加載
? 不要懶加載首屏關(guān)鍵資源

內(nèi)存管理

? 及時(shí)清理事件監(jiān)聽(tīng)器和定時(shí)器
? 避免不必要的閉包引用
? 使用Chrome DevTools定期檢查內(nèi)存使用
? 不要在全局對(duì)象上積累大量數(shù)據(jù)

性能優(yōu)化

? 使用Performance API監(jiān)測(cè)關(guān)鍵指標(biāo)
? 優(yōu)化主線程任務(wù),保持60fps
? 使用緩存策略減少重復(fù)計(jì)算
? 避免過(guò)早優(yōu)化,先測(cè)量后優(yōu)化

參考資料

到此這篇關(guān)于JavaScript性能調(diào)優(yōu)的文章就介紹到這了,更多相關(guān)JS性能調(diào)優(yōu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • javascript for循環(huán)性能測(cè)試示例

    javascript for循環(huán)性能測(cè)試示例

    這篇文章主要介紹了javascript for循環(huán)性能測(cè)試,結(jié)合實(shí)例形式分析了javascript使用for循環(huán)遍歷數(shù)組的三種常用方法及對(duì)應(yīng)的時(shí)間消耗,總結(jié)javascript使用for循環(huán)遍歷數(shù)組的相關(guān)操作技巧,需要的朋友可以參考下
    2019-08-08
  • TypeScript中yield和Generator的使用指南

    TypeScript中yield和Generator的使用指南

    本文主要介紹了TypeScript中Generator與yield的用法,涵蓋其語(yǔ)法、類(lèi)型定義、典型應(yīng)用場(chǎng)景及注意事項(xiàng),具有一定的參考價(jià)值,感興趣的可以來(lái)了解一下
    2025-08-08
  • uniapp小程序配置tabbar底部導(dǎo)航欄實(shí)戰(zhàn)指南

    uniapp小程序配置tabbar底部導(dǎo)航欄實(shí)戰(zhàn)指南

    tabBar如果應(yīng)用是一個(gè)多tab應(yīng)用,可以通過(guò)tabBar配置項(xiàng)指定tab欄的表現(xiàn),以及tab切換時(shí)顯示的對(duì)應(yīng)頁(yè),下面這篇文章主要給大家介紹了關(guān)于uniapp小程序配置tabbar底部導(dǎo)航欄的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • CocosCreator骨骼動(dòng)畫(huà)之龍骨DragonBones

    CocosCreator骨骼動(dòng)畫(huà)之龍骨DragonBones

    這篇文章主要介紹了怎么在CocosCreator中使用骨骼動(dòng)畫(huà)龍骨DragonBones,對(duì)骨骼動(dòng)畫(huà)感興趣的同學(xué),可以試一下
    2021-04-04
  • javascript頭像上傳代碼實(shí)例

    javascript頭像上傳代碼實(shí)例

    這篇文章主要介紹了javascript頭像上傳代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • 深入剖析JavaScript instanceof 運(yùn)算符

    深入剖析JavaScript instanceof 運(yùn)算符

    這篇文章主要介紹了深入剖析JavaScript instanceof 運(yùn)算符,ECMAScript 引入了另一個(gè) Java 運(yùn)算符 instanceof 來(lái)解決這個(gè)問(wèn)題。instanceof 運(yùn)算符與 typeof 運(yùn)算符相似,用于識(shí)別正在處理的對(duì)象的類(lèi)型。,需要的朋友可以參考下
    2019-06-06
  • 微信小程序中不同頁(yè)面?zhèn)鬟f參數(shù)的操作方法

    微信小程序中不同頁(yè)面?zhèn)鬟f參數(shù)的操作方法

    這篇文章主要介紹了微信小程序中不同頁(yè)面?zhèn)鬟f參數(shù)的操作方法,在開(kāi)發(fā)項(xiàng)目中,避免不了不同頁(yè)面之間傳遞數(shù)據(jù)等,那么就需要進(jìn)行不同頁(yè)面之間的一個(gè)數(shù)據(jù)傳遞的,需要的朋友可以參考下
    2023-12-12
  • 一個(gè)css與js結(jié)合的下拉菜單支持主流瀏覽器

    一個(gè)css與js結(jié)合的下拉菜單支持主流瀏覽器

    這是一個(gè)css和js結(jié)合的下拉菜單,經(jīng)測(cè)試支持主流瀏覽器,比較適合企業(yè)站,需要的朋友可以參考下
    2014-10-10
  • 詳解JS如何使用Promise緩存網(wǎng)絡(luò)請(qǐng)求

    詳解JS如何使用Promise緩存網(wǎng)絡(luò)請(qǐng)求

    網(wǎng)絡(luò)請(qǐng)求是現(xiàn)代Web應(yīng)用中的常見(jiàn)操作,很多時(shí)候需要獲取服務(wù)器上的數(shù)據(jù),在進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí),為了減輕服務(wù)器的壓力,緩存策略常被用來(lái)避免對(duì)同一數(shù)據(jù)的重復(fù)請(qǐng)求,本文將探討如何使用Promise結(jié)合緩存來(lái)高效處理網(wǎng)絡(luò)請(qǐng)求,需要的朋友可以參考下
    2023-12-12
  • 前端使用JS內(nèi)置Blob實(shí)現(xiàn)下載各種形式的文件實(shí)例

    前端使用JS內(nèi)置Blob實(shí)現(xiàn)下載各種形式的文件實(shí)例

    通過(guò)使用JavaScript我們可以很方便地實(shí)現(xiàn)文件的下載功能,這篇文章主要給大家介紹了關(guān)于前端使用JS內(nèi)置Blob實(shí)現(xiàn)下載各種形式文件的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06

最新評(píng)論

格尔木市| 桦南县| 京山县| 西吉县| 和田市| 内黄县| 泗阳县| 惠安县| 丰城市| 黄山市| 扎兰屯市| 莫力| 疏附县| 射洪县| 民权县| 中西区| 中卫市| 南岸区| 孙吴县| 彝良县| 庆元县| 图木舒克市| 修水县| 南宫市| 成武县| 栾城县| 清徐县| 新建县| 靖江市| 安徽省| 湖口县| 枣庄市| 奉节县| 璧山县| 博湖县| 新平| 阿合奇县| 长春市| 梨树县| 涞源县| 葵青区|