[{"data":1,"prerenderedAt":3866},["ShallowReactive",2],{"article-/topics/performance/large-list-rendering-guide":3,"related-performance":365,"content-query-X4uPVMAy7B":3589},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":12,"image":18,"featured":6,"readingTime":19,"body":20,"_type":359,"_id":360,"_source":361,"_file":362,"_stem":363,"_extension":364},"/topics/performance/large-list-rendering-guide","performance",false,"","大列表渲染性能方案完全指南","深入解析前端大列表渲染的性能挑战与解决方案，从虚拟滚动到分页加载，掌握处理海量数据展示的核心技术和最佳实践。","2024-12-25","HTMLPAGE 团队",[13,14,15,16,17],"大列表","性能优化","虚拟滚动","分页加载","前端渲染","/images/topics/performance-guide.jpg",18,{"type":21,"children":22,"toc":328},"root",[23,30,35,41,51,56,62,70,76,81,86,97,102,111,117,122,127,136,141,152,158,163,168,176,181,190,195,204,209,214,223,229,234,243,249,254,263,268,273,282,287,296,301,310,315,323],{"type":24,"tag":25,"props":26,"children":27},"element","h2",{"id":8},[28],{"type":29,"value":8},"text",{"type":24,"tag":25,"props":31,"children":33},{"id":32},"大列表渲染的挑战",[34],{"type":29,"value":32},{"type":24,"tag":36,"props":37,"children":38},"p",{},[39],{"type":29,"value":40},"在现代 Web 应用中，展示大量数据是常见需求：电商产品列表、社交媒体信息流、日志查看器、数据表格等。当数据量从几百条增长到几万甚至几十万条时，传统的渲染方式会遇到严重的性能问题。",{"type":24,"tag":42,"props":43,"children":45},"pre",{"code":44},"大列表渲染的性能问题：\n\n数据量        DOM 节点数      内存占用        渲染时间\n───────────────────────────────────────────────────────\n100 条        ~500           ~5 MB          ~50ms   ✅\n1,000 条      ~5,000         ~50 MB         ~500ms  ⚠️\n10,000 条     ~50,000        ~500 MB        ~5s     ❌\n100,000 条    ~500,000       ~5 GB          崩溃    💀\n\n问题根源：\n┌─────────────────────────────────────────────────────────┐\n│                                                         │\n│  1. DOM 节点过多                                        │\n│     → 内存占用高                                        │\n│     → 布局计算慢                                        │\n│     → 滚动卡顿                                          │\n│                                                         │\n│  2. 初始渲染耗时                                        │\n│     → 首屏白屏时间长                                    │\n│     → 用户等待体验差                                    │\n│                                                         │\n│  3. 交互响应慢                                          │\n│     → 搜索/筛选延迟                                     │\n│     → 更新单项导致全量重渲染                            │\n│                                                         │\n└─────────────────────────────────────────────────────────┘\n",[46],{"type":24,"tag":47,"props":48,"children":49},"code",{"__ignoreMap":7},[50],{"type":29,"value":44},{"type":24,"tag":25,"props":52,"children":54},{"id":53},"解决方案概览",[55],{"type":29,"value":53},{"type":24,"tag":57,"props":58,"children":60},"h3",{"id":59},"方案对比",[61],{"type":29,"value":59},{"type":24,"tag":42,"props":63,"children":65},{"code":64},"大列表渲染方案对比：\n\n┌──────────────────┬──────────────┬──────────────┬──────────────┐\n│      方案        │   适用场景   │    复杂度    │   性能提升   │\n├──────────────────┼──────────────┼──────────────┼──────────────┤\n│ 分页加载         │ \u003C10,000 条   │    低        │    中        │\n│ 懒加载/无限滚动  │ \u003C10,000 条   │    低        │    中        │\n│ 虚拟滚动         │ 任意数量     │    中        │    高        │\n│ 时间切片         │ 初始渲染     │    中        │    中        │\n│ Web Worker       │ 复杂计算     │    高        │    高        │\n│ Canvas/WebGL     │ 超大量数据   │    高        │    极高      │\n└──────────────────┴──────────────┴──────────────┴──────────────┘\n",[66],{"type":24,"tag":47,"props":67,"children":68},{"__ignoreMap":7},[69],{"type":29,"value":64},{"type":24,"tag":25,"props":71,"children":73},{"id":72},"方案一分页加载",[74],{"type":29,"value":75},"方案一：分页加载",{"type":24,"tag":36,"props":77,"children":78},{},[79],{"type":29,"value":80},"分页是最简单直接的解决方案，将大量数据分割成小块按需加载。",{"type":24,"tag":57,"props":82,"children":84},{"id":83},"传统分页实现",[85],{"type":29,"value":83},{"type":24,"tag":42,"props":87,"children":92},{"code":88,"language":89,"meta":7,"className":90},"// composables/usePagination.ts\nimport { ref, computed, watch } from 'vue';\n\ninterface PaginationOptions\u003CT> {\n  data: T[] | (() => Promise\u003CT[]>);\n  pageSize?: number;\n  initialPage?: number;\n}\n\nexport function usePagination\u003CT>(options: PaginationOptions\u003CT>) {\n  const {\n    data,\n    pageSize = 20,\n    initialPage = 1\n  } = options;\n  \n  const currentPage = ref(initialPage);\n  const allData = ref\u003CT[]>([]);\n  const isLoading = ref(false);\n  const totalCount = ref(0);\n  \n  // 计算总页数\n  const totalPages = computed(() => \n    Math.ceil(totalCount.value / pageSize)\n  );\n  \n  // 当前页数据\n  const currentData = computed(() => {\n    const start = (currentPage.value - 1) * pageSize;\n    const end = start + pageSize;\n    return allData.value.slice(start, end);\n  });\n  \n  // 分页信息\n  const pagination = computed(() => ({\n    current: currentPage.value,\n    pageSize,\n    total: totalCount.value,\n    totalPages: totalPages.value,\n    hasNext: currentPage.value \u003C totalPages.value,\n    hasPrev: currentPage.value > 1\n  }));\n  \n  // 加载数据\n  async function loadData() {\n    isLoading.value = true;\n    try {\n      const result = typeof data === 'function' ? await data() : data;\n      allData.value = result;\n      totalCount.value = result.length;\n    } finally {\n      isLoading.value = false;\n    }\n  }\n  \n  // 页面导航\n  function goToPage(page: number) {\n    if (page >= 1 && page \u003C= totalPages.value) {\n      currentPage.value = page;\n    }\n  }\n  \n  function nextPage() {\n    if (pagination.value.hasNext) {\n      currentPage.value++;\n    }\n  }\n  \n  function prevPage() {\n    if (pagination.value.hasPrev) {\n      currentPage.value--;\n    }\n  }\n  \n  // 初始加载\n  loadData();\n  \n  return {\n    data: currentData,\n    pagination,\n    isLoading,\n    goToPage,\n    nextPage,\n    prevPage,\n    reload: loadData\n  };\n}\n","typescript",[91],"language-typescript",[93],{"type":24,"tag":47,"props":94,"children":95},{"__ignoreMap":7},[96],{"type":29,"value":88},{"type":24,"tag":57,"props":98,"children":100},{"id":99},"服务端分页",[101],{"type":29,"value":99},{"type":24,"tag":42,"props":103,"children":106},{"code":104,"language":89,"meta":7,"className":105},"// composables/useServerPagination.ts\ninterface ServerPaginationOptions {\n  fetchFn: (params: { page: number; pageSize: number }) => Promise\u003C{\n    data: any[];\n    total: number;\n  }>;\n  pageSize?: number;\n}\n\nexport function useServerPagination(options: ServerPaginationOptions) {\n  const { fetchFn, pageSize = 20 } = options;\n  \n  const data = ref([]);\n  const currentPage = ref(1);\n  const total = ref(0);\n  const isLoading = ref(false);\n  const error = ref\u003CError | null>(null);\n  \n  const totalPages = computed(() => Math.ceil(total.value / pageSize));\n  \n  async function fetchPage(page: number) {\n    isLoading.value = true;\n    error.value = null;\n    \n    try {\n      const result = await fetchFn({ page, pageSize });\n      data.value = result.data;\n      total.value = result.total;\n      currentPage.value = page;\n    } catch (e) {\n      error.value = e as Error;\n    } finally {\n      isLoading.value = false;\n    }\n  }\n  \n  // 预加载下一页\n  async function prefetchNext() {\n    if (currentPage.value \u003C totalPages.value) {\n      // 静默预加载，不更新状态\n      await fetchFn({ page: currentPage.value + 1, pageSize });\n    }\n  }\n  \n  // 初始加载\n  fetchPage(1);\n  \n  return {\n    data,\n    currentPage,\n    total,\n    totalPages,\n    isLoading,\n    error,\n    goToPage: fetchPage,\n    prefetchNext\n  };\n}\n\n// 使用示例\nconst { data, pagination, goToPage } = useServerPagination({\n  fetchFn: async ({ page, pageSize }) => {\n    const response = await fetch(\n      `/api/products?page=${page}&limit=${pageSize}`\n    );\n    return response.json();\n  },\n  pageSize: 20\n});\n",[91],[107],{"type":24,"tag":47,"props":108,"children":109},{"__ignoreMap":7},[110],{"type":29,"value":104},{"type":24,"tag":25,"props":112,"children":114},{"id":113},"方案二无限滚动",[115],{"type":29,"value":116},"方案二：无限滚动",{"type":24,"tag":36,"props":118,"children":119},{},[120],{"type":29,"value":121},"无限滚动（也叫懒加载）在用户滚动到底部时自动加载更多数据，适合社交媒体、新闻流等场景。",{"type":24,"tag":57,"props":123,"children":125},{"id":124},"基础无限滚动",[126],{"type":29,"value":124},{"type":24,"tag":42,"props":128,"children":131},{"code":129,"language":89,"meta":7,"className":130},"// composables/useInfiniteScroll.ts\nimport { ref, onMounted, onUnmounted } from 'vue';\n\ninterface InfiniteScrollOptions\u003CT> {\n  fetchFn: (page: number) => Promise\u003CT[]>;\n  pageSize?: number;\n  threshold?: number; // 距离底部多少像素时触发加载\n  container?: HTMLElement | null;\n}\n\nexport function useInfiniteScroll\u003CT>(options: InfiniteScrollOptions\u003CT>) {\n  const {\n    fetchFn,\n    pageSize = 20,\n    threshold = 200,\n    container = null\n  } = options;\n  \n  const items = ref\u003CT[]>([]);\n  const page = ref(1);\n  const isLoading = ref(false);\n  const hasMore = ref(true);\n  const error = ref\u003CError | null>(null);\n  \n  // 加载更多数据\n  async function loadMore() {\n    if (isLoading.value || !hasMore.value) return;\n    \n    isLoading.value = true;\n    error.value = null;\n    \n    try {\n      const newItems = await fetchFn(page.value);\n      \n      if (newItems.length \u003C pageSize) {\n        hasMore.value = false;\n      }\n      \n      items.value = [...items.value, ...newItems];\n      page.value++;\n    } catch (e) {\n      error.value = e as Error;\n    } finally {\n      isLoading.value = false;\n    }\n  }\n  \n  // 滚动处理\n  function handleScroll() {\n    const scrollContainer = container || document.documentElement;\n    const scrollTop = scrollContainer.scrollTop;\n    const scrollHeight = scrollContainer.scrollHeight;\n    const clientHeight = scrollContainer.clientHeight;\n    \n    // 距离底部小于阈值时加载\n    if (scrollHeight - scrollTop - clientHeight \u003C threshold) {\n      loadMore();\n    }\n  }\n  \n  // 使用 IntersectionObserver 优化\n  let observer: IntersectionObserver | null = null;\n  const sentinelRef = ref\u003CHTMLElement | null>(null);\n  \n  function setupObserver() {\n    if (!sentinelRef.value) return;\n    \n    observer = new IntersectionObserver(\n      (entries) => {\n        if (entries[0].isIntersecting) {\n          loadMore();\n        }\n      },\n      {\n        root: container,\n        rootMargin: `${threshold}px`\n      }\n    );\n    \n    observer.observe(sentinelRef.value);\n  }\n  \n  // 重置\n  function reset() {\n    items.value = [];\n    page.value = 1;\n    hasMore.value = true;\n    error.value = null;\n  }\n  \n  onMounted(() => {\n    loadMore(); // 初始加载\n    setupObserver();\n  });\n  \n  onUnmounted(() => {\n    observer?.disconnect();\n  });\n  \n  return {\n    items,\n    isLoading,\n    hasMore,\n    error,\n    sentinelRef,\n    loadMore,\n    reset\n  };\n}\n",[91],[132],{"type":24,"tag":47,"props":133,"children":134},{"__ignoreMap":7},[135],{"type":29,"value":129},{"type":24,"tag":57,"props":137,"children":139},{"id":138},"无限滚动组件",[140],{"type":29,"value":138},{"type":24,"tag":42,"props":142,"children":147},{"code":143,"language":144,"meta":7,"className":145},"\u003C!-- components/InfiniteList.vue -->\n\u003Cscript setup lang=\"ts\" generic=\"T\">\nimport { useInfiniteScroll } from '@/composables/useInfiniteScroll';\n\nconst props = defineProps\u003C{\n  fetchFn: (page: number) => Promise\u003CT[]>;\n  pageSize?: number;\n  threshold?: number;\n}>();\n\nconst { items, isLoading, hasMore, error, sentinelRef, reset } = \n  useInfiniteScroll({\n    fetchFn: props.fetchFn,\n    pageSize: props.pageSize,\n    threshold: props.threshold\n  });\n\ndefineExpose({ reset });\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"infinite-list\">\n    \u003C!-- 列表项 -->\n    \u003Cdiv v-for=\"(item, index) in items\" :key=\"index\" class=\"list-item\">\n      \u003Cslot :item=\"item\" :index=\"index\" />\n    \u003C/div>\n    \n    \u003C!-- 加载状态 -->\n    \u003Cdiv v-if=\"isLoading\" class=\"loading\">\n      \u003Cslot name=\"loading\">\n        \u003Cdiv class=\"loading-spinner\">加载中...\u003C/div>\n      \u003C/slot>\n    \u003C/div>\n    \n    \u003C!-- 错误状态 -->\n    \u003Cdiv v-else-if=\"error\" class=\"error\">\n      \u003Cslot name=\"error\" :error=\"error\">\n        \u003Cp>加载失败: {{ error.message }}\u003C/p>\n        \u003Cbutton @click=\"reset\">重试\u003C/button>\n      \u003C/slot>\n    \u003C/div>\n    \n    \u003C!-- 没有更多 -->\n    \u003Cdiv v-else-if=\"!hasMore && items.length > 0\" class=\"no-more\">\n      \u003Cslot name=\"no-more\">\n        \u003Cp>已加载全部内容\u003C/p>\n      \u003C/slot>\n    \u003C/div>\n    \n    \u003C!-- 空状态 -->\n    \u003Cdiv v-else-if=\"!hasMore && items.length === 0\" class=\"empty\">\n      \u003Cslot name=\"empty\">\n        \u003Cp>暂无数据\u003C/p>\n      \u003C/slot>\n    \u003C/div>\n    \n    \u003C!-- 观察哨兵元素 -->\n    \u003Cdiv ref=\"sentinelRef\" class=\"sentinel\" />\n  \u003C/div>\n\u003C/template>\n\n\u003Cstyle scoped>\n.infinite-list {\n  position: relative;\n}\n\n.sentinel {\n  height: 1px;\n  visibility: hidden;\n}\n\n.loading, .error, .no-more, .empty {\n  padding: 20px;\n  text-align: center;\n}\n\n.loading-spinner {\n  display: inline-block;\n  animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n  from { transform: rotate(0deg); }\n  to { transform: rotate(360deg); }\n}\n\u003C/style>\n","vue",[146],"language-vue",[148],{"type":24,"tag":47,"props":149,"children":150},{"__ignoreMap":7},[151],{"type":29,"value":143},{"type":24,"tag":25,"props":153,"children":155},{"id":154},"方案三虚拟滚动",[156],{"type":29,"value":157},"方案三：虚拟滚动",{"type":24,"tag":36,"props":159,"children":160},{},[161],{"type":29,"value":162},"虚拟滚动是处理超大列表的最佳方案，核心思想是只渲染可视区域内的元素。",{"type":24,"tag":57,"props":164,"children":166},{"id":165},"虚拟滚动原理",[167],{"type":29,"value":165},{"type":24,"tag":42,"props":169,"children":171},{"code":170},"虚拟滚动工作原理：\n\n完整数据（10万条）                      实际渲染（~20条）\n┌─────────────────┐                    ┌─────────────────┐\n│ Item 0          │                    │                 │\n│ Item 1          │                    │                 │\n│ ...             │                    │                 │\n│ Item 999        │                    │                 │\n├─────────────────┤ ← 可视区域顶部 →   ├─────────────────┤\n│ Item 1000       │    ═══════════    │ Item 1000       │\n│ Item 1001       │    可视区域       │ Item 1001       │\n│ Item 1002       │    用户看到的     │ Item 1002       │\n│ ...             │    只有这些       │ ...             │\n│ Item 1019       │    ═══════════    │ Item 1019       │\n├─────────────────┤ ← 可视区域底部 →   ├─────────────────┤\n│ Item 1020       │                    │                 │\n│ ...             │                    │                 │\n│ Item 99999      │                    │                 │\n└─────────────────┘                    └─────────────────┘\n\n关键计算：\n- startIndex = Math.floor(scrollTop / itemHeight)\n- endIndex = startIndex + Math.ceil(viewportHeight / itemHeight)\n- 渲染 items[startIndex...endIndex]\n- 使用 paddingTop 模拟滚动位置\n",[172],{"type":24,"tag":47,"props":173,"children":174},{"__ignoreMap":7},[175],{"type":29,"value":170},{"type":24,"tag":57,"props":177,"children":179},{"id":178},"固定高度虚拟滚动实现",[180],{"type":29,"value":178},{"type":24,"tag":42,"props":182,"children":185},{"code":183,"language":89,"meta":7,"className":184},"// composables/useVirtualList.ts\nimport { ref, computed, onMounted, onUnmounted, watch } from 'vue';\n\ninterface VirtualListOptions\u003CT> {\n  items: T[];\n  itemHeight: number;\n  overscan?: number; // 额外渲染的缓冲项数\n}\n\nexport function useVirtualList\u003CT>(options: VirtualListOptions\u003CT>) {\n  const { items, itemHeight, overscan = 5 } = options;\n  \n  const containerRef = ref\u003CHTMLElement | null>(null);\n  const scrollTop = ref(0);\n  const containerHeight = ref(0);\n  \n  // 计算可见范围\n  const visibleRange = computed(() => {\n    const start = Math.floor(scrollTop.value / itemHeight);\n    const visibleCount = Math.ceil(containerHeight.value / itemHeight);\n    \n    // 添加缓冲区\n    const startIndex = Math.max(0, start - overscan);\n    const endIndex = Math.min(items.length, start + visibleCount + overscan);\n    \n    return { startIndex, endIndex };\n  });\n  \n  // 可见项\n  const visibleItems = computed(() => {\n    const { startIndex, endIndex } = visibleRange.value;\n    return items.slice(startIndex, endIndex).map((item, index) => ({\n      item,\n      index: startIndex + index\n    }));\n  });\n  \n  // 总高度（用于滚动条）\n  const totalHeight = computed(() => items.length * itemHeight);\n  \n  // 顶部偏移（用于定位可见项）\n  const offsetTop = computed(() => \n    visibleRange.value.startIndex * itemHeight\n  );\n  \n  // 滚动处理\n  function handleScroll(e: Event) {\n    const target = e.target as HTMLElement;\n    scrollTop.value = target.scrollTop;\n  }\n  \n  // 滚动到指定索引\n  function scrollToIndex(index: number, behavior: ScrollBehavior = 'auto') {\n    if (!containerRef.value) return;\n    \n    const offset = index * itemHeight;\n    containerRef.value.scrollTo({\n      top: offset,\n      behavior\n    });\n  }\n  \n  // 监听容器大小变化\n  let resizeObserver: ResizeObserver | null = null;\n  \n  onMounted(() => {\n    if (!containerRef.value) return;\n    \n    containerHeight.value = containerRef.value.clientHeight;\n    \n    resizeObserver = new ResizeObserver((entries) => {\n      containerHeight.value = entries[0].contentRect.height;\n    });\n    \n    resizeObserver.observe(containerRef.value);\n  });\n  \n  onUnmounted(() => {\n    resizeObserver?.disconnect();\n  });\n  \n  return {\n    containerRef,\n    visibleItems,\n    totalHeight,\n    offsetTop,\n    handleScroll,\n    scrollToIndex\n  };\n}\n",[91],[186],{"type":24,"tag":47,"props":187,"children":188},{"__ignoreMap":7},[189],{"type":29,"value":183},{"type":24,"tag":57,"props":191,"children":193},{"id":192},"虚拟滚动组件",[194],{"type":29,"value":192},{"type":24,"tag":42,"props":196,"children":199},{"code":197,"language":144,"meta":7,"className":198},"\u003C!-- components/VirtualList.vue -->\n\u003Cscript setup lang=\"ts\" generic=\"T\">\nimport { useVirtualList } from '@/composables/useVirtualList';\n\nconst props = defineProps\u003C{\n  items: T[];\n  itemHeight: number;\n  overscan?: number;\n}>();\n\nconst {\n  containerRef,\n  visibleItems,\n  totalHeight,\n  offsetTop,\n  handleScroll,\n  scrollToIndex\n} = useVirtualList({\n  items: props.items,\n  itemHeight: props.itemHeight,\n  overscan: props.overscan\n});\n\ndefineExpose({ scrollToIndex });\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cdiv \n    ref=\"containerRef\"\n    class=\"virtual-list-container\"\n    @scroll=\"handleScroll\"\n  >\n    \u003C!-- 撑开滚动高度的占位元素 -->\n    \u003Cdiv \n      class=\"virtual-list-spacer\"\n      :style=\"{ height: `${totalHeight}px` }\"\n    >\n      \u003C!-- 实际渲染的列表项 -->\n      \u003Cdiv \n        class=\"virtual-list-content\"\n        :style=\"{ transform: `translateY(${offsetTop}px)` }\"\n      >\n        \u003Cdiv\n          v-for=\"{ item, index } in visibleItems\"\n          :key=\"index\"\n          class=\"virtual-list-item\"\n          :style=\"{ height: `${itemHeight}px` }\"\n        >\n          \u003Cslot :item=\"item\" :index=\"index\" />\n        \u003C/div>\n      \u003C/div>\n    \u003C/div>\n  \u003C/div>\n\u003C/template>\n\n\u003Cstyle scoped>\n.virtual-list-container {\n  overflow-y: auto;\n  height: 100%;\n}\n\n.virtual-list-spacer {\n  position: relative;\n}\n\n.virtual-list-content {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n}\n\n.virtual-list-item {\n  box-sizing: border-box;\n}\n\u003C/style>\n",[146],[200],{"type":24,"tag":47,"props":201,"children":202},{"__ignoreMap":7},[203],{"type":29,"value":197},{"type":24,"tag":57,"props":205,"children":207},{"id":206},"动态高度虚拟滚动",[208],{"type":29,"value":206},{"type":24,"tag":36,"props":210,"children":211},{},[212],{"type":29,"value":213},"当列表项高度不固定时，需要更复杂的实现：",{"type":24,"tag":42,"props":215,"children":218},{"code":216,"language":89,"meta":7,"className":217},"// composables/useDynamicVirtualList.ts\ninterface DynamicVirtualListOptions\u003CT> {\n  items: T[];\n  estimatedItemHeight: number;\n  getItemKey: (item: T, index: number) => string | number;\n}\n\nexport function useDynamicVirtualList\u003CT>(\n  options: DynamicVirtualListOptions\u003CT>\n) {\n  const { items, estimatedItemHeight, getItemKey } = options;\n  \n  const containerRef = ref\u003CHTMLElement | null>(null);\n  const scrollTop = ref(0);\n  const containerHeight = ref(0);\n  \n  // 缓存每个项的实际高度\n  const itemHeights = ref\u003CMap\u003Cstring | number, number>>(new Map());\n  \n  // 获取项的高度（已测量或估算）\n  function getItemHeight(index: number): number {\n    const key = getItemKey(items[index], index);\n    return itemHeights.value.get(key) || estimatedItemHeight;\n  }\n  \n  // 更新项的高度\n  function updateItemHeight(index: number, height: number) {\n    const key = getItemKey(items[index], index);\n    if (itemHeights.value.get(key) !== height) {\n      itemHeights.value.set(key, height);\n    }\n  }\n  \n  // 计算项的位置\n  const positions = computed(() => {\n    const result: { top: number; height: number }[] = [];\n    let top = 0;\n    \n    for (let i = 0; i \u003C items.length; i++) {\n      const height = getItemHeight(i);\n      result.push({ top, height });\n      top += height;\n    }\n    \n    return result;\n  });\n  \n  // 总高度\n  const totalHeight = computed(() => {\n    if (positions.value.length === 0) return 0;\n    const last = positions.value[positions.value.length - 1];\n    return last.top + last.height;\n  });\n  \n  // 二分查找起始索引\n  function findStartIndex(scrollTop: number): number {\n    let low = 0;\n    let high = positions.value.length - 1;\n    \n    while (low \u003C= high) {\n      const mid = Math.floor((low + high) / 2);\n      const pos = positions.value[mid];\n      \n      if (pos.top + pos.height \u003C= scrollTop) {\n        low = mid + 1;\n      } else if (pos.top > scrollTop) {\n        high = mid - 1;\n      } else {\n        return mid;\n      }\n    }\n    \n    return Math.max(0, low);\n  }\n  \n  // 可见范围\n  const visibleRange = computed(() => {\n    const start = findStartIndex(scrollTop.value);\n    let end = start;\n    let accHeight = 0;\n    \n    while (end \u003C items.length && accHeight \u003C containerHeight.value + 200) {\n      accHeight += getItemHeight(end);\n      end++;\n    }\n    \n    // 添加缓冲\n    const startIndex = Math.max(0, start - 3);\n    const endIndex = Math.min(items.length, end + 3);\n    \n    return { startIndex, endIndex };\n  });\n  \n  // 可见项\n  const visibleItems = computed(() => {\n    const { startIndex, endIndex } = visibleRange.value;\n    return items.slice(startIndex, endIndex).map((item, i) => ({\n      item,\n      index: startIndex + i,\n      style: {\n        position: 'absolute' as const,\n        top: `${positions.value[startIndex + i]?.top || 0}px`,\n        left: 0,\n        right: 0\n      }\n    }));\n  });\n  \n  return {\n    containerRef,\n    visibleItems,\n    totalHeight,\n    updateItemHeight,\n    handleScroll: (e: Event) => {\n      scrollTop.value = (e.target as HTMLElement).scrollTop;\n    }\n  };\n}\n",[91],[219],{"type":24,"tag":47,"props":220,"children":221},{"__ignoreMap":7},[222],{"type":29,"value":216},{"type":24,"tag":25,"props":224,"children":226},{"id":225},"方案四时间切片渲染",[227],{"type":29,"value":228},"方案四：时间切片渲染",{"type":24,"tag":36,"props":230,"children":231},{},[232],{"type":29,"value":233},"对于初始渲染大量数据的场景，可以使用时间切片避免阻塞主线程。",{"type":24,"tag":42,"props":235,"children":238},{"code":236,"language":89,"meta":7,"className":237},"// utils/timeSlice.ts\n\n// 将大任务分割成小块执行\nasync function timeSliceRender\u003CT>(\n  items: T[],\n  renderFn: (item: T, index: number) => void,\n  options: {\n    chunkSize?: number;\n    yieldInterval?: number;\n  } = {}\n) {\n  const { chunkSize = 50, yieldInterval = 16 } = options;\n  \n  let index = 0;\n  \n  while (index \u003C items.length) {\n    const chunk = items.slice(index, index + chunkSize);\n    \n    // 渲染一批\n    chunk.forEach((item, i) => {\n      renderFn(item, index + i);\n    });\n    \n    index += chunkSize;\n    \n    // 让出主线程\n    if (index \u003C items.length) {\n      await yieldToMain(yieldInterval);\n    }\n  }\n}\n\n// 让出主线程控制权\nfunction yieldToMain(ms: number = 0): Promise\u003Cvoid> {\n  return new Promise(resolve => {\n    if ('scheduler' in globalThis && 'yield' in (globalThis as any).scheduler) {\n      // 使用 Scheduler API（如果可用）\n      (globalThis as any).scheduler.yield().then(resolve);\n    } else if (typeof requestIdleCallback !== 'undefined') {\n      // 使用 requestIdleCallback\n      requestIdleCallback(() => resolve());\n    } else {\n      // 降级到 setTimeout\n      setTimeout(resolve, ms);\n    }\n  });\n}\n\n// Vue composable\nexport function useTimeSliceList\u003CT>(items: Ref\u003CT[]>, chunkSize = 50) {\n  const renderedItems = ref\u003CT[]>([]);\n  const isRendering = ref(false);\n  const progress = ref(0);\n  \n  async function render() {\n    renderedItems.value = [];\n    isRendering.value = true;\n    progress.value = 0;\n    \n    await timeSliceRender(\n      items.value,\n      (item, index) => {\n        renderedItems.value.push(item);\n        progress.value = (index + 1) / items.value.length;\n      },\n      { chunkSize }\n    );\n    \n    isRendering.value = false;\n  }\n  \n  watch(items, render, { immediate: true });\n  \n  return {\n    renderedItems,\n    isRendering,\n    progress\n  };\n}\n",[91],[239],{"type":24,"tag":47,"props":240,"children":241},{"__ignoreMap":7},[242],{"type":29,"value":236},{"type":24,"tag":25,"props":244,"children":246},{"id":245},"方案五使用-web-worker",[247],{"type":29,"value":248},"方案五：使用 Web Worker",{"type":24,"tag":36,"props":250,"children":251},{},[252],{"type":29,"value":253},"对于需要复杂计算的列表（如搜索、排序、筛选），可以将计算放到 Web Worker 中。",{"type":24,"tag":42,"props":255,"children":258},{"code":256,"language":89,"meta":7,"className":257},"// workers/listWorker.ts\ninterface WorkerMessage {\n  type: 'filter' | 'sort' | 'search';\n  data: any[];\n  params: any;\n}\n\nself.onmessage = (e: MessageEvent\u003CWorkerMessage>) => {\n  const { type, data, params } = e.data;\n  \n  let result: any[];\n  \n  switch (type) {\n    case 'filter':\n      result = filterData(data, params.filters);\n      break;\n    case 'sort':\n      result = sortData(data, params.sortBy, params.order);\n      break;\n    case 'search':\n      result = searchData(data, params.query, params.fields);\n      break;\n    default:\n      result = data;\n  }\n  \n  self.postMessage({ type, result });\n};\n\nfunction filterData(data: any[], filters: Record\u003Cstring, any>) {\n  return data.filter(item => {\n    return Object.entries(filters).every(([key, value]) => {\n      if (value === undefined || value === null || value === '') {\n        return true;\n      }\n      return item[key] === value;\n    });\n  });\n}\n\nfunction sortData(data: any[], sortBy: string, order: 'asc' | 'desc') {\n  return [...data].sort((a, b) => {\n    const aVal = a[sortBy];\n    const bVal = b[sortBy];\n    const comparison = aVal \u003C bVal ? -1 : aVal > bVal ? 1 : 0;\n    return order === 'asc' ? comparison : -comparison;\n  });\n}\n\nfunction searchData(data: any[], query: string, fields: string[]) {\n  const lowerQuery = query.toLowerCase();\n  return data.filter(item => {\n    return fields.some(field => {\n      const value = String(item[field] || '').toLowerCase();\n      return value.includes(lowerQuery);\n    });\n  });\n}\n\n// composables/useListWorker.ts\nexport function useListWorker\u003CT>() {\n  const worker = new Worker(\n    new URL('@/workers/listWorker.ts', import.meta.url)\n  );\n  \n  const result = ref\u003CT[]>([]);\n  const isProcessing = ref(false);\n  \n  function process(type: string, data: T[], params: any): Promise\u003CT[]> {\n    return new Promise((resolve) => {\n      isProcessing.value = true;\n      \n      worker.onmessage = (e) => {\n        result.value = e.data.result;\n        isProcessing.value = false;\n        resolve(e.data.result);\n      };\n      \n      worker.postMessage({ type, data, params });\n    });\n  }\n  \n  onUnmounted(() => {\n    worker.terminate();\n  });\n  \n  return {\n    result,\n    isProcessing,\n    filter: (data: T[], filters: Record\u003Cstring, any>) => \n      process('filter', data, { filters }),\n    sort: (data: T[], sortBy: string, order: 'asc' | 'desc') => \n      process('sort', data, { sortBy, order }),\n    search: (data: T[], query: string, fields: string[]) => \n      process('search', data, { query, fields })\n  };\n}\n",[91],[259],{"type":24,"tag":47,"props":260,"children":261},{"__ignoreMap":7},[262],{"type":29,"value":256},{"type":24,"tag":25,"props":264,"children":266},{"id":265},"综合优化策略",[267],{"type":29,"value":265},{"type":24,"tag":57,"props":269,"children":271},{"id":270},"列表项优化",[272],{"type":29,"value":270},{"type":24,"tag":42,"props":274,"children":277},{"code":275,"language":144,"meta":7,"className":276},"\u003C!-- components/OptimizedListItem.vue -->\n\u003Cscript setup lang=\"ts\">\nimport { computed } from 'vue';\n\nconst props = defineProps\u003C{\n  item: any;\n  isVisible: boolean;\n}>();\n\n// 只在可见时计算复杂内容\nconst expensiveData = computed(() => {\n  if (!props.isVisible) return null;\n  return processExpensiveData(props.item);\n});\n\n// 使用 v-once 渲染不变的内容\n// 使用 v-memo 缓存条件渲染\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"list-item\" v-memo=\"[item.id, item.updatedAt]\">\n    \u003C!-- 静态内容使用 v-once -->\n    \u003Cdiv v-once class=\"static-content\">\n      {{ item.staticField }}\n    \u003C/div>\n    \n    \u003C!-- 动态内容 -->\n    \u003Cdiv class=\"dynamic-content\">\n      {{ item.dynamicField }}\n    \u003C/div>\n    \n    \u003C!-- 延迟渲染复杂内容 -->\n    \u003Cdiv v-if=\"expensiveData\" class=\"expensive-content\">\n      {{ expensiveData }}\n    \u003C/div>\n  \u003C/div>\n\u003C/template>\n",[146],[278],{"type":24,"tag":47,"props":279,"children":280},{"__ignoreMap":7},[281],{"type":29,"value":275},{"type":24,"tag":57,"props":283,"children":285},{"id":284},"骨架屏占位",[286],{"type":29,"value":284},{"type":24,"tag":42,"props":288,"children":291},{"code":289,"language":144,"meta":7,"className":290},"\u003C!-- components/ListSkeleton.vue -->\n\u003Cscript setup lang=\"ts\">\nconst props = defineProps\u003C{\n  count?: number;\n  itemHeight?: number;\n}>();\n\nconst skeletonItems = Array(props.count || 5).fill(null);\n\u003C/script>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"skeleton-list\">\n    \u003Cdiv \n      v-for=\"(_, index) in skeletonItems\"\n      :key=\"index\"\n      class=\"skeleton-item\"\n      :style=\"{ height: `${itemHeight || 60}px` }\"\n    >\n      \u003Cdiv class=\"skeleton-avatar\" />\n      \u003Cdiv class=\"skeleton-content\">\n        \u003Cdiv class=\"skeleton-title\" />\n        \u003Cdiv class=\"skeleton-text\" />\n      \u003C/div>\n    \u003C/div>\n  \u003C/div>\n\u003C/template>\n\n\u003Cstyle scoped>\n.skeleton-item {\n  display: flex;\n  align-items: center;\n  padding: 12px;\n  gap: 12px;\n}\n\n.skeleton-avatar {\n  width: 40px;\n  height: 40px;\n  border-radius: 50%;\n  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);\n  background-size: 200% 100%;\n  animation: shimmer 1.5s infinite;\n}\n\n.skeleton-content {\n  flex: 1;\n}\n\n.skeleton-title, .skeleton-text {\n  height: 12px;\n  border-radius: 4px;\n  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);\n  background-size: 200% 100%;\n  animation: shimmer 1.5s infinite;\n}\n\n.skeleton-title {\n  width: 60%;\n  margin-bottom: 8px;\n}\n\n.skeleton-text {\n  width: 80%;\n}\n\n@keyframes shimmer {\n  0% { background-position: 200% 0; }\n  100% { background-position: -200% 0; }\n}\n\u003C/style>\n",[146],[292],{"type":24,"tag":47,"props":293,"children":294},{"__ignoreMap":7},[295],{"type":29,"value":289},{"type":24,"tag":25,"props":297,"children":299},{"id":298},"性能指标监控",[300],{"type":29,"value":298},{"type":24,"tag":42,"props":302,"children":305},{"code":303,"language":89,"meta":7,"className":304},"// utils/listPerformanceMonitor.ts\nclass ListPerformanceMonitor {\n  private metrics: {\n    renderTime: number[];\n    scrollFPS: number[];\n    memoryUsage: number[];\n  } = {\n    renderTime: [],\n    scrollFPS: [],\n    memoryUsage: []\n  };\n  \n  private lastScrollTime = 0;\n  private scrollFrameCount = 0;\n  \n  // 测量渲染时间\n  measureRender\u003CT>(fn: () => T): T {\n    const start = performance.now();\n    const result = fn();\n    const end = performance.now();\n    \n    this.metrics.renderTime.push(end - start);\n    \n    return result;\n  }\n  \n  // 监控滚动帧率\n  trackScrollFPS() {\n    const now = performance.now();\n    this.scrollFrameCount++;\n    \n    if (now - this.lastScrollTime >= 1000) {\n      this.metrics.scrollFPS.push(this.scrollFrameCount);\n      this.scrollFrameCount = 0;\n      this.lastScrollTime = now;\n    }\n  }\n  \n  // 获取内存使用\n  trackMemory() {\n    if ('memory' in performance) {\n      const memory = (performance as any).memory;\n      this.metrics.memoryUsage.push(memory.usedJSHeapSize / 1024 / 1024);\n    }\n  }\n  \n  // 获取报告\n  getReport() {\n    const avg = (arr: number[]) => \n      arr.length ? arr.reduce((a, b) => a + b) / arr.length : 0;\n    \n    return {\n      avgRenderTime: avg(this.metrics.renderTime).toFixed(2) + 'ms',\n      avgScrollFPS: Math.round(avg(this.metrics.scrollFPS)),\n      avgMemoryMB: avg(this.metrics.memoryUsage).toFixed(2) + 'MB',\n      samples: {\n        renderTime: this.metrics.renderTime.length,\n        scrollFPS: this.metrics.scrollFPS.length,\n        memory: this.metrics.memoryUsage.length\n      }\n    };\n  }\n}\n\nexport const listMonitor = new ListPerformanceMonitor();\n",[91],[306],{"type":24,"tag":47,"props":307,"children":308},{"__ignoreMap":7},[309],{"type":29,"value":303},{"type":24,"tag":25,"props":311,"children":313},{"id":312},"最佳实践总结",[314],{"type":29,"value":312},{"type":24,"tag":42,"props":316,"children":318},{"code":317},"大列表渲染最佳实践：\n\n方案选择：\n✓ 数据量 \u003C 1000：考虑简单分页或懒加载\n✓ 数据量 1000-10000：使用虚拟滚动\n✓ 数据量 > 10000：虚拟滚动 + 服务端分页\n✓ 需要复杂计算：结合 Web Worker\n\n性能优化：\n✓ 使用 key 帮助 Vue 追踪 DOM\n✓ 使用 v-memo 缓存列表项\n✓ 避免在列表项中进行复杂计算\n✓ 对滚动事件进行节流\n✓ 使用骨架屏改善感知性能\n\n用户体验：\n✓ 提供加载状态反馈\n✓ 支持跳转到指定位置\n✓ 保持滚动位置在刷新后恢复\n✓ 提供搜索和筛选功能\n\n监控和调试：\n✓ 监控渲染时间和帧率\n✓ 使用 Performance 面板分析\n✓ 设置性能预算告警\n✓ 在真实设备上测试\n",[319],{"type":24,"tag":47,"props":320,"children":321},{"__ignoreMap":7},[322],{"type":29,"value":317},{"type":24,"tag":36,"props":324,"children":325},{},[326],{"type":29,"value":327},"选择合适的大列表渲染方案，需要根据实际的数据量、交互需求和目标设备来综合考虑。虚拟滚动是解决超大列表的核心技术，但也要注意其复杂性和维护成本。",{"title":7,"searchDepth":329,"depth":329,"links":330},3,[331,333,334,337,341,345,351,352,353,357,358],{"id":8,"depth":332,"text":8},2,{"id":32,"depth":332,"text":32},{"id":53,"depth":332,"text":53,"children":335},[336],{"id":59,"depth":329,"text":59},{"id":72,"depth":332,"text":75,"children":338},[339,340],{"id":83,"depth":329,"text":83},{"id":99,"depth":329,"text":99},{"id":113,"depth":332,"text":116,"children":342},[343,344],{"id":124,"depth":329,"text":124},{"id":138,"depth":329,"text":138},{"id":154,"depth":332,"text":157,"children":346},[347,348,349,350],{"id":165,"depth":329,"text":165},{"id":178,"depth":329,"text":178},{"id":192,"depth":329,"text":192},{"id":206,"depth":329,"text":206},{"id":225,"depth":332,"text":228},{"id":245,"depth":332,"text":248},{"id":265,"depth":332,"text":265,"children":354},[355,356],{"id":270,"depth":329,"text":270},{"id":284,"depth":329,"text":284},{"id":298,"depth":332,"text":298},{"id":312,"depth":332,"text":312},"markdown","content:topics:performance:large-list-rendering-guide.md","content","topics/performance/large-list-rendering-guide.md","topics/performance/large-list-rendering-guide","md",[366,1327,2656],{"_path":367,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":368,"description":369,"date":370,"topic":5,"author":11,"tags":371,"image":376,"featured":377,"readingTime":378,"body":379,"_type":359,"_id":1324,"_source":361,"_file":1325,"_stem":1326,"_extension":364},"/topics/performance/lcp-optimization-theory-practice","LCP 优化：从理论到实践","从渲染管线与 LCP 候选元素规则出发，系统讲清 LCP 变慢的根因，提供可复现的诊断流程与可落地的优化手段（图片、字体、SSR/CSR、资源优先级、CDN）。","2026-01-20",[14,372,373,374,375],"LCP","Core Web Vitals","图片优化","CDN","/images/topics/performance/lcp.jpg",true,23,{"type":21,"children":380,"toc":1298},[381,386,391,396,416,421,442,447,465,469,475,480,516,521,526,529,535,540,553,558,571,576,599,602,608,613,618,623,646,649,655,661,666,679,684,697,703,708,721,727,745,748,754,760,765,784,790,803,822,828,833,844,849,858,863,869,887,890,896,902,907,920,926,931,936,958,961,967,972,977,990,995,1013,1018,1036,1039,1045,1050,1059,1064,1067,1073,1196,1199,1205,1267,1270,1275,1280],{"type":24,"tag":25,"props":382,"children":384},{"id":383},"lcp-优化从理论到实践",[385],{"type":29,"value":368},{"type":24,"tag":36,"props":387,"children":388},{},[389],{"type":29,"value":390},"LCP（Largest Contentful Paint）是 Core Web Vitals 中最能代表“用户主观感受”的指标之一：它近似回答了“页面主要内容什么时候真正出来”。",{"type":24,"tag":36,"props":392,"children":393},{},[394],{"type":29,"value":395},"很多团队优化 LCP 的方式是：",{"type":24,"tag":397,"props":398,"children":399},"ul",{},[400,406,411],{"type":24,"tag":401,"props":402,"children":403},"li",{},[404],{"type":29,"value":405},"“把图片压一压”",{"type":24,"tag":401,"props":407,"children":408},{},[409],{"type":29,"value":410},"“上 CDN”",{"type":24,"tag":401,"props":412,"children":413},{},[414],{"type":29,"value":415},"“开 SSR”",{"type":24,"tag":36,"props":417,"children":418},{},[419],{"type":29,"value":420},"这些手段可能有效，但也经常无效——因为你没搞清楚：",{"type":24,"tag":397,"props":422,"children":423},{},[424,437],{"type":24,"tag":401,"props":425,"children":426},{},[427,429,435],{"type":29,"value":428},"LCP 的",{"type":24,"tag":430,"props":431,"children":432},"strong",{},[433],{"type":29,"value":434},"候选元素",{"type":29,"value":436},"到底是谁？",{"type":24,"tag":401,"props":438,"children":439},{},[440],{"type":29,"value":441},"LCP 的时间到底卡在：网络、解析、布局、绘制还是 JS？",{"type":24,"tag":36,"props":443,"children":444},{},[445],{"type":29,"value":446},"这篇文章按“你能在生产上稳定把 LCP 做到好”为目标，给出：",{"type":24,"tag":397,"props":448,"children":449},{},[450,455,460],{"type":24,"tag":401,"props":451,"children":452},{},[453],{"type":29,"value":454},"一套诊断流程（从现象到根因）",{"type":24,"tag":401,"props":456,"children":457},{},[458],{"type":29,"value":459},"一套常见根因 → 对应策略的映射",{"type":24,"tag":401,"props":461,"children":462},{},[463],{"type":29,"value":464},"可落地的优化手段与检查清单",{"type":24,"tag":466,"props":467,"children":468},"hr",{},[],{"type":24,"tag":25,"props":470,"children":472},{"id":471},"_1-lcp-的本质不是首屏渲染而是最大内容出现",[473],{"type":29,"value":474},"1. LCP 的本质：不是“首屏渲染”，而是“最大内容出现”",{"type":24,"tag":36,"props":476,"children":477},{},[478],{"type":29,"value":479},"浏览器会从候选元素里选一个“最大内容元素”作为 LCP 候选，常见类型包括：",{"type":24,"tag":397,"props":481,"children":482},{},[483,492,503],{"type":24,"tag":401,"props":484,"children":485},{},[486],{"type":24,"tag":47,"props":487,"children":489},{"className":488},[],[490],{"type":29,"value":491},"img",{"type":24,"tag":401,"props":493,"children":494},{},[495,501],{"type":24,"tag":47,"props":496,"children":498},{"className":497},[],[499],{"type":29,"value":500},"image",{"type":29,"value":502}," 的背景图（某些情况下）",{"type":24,"tag":401,"props":504,"children":505},{},[506,508,514],{"type":29,"value":507},"大块文本（如 ",{"type":24,"tag":47,"props":509,"children":511},{"className":510},[],[512],{"type":29,"value":513},"h1",{"type":29,"value":515},"/正文块）",{"type":24,"tag":36,"props":517,"children":518},{},[519],{"type":29,"value":520},"LCP 的时间点是：这个最大元素被绘制到屏幕上的时间。",{"type":24,"tag":36,"props":522,"children":523},{},[524],{"type":29,"value":525},"关键：LCP 不是你以为的“所有内容都 ready”。它只关注“最大那个”。",{"type":24,"tag":466,"props":527,"children":528},{},[],{"type":24,"tag":25,"props":530,"children":532},{"id":531},"_2-先做识别你的-lcp-元素是谁",[533],{"type":29,"value":534},"2. 先做识别：你的 LCP 元素是谁？",{"type":24,"tag":36,"props":536,"children":537},{},[538],{"type":29,"value":539},"在 Chrome DevTools：",{"type":24,"tag":397,"props":541,"children":542},{},[543,548],{"type":24,"tag":401,"props":544,"children":545},{},[546],{"type":29,"value":547},"Performance 面板 → Web Vitals",{"type":24,"tag":401,"props":549,"children":550},{},[551],{"type":29,"value":552},"或 Lighthouse 报告里会告诉你 LCP 元素",{"type":24,"tag":36,"props":554,"children":555},{},[556],{"type":29,"value":557},"你要记录两件事：",{"type":24,"tag":397,"props":559,"children":560},{},[561,566],{"type":24,"tag":401,"props":562,"children":563},{},[564],{"type":29,"value":565},"LCP 元素类型（图片/文本）",{"type":24,"tag":401,"props":567,"children":568},{},[569],{"type":29,"value":570},"LCP 元素来源（HTML 直出、JS 渲染、背景图、第三方）",{"type":24,"tag":36,"props":572,"children":573},{},[574],{"type":29,"value":575},"因为两类 LCP 的优化路线完全不同：",{"type":24,"tag":397,"props":577,"children":578},{},[579,589],{"type":24,"tag":401,"props":580,"children":581},{},[582,587],{"type":24,"tag":430,"props":583,"children":584},{},[585],{"type":29,"value":586},"图片 LCP",{"type":29,"value":588},"：网络 + 解码 + 优先级",{"type":24,"tag":401,"props":590,"children":591},{},[592,597],{"type":24,"tag":430,"props":593,"children":594},{},[595],{"type":29,"value":596},"文本 LCP",{"type":29,"value":598},"：CSS/字体阻塞 + layout + 渲染",{"type":24,"tag":466,"props":600,"children":601},{},[],{"type":24,"tag":25,"props":603,"children":605},{"id":604},"_3-分解-lcp把总时间拆成可行动的部分",[606],{"type":29,"value":607},"3. 分解 LCP：把“总时间”拆成可行动的部分",{"type":24,"tag":36,"props":609,"children":610},{},[611],{"type":29,"value":612},"一个可用的分解模型：",{"type":24,"tag":36,"props":614,"children":615},{},[616],{"type":29,"value":617},"$$\nLCP \\approx TTFB + \\text{资源发现/调度} + \\text{下载} + \\text{解码/布局/绘制}\n$$",{"type":24,"tag":36,"props":619,"children":620},{},[621],{"type":29,"value":622},"你不需要完全精确，但必须能回答：",{"type":24,"tag":397,"props":624,"children":625},{},[626,631,636,641],{"type":24,"tag":401,"props":627,"children":628},{},[629],{"type":29,"value":630},"慢在服务器？（TTFB）",{"type":24,"tag":401,"props":632,"children":633},{},[634],{"type":29,"value":635},"慢在资源发现？（preload/优先级）",{"type":24,"tag":401,"props":637,"children":638},{},[639],{"type":29,"value":640},"慢在下载？（图片太大、带宽、CDN）",{"type":24,"tag":401,"props":642,"children":643},{},[644],{"type":29,"value":645},"慢在主线程？（长任务、渲染阻塞）",{"type":24,"tag":466,"props":647,"children":648},{},[],{"type":24,"tag":25,"props":650,"children":652},{"id":651},"_4-诊断流程生产可用版",[653],{"type":29,"value":654},"4. 诊断流程（生产可用版）",{"type":24,"tag":57,"props":656,"children":658},{"id":657},"step-1看字段真实用户的-lcp-分布",[659],{"type":29,"value":660},"Step 1：看字段——真实用户的 LCP 分布",{"type":24,"tag":36,"props":662,"children":663},{},[664],{"type":29,"value":665},"如果你有 RUM：",{"type":24,"tag":397,"props":667,"children":668},{},[669,674],{"type":24,"tag":401,"props":670,"children":671},{},[672],{"type":29,"value":673},"p75 LCP",{"type":24,"tag":401,"props":675,"children":676},{},[677],{"type":29,"value":678},"按路由/地区/设备档位切片",{"type":24,"tag":36,"props":680,"children":681},{},[682],{"type":29,"value":683},"你会发现：",{"type":24,"tag":397,"props":685,"children":686},{},[687,692],{"type":24,"tag":401,"props":688,"children":689},{},[690],{"type":29,"value":691},"可能只有低端机慢",{"type":24,"tag":401,"props":693,"children":694},{},[695],{"type":29,"value":696},"或只有某地区慢（CDN/回源）",{"type":24,"tag":57,"props":698,"children":700},{"id":699},"step-2看实验室复现-标记-lcp-元素",[701],{"type":29,"value":702},"Step 2：看实验室——复现 + 标记 LCP 元素",{"type":24,"tag":36,"props":704,"children":705},{},[706],{"type":29,"value":707},"用固定网络/CPU 限制：",{"type":24,"tag":397,"props":709,"children":710},{},[711,716],{"type":24,"tag":401,"props":712,"children":713},{},[714],{"type":29,"value":715},"Network: Fast 3G / Slow 4G",{"type":24,"tag":401,"props":717,"children":718},{},[719],{"type":29,"value":720},"CPU: 4x slowdown",{"type":24,"tag":57,"props":722,"children":724},{"id":723},"step-3拆链路ttfb-资源-主线程",[725],{"type":29,"value":726},"Step 3：拆链路——TTFB / 资源 / 主线程",{"type":24,"tag":397,"props":728,"children":729},{},[730,735,740],{"type":24,"tag":401,"props":731,"children":732},{},[733],{"type":29,"value":734},"TTFB 高：先查服务端、缓存、回源",{"type":24,"tag":401,"props":736,"children":737},{},[738],{"type":29,"value":739},"下载慢：查图片体积、格式、CDN、优先级",{"type":24,"tag":401,"props":741,"children":742},{},[743],{"type":29,"value":744},"主线程忙：查长任务、hydration、bundle",{"type":24,"tag":466,"props":746,"children":747},{},[],{"type":24,"tag":25,"props":749,"children":751},{"id":750},"_5-图片型-lcp优先级与体积是第一性",[752],{"type":29,"value":753},"5. 图片型 LCP：优先级与体积是第一性",{"type":24,"tag":57,"props":755,"children":757},{"id":756},"_51-把-lcp-图片变小但不要牺牲清晰度",[758],{"type":29,"value":759},"5.1 把 LCP 图片“变小”但不要牺牲清晰度",{"type":24,"tag":36,"props":761,"children":762},{},[763],{"type":29,"value":764},"实践优先级：",{"type":24,"tag":766,"props":767,"children":768},"ol",{},[769,774,779],{"type":24,"tag":401,"props":770,"children":771},{},[772],{"type":29,"value":773},"用现代格式：AVIF/WebP",{"type":24,"tag":401,"props":775,"children":776},{},[777],{"type":29,"value":778},"合理尺寸：不要把 2000px 的图缩到 400px 显示",{"type":24,"tag":401,"props":780,"children":781},{},[782],{"type":29,"value":783},"质量参数：基于视觉对比选一个阈值",{"type":24,"tag":57,"props":785,"children":787},{"id":786},"_52-确保-lcp-图片不是懒加载",[788],{"type":29,"value":789},"5.2 确保 LCP 图片不是懒加载",{"type":24,"tag":36,"props":791,"children":792},{},[793,795,801],{"type":29,"value":794},"LCP 图片如果被 ",{"type":24,"tag":47,"props":796,"children":798},{"className":797},[],[799],{"type":29,"value":800},"loading=\"lazy\"",{"type":29,"value":802},"，通常会变慢。",{"type":24,"tag":397,"props":804,"children":805},{},[806,817],{"type":24,"tag":401,"props":807,"children":808},{},[809,811],{"type":29,"value":810},"LCP 相关图片：",{"type":24,"tag":47,"props":812,"children":814},{"className":813},[],[815],{"type":29,"value":816},"loading=\"eager\"",{"type":24,"tag":401,"props":818,"children":819},{},[820],{"type":29,"value":821},"或显式 preload",{"type":24,"tag":57,"props":823,"children":825},{"id":824},"_53-明确资源优先级preload-fetchpriority",[826],{"type":29,"value":827},"5.3 明确资源优先级（preload / fetchpriority）",{"type":24,"tag":36,"props":829,"children":830},{},[831],{"type":29,"value":832},"对 LCP 图片：",{"type":24,"tag":42,"props":834,"children":839},{"className":835,"code":837,"language":838,"meta":7},[836],"language-html","\u003Clink rel=\"preload\" as=\"image\" href=\"/hero.avif\" />\n","html",[840],{"type":24,"tag":47,"props":841,"children":842},{"__ignoreMap":7},[843],{"type":29,"value":837},{"type":24,"tag":36,"props":845,"children":846},{},[847],{"type":29,"value":848},"或（浏览器支持时）：",{"type":24,"tag":42,"props":850,"children":853},{"className":851,"code":852,"language":838,"meta":7},[836],"\u003Cimg src=\"/hero.avif\" fetchpriority=\"high\" />\n",[854],{"type":24,"tag":47,"props":855,"children":856},{"__ignoreMap":7},[857],{"type":29,"value":852},{"type":24,"tag":36,"props":859,"children":860},{},[861],{"type":29,"value":862},"目标是：让浏览器更早发现它、更早调度它。",{"type":24,"tag":57,"props":864,"children":866},{"id":865},"_54-cdn减少-rtt-与回源",[867],{"type":29,"value":868},"5.4 CDN：减少 RTT 与回源",{"type":24,"tag":397,"props":870,"children":871},{},[872,877,882],{"type":24,"tag":401,"props":873,"children":874},{},[875],{"type":29,"value":876},"图片必须走 CDN",{"type":24,"tag":401,"props":878,"children":879},{},[880],{"type":29,"value":881},"开启压缩、HTTP/2/3",{"type":24,"tag":401,"props":883,"children":884},{},[885],{"type":29,"value":886},"确保 cache hit",{"type":24,"tag":466,"props":888,"children":889},{},[],{"type":24,"tag":25,"props":891,"children":893},{"id":892},"_6-文本型-lcpcss字体阻塞是常见杀手",[894],{"type":29,"value":895},"6. 文本型 LCP：CSS/字体阻塞是常见杀手",{"type":24,"tag":57,"props":897,"children":899},{"id":898},"_61-关键-css-与阻塞",[900],{"type":29,"value":901},"6.1 关键 CSS 与阻塞",{"type":24,"tag":36,"props":903,"children":904},{},[905],{"type":29,"value":906},"如果你的主 CSS 很大且阻塞：",{"type":24,"tag":397,"props":908,"children":909},{},[910,915],{"type":24,"tag":401,"props":911,"children":912},{},[913],{"type":29,"value":914},"拆关键 CSS",{"type":24,"tag":401,"props":916,"children":917},{},[918],{"type":29,"value":919},"延后非关键 CSS",{"type":24,"tag":57,"props":921,"children":923},{"id":922},"_62-字体foitfout-与-lcp",[924],{"type":29,"value":925},"6.2 字体：FOIT/FOUT 与 LCP",{"type":24,"tag":36,"props":927,"children":928},{},[929],{"type":29,"value":930},"大标题是文本 LCP 时，字体策略会直接影响 LCP。",{"type":24,"tag":36,"props":932,"children":933},{},[934],{"type":29,"value":935},"建议：",{"type":24,"tag":397,"props":937,"children":938},{},[939,948,953],{"type":24,"tag":401,"props":940,"children":941},{},[942],{"type":24,"tag":47,"props":943,"children":945},{"className":944},[],[946],{"type":29,"value":947},"font-display: swap",{"type":24,"tag":401,"props":949,"children":950},{},[951],{"type":29,"value":952},"对关键字体文件 preload",{"type":24,"tag":401,"props":954,"children":955},{},[956],{"type":29,"value":957},"子集化（只保留需要的字形）",{"type":24,"tag":466,"props":959,"children":960},{},[],{"type":24,"tag":25,"props":962,"children":964},{"id":963},"_7-ssrcsr-与-hydration别让-js-抢走首屏",[965],{"type":29,"value":966},"7. SSR/CSR 与 Hydration：别让 JS 抢走首屏",{"type":24,"tag":36,"props":968,"children":969},{},[970],{"type":29,"value":971},"SSR 通常能改善 LCP，但并非必然。",{"type":24,"tag":36,"props":973,"children":974},{},[975],{"type":29,"value":976},"常见反例：",{"type":24,"tag":397,"props":978,"children":979},{},[980,985],{"type":24,"tag":401,"props":981,"children":982},{},[983],{"type":29,"value":984},"SSR 直出了 HTML",{"type":24,"tag":401,"props":986,"children":987},{},[988],{"type":29,"value":989},"但首屏仍要等一堆 JS 执行、样式计算、hydrate 才能稳定",{"type":24,"tag":36,"props":991,"children":992},{},[993],{"type":29,"value":994},"你需要关注：",{"type":24,"tag":397,"props":996,"children":997},{},[998,1003,1008],{"type":24,"tag":401,"props":999,"children":1000},{},[1001],{"type":29,"value":1002},"首屏 bundle 体积",{"type":24,"tag":401,"props":1004,"children":1005},{},[1006],{"type":29,"value":1007},"hydration 的长任务",{"type":24,"tag":401,"props":1009,"children":1010},{},[1011],{"type":29,"value":1012},"第三方脚本（埋点、广告）阻塞",{"type":24,"tag":36,"props":1014,"children":1015},{},[1016],{"type":29,"value":1017},"实践建议：",{"type":24,"tag":397,"props":1019,"children":1020},{},[1021,1026,1031],{"type":24,"tag":401,"props":1022,"children":1023},{},[1024],{"type":29,"value":1025},"首屏只 hydrate 必要组件",{"type":24,"tag":401,"props":1027,"children":1028},{},[1029],{"type":29,"value":1030},"大型交互组件延后",{"type":24,"tag":401,"props":1032,"children":1033},{},[1034],{"type":29,"value":1035},"第三方脚本延迟加载",{"type":24,"tag":466,"props":1037,"children":1038},{},[],{"type":24,"tag":25,"props":1040,"children":1042},{"id":1041},"_8-资源调度preconnect-dns-prefetch",[1043],{"type":29,"value":1044},"8. 资源调度：preconnect / dns-prefetch",{"type":24,"tag":36,"props":1046,"children":1047},{},[1048],{"type":29,"value":1049},"如果 LCP 资源来自第三方域名：",{"type":24,"tag":42,"props":1051,"children":1054},{"className":1052,"code":1053,"language":838,"meta":7},[836],"\u003Clink rel=\"preconnect\" href=\"https://cdn.example.com\" />\n",[1055],{"type":24,"tag":47,"props":1056,"children":1057},{"__ignoreMap":7},[1058],{"type":29,"value":1053},{"type":24,"tag":36,"props":1060,"children":1061},{},[1062],{"type":29,"value":1063},"这能减少握手开销。",{"type":24,"tag":466,"props":1065,"children":1066},{},[],{"type":24,"tag":25,"props":1068,"children":1070},{"id":1069},"_9-常见根因-对应策略速查表",[1071],{"type":29,"value":1072},"9. 常见根因 → 对应策略速查表",{"type":24,"tag":1074,"props":1075,"children":1076},"table",{},[1077,1101],{"type":24,"tag":1078,"props":1079,"children":1080},"thead",{},[1081],{"type":24,"tag":1082,"props":1083,"children":1084},"tr",{},[1085,1091,1096],{"type":24,"tag":1086,"props":1087,"children":1088},"th",{},[1089],{"type":29,"value":1090},"根因",{"type":24,"tag":1086,"props":1092,"children":1093},{},[1094],{"type":29,"value":1095},"现象",{"type":24,"tag":1086,"props":1097,"children":1098},{},[1099],{"type":29,"value":1100},"典型修复",{"type":24,"tag":1102,"props":1103,"children":1104},"tbody",{},[1105,1124,1142,1160,1178],{"type":24,"tag":1082,"props":1106,"children":1107},{},[1108,1114,1119],{"type":24,"tag":1109,"props":1110,"children":1111},"td",{},[1112],{"type":29,"value":1113},"TTFB 高",{"type":24,"tag":1109,"props":1115,"children":1116},{},[1117],{"type":29,"value":1118},"所有资源都晚开始",{"type":24,"tag":1109,"props":1120,"children":1121},{},[1122],{"type":29,"value":1123},"服务端缓存、CDN、减少回源",{"type":24,"tag":1082,"props":1125,"children":1126},{},[1127,1132,1137],{"type":24,"tag":1109,"props":1128,"children":1129},{},[1130],{"type":29,"value":1131},"LCP 图片太大",{"type":24,"tag":1109,"props":1133,"children":1134},{},[1135],{"type":29,"value":1136},"下载阶段耗时长",{"type":24,"tag":1109,"props":1138,"children":1139},{},[1140],{"type":29,"value":1141},"AVIF/WebP、尺寸裁切、CDN",{"type":24,"tag":1082,"props":1143,"children":1144},{},[1145,1150,1155],{"type":24,"tag":1109,"props":1146,"children":1147},{},[1148],{"type":29,"value":1149},"LCP 图片优先级低",{"type":24,"tag":1109,"props":1151,"children":1152},{},[1153],{"type":29,"value":1154},"LCP 资源很晚才发起",{"type":24,"tag":1109,"props":1156,"children":1157},{},[1158],{"type":29,"value":1159},"preload/fetchpriority",{"type":24,"tag":1082,"props":1161,"children":1162},{},[1163,1168,1173],{"type":24,"tag":1109,"props":1164,"children":1165},{},[1166],{"type":29,"value":1167},"字体阻塞",{"type":24,"tag":1109,"props":1169,"children":1170},{},[1171],{"type":29,"value":1172},"文本 LCP 晚出现",{"type":24,"tag":1109,"props":1174,"children":1175},{},[1176],{"type":29,"value":1177},"font-display: swap + preload",{"type":24,"tag":1082,"props":1179,"children":1180},{},[1181,1186,1191],{"type":24,"tag":1109,"props":1182,"children":1183},{},[1184],{"type":29,"value":1185},"主线程长任务",{"type":24,"tag":1109,"props":1187,"children":1188},{},[1189],{"type":29,"value":1190},"LCP 前 JS 很忙",{"type":24,"tag":1109,"props":1192,"children":1193},{},[1194],{"type":29,"value":1195},"拆包、延后非关键组件/第三方",{"type":24,"tag":466,"props":1197,"children":1198},{},[],{"type":24,"tag":25,"props":1200,"children":1202},{"id":1201},"_10-上线检查清单",[1203],{"type":29,"value":1204},"10. 上线检查清单",{"type":24,"tag":397,"props":1206,"children":1209},{"className":1207},[1208],"contains-task-list",[1210,1222,1231,1240,1249,1258],{"type":24,"tag":401,"props":1211,"children":1214},{"className":1212},[1213],"task-list-item",[1215,1220],{"type":24,"tag":1216,"props":1217,"children":1219},"input",{"disabled":377,"type":1218},"checkbox",[],{"type":29,"value":1221}," LCP 元素是否明确（图片/文本）",{"type":24,"tag":401,"props":1223,"children":1225},{"className":1224},[1213],[1226,1229],{"type":24,"tag":1216,"props":1227,"children":1228},{"disabled":377,"type":1218},[],{"type":29,"value":1230}," LCP 资源是否高优先级（preload/priority）",{"type":24,"tag":401,"props":1232,"children":1234},{"className":1233},[1213],[1235,1238],{"type":24,"tag":1216,"props":1236,"children":1237},{"disabled":377,"type":1218},[],{"type":29,"value":1239}," LCP 图片是否避免 lazy",{"type":24,"tag":401,"props":1241,"children":1243},{"className":1242},[1213],[1244,1247],{"type":24,"tag":1216,"props":1245,"children":1246},{"disabled":377,"type":1218},[],{"type":29,"value":1248}," 是否有 RUM 维度切片（设备/地区/版本）",{"type":24,"tag":401,"props":1250,"children":1252},{"className":1251},[1213],[1253,1256],{"type":24,"tag":1216,"props":1254,"children":1255},{"disabled":377,"type":1218},[],{"type":29,"value":1257}," TTFB 是否受控（缓存/回源）",{"type":24,"tag":401,"props":1259,"children":1261},{"className":1260},[1213],[1262,1265],{"type":24,"tag":1216,"props":1263,"children":1264},{"disabled":377,"type":1218},[],{"type":29,"value":1266}," 第三方脚本是否延后",{"type":24,"tag":466,"props":1268,"children":1269},{},[],{"type":24,"tag":25,"props":1271,"children":1273},{"id":1272},"总结",[1274],{"type":29,"value":1272},{"type":24,"tag":36,"props":1276,"children":1277},{},[1278],{"type":29,"value":1279},"LCP 优化的核心是：",{"type":24,"tag":397,"props":1281,"children":1282},{},[1283,1288,1293],{"type":24,"tag":401,"props":1284,"children":1285},{},[1286],{"type":29,"value":1287},"先识别 LCP 元素",{"type":24,"tag":401,"props":1289,"children":1290},{},[1291],{"type":29,"value":1292},"再把 LCP 拆成链路各段",{"type":24,"tag":401,"props":1294,"children":1295},{},[1296],{"type":29,"value":1297},"针对性优化（资源优先级、体积、TTFB、主线程）",{"title":7,"searchDepth":329,"depth":329,"links":1299},[1300,1301,1302,1303,1304,1309,1315,1319,1320,1321,1322,1323],{"id":383,"depth":332,"text":368},{"id":471,"depth":332,"text":474},{"id":531,"depth":332,"text":534},{"id":604,"depth":332,"text":607},{"id":651,"depth":332,"text":654,"children":1305},[1306,1307,1308],{"id":657,"depth":329,"text":660},{"id":699,"depth":329,"text":702},{"id":723,"depth":329,"text":726},{"id":750,"depth":332,"text":753,"children":1310},[1311,1312,1313,1314],{"id":756,"depth":329,"text":759},{"id":786,"depth":329,"text":789},{"id":824,"depth":329,"text":827},{"id":865,"depth":329,"text":868},{"id":892,"depth":332,"text":895,"children":1316},[1317,1318],{"id":898,"depth":329,"text":901},{"id":922,"depth":329,"text":925},{"id":963,"depth":332,"text":966},{"id":1041,"depth":332,"text":1044},{"id":1069,"depth":332,"text":1072},{"id":1201,"depth":332,"text":1204},{"id":1272,"depth":332,"text":1272},"content:topics:performance:lcp-optimization-theory-practice.md","topics/performance/lcp-optimization-theory-practice.md","topics/performance/lcp-optimization-theory-practice",{"_path":1328,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1329,"description":1330,"date":370,"topic":5,"author":11,"tags":1331,"image":1336,"featured":377,"readingTime":1337,"body":1338,"_type":359,"_id":2653,"_source":361,"_file":2654,"_stem":2655,"_extension":364},"/topics/performance/rum-real-user-monitoring-design","RUM 真实用户监控方案设计","从指标体系、采样策略、上报链路到数据建模与告警，完整设计一套可落地的 RUM（Real User Monitoring）体系，解决“线上性能到底怎样”的问题。",[1332,1333,1334,373,1335],"性能监控","RUM","可观测性","采样","/images/topics/performance/rum.jpg",22,{"type":21,"children":1339,"toc":2615},[1340,1345,1357,1375,1387,1425,1428,1434,1439,1448,1453,1486,1491,1494,1500,1506,1511,1539,1544,1562,1568,1573,1578,1604,1609,1617,1620,1626,1631,1637,1686,1691,1697,1702,1720,1726,1731,1744,1749,1762,1765,1771,1777,1782,1803,1808,1821,1827,1840,1845,1987,1992,2005,2008,2014,2034,2060,2066,2070,2088,2094,2125,2131,2136,2140,2166,2169,2175,2181,2186,2245,2250,2256,2261,2274,2279,2306,2312,2317,2322,2335,2340,2343,2349,2354,2359,2392,2397,2402,2415,2418,2424,2430,2435,2453,2459,2464,2477,2483,2488,2501,2504,2510,2533,2538,2541,2547,2552,2575,2578,2582,2587],{"type":24,"tag":25,"props":1341,"children":1343},{"id":1342},"rum-真实用户监控方案设计",[1344],{"type":29,"value":1329},{"type":24,"tag":36,"props":1346,"children":1347},{},[1348,1350,1355],{"type":29,"value":1349},"如果说 Lighthouse/Performance 面板告诉你“理论上能跑多快”，那么 ",{"type":24,"tag":430,"props":1351,"children":1352},{},[1353],{"type":29,"value":1354},"RUM（Real User Monitoring）",{"type":29,"value":1356}," 才能回答：",{"type":24,"tag":397,"props":1358,"children":1359},{},[1360,1365,1370],{"type":24,"tag":401,"props":1361,"children":1362},{},[1363],{"type":29,"value":1364},"真实用户到底慢在哪？",{"type":24,"tag":401,"props":1366,"children":1367},{},[1368],{"type":29,"value":1369},"慢是“某地区/某机型/某版本/某网络”的问题还是全局问题？",{"type":24,"tag":401,"props":1371,"children":1372},{},[1373],{"type":29,"value":1374},"优化上线后是否真的变好？有无回归？",{"type":24,"tag":36,"props":1376,"children":1377},{},[1378,1380,1385],{"type":29,"value":1379},"这篇文章不讲“把 SDK 丢进去就完事”，而是站在",{"type":24,"tag":430,"props":1381,"children":1382},{},[1383],{"type":29,"value":1384},"体系设计",{"type":29,"value":1386},"角度，从 0 到 1 设计一套能长期演进的 RUM：",{"type":24,"tag":397,"props":1388,"children":1389},{},[1390,1395,1400,1405,1410,1415,1420],{"type":24,"tag":401,"props":1391,"children":1392},{},[1393],{"type":29,"value":1394},"指标体系（Core Web Vitals + 业务指标）",{"type":24,"tag":401,"props":1396,"children":1397},{},[1398],{"type":29,"value":1399},"采样与成本控制",{"type":24,"tag":401,"props":1401,"children":1402},{},[1403],{"type":29,"value":1404},"上报协议与可靠性",{"type":24,"tag":401,"props":1406,"children":1407},{},[1408],{"type":29,"value":1409},"事件模型与数据仓库建模",{"type":24,"tag":401,"props":1411,"children":1412},{},[1413],{"type":29,"value":1414},"分析维度（分桶）与聚合",{"type":24,"tag":401,"props":1416,"children":1417},{},[1418],{"type":29,"value":1419},"告警与 SLO",{"type":24,"tag":401,"props":1421,"children":1422},{},[1423],{"type":29,"value":1424},"隐私与合规",{"type":24,"tag":466,"props":1426,"children":1427},{},[],{"type":24,"tag":25,"props":1429,"children":1431},{"id":1430},"_1-先定目标rum-的产品定义",[1432],{"type":29,"value":1433},"1. 先定目标：RUM 的“产品定义”",{"type":24,"tag":36,"props":1435,"children":1436},{},[1437],{"type":29,"value":1438},"在做任何技术实现前，你必须把目标写成一句可验证的话：",{"type":24,"tag":1440,"props":1441,"children":1442},"blockquote",{},[1443],{"type":24,"tag":36,"props":1444,"children":1445},{},[1446],{"type":29,"value":1447},"我们要用 RUM 在 7 天内发现性能回归，并能定位到“哪类用户/哪条链路/哪个版本”导致 INP 恶化。",{"type":24,"tag":36,"props":1449,"children":1450},{},[1451],{"type":29,"value":1452},"RUM 的价值通常分三层：",{"type":24,"tag":766,"props":1454,"children":1455},{},[1456,1466,1476],{"type":24,"tag":401,"props":1457,"children":1458},{},[1459,1464],{"type":24,"tag":430,"props":1460,"children":1461},{},[1462],{"type":29,"value":1463},"可见性",{"type":29,"value":1465},"：上线后真实体验有数据",{"type":24,"tag":401,"props":1467,"children":1468},{},[1469,1474],{"type":24,"tag":430,"props":1470,"children":1471},{},[1472],{"type":29,"value":1473},"可定位",{"type":29,"value":1475},"：能切片（地区/设备/页面/版本）",{"type":24,"tag":401,"props":1477,"children":1478},{},[1479,1484],{"type":24,"tag":430,"props":1480,"children":1481},{},[1482],{"type":29,"value":1483},"可闭环",{"type":29,"value":1485},"：告警 → 归因 → 修复 → 复盘",{"type":24,"tag":36,"props":1487,"children":1488},{},[1489],{"type":29,"value":1490},"如果你只做第 1 层，它会退化成“报表”。",{"type":24,"tag":466,"props":1492,"children":1493},{},[],{"type":24,"tag":25,"props":1495,"children":1497},{"id":1496},"_2-指标体系不要只盯-core-web-vitals",[1498],{"type":29,"value":1499},"2. 指标体系：不要只盯 Core Web Vitals",{"type":24,"tag":57,"props":1501,"children":1503},{"id":1502},"_21-必选core-web-vitals-补充指标",[1504],{"type":29,"value":1505},"2.1 必选：Core Web Vitals + 补充指标",{"type":24,"tag":36,"props":1507,"children":1508},{},[1509],{"type":29,"value":1510},"建议最小集合：",{"type":24,"tag":397,"props":1512,"children":1513},{},[1514,1519,1524,1529,1534],{"type":24,"tag":401,"props":1515,"children":1516},{},[1517],{"type":29,"value":1518},"LCP（最大内容绘制）",{"type":24,"tag":401,"props":1520,"children":1521},{},[1522],{"type":29,"value":1523},"INP（交互到下一次绘制）",{"type":24,"tag":401,"props":1525,"children":1526},{},[1527],{"type":29,"value":1528},"CLS（布局偏移）",{"type":24,"tag":401,"props":1530,"children":1531},{},[1532],{"type":29,"value":1533},"TTFB（服务端首字节）",{"type":24,"tag":401,"props":1535,"children":1536},{},[1537],{"type":29,"value":1538},"FCP（首屏内容绘制，辅助解释 LCP）",{"type":24,"tag":36,"props":1540,"children":1541},{},[1542],{"type":29,"value":1543},"但这还不够，你还需要“解释型指标”：",{"type":24,"tag":397,"props":1545,"children":1546},{},[1547,1552,1557],{"type":24,"tag":401,"props":1548,"children":1549},{},[1550],{"type":29,"value":1551},"Long Task（长任务）统计（数量/总时长/Top 贡献）",{"type":24,"tag":401,"props":1553,"children":1554},{},[1555],{"type":29,"value":1556},"Resource timing（关键资源耗时）",{"type":24,"tag":401,"props":1558,"children":1559},{},[1560],{"type":29,"value":1561},"JS 错误/资源错误（与性能回归强相关）",{"type":24,"tag":57,"props":1563,"children":1565},{"id":1564},"_22-业务指标把性能与转化绑定",[1566],{"type":29,"value":1567},"2.2 业务指标：把性能与转化绑定",{"type":24,"tag":36,"props":1569,"children":1570},{},[1571],{"type":29,"value":1572},"纯性能指标很容易变成“工程师自嗨”。",{"type":24,"tag":36,"props":1574,"children":1575},{},[1576],{"type":29,"value":1577},"建议定义 1~3 个业务级指标：",{"type":24,"tag":397,"props":1579,"children":1580},{},[1581,1586,1599],{"type":24,"tag":401,"props":1582,"children":1583},{},[1584],{"type":29,"value":1585},"首次有效交互时间（例如搜索可用、下单可点）",{"type":24,"tag":401,"props":1587,"children":1588},{},[1589,1591,1597],{"type":29,"value":1590},"首次关键接口完成（例如 ",{"type":24,"tag":47,"props":1592,"children":1594},{"className":1593},[],[1595],{"type":29,"value":1596},"/api/me",{"type":29,"value":1598}," 完成）",{"type":24,"tag":401,"props":1600,"children":1601},{},[1602],{"type":29,"value":1603},"转化漏斗关键点耗时（例如“加入购物车”到“支付成功”）",{"type":24,"tag":36,"props":1605,"children":1606},{},[1607],{"type":29,"value":1608},"这些指标让你能回答：",{"type":24,"tag":397,"props":1610,"children":1611},{},[1612],{"type":24,"tag":401,"props":1613,"children":1614},{},[1615],{"type":29,"value":1616},"“慢 200ms 对业务有没有影响？”",{"type":24,"tag":466,"props":1618,"children":1619},{},[],{"type":24,"tag":25,"props":1621,"children":1623},{"id":1622},"_3-采样策略rum-成败的-50",[1624],{"type":29,"value":1625},"3. 采样策略：RUM 成败的 50%",{"type":24,"tag":36,"props":1627,"children":1628},{},[1629],{"type":29,"value":1630},"RUM 的难点不是“能不能采”，而是“采多少、怎么采、怎么省钱”。",{"type":24,"tag":57,"props":1632,"children":1634},{"id":1633},"_31-两种采样会话采样-vs-事件采样",[1635],{"type":29,"value":1636},"3.1 两种采样：会话采样 vs 事件采样",{"type":24,"tag":397,"props":1638,"children":1639},{},[1640,1663],{"type":24,"tag":401,"props":1641,"children":1642},{},[1643,1648,1650],{"type":24,"tag":430,"props":1644,"children":1645},{},[1646],{"type":29,"value":1647},"会话采样",{"type":29,"value":1649},"：进入站点就决定该会话是否采集全部指标",{"type":24,"tag":397,"props":1651,"children":1652},{},[1653,1658],{"type":24,"tag":401,"props":1654,"children":1655},{},[1656],{"type":29,"value":1657},"优点：链路完整、便于归因",{"type":24,"tag":401,"props":1659,"children":1660},{},[1661],{"type":29,"value":1662},"缺点：成本高",{"type":24,"tag":401,"props":1664,"children":1665},{},[1666,1671,1673],{"type":24,"tag":430,"props":1667,"children":1668},{},[1669],{"type":29,"value":1670},"事件采样",{"type":29,"value":1672},"：每种事件独立采样（例如性能 10%、错误 100%）",{"type":24,"tag":397,"props":1674,"children":1675},{},[1676,1681],{"type":24,"tag":401,"props":1677,"children":1678},{},[1679],{"type":29,"value":1680},"优点：更灵活",{"type":24,"tag":401,"props":1682,"children":1683},{},[1684],{"type":29,"value":1685},"缺点：同一会话数据不完整",{"type":24,"tag":36,"props":1687,"children":1688},{},[1689],{"type":29,"value":1690},"生产建议：两者结合。",{"type":24,"tag":57,"props":1692,"children":1694},{"id":1693},"_32-分层采样建议",[1695],{"type":29,"value":1696},"3.2 分层采样建议",{"type":24,"tag":36,"props":1698,"children":1699},{},[1700],{"type":29,"value":1701},"一个可用的默认配置：",{"type":24,"tag":397,"props":1703,"children":1704},{},[1705,1710,1715],{"type":24,"tag":401,"props":1706,"children":1707},{},[1708],{"type":29,"value":1709},"性能指标：10%（按会话）",{"type":24,"tag":401,"props":1711,"children":1712},{},[1713],{"type":29,"value":1714},"错误指标：100%（或至少 50%）",{"type":24,"tag":401,"props":1716,"children":1717},{},[1718],{"type":29,"value":1719},"关键交易链路：100%（只对“完成交易”的会话上报完整链路）",{"type":24,"tag":57,"props":1721,"children":1723},{"id":1722},"_33-动态采样用预算来管理",[1724],{"type":29,"value":1725},"3.3 动态采样：用“预算”来管理",{"type":24,"tag":36,"props":1727,"children":1728},{},[1729],{"type":29,"value":1730},"你可以给 RUM 设预算：",{"type":24,"tag":397,"props":1732,"children":1733},{},[1734,1739],{"type":24,"tag":401,"props":1735,"children":1736},{},[1737],{"type":29,"value":1738},"每日上报不超过 N 条",{"type":24,"tag":401,"props":1740,"children":1741},{},[1742],{"type":29,"value":1743},"每秒不超过 M 条",{"type":24,"tag":36,"props":1745,"children":1746},{},[1747],{"type":29,"value":1748},"当超预算时：",{"type":24,"tag":397,"props":1750,"children":1751},{},[1752,1757],{"type":24,"tag":401,"props":1753,"children":1754},{},[1755],{"type":29,"value":1756},"降低非关键事件采样率",{"type":24,"tag":401,"props":1758,"children":1759},{},[1760],{"type":29,"value":1761},"只保留异常事件（例如 LCP > 4s）",{"type":24,"tag":466,"props":1763,"children":1764},{},[],{"type":24,"tag":25,"props":1766,"children":1768},{"id":1767},"_4-客户端采集指标怎么拿",[1769],{"type":29,"value":1770},"4. 客户端采集：指标怎么拿？",{"type":24,"tag":57,"props":1772,"children":1774},{"id":1773},"_41-web-vitals-指标",[1775],{"type":29,"value":1776},"4.1 Web Vitals 指标",{"type":24,"tag":36,"props":1778,"children":1779},{},[1780],{"type":29,"value":1781},"现代浏览器提供了可观测入口：",{"type":24,"tag":397,"props":1783,"children":1784},{},[1785,1794],{"type":24,"tag":401,"props":1786,"children":1787},{},[1788],{"type":24,"tag":47,"props":1789,"children":1791},{"className":1790},[],[1792],{"type":29,"value":1793},"PerformanceObserver",{"type":24,"tag":401,"props":1795,"children":1796},{},[1797],{"type":24,"tag":47,"props":1798,"children":1800},{"className":1799},[],[1801],{"type":29,"value":1802},"performance.getEntriesByType()",{"type":24,"tag":36,"props":1804,"children":1805},{},[1806],{"type":29,"value":1807},"你可以使用社区实现（例如 web-vitals 思路），但要明确：",{"type":24,"tag":397,"props":1809,"children":1810},{},[1811,1816],{"type":24,"tag":401,"props":1812,"children":1813},{},[1814],{"type":29,"value":1815},"指标口径要固定（同一页面、同一版本）",{"type":24,"tag":401,"props":1817,"children":1818},{},[1819],{"type":29,"value":1820},"不同浏览器差异要做兼容",{"type":24,"tag":57,"props":1822,"children":1824},{"id":1823},"_42-上下文context是-rum-的灵魂",[1825],{"type":29,"value":1826},"4.2 上下文（Context）是 RUM 的灵魂",{"type":24,"tag":36,"props":1828,"children":1829},{},[1830,1832,1838],{"type":29,"value":1831},"只上报 ",{"type":24,"tag":47,"props":1833,"children":1835},{"className":1834},[],[1836],{"type":29,"value":1837},"LCP=2500ms",{"type":29,"value":1839}," 没意义。",{"type":24,"tag":36,"props":1841,"children":1842},{},[1843],{"type":29,"value":1844},"你至少需要这些维度：",{"type":24,"tag":397,"props":1846,"children":1847},{},[1848,1874,1887,1912,1937,1962],{"type":24,"tag":401,"props":1849,"children":1850},{},[1851,1853,1859,1861,1867,1868],{"type":29,"value":1852},"页面：",{"type":24,"tag":47,"props":1854,"children":1856},{"className":1855},[],[1857],{"type":29,"value":1858},"path",{"type":29,"value":1860},", ",{"type":24,"tag":47,"props":1862,"children":1864},{"className":1863},[],[1865],{"type":29,"value":1866},"routeName",{"type":29,"value":1860},{"type":24,"tag":47,"props":1869,"children":1871},{"className":1870},[],[1872],{"type":29,"value":1873},"referrer",{"type":24,"tag":401,"props":1875,"children":1876},{},[1877,1879,1885],{"type":29,"value":1878},"用户：匿名 ",{"type":24,"tag":47,"props":1880,"children":1882},{"className":1881},[],[1883],{"type":29,"value":1884},"userIdHash",{"type":29,"value":1886},"（可选）",{"type":24,"tag":401,"props":1888,"children":1889},{},[1890,1892,1898,1899,1905,1906],{"type":29,"value":1891},"设备：",{"type":24,"tag":47,"props":1893,"children":1895},{"className":1894},[],[1896],{"type":29,"value":1897},"deviceMemory",{"type":29,"value":1860},{"type":24,"tag":47,"props":1900,"children":1902},{"className":1901},[],[1903],{"type":29,"value":1904},"hardwareConcurrency",{"type":29,"value":1860},{"type":24,"tag":47,"props":1907,"children":1909},{"className":1908},[],[1910],{"type":29,"value":1911},"viewport",{"type":24,"tag":401,"props":1913,"children":1914},{},[1915,1917,1923,1924,1930,1931],{"type":29,"value":1916},"网络：",{"type":24,"tag":47,"props":1918,"children":1920},{"className":1919},[],[1921],{"type":29,"value":1922},"effectiveType",{"type":29,"value":1860},{"type":24,"tag":47,"props":1925,"children":1927},{"className":1926},[],[1928],{"type":29,"value":1929},"rtt",{"type":29,"value":1860},{"type":24,"tag":47,"props":1932,"children":1934},{"className":1933},[],[1935],{"type":29,"value":1936},"downlink",{"type":24,"tag":401,"props":1938,"children":1939},{},[1940,1942,1948,1949,1955,1956],{"type":29,"value":1941},"版本：",{"type":24,"tag":47,"props":1943,"children":1945},{"className":1944},[],[1946],{"type":29,"value":1947},"appVersion",{"type":29,"value":1860},{"type":24,"tag":47,"props":1950,"children":1952},{"className":1951},[],[1953],{"type":29,"value":1954},"buildId",{"type":29,"value":1860},{"type":24,"tag":47,"props":1957,"children":1959},{"className":1958},[],[1960],{"type":29,"value":1961},"commitSha",{"type":24,"tag":401,"props":1963,"children":1964},{},[1965,1967,1973,1974,1980,1981],{"type":29,"value":1966},"环境：",{"type":24,"tag":47,"props":1968,"children":1970},{"className":1969},[],[1971],{"type":29,"value":1972},"prod/staging",{"type":29,"value":1860},{"type":24,"tag":47,"props":1975,"children":1977},{"className":1976},[],[1978],{"type":29,"value":1979},"region",{"type":29,"value":1860},{"type":24,"tag":47,"props":1982,"children":1984},{"className":1983},[],[1985],{"type":29,"value":1986},"cdnPop",{"type":24,"tag":36,"props":1988,"children":1989},{},[1990],{"type":29,"value":1991},"这些字段必须：",{"type":24,"tag":397,"props":1993,"children":1994},{},[1995,2000],{"type":24,"tag":401,"props":1996,"children":1997},{},[1998],{"type":29,"value":1999},"控制体积（短字段名/枚举）",{"type":24,"tag":401,"props":2001,"children":2002},{},[2003],{"type":29,"value":2004},"可被服务器校验（防伪造污染）",{"type":24,"tag":466,"props":2006,"children":2007},{},[],{"type":24,"tag":25,"props":2009,"children":2011},{"id":2010},"_5-上报链路可靠性与性能不能两头都丢",[2012],{"type":29,"value":2013},"5. 上报链路：可靠性与性能不能两头都丢",{"type":24,"tag":57,"props":2015,"children":2017},{"id":2016},"_51-传输优先-sendbeacon降级到-fetchkeepalive",[2018,2020,2026,2028],{"type":29,"value":2019},"5.1 传输：优先 ",{"type":24,"tag":47,"props":2021,"children":2023},{"className":2022},[],[2024],{"type":29,"value":2025},"sendBeacon",{"type":29,"value":2027},"，降级到 ",{"type":24,"tag":47,"props":2029,"children":2031},{"className":2030},[],[2032],{"type":29,"value":2033},"fetch(keepalive)",{"type":24,"tag":397,"props":2035,"children":2036},{},[2037,2049],{"type":24,"tag":401,"props":2038,"children":2039},{},[2040,2042,2047],{"type":29,"value":2041},"页面卸载时 ",{"type":24,"tag":47,"props":2043,"children":2045},{"className":2044},[],[2046],{"type":29,"value":2025},{"type":29,"value":2048}," 更可靠",{"type":24,"tag":401,"props":2050,"children":2051},{},[2052,2054],{"type":29,"value":2053},"非卸载场景可用批量 ",{"type":24,"tag":47,"props":2055,"children":2057},{"className":2056},[],[2058],{"type":29,"value":2059},"fetch",{"type":24,"tag":57,"props":2061,"children":2063},{"id":2062},"_52-批量与压缩",[2064],{"type":29,"value":2065},"5.2 批量与压缩",{"type":24,"tag":36,"props":2067,"children":2068},{},[2069],{"type":29,"value":935},{"type":24,"tag":397,"props":2071,"children":2072},{},[2073,2078,2083],{"type":24,"tag":401,"props":2074,"children":2075},{},[2076],{"type":29,"value":2077},"以 5~20 条为一个 batch",{"type":24,"tag":401,"props":2079,"children":2080},{},[2081],{"type":29,"value":2082},"gzip/br 压缩（服务器支持）",{"type":24,"tag":401,"props":2084,"children":2085},{},[2086],{"type":29,"value":2087},"限制 payload 大小（例如 \u003C 64KB）",{"type":24,"tag":57,"props":2089,"children":2091},{"id":2090},"_53-去重与重试",[2092],{"type":29,"value":2093},"5.3 去重与重试",{"type":24,"tag":397,"props":2095,"children":2096},{},[2097,2108,2120],{"type":24,"tag":401,"props":2098,"children":2099},{},[2100,2102],{"type":29,"value":2101},"给每条事件生成 ",{"type":24,"tag":47,"props":2103,"children":2105},{"className":2104},[],[2106],{"type":29,"value":2107},"eventId",{"type":24,"tag":401,"props":2109,"children":2110},{},[2111,2113,2118],{"type":29,"value":2112},"服务端按 ",{"type":24,"tag":47,"props":2114,"children":2116},{"className":2115},[],[2117],{"type":29,"value":2107},{"type":29,"value":2119}," 去重",{"type":24,"tag":401,"props":2121,"children":2122},{},[2123],{"type":29,"value":2124},"客户端失败重试次数有限（例如 2 次）",{"type":24,"tag":57,"props":2126,"children":2128},{"id":2127},"_54-采集对页面的影响",[2129],{"type":29,"value":2130},"5.4 采集对页面的影响",{"type":24,"tag":36,"props":2132,"children":2133},{},[2134],{"type":29,"value":2135},"RUM 本身不能成为性能负担。",{"type":24,"tag":36,"props":2137,"children":2138},{},[2139],{"type":29,"value":1017},{"type":24,"tag":397,"props":2141,"children":2142},{},[2143,2156,2161],{"type":24,"tag":401,"props":2144,"children":2145},{},[2146,2148,2154],{"type":29,"value":2147},"采集计算尽量放在 idle（",{"type":24,"tag":47,"props":2149,"children":2151},{"className":2150},[],[2152],{"type":29,"value":2153},"requestIdleCallback",{"type":29,"value":2155},"）",{"type":24,"tag":401,"props":2157,"children":2158},{},[2159],{"type":29,"value":2160},"上报放在后台、批量",{"type":24,"tag":401,"props":2162,"children":2163},{},[2164],{"type":29,"value":2165},"SDK 初始化延后（首屏后）",{"type":24,"tag":466,"props":2167,"children":2168},{},[],{"type":24,"tag":25,"props":2170,"children":2172},{"id":2171},"_6-事件模型与数据建模决定你能不能分析",[2173],{"type":29,"value":2174},"6. 事件模型与数据建模：决定你能不能分析",{"type":24,"tag":57,"props":2176,"children":2178},{"id":2177},"_61-事件类型event-types",[2179],{"type":29,"value":2180},"6.1 事件类型（Event Types）",{"type":24,"tag":36,"props":2182,"children":2183},{},[2184],{"type":29,"value":2185},"建议至少定义：",{"type":24,"tag":397,"props":2187,"children":2188},{},[2189,2198,2207,2216,2225,2234],{"type":24,"tag":401,"props":2190,"children":2191},{},[2192],{"type":24,"tag":47,"props":2193,"children":2195},{"className":2194},[],[2196],{"type":29,"value":2197},"page_view",{"type":24,"tag":401,"props":2199,"children":2200},{},[2201],{"type":24,"tag":47,"props":2202,"children":2204},{"className":2203},[],[2205],{"type":29,"value":2206},"web_vitals",{"type":24,"tag":401,"props":2208,"children":2209},{},[2210],{"type":24,"tag":47,"props":2211,"children":2213},{"className":2212},[],[2214],{"type":29,"value":2215},"resource_timing",{"type":24,"tag":401,"props":2217,"children":2218},{},[2219],{"type":24,"tag":47,"props":2220,"children":2222},{"className":2221},[],[2223],{"type":29,"value":2224},"long_task",{"type":24,"tag":401,"props":2226,"children":2227},{},[2228],{"type":24,"tag":47,"props":2229,"children":2231},{"className":2230},[],[2232],{"type":29,"value":2233},"js_error",{"type":24,"tag":401,"props":2235,"children":2236},{},[2237,2243],{"type":24,"tag":47,"props":2238,"children":2240},{"className":2239},[],[2241],{"type":29,"value":2242},"api_timing",{"type":29,"value":2244},"（业务关键接口）",{"type":24,"tag":36,"props":2246,"children":2247},{},[2248],{"type":29,"value":2249},"每种事件都有“必填字段”和“可选字段”。",{"type":24,"tag":57,"props":2251,"children":2253},{"id":2252},"_62-存储建模宽表-明细表",[2254],{"type":29,"value":2255},"6.2 存储建模：宽表 + 明细表",{"type":24,"tag":36,"props":2257,"children":2258},{},[2259],{"type":29,"value":2260},"常见两种设计：",{"type":24,"tag":397,"props":2262,"children":2263},{},[2264,2269],{"type":24,"tag":401,"props":2265,"children":2266},{},[2267],{"type":29,"value":2268},"宽表（便于查询）：把常用字段打平",{"type":24,"tag":401,"props":2270,"children":2271},{},[2272],{"type":29,"value":2273},"明细表（便于追踪）：存完整 payload（可选）",{"type":24,"tag":36,"props":2275,"children":2276},{},[2277],{"type":29,"value":2278},"一个可落地的折中：",{"type":24,"tag":397,"props":2280,"children":2281},{},[2282,2294],{"type":24,"tag":401,"props":2283,"children":2284},{},[2285,2287,2292],{"type":29,"value":2286},"以 ",{"type":24,"tag":47,"props":2288,"children":2290},{"className":2289},[],[2291],{"type":29,"value":2206},{"type":29,"value":2293}," 为核心宽表（用于大盘/SLO）",{"type":24,"tag":401,"props":2295,"children":2296},{},[2297,2298,2304],{"type":29,"value":2286},{"type":24,"tag":47,"props":2299,"children":2301},{"className":2300},[],[2302],{"type":29,"value":2303},"session_trace",{"type":29,"value":2305}," 为明细表（只对采样会话存）",{"type":24,"tag":57,"props":2307,"children":2309},{"id":2308},"_63-聚合口径p75p90p95p99",[2310],{"type":29,"value":2311},"6.3 聚合口径：p75/p90/p95/p99",{"type":24,"tag":36,"props":2313,"children":2314},{},[2315],{"type":29,"value":2316},"RUM 的统计不要只用平均值。",{"type":24,"tag":36,"props":2318,"children":2319},{},[2320],{"type":29,"value":2321},"推荐：",{"type":24,"tag":397,"props":2323,"children":2324},{},[2325,2330],{"type":24,"tag":401,"props":2326,"children":2327},{},[2328],{"type":29,"value":2329},"p75：更贴近“多数用户体验”",{"type":24,"tag":401,"props":2331,"children":2332},{},[2333],{"type":29,"value":2334},"p95/p99：定位极端慢问题",{"type":24,"tag":36,"props":2336,"children":2337},{},[2338],{"type":29,"value":2339},"对于 Web Vitals，Google 口径常用 p75。",{"type":24,"tag":466,"props":2341,"children":2342},{},[],{"type":24,"tag":25,"props":2344,"children":2346},{"id":2345},"_7-分析维度怎么做到可定位",[2347],{"type":29,"value":2348},"7. 分析维度：怎么做到“可定位”？",{"type":24,"tag":36,"props":2350,"children":2351},{},[2352],{"type":29,"value":2353},"可定位的关键在于“维度设计”。",{"type":24,"tag":36,"props":2355,"children":2356},{},[2357],{"type":29,"value":2358},"建议固定一套常用切片：",{"type":24,"tag":397,"props":2360,"children":2361},{},[2362,2367,2372,2377,2382,2387],{"type":24,"tag":401,"props":2363,"children":2364},{},[2365],{"type":29,"value":2366},"页面（路由）",{"type":24,"tag":401,"props":2368,"children":2369},{},[2370],{"type":29,"value":2371},"地区/运营商",{"type":24,"tag":401,"props":2373,"children":2374},{},[2375],{"type":29,"value":2376},"设备档位（内存/CPU 核数分桶）",{"type":24,"tag":401,"props":2378,"children":2379},{},[2380],{"type":29,"value":2381},"网络（4g/3g/slow-4g）",{"type":24,"tag":401,"props":2383,"children":2384},{},[2385],{"type":29,"value":2386},"版本（最近 10 个 buildId）",{"type":24,"tag":401,"props":2388,"children":2389},{},[2390],{"type":29,"value":2391},"入口来源（referrer/channel）",{"type":24,"tag":36,"props":2393,"children":2394},{},[2395],{"type":29,"value":2396},"注意：维度越多，成本越高。",{"type":24,"tag":36,"props":2398,"children":2399},{},[2400],{"type":29,"value":2401},"实践做法：",{"type":24,"tag":397,"props":2403,"children":2404},{},[2405,2410],{"type":24,"tag":401,"props":2406,"children":2407},{},[2408],{"type":29,"value":2409},"维度字段做枚举/分桶",{"type":24,"tag":401,"props":2411,"children":2412},{},[2413],{"type":29,"value":2414},"对长尾维度做 Top N",{"type":24,"tag":466,"props":2416,"children":2417},{},[],{"type":24,"tag":25,"props":2419,"children":2421},{"id":2420},"_8-告警与-slo让-rum-成为系统",[2422],{"type":29,"value":2423},"8. 告警与 SLO：让 RUM 成为“系统”",{"type":24,"tag":57,"props":2425,"children":2427},{"id":2426},"_81-slo-的定义",[2428],{"type":29,"value":2429},"8.1 SLO 的定义",{"type":24,"tag":36,"props":2431,"children":2432},{},[2433],{"type":29,"value":2434},"示例：",{"type":24,"tag":397,"props":2436,"children":2437},{},[2438,2443,2448],{"type":24,"tag":401,"props":2439,"children":2440},{},[2441],{"type":29,"value":2442},"过去 1 天内，p75 LCP \u003C 2500ms",{"type":24,"tag":401,"props":2444,"children":2445},{},[2446],{"type":29,"value":2447},"过去 1 天内，p75 INP \u003C 200ms",{"type":24,"tag":401,"props":2449,"children":2450},{},[2451],{"type":29,"value":2452},"过去 1 天内，CLS p75 \u003C 0.1",{"type":24,"tag":57,"props":2454,"children":2456},{"id":2455},"_82-告警规则",[2457],{"type":29,"value":2458},"8.2 告警规则",{"type":24,"tag":36,"props":2460,"children":2461},{},[2462],{"type":29,"value":2463},"建议用“双阈值”防抖：",{"type":24,"tag":397,"props":2465,"children":2466},{},[2467,2472],{"type":24,"tag":401,"props":2468,"children":2469},{},[2470],{"type":29,"value":2471},"5 分钟窗口连续超阈值",{"type":24,"tag":401,"props":2473,"children":2474},{},[2475],{"type":29,"value":2476},"且样本量 > 最小值（例如 300）",{"type":24,"tag":57,"props":2478,"children":2480},{"id":2479},"_83-回归检测",[2481],{"type":29,"value":2482},"8.3 回归检测",{"type":24,"tag":36,"props":2484,"children":2485},{},[2486],{"type":29,"value":2487},"把版本作为维度：",{"type":24,"tag":397,"props":2489,"children":2490},{},[2491,2496],{"type":24,"tag":401,"props":2492,"children":2493},{},[2494],{"type":29,"value":2495},"新版本的 p75 INP 相比上版本恶化 > 15%",{"type":24,"tag":401,"props":2497,"children":2498},{},[2499],{"type":29,"value":2500},"触发告警并附带“top 页面 / top 设备 / top 地区”",{"type":24,"tag":466,"props":2502,"children":2503},{},[],{"type":24,"tag":25,"props":2505,"children":2507},{"id":2506},"_9-隐私与合规你必须提前做的约束",[2508],{"type":29,"value":2509},"9. 隐私与合规：你必须提前做的约束",{"type":24,"tag":397,"props":2511,"children":2512},{},[2513,2518,2523,2528],{"type":24,"tag":401,"props":2514,"children":2515},{},[2516],{"type":29,"value":2517},"禁止上报 PII（手机号、邮箱、身份证等）",{"type":24,"tag":401,"props":2519,"children":2520},{},[2521],{"type":29,"value":2522},"URL 中 query/fragment 需要脱敏",{"type":24,"tag":401,"props":2524,"children":2525},{},[2526],{"type":29,"value":2527},"事件 payload 做字段白名单",{"type":24,"tag":401,"props":2529,"children":2530},{},[2531],{"type":29,"value":2532},"允许用户 opt-out（尤其是欧盟地区）",{"type":24,"tag":36,"props":2534,"children":2535},{},[2536],{"type":29,"value":2537},"如果产品面向多地区，建议把合规当成“需求”，不是“上线前检查”。",{"type":24,"tag":466,"props":2539,"children":2540},{},[],{"type":24,"tag":25,"props":2542,"children":2544},{"id":2543},"_10-最小可用方案mvp建议",[2545],{"type":29,"value":2546},"10. 最小可用方案（MVP）建议",{"type":24,"tag":36,"props":2548,"children":2549},{},[2550],{"type":29,"value":2551},"如果你要在 1~2 周内把 RUM 跑起来：",{"type":24,"tag":766,"props":2553,"children":2554},{},[2555,2560,2565,2570],{"type":24,"tag":401,"props":2556,"children":2557},{},[2558],{"type":29,"value":2559},"先采：page_view + web_vitals + js_error",{"type":24,"tag":401,"props":2561,"children":2562},{},[2563],{"type":29,"value":2564},"先做：按路由维度的大盘（p75）",{"type":24,"tag":401,"props":2566,"children":2567},{},[2568],{"type":29,"value":2569},"再做：版本对比（回归检测）",{"type":24,"tag":401,"props":2571,"children":2572},{},[2573],{"type":29,"value":2574},"最后做：会话 trace（定位长尾）",{"type":24,"tag":466,"props":2576,"children":2577},{},[],{"type":24,"tag":25,"props":2579,"children":2580},{"id":1272},[2581],{"type":29,"value":1272},{"type":24,"tag":36,"props":2583,"children":2584},{},[2585],{"type":29,"value":2586},"RUM 的关键不是 SDK，而是：",{"type":24,"tag":397,"props":2588,"children":2589},{},[2590,2595,2600,2605,2610],{"type":24,"tag":401,"props":2591,"children":2592},{},[2593],{"type":29,"value":2594},"指标体系与业务目标绑定",{"type":24,"tag":401,"props":2596,"children":2597},{},[2598],{"type":29,"value":2599},"采样与预算控制成本",{"type":24,"tag":401,"props":2601,"children":2602},{},[2603],{"type":29,"value":2604},"上报可靠且不影响性能",{"type":24,"tag":401,"props":2606,"children":2607},{},[2608],{"type":29,"value":2609},"数据建模支持切片与聚合",{"type":24,"tag":401,"props":2611,"children":2612},{},[2613],{"type":29,"value":2614},"告警/SLO 能闭环",{"title":7,"searchDepth":329,"depth":329,"links":2616},[2617,2618,2619,2623,2628,2632,2639,2644,2645,2650,2651,2652],{"id":1342,"depth":332,"text":1329},{"id":1430,"depth":332,"text":1433},{"id":1496,"depth":332,"text":1499,"children":2620},[2621,2622],{"id":1502,"depth":329,"text":1505},{"id":1564,"depth":329,"text":1567},{"id":1622,"depth":332,"text":1625,"children":2624},[2625,2626,2627],{"id":1633,"depth":329,"text":1636},{"id":1693,"depth":329,"text":1696},{"id":1722,"depth":329,"text":1725},{"id":1767,"depth":332,"text":1770,"children":2629},[2630,2631],{"id":1773,"depth":329,"text":1776},{"id":1823,"depth":329,"text":1826},{"id":2010,"depth":332,"text":2013,"children":2633},[2634,2636,2637,2638],{"id":2016,"depth":329,"text":2635},"5.1 传输：优先 sendBeacon，降级到 fetch(keepalive)",{"id":2062,"depth":329,"text":2065},{"id":2090,"depth":329,"text":2093},{"id":2127,"depth":329,"text":2130},{"id":2171,"depth":332,"text":2174,"children":2640},[2641,2642,2643],{"id":2177,"depth":329,"text":2180},{"id":2252,"depth":329,"text":2255},{"id":2308,"depth":329,"text":2311},{"id":2345,"depth":332,"text":2348},{"id":2420,"depth":332,"text":2423,"children":2646},[2647,2648,2649],{"id":2426,"depth":329,"text":2429},{"id":2455,"depth":329,"text":2458},{"id":2479,"depth":329,"text":2482},{"id":2506,"depth":332,"text":2509},{"id":2543,"depth":332,"text":2546},{"id":1272,"depth":332,"text":1272},"content:topics:performance:rum-real-user-monitoring-design.md","topics/performance/rum-real-user-monitoring-design.md","topics/performance/rum-real-user-monitoring-design",{"_path":2657,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2658,"description":2659,"date":2660,"topic":5,"author":11,"tags":2661,"image":2665,"featured":377,"readingTime":2666,"body":2667,"_type":359,"_id":3586,"_source":361,"_file":3587,"_stem":3588,"_extension":364},"/topics/performance/core-web-vitals-2026-guide","Core Web Vitals 2026 新标准解读：INP 时代的性能优化策略","全面解读 2026 年 Core Web Vitals 的重大变化，深入分析 INP 取代 FID 的技术背景、测量方法与优化策略，帮助开发者适应新的性能评估体系。","2026-01-15",[373,2662,14,2663,2664],"INP","Web 性能","Google 排名","/images/topics/core-web-vitals-2026.jpg",15,{"type":21,"children":2668,"toc":3545},[2669,2675,2681,2693,2699,2707,2781,2789,2797,2803,2951,2957,2963,2975,2986,2994,3012,3018,3023,3032,3038,3043,3051,3057,3063,3072,3078,3087,3093,3102,3108,3114,3119,3180,3189,3195,3205,3210,3219,3225,3230,3239,3245,3254,3260,3269,3275,3284,3290,3296,3305,3311,3320,3325,3331,3336,3344,3352,3370,3375,3384,3388,3393,3398,3457,3462,3485,3490],{"type":24,"tag":25,"props":2670,"children":2672},{"id":2671},"core-web-vitals-2026-新标准解读",[2673],{"type":29,"value":2674},"Core Web Vitals 2026 新标准解读",{"type":24,"tag":25,"props":2676,"children":2678},{"id":2677},"_2026-年的重大变化inp-正式取代-fid",[2679],{"type":29,"value":2680},"2026 年的重大变化：INP 正式取代 FID",{"type":24,"tag":36,"props":2682,"children":2683},{},[2684,2686,2691],{"type":29,"value":2685},"2024 年 3 月，Google 正式宣布 ",{"type":24,"tag":430,"props":2687,"children":2688},{},[2689],{"type":29,"value":2690},"INP（Interaction to Next Paint）",{"type":29,"value":2692}," 取代 FID（First Input Delay）成为 Core Web Vitals 的核心指标。经过两年的过渡期，2026 年 INP 已完全成为搜索排名的重要因素。",{"type":24,"tag":57,"props":2694,"children":2696},{"id":2695},"为什么要替换-fid",[2697],{"type":29,"value":2698},"为什么要替换 FID？",{"type":24,"tag":36,"props":2700,"children":2701},{},[2702],{"type":24,"tag":430,"props":2703,"children":2704},{},[2705],{"type":29,"value":2706},"FID 的局限性：",{"type":24,"tag":1074,"props":2708,"children":2709},{},[2710,2726],{"type":24,"tag":1078,"props":2711,"children":2712},{},[2713],{"type":24,"tag":1082,"props":2714,"children":2715},{},[2716,2721],{"type":24,"tag":1086,"props":2717,"children":2718},{},[2719],{"type":29,"value":2720},"问题",{"type":24,"tag":1086,"props":2722,"children":2723},{},[2724],{"type":29,"value":2725},"说明",{"type":24,"tag":1102,"props":2727,"children":2728},{},[2729,2742,2755,2768],{"type":24,"tag":1082,"props":2730,"children":2731},{},[2732,2737],{"type":24,"tag":1109,"props":2733,"children":2734},{},[2735],{"type":29,"value":2736},"只测量首次交互",{"type":24,"tag":1109,"props":2738,"children":2739},{},[2740],{"type":29,"value":2741},"忽略后续所有交互的延迟",{"type":24,"tag":1082,"props":2743,"children":2744},{},[2745,2750],{"type":24,"tag":1109,"props":2746,"children":2747},{},[2748],{"type":29,"value":2749},"只测量输入延迟",{"type":24,"tag":1109,"props":2751,"children":2752},{},[2753],{"type":29,"value":2754},"不包含事件处理和渲染时间",{"type":24,"tag":1082,"props":2756,"children":2757},{},[2758,2763],{"type":24,"tag":1109,"props":2759,"children":2760},{},[2761],{"type":29,"value":2762},"样本偏差",{"type":24,"tag":1109,"props":2764,"children":2765},{},[2766],{"type":29,"value":2767},"某些用户可能永远不触发交互",{"type":24,"tag":1082,"props":2769,"children":2770},{},[2771,2776],{"type":24,"tag":1109,"props":2772,"children":2773},{},[2774],{"type":29,"value":2775},"无法反映真实体验",{"type":24,"tag":1109,"props":2777,"children":2778},{},[2779],{"type":29,"value":2780},"首次交互可能很快，但后续很慢",{"type":24,"tag":36,"props":2782,"children":2783},{},[2784],{"type":24,"tag":430,"props":2785,"children":2786},{},[2787],{"type":29,"value":2788},"INP 的优势：",{"type":24,"tag":42,"props":2790,"children":2792},{"code":2791},"FID 测量范围：\n┌──────────────────────────────────────────────────┐\n│  用户点击  →  浏览器开始处理  │  FID 结束      │\n│     ▼              ▼          │                │\n│   [===========]               │                │\n│   只测这一段                   │                │\n└──────────────────────────────────────────────────┘\n\nINP 测量范围：\n┌──────────────────────────────────────────────────┐\n│  用户点击  →  处理事件  →  渲染完成  │  INP 结束 │\n│     ▼          ▼           ▼        │          │\n│   [================================]            │\n│   完整的交互响应时间                             │\n└──────────────────────────────────────────────────┘\n",[2793],{"type":24,"tag":47,"props":2794,"children":2795},{"__ignoreMap":7},[2796],{"type":29,"value":2791},{"type":24,"tag":57,"props":2798,"children":2800},{"id":2799},"_2026-年-core-web-vitals-完整指标体系",[2801],{"type":29,"value":2802},"2026 年 Core Web Vitals 完整指标体系",{"type":24,"tag":1074,"props":2804,"children":2805},{},[2806,2842],{"type":24,"tag":1078,"props":2807,"children":2808},{},[2809],{"type":24,"tag":1082,"props":2810,"children":2811},{},[2812,2817,2822,2827,2832,2837],{"type":24,"tag":1086,"props":2813,"children":2814},{},[2815],{"type":29,"value":2816},"指标",{"type":24,"tag":1086,"props":2818,"children":2819},{},[2820],{"type":29,"value":2821},"全称",{"type":24,"tag":1086,"props":2823,"children":2824},{},[2825],{"type":29,"value":2826},"测量内容",{"type":24,"tag":1086,"props":2828,"children":2829},{},[2830],{"type":29,"value":2831},"良好阈值",{"type":24,"tag":1086,"props":2833,"children":2834},{},[2835],{"type":29,"value":2836},"需改进",{"type":24,"tag":1086,"props":2838,"children":2839},{},[2840],{"type":29,"value":2841},"差",{"type":24,"tag":1102,"props":2843,"children":2844},{},[2845,2880,2915],{"type":24,"tag":1082,"props":2846,"children":2847},{},[2848,2855,2860,2865,2870,2875],{"type":24,"tag":1109,"props":2849,"children":2850},{},[2851],{"type":24,"tag":430,"props":2852,"children":2853},{},[2854],{"type":29,"value":372},{"type":24,"tag":1109,"props":2856,"children":2857},{},[2858],{"type":29,"value":2859},"Largest Contentful Paint",{"type":24,"tag":1109,"props":2861,"children":2862},{},[2863],{"type":29,"value":2864},"最大内容绘制时间",{"type":24,"tag":1109,"props":2866,"children":2867},{},[2868],{"type":29,"value":2869},"≤ 2.5s",{"type":24,"tag":1109,"props":2871,"children":2872},{},[2873],{"type":29,"value":2874},"≤ 4s",{"type":24,"tag":1109,"props":2876,"children":2877},{},[2878],{"type":29,"value":2879},"> 4s",{"type":24,"tag":1082,"props":2881,"children":2882},{},[2883,2890,2895,2900,2905,2910],{"type":24,"tag":1109,"props":2884,"children":2885},{},[2886],{"type":24,"tag":430,"props":2887,"children":2888},{},[2889],{"type":29,"value":2662},{"type":24,"tag":1109,"props":2891,"children":2892},{},[2893],{"type":29,"value":2894},"Interaction to Next Paint",{"type":24,"tag":1109,"props":2896,"children":2897},{},[2898],{"type":29,"value":2899},"交互到下一次绘制",{"type":24,"tag":1109,"props":2901,"children":2902},{},[2903],{"type":29,"value":2904},"≤ 200ms",{"type":24,"tag":1109,"props":2906,"children":2907},{},[2908],{"type":29,"value":2909},"≤ 500ms",{"type":24,"tag":1109,"props":2911,"children":2912},{},[2913],{"type":29,"value":2914},"> 500ms",{"type":24,"tag":1082,"props":2916,"children":2917},{},[2918,2926,2931,2936,2941,2946],{"type":24,"tag":1109,"props":2919,"children":2920},{},[2921],{"type":24,"tag":430,"props":2922,"children":2923},{},[2924],{"type":29,"value":2925},"CLS",{"type":24,"tag":1109,"props":2927,"children":2928},{},[2929],{"type":29,"value":2930},"Cumulative Layout Shift",{"type":24,"tag":1109,"props":2932,"children":2933},{},[2934],{"type":29,"value":2935},"累积布局偏移",{"type":24,"tag":1109,"props":2937,"children":2938},{},[2939],{"type":29,"value":2940},"≤ 0.1",{"type":24,"tag":1109,"props":2942,"children":2943},{},[2944],{"type":29,"value":2945},"≤ 0.25",{"type":24,"tag":1109,"props":2947,"children":2948},{},[2949],{"type":29,"value":2950},"> 0.25",{"type":24,"tag":25,"props":2952,"children":2954},{"id":2953},"inp-深度解析",[2955],{"type":29,"value":2956},"INP 深度解析",{"type":24,"tag":57,"props":2958,"children":2960},{"id":2959},"inp-的计算方式",[2961],{"type":29,"value":2962},"INP 的计算方式",{"type":24,"tag":36,"props":2964,"children":2965},{},[2966,2968,2973],{"type":29,"value":2967},"INP 并非所有交互延迟的平均值，而是采用",{"type":24,"tag":430,"props":2969,"children":2970},{},[2971],{"type":29,"value":2972},"高百分位值",{"type":29,"value":2974},"策略：",{"type":24,"tag":42,"props":2976,"children":2981},{"code":2977,"language":2978,"meta":7,"className":2979},"// INP 计算逻辑（简化版）\nfunction calculateINP(interactions) {\n  // 按延迟时间排序\n  const sorted = interactions.sort((a, b) => b.duration - a.duration);\n  \n  // 根据交互数量选择百分位\n  // 交互少于 50 次：取最大值\n  // 交互 50 次以上：取第 98 百分位\n  const count = sorted.length;\n  \n  if (count \u003C 50) {\n    return sorted[0]?.duration ?? 0;\n  }\n  \n  // 98th percentile\n  const index = Math.floor(count * 0.02);\n  return sorted[index].duration;\n}\n","javascript",[2980],"language-javascript",[2982],{"type":24,"tag":47,"props":2983,"children":2984},{"__ignoreMap":7},[2985],{"type":29,"value":2977},{"type":24,"tag":36,"props":2987,"children":2988},{},[2989],{"type":24,"tag":430,"props":2990,"children":2991},{},[2992],{"type":29,"value":2993},"为什么选择高百分位而非平均值？",{"type":24,"tag":397,"props":2995,"children":2996},{},[2997,3002,3007],{"type":24,"tag":401,"props":2998,"children":2999},{},[3000],{"type":29,"value":3001},"平均值会被大量快速交互\"稀释\"",{"type":24,"tag":401,"props":3003,"children":3004},{},[3005],{"type":29,"value":3006},"用户对慢交互的感知更强烈",{"type":24,"tag":401,"props":3008,"children":3009},{},[3010],{"type":29,"value":3011},"高百分位更能反映\"最坏情况\"体验",{"type":24,"tag":57,"props":3013,"children":3015},{"id":3014},"什么算作一次交互",[3016],{"type":29,"value":3017},"什么算作一次交互？",{"type":24,"tag":36,"props":3019,"children":3020},{},[3021],{"type":29,"value":3022},"INP 追踪以下类型的交互：",{"type":24,"tag":42,"props":3024,"children":3027},{"code":3025,"language":2978,"meta":7,"className":3026},"// INP 追踪的交互类型\nconst trackedInteractions = {\n  // 点击相关\n  click: true,\n  dblclick: true,\n  contextmenu: true,\n  \n  // 键盘相关\n  keydown: true,\n  keyup: true,\n  keypress: true,\n  \n  // 触摸相关\n  touchstart: true,\n  touchend: true,\n  \n  // 指针相关\n  pointerdown: true,\n  pointerup: true\n};\n\n// 以下不计入 INP\nconst notTracked = {\n  scroll: '滚动不是离散交互',\n  mousemove: '持续性事件',\n  hover: '非用户主动操作'\n};\n",[2980],[3028],{"type":24,"tag":47,"props":3029,"children":3030},{"__ignoreMap":7},[3031],{"type":29,"value":3025},{"type":24,"tag":57,"props":3033,"children":3035},{"id":3034},"inp-的三个阶段",[3036],{"type":29,"value":3037},"INP 的三个阶段",{"type":24,"tag":36,"props":3039,"children":3040},{},[3041],{"type":29,"value":3042},"一次完整的交互由三个阶段组成：",{"type":24,"tag":42,"props":3044,"children":3046},{"code":3045},"┌─────────────────────────────────────────────────────────────┐\n│                      INP 总时长                             │\n├───────────────┬───────────────────────┬────────────────────┤\n│   Input Delay │   Processing Time     │   Presentation     │\n│   输入延迟    │   处理时间            │   呈现延迟         │\n├───────────────┼───────────────────────┼────────────────────┤\n│ 主线程被占用  │ 事件处理函数执行      │ 样式计算+布局+绘制 │\n│ 导致的等待    │                       │                    │\n├───────────────┼───────────────────────┼────────────────────┤\n│   优化方向：  │   优化方向：          │   优化方向：       │\n│ - 减少长任务  │ - 优化事件处理逻辑    │ - 减少 DOM 操作    │\n│ - 使用调度器  │ - 避免同步布局        │ - 使用 transform   │\n│ - Web Worker  │ - 防抖/节流           │ - 避免 Layout      │\n└───────────────┴───────────────────────┴────────────────────┘\n",[3047],{"type":24,"tag":47,"props":3048,"children":3049},{"__ignoreMap":7},[3050],{"type":29,"value":3045},{"type":24,"tag":25,"props":3052,"children":3054},{"id":3053},"测量-core-web-vitals",[3055],{"type":29,"value":3056},"测量 Core Web Vitals",{"type":24,"tag":57,"props":3058,"children":3060},{"id":3059},"使用-web-vitals-库",[3061],{"type":29,"value":3062},"使用 web-vitals 库",{"type":24,"tag":42,"props":3064,"children":3067},{"code":3065,"language":2978,"meta":7,"className":3066},"import { onCLS, onINP, onLCP } from 'web-vitals';\n\n// 基础用法\nonLCP(console.log);\nonINP(console.log);\nonCLS(console.log);\n\n// 详细报告\nfunction sendToAnalytics(metric) {\n  const body = {\n    name: metric.name,\n    value: metric.value,\n    rating: metric.rating,        // 'good' | 'needs-improvement' | 'poor'\n    delta: metric.delta,          // 与上次报告的差值\n    id: metric.id,                // 唯一标识\n    navigationType: metric.navigationType,\n    entries: metric.entries       // 相关 PerformanceEntry\n  };\n  \n  // 发送到分析服务\n  navigator.sendBeacon('/analytics', JSON.stringify(body));\n}\n\nonLCP(sendToAnalytics);\nonINP(sendToAnalytics);\nonCLS(sendToAnalytics);\n",[2980],[3068],{"type":24,"tag":47,"props":3069,"children":3070},{"__ignoreMap":7},[3071],{"type":29,"value":3065},{"type":24,"tag":57,"props":3073,"children":3075},{"id":3074},"在-vuenuxt-中集成",[3076],{"type":29,"value":3077},"在 Vue/Nuxt 中集成",{"type":24,"tag":42,"props":3079,"children":3082},{"code":3080,"language":89,"meta":7,"className":3081},"// plugins/web-vitals.client.ts\nimport { onCLS, onINP, onLCP } from 'web-vitals';\n\nexport default defineNuxtPlugin(() => {\n  // 仅在客户端运行\n  if (process.server) return;\n  \n  const sendMetric = (metric: Metric) => {\n    // 发送到你的分析服务\n    $fetch('/api/analytics/vitals', {\n      method: 'POST',\n      body: {\n        name: metric.name,\n        value: metric.value,\n        rating: metric.rating,\n        url: window.location.pathname,\n        timestamp: Date.now()\n      }\n    });\n  };\n  \n  onLCP(sendMetric);\n  onINP(sendMetric);\n  onCLS(sendMetric);\n});\n",[91],[3083],{"type":24,"tag":47,"props":3084,"children":3085},{"__ignoreMap":7},[3086],{"type":29,"value":3080},{"type":24,"tag":57,"props":3088,"children":3090},{"id":3089},"在-nextjs-中集成",[3091],{"type":29,"value":3092},"在 Next.js 中集成",{"type":24,"tag":42,"props":3094,"children":3097},{"code":3095,"language":89,"meta":7,"className":3096},"// app/components/WebVitals.tsx\n'use client';\n\nimport { useEffect } from 'react';\nimport { onCLS, onINP, onLCP } from 'web-vitals';\n\nexport function WebVitals() {\n  useEffect(() => {\n    const sendMetric = (metric) => {\n      // 使用 Next.js 的 Analytics\n      window.gtag?.('event', metric.name, {\n        value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),\n        event_label: metric.id,\n        non_interaction: true,\n      });\n    };\n\n    onLCP(sendMetric);\n    onINP(sendMetric);\n    onCLS(sendMetric);\n  }, []);\n\n  return null;\n}\n\n// app/layout.tsx\nimport { WebVitals } from './components/WebVitals';\n\nexport default function RootLayout({ children }) {\n  return (\n    \u003Chtml>\n      \u003Cbody>\n        \u003CWebVitals />\n        {children}\n      \u003C/body>\n    \u003C/html>\n  );\n}\n",[91],[3098],{"type":24,"tag":47,"props":3099,"children":3100},{"__ignoreMap":7},[3101],{"type":29,"value":3095},{"type":24,"tag":25,"props":3103,"children":3105},{"id":3104},"lcp-优化策略-2026",[3106],{"type":29,"value":3107},"LCP 优化策略 2026",{"type":24,"tag":57,"props":3109,"children":3111},{"id":3110},"lcp-元素识别",[3112],{"type":29,"value":3113},"LCP 元素识别",{"type":24,"tag":36,"props":3115,"children":3116},{},[3117],{"type":29,"value":3118},"LCP 候选元素类型：",{"type":24,"tag":397,"props":3120,"children":3121},{},[3122,3133,3151,3162,3175],{"type":24,"tag":401,"props":3123,"children":3124},{},[3125,3131],{"type":24,"tag":47,"props":3126,"children":3128},{"className":3127},[],[3129],{"type":29,"value":3130},"\u003Cimg>",{"type":29,"value":3132}," 元素",{"type":24,"tag":401,"props":3134,"children":3135},{},[3136,3142,3144,3150],{"type":24,"tag":47,"props":3137,"children":3139},{"className":3138},[],[3140],{"type":29,"value":3141},"\u003Csvg>",{"type":29,"value":3143}," 内的 ",{"type":24,"tag":47,"props":3145,"children":3147},{"className":3146},[],[3148],{"type":29,"value":3149},"\u003Cimage>",{"type":29,"value":3132},{"type":24,"tag":401,"props":3152,"children":3153},{},[3154,3160],{"type":24,"tag":47,"props":3155,"children":3157},{"className":3156},[],[3158],{"type":29,"value":3159},"\u003Cvideo>",{"type":29,"value":3161}," 元素的封面图",{"type":24,"tag":401,"props":3163,"children":3164},{},[3165,3167,3173],{"type":29,"value":3166},"通过 ",{"type":24,"tag":47,"props":3168,"children":3170},{"className":3169},[],[3171],{"type":29,"value":3172},"background-image",{"type":29,"value":3174}," 加载图片的元素",{"type":24,"tag":401,"props":3176,"children":3177},{},[3178],{"type":29,"value":3179},"包含文本节点的块级元素",{"type":24,"tag":42,"props":3181,"children":3184},{"code":3182,"language":2978,"meta":7,"className":3183},"// 获取 LCP 元素信息\nconst observer = new PerformanceObserver((list) => {\n  const entries = list.getEntries();\n  const lastEntry = entries[entries.length - 1];\n  \n  console.log('LCP 元素:', lastEntry.element);\n  console.log('LCP 时间:', lastEntry.startTime);\n  console.log('LCP 大小:', lastEntry.size);\n});\n\nobserver.observe({ type: 'largest-contentful-paint', buffered: true });\n",[2980],[3185],{"type":24,"tag":47,"props":3186,"children":3187},{"__ignoreMap":7},[3188],{"type":29,"value":3182},{"type":24,"tag":57,"props":3190,"children":3192},{"id":3191},"_2026-年-lcp-优化清单",[3193],{"type":29,"value":3194},"2026 年 LCP 优化清单",{"type":24,"tag":42,"props":3196,"children":3200},{"code":3197,"language":359,"meta":7,"className":3198},"## 资源发现优化\n- [ ] 使用 `\u003Clink rel=\"preload\">` 预加载 LCP 图片\n- [ ] 避免懒加载首屏 LCP 元素\n- [ ] 优化资源发现时间（fetchpriority=\"high\"）\n\n## 资源加载优化\n- [ ] 使用现代图片格式（AVIF > WebP > JPEG）\n- [ ] 实施响应式图片（srcset + sizes）\n- [ ] 启用 HTTP/2 或 HTTP/3\n- [ ] 优化 CDN 配置\n\n## 渲染优化\n- [ ] 减少关键 CSS\n- [ ] 避免渲染阻塞资源\n- [ ] 优化 Web 字体加载\n",[3199],"language-markdown",[3201],{"type":24,"tag":47,"props":3202,"children":3203},{"__ignoreMap":7},[3204],{"type":29,"value":3197},{"type":24,"tag":57,"props":3206,"children":3208},{"id":3207},"资源优先级提示",[3209],{"type":29,"value":3207},{"type":24,"tag":42,"props":3211,"children":3214},{"code":3212,"language":838,"meta":7,"className":3213},"\u003C!-- 2026 年推荐的资源优先级设置 -->\n\n\u003C!-- LCP 图片：最高优先级 -->\n\u003Cimg \n  src=\"/hero.webp\" \n  fetchpriority=\"high\"\n  decoding=\"async\"\n  alt=\"Hero Image\"\n/>\n\n\u003C!-- 预加载关键资源 -->\n\u003Clink rel=\"preload\" href=\"/hero.webp\" as=\"image\" fetchpriority=\"high\">\n\u003Clink rel=\"preload\" href=\"/critical.css\" as=\"style\">\n\u003Clink rel=\"preload\" href=\"/main-font.woff2\" as=\"font\" type=\"font/woff2\" crossorigin>\n\n\u003C!-- 预连接到关键域名 -->\n\u003Clink rel=\"preconnect\" href=\"https://cdn.example.com\">\n\u003Clink rel=\"dns-prefetch\" href=\"https://analytics.example.com\">\n\n\u003C!-- 降低非关键资源优先级 -->\n\u003Cimg src=\"/below-fold.webp\" fetchpriority=\"low\" loading=\"lazy\" />\n",[836],[3215],{"type":24,"tag":47,"props":3216,"children":3217},{"__ignoreMap":7},[3218],{"type":29,"value":3212},{"type":24,"tag":25,"props":3220,"children":3222},{"id":3221},"inp-优化策略-2026",[3223],{"type":29,"value":3224},"INP 优化策略 2026",{"type":24,"tag":57,"props":3226,"children":3228},{"id":3227},"识别慢交互",[3229],{"type":29,"value":3227},{"type":24,"tag":42,"props":3231,"children":3234},{"code":3232,"language":2978,"meta":7,"className":3233},"// 使用 PerformanceObserver 监控慢交互\nconst slowInteractions = [];\n\nconst observer = new PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (entry.duration > 200) { // INP 阈值\n      slowInteractions.push({\n        name: entry.name,\n        duration: entry.duration,\n        startTime: entry.startTime,\n        processingStart: entry.processingStart,\n        processingEnd: entry.processingEnd,\n        // 分解三个阶段\n        inputDelay: entry.processingStart - entry.startTime,\n        processingTime: entry.processingEnd - entry.processingStart,\n        presentationDelay: entry.duration - (entry.processingEnd - entry.startTime)\n      });\n      \n      console.warn('慢交互检测:', slowInteractions[slowInteractions.length - 1]);\n    }\n  }\n});\n\nobserver.observe({ type: 'event', buffered: true, durationThreshold: 16 });\n",[2980],[3235],{"type":24,"tag":47,"props":3236,"children":3237},{"__ignoreMap":7},[3238],{"type":29,"value":3232},{"type":24,"tag":57,"props":3240,"children":3242},{"id":3241},"优化输入延迟input-delay",[3243],{"type":29,"value":3244},"优化输入延迟（Input Delay）",{"type":24,"tag":42,"props":3246,"children":3249},{"code":3247,"language":2978,"meta":7,"className":3248},"// ❌ 长任务阻塞主线程\nfunction processLargeData(data) {\n  // 同步处理 100000 条数据\n  return data.map(item => heavyComputation(item));\n}\n\n// ✅ 使用 scheduler.yield() 拆分长任务\nasync function processLargeDataOptimized(data) {\n  const results = [];\n  const chunkSize = 1000;\n  \n  for (let i = 0; i \u003C data.length; i += chunkSize) {\n    const chunk = data.slice(i, i + chunkSize);\n    results.push(...chunk.map(item => heavyComputation(item)));\n    \n    // 让出主线程，允许处理用户交互\n    if (i + chunkSize \u003C data.length) {\n      await scheduler.yield();\n    }\n  }\n  \n  return results;\n}\n\n// ✅ 使用 requestIdleCallback 处理非关键任务\nfunction scheduleNonCriticalWork(callback) {\n  if ('requestIdleCallback' in window) {\n    requestIdleCallback(callback, { timeout: 1000 });\n  } else {\n    setTimeout(callback, 0);\n  }\n}\n",[2980],[3250],{"type":24,"tag":47,"props":3251,"children":3252},{"__ignoreMap":7},[3253],{"type":29,"value":3247},{"type":24,"tag":57,"props":3255,"children":3257},{"id":3256},"优化处理时间processing-time",[3258],{"type":29,"value":3259},"优化处理时间（Processing Time）",{"type":24,"tag":42,"props":3261,"children":3264},{"code":3262,"language":2978,"meta":7,"className":3263},"// ❌ 事件处理中包含大量同步操作\nbutton.addEventListener('click', () => {\n  // 同步数据处理\n  const result = processData(data);\n  \n  // 同步 DOM 更新\n  updateDOM(result);\n  \n  // 同步网络请求\n  fetch('/api/save', { method: 'POST', body: JSON.stringify(result) });\n});\n\n// ✅ 优化后：最小化同步操作\nbutton.addEventListener('click', async () => {\n  // 立即提供视觉反馈\n  button.disabled = true;\n  showLoadingState();\n  \n  // 使用 queueMicrotask 延迟非关键更新\n  queueMicrotask(() => {\n    // 数据处理移到微任务\n    const result = processData(data);\n    \n    // 使用 requestAnimationFrame 批量 DOM 更新\n    requestAnimationFrame(() => {\n      updateDOM(result);\n    });\n    \n    // 异步网络请求\n    fetch('/api/save', { method: 'POST', body: JSON.stringify(result) });\n  });\n});\n",[2980],[3265],{"type":24,"tag":47,"props":3266,"children":3267},{"__ignoreMap":7},[3268],{"type":29,"value":3262},{"type":24,"tag":57,"props":3270,"children":3272},{"id":3271},"优化呈现延迟presentation-delay",[3273],{"type":29,"value":3274},"优化呈现延迟（Presentation Delay）",{"type":24,"tag":42,"props":3276,"children":3279},{"code":3277,"language":2978,"meta":7,"className":3278},"// ❌ 触发强制同步布局\nelement.addEventListener('click', () => {\n  // 修改样式\n  element.style.width = '100px';\n  \n  // 立即读取布局信息 → 触发强制同步布局\n  const height = element.offsetHeight;\n  \n  // 再次修改样式 → 布局失效\n  element.style.height = height + 'px';\n});\n\n// ✅ 读写分离，使用 transform\nelement.addEventListener('click', () => {\n  // 使用 transform 避免布局计算\n  element.style.transform = 'scale(1.1)';\n  \n  // 批量读取\n  requestAnimationFrame(() => {\n    const rect = element.getBoundingClientRect();\n    // 批量写入\n    requestAnimationFrame(() => {\n      updateRelatedElements(rect);\n    });\n  });\n});\n",[2980],[3280],{"type":24,"tag":47,"props":3281,"children":3282},{"__ignoreMap":7},[3283],{"type":29,"value":3277},{"type":24,"tag":25,"props":3285,"children":3287},{"id":3286},"cls-优化策略-2026",[3288],{"type":29,"value":3289},"CLS 优化策略 2026",{"type":24,"tag":57,"props":3291,"children":3293},{"id":3292},"cls-来源分析",[3294],{"type":29,"value":3295},"CLS 来源分析",{"type":24,"tag":42,"props":3297,"children":3300},{"code":3298,"language":2978,"meta":7,"className":3299},"// 监控并分析 CLS 来源\nconst clsSources = [];\n\nconst observer = new PerformanceObserver((list) => {\n  for (const entry of list.getEntries()) {\n    if (!entry.hadRecentInput) { // 排除用户交互导致的偏移\n      for (const source of entry.sources || []) {\n        clsSources.push({\n          value: entry.value,\n          element: source.node,\n          previousRect: source.previousRect,\n          currentRect: source.currentRect\n        });\n      }\n    }\n  }\n});\n\nobserver.observe({ type: 'layout-shift', buffered: true });\n",[2980],[3301],{"type":24,"tag":47,"props":3302,"children":3303},{"__ignoreMap":7},[3304],{"type":29,"value":3298},{"type":24,"tag":57,"props":3306,"children":3308},{"id":3307},"_2026-年-cls-优化清单",[3309],{"type":29,"value":3310},"2026 年 CLS 优化清单",{"type":24,"tag":42,"props":3312,"children":3315},{"code":3313,"language":838,"meta":7,"className":3314},"\u003C!-- 1. 图片和视频预留空间 -->\n\u003Cimg \n  src=\"/image.webp\" \n  width=\"800\" \n  height=\"600\" \n  alt=\"...\"\n  style=\"aspect-ratio: 800/600;\"\n/>\n\n\u003C!-- 2. 使用 CSS aspect-ratio -->\n\u003Cstyle>\n.video-container {\n  aspect-ratio: 16/9;\n  width: 100%;\n}\n\u003C/style>\n\n\u003C!-- 3. 为动态内容预留空间 -->\n\u003Cstyle>\n.ad-slot {\n  min-height: 250px; /* 广告最小高度 */\n}\n.skeleton {\n  min-height: 200px; /* 骨架屏占位 */\n}\n\u003C/style>\n\n\u003C!-- 4. 避免在现有内容上方插入内容 -->\n\u003C!-- ❌ 错误 -->\n\u003Cdiv class=\"new-banner\" style=\"position: relative;\">新公告\u003C/div>\n\n\u003C!-- ✅ 正确：预留空间或使用 transform -->\n\u003Cdiv class=\"banner-slot\" style=\"min-height: 50px;\">\n  \u003Cdiv class=\"new-banner\">新公告\u003C/div>\n\u003C/div>\n",[836],[3316],{"type":24,"tag":47,"props":3317,"children":3318},{"__ignoreMap":7},[3319],{"type":29,"value":3313},{"type":24,"tag":25,"props":3321,"children":3323},{"id":3322},"搜索排名影响与应对策略",[3324],{"type":29,"value":3322},{"type":24,"tag":57,"props":3326,"children":3328},{"id":3327},"core-web-vitals-在排名中的权重",[3329],{"type":29,"value":3330},"Core Web Vitals 在排名中的权重",{"type":24,"tag":36,"props":3332,"children":3333},{},[3334],{"type":29,"value":3335},"根据 Google 官方说明和实际观察：",{"type":24,"tag":42,"props":3337,"children":3339},{"code":3338},"排名因素权重（估算）：\n├─ 内容相关性 ████████████████████ 40%+\n├─ 反向链接质量 ████████████ 25%\n├─ 页面体验信号\n│   ├─ HTTPS ██ 5%\n│   ├─ Mobile-Friendly ███ 7%\n│   ├─ No Intrusive Interstitials ██ 3%\n│   └─ Core Web Vitals ████ 8-10%\n└─ 其他因素 █████ 10%\n",[3340],{"type":24,"tag":47,"props":3341,"children":3342},{"__ignoreMap":7},[3343],{"type":29,"value":3338},{"type":24,"tag":36,"props":3345,"children":3346},{},[3347],{"type":24,"tag":430,"props":3348,"children":3349},{},[3350],{"type":29,"value":3351},"重要提示：",{"type":24,"tag":397,"props":3353,"children":3354},{},[3355,3360,3365],{"type":24,"tag":401,"props":3356,"children":3357},{},[3358],{"type":29,"value":3359},"Core Web Vitals 是\"入围资格\"而非\"排名加分\"",{"type":24,"tag":401,"props":3361,"children":3362},{},[3363],{"type":29,"value":3364},"当内容质量相近时，性能优势更明显",{"type":24,"tag":401,"props":3366,"children":3367},{},[3368],{"type":29,"value":3369},"差的 Core Web Vitals 可能导致排名下降",{"type":24,"tag":57,"props":3371,"children":3373},{"id":3372},"监控与预警系统",[3374],{"type":29,"value":3372},{"type":24,"tag":42,"props":3376,"children":3379},{"code":3377,"language":89,"meta":7,"className":3378},"// 建立 Core Web Vitals 监控系统\ninterface VitalsThreshold {\n  good: number;\n  needsImprovement: number;\n}\n\nconst thresholds: Record\u003Cstring, VitalsThreshold> = {\n  LCP: { good: 2500, needsImprovement: 4000 },\n  INP: { good: 200, needsImprovement: 500 },\n  CLS: { good: 0.1, needsImprovement: 0.25 }\n};\n\nfunction analyzeVitals(metrics: Record\u003Cstring, number>) {\n  const report = {};\n  \n  for (const [name, value] of Object.entries(metrics)) {\n    const threshold = thresholds[name];\n    \n    let status: 'good' | 'needs-improvement' | 'poor';\n    if (value \u003C= threshold.good) {\n      status = 'good';\n    } else if (value \u003C= threshold.needsImprovement) {\n      status = 'needs-improvement';\n    } else {\n      status = 'poor';\n    }\n    \n    report[name] = { value, status };\n    \n    // 触发告警\n    if (status === 'poor') {\n      sendAlert(`${name} 指标严重不达标: ${value}`);\n    }\n  }\n  \n  return report;\n}\n",[91],[3380],{"type":24,"tag":47,"props":3381,"children":3382},{"__ignoreMap":7},[3383],{"type":29,"value":3377},{"type":24,"tag":25,"props":3385,"children":3386},{"id":1272},[3387],{"type":29,"value":1272},{"type":24,"tag":36,"props":3389,"children":3390},{},[3391],{"type":29,"value":3392},"2026 年的 Core Web Vitals 以 INP 为核心，标志着性能评估从\"首次加载\"向\"全程交互\"的转变。",{"type":24,"tag":57,"props":3394,"children":3396},{"id":3395},"关键要点回顾",[3397],{"type":29,"value":3395},{"type":24,"tag":397,"props":3399,"children":3400},{},[3401,3413,3424,3435,3446],{"type":24,"tag":401,"props":3402,"children":3403},{},[3404,3406,3411],{"type":29,"value":3405},"✅ ",{"type":24,"tag":430,"props":3407,"children":3408},{},[3409],{"type":29,"value":3410},"INP 取代 FID",{"type":29,"value":3412},"：测量完整交互响应时间",{"type":24,"tag":401,"props":3414,"children":3415},{},[3416,3417,3422],{"type":29,"value":3405},{"type":24,"tag":430,"props":3418,"children":3419},{},[3420],{"type":29,"value":3421},"三大指标协同",{"type":29,"value":3423},"：LCP（加载）+ INP（交互）+ CLS（稳定）",{"type":24,"tag":401,"props":3425,"children":3426},{},[3427,3428,3433],{"type":29,"value":3405},{"type":24,"tag":430,"props":3429,"children":3430},{},[3431],{"type":29,"value":3432},"优化优先级",{"type":29,"value":3434},"：先保证 INP，再优化 LCP 和 CLS",{"type":24,"tag":401,"props":3436,"children":3437},{},[3438,3439,3444],{"type":29,"value":3405},{"type":24,"tag":430,"props":3440,"children":3441},{},[3442],{"type":29,"value":3443},"持续监控",{"type":29,"value":3445},"：建立实时监控和预警机制",{"type":24,"tag":401,"props":3447,"children":3448},{},[3449,3450,3455],{"type":29,"value":3405},{"type":24,"tag":430,"props":3451,"children":3452},{},[3453],{"type":29,"value":3454},"渐进优化",{"type":29,"value":3456},"：从高流量页面开始，逐步覆盖全站",{"type":24,"tag":57,"props":3458,"children":3460},{"id":3459},"立即行动",[3461],{"type":29,"value":3459},{"type":24,"tag":766,"props":3463,"children":3464},{},[3465,3470,3475,3480],{"type":24,"tag":401,"props":3466,"children":3467},{},[3468],{"type":29,"value":3469},"使用 web-vitals 库集成性能监控",{"type":24,"tag":401,"props":3471,"children":3472},{},[3473],{"type":29,"value":3474},"在 Chrome DevTools 中检查慢交互",{"type":24,"tag":401,"props":3476,"children":3477},{},[3478],{"type":29,"value":3479},"重点优化 INP > 200ms 的交互",{"type":24,"tag":401,"props":3481,"children":3482},{},[3483],{"type":29,"value":3484},"建立周期性的性能审计流程",{"type":24,"tag":25,"props":3486,"children":3488},{"id":3487},"相关资源",[3489],{"type":29,"value":3487},{"type":24,"tag":397,"props":3491,"children":3492},{},[3493,3505,3515,3525,3535],{"type":24,"tag":401,"props":3494,"children":3495},{},[3496],{"type":24,"tag":3497,"props":3498,"children":3502},"a",{"href":3499,"rel":3500},"https://web.dev/vitals/",[3501],"nofollow",[3503],{"type":29,"value":3504},"web.dev - Core Web Vitals",{"type":24,"tag":401,"props":3506,"children":3507},{},[3508],{"type":24,"tag":3497,"props":3509,"children":3512},{"href":3510,"rel":3511},"https://developer.chrome.com/docs/devtools/performance/",[3501],[3513],{"type":29,"value":3514},"Chrome DevTools - 性能分析",{"type":24,"tag":401,"props":3516,"children":3517},{},[3518],{"type":24,"tag":3497,"props":3519,"children":3522},{"href":3520,"rel":3521},"https://github.com/GoogleChrome/web-vitals",[3501],[3523],{"type":29,"value":3524},"web-vitals 库",{"type":24,"tag":401,"props":3526,"children":3527},{},[3528],{"type":24,"tag":3497,"props":3529,"children":3532},{"href":3530,"rel":3531},"https://pagespeed.web.dev/",[3501],[3533],{"type":29,"value":3534},"PageSpeed Insights",{"type":24,"tag":401,"props":3536,"children":3537},{},[3538],{"type":24,"tag":3497,"props":3539,"children":3542},{"href":3540,"rel":3541},"https://search.google.com/search-console/",[3501],[3543],{"type":29,"value":3544},"Search Console Core Web Vitals 报告",{"title":7,"searchDepth":329,"depth":329,"links":3546},[3547,3548,3552,3557,3562,3567,3573,3577,3581,3585],{"id":2671,"depth":332,"text":2674},{"id":2677,"depth":332,"text":2680,"children":3549},[3550,3551],{"id":2695,"depth":329,"text":2698},{"id":2799,"depth":329,"text":2802},{"id":2953,"depth":332,"text":2956,"children":3553},[3554,3555,3556],{"id":2959,"depth":329,"text":2962},{"id":3014,"depth":329,"text":3017},{"id":3034,"depth":329,"text":3037},{"id":3053,"depth":332,"text":3056,"children":3558},[3559,3560,3561],{"id":3059,"depth":329,"text":3062},{"id":3074,"depth":329,"text":3077},{"id":3089,"depth":329,"text":3092},{"id":3104,"depth":332,"text":3107,"children":3563},[3564,3565,3566],{"id":3110,"depth":329,"text":3113},{"id":3191,"depth":329,"text":3194},{"id":3207,"depth":329,"text":3207},{"id":3221,"depth":332,"text":3224,"children":3568},[3569,3570,3571,3572],{"id":3227,"depth":329,"text":3227},{"id":3241,"depth":329,"text":3244},{"id":3256,"depth":329,"text":3259},{"id":3271,"depth":329,"text":3274},{"id":3286,"depth":332,"text":3289,"children":3574},[3575,3576],{"id":3292,"depth":329,"text":3295},{"id":3307,"depth":329,"text":3310},{"id":3322,"depth":332,"text":3322,"children":3578},[3579,3580],{"id":3327,"depth":329,"text":3330},{"id":3372,"depth":329,"text":3372},{"id":1272,"depth":332,"text":1272,"children":3582},[3583,3584],{"id":3395,"depth":329,"text":3395},{"id":3459,"depth":329,"text":3459},{"id":3487,"depth":332,"text":3487},"content:topics:performance:core-web-vitals-2026-guide.md","topics/performance/core-web-vitals-2026-guide.md","topics/performance/core-web-vitals-2026-guide",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":3590,"image":18,"featured":6,"readingTime":19,"body":3591,"_type":359,"_id":360,"_source":361,"_file":362,"_stem":363,"_extension":364},[13,14,15,16,17],{"type":21,"children":3592,"toc":3837},[3593,3597,3601,3605,3612,3616,3620,3627,3631,3635,3639,3647,3651,3659,3663,3667,3671,3679,3683,3691,3695,3699,3703,3710,3714,3722,3726,3734,3738,3742,3750,3754,3758,3766,3770,3774,3782,3786,3790,3798,3802,3810,3814,3822,3826,3833],{"type":24,"tag":25,"props":3594,"children":3595},{"id":8},[3596],{"type":29,"value":8},{"type":24,"tag":25,"props":3598,"children":3599},{"id":32},[3600],{"type":29,"value":32},{"type":24,"tag":36,"props":3602,"children":3603},{},[3604],{"type":29,"value":40},{"type":24,"tag":42,"props":3606,"children":3607},{"code":44},[3608],{"type":24,"tag":47,"props":3609,"children":3610},{"__ignoreMap":7},[3611],{"type":29,"value":44},{"type":24,"tag":25,"props":3613,"children":3614},{"id":53},[3615],{"type":29,"value":53},{"type":24,"tag":57,"props":3617,"children":3618},{"id":59},[3619],{"type":29,"value":59},{"type":24,"tag":42,"props":3621,"children":3622},{"code":64},[3623],{"type":24,"tag":47,"props":3624,"children":3625},{"__ignoreMap":7},[3626],{"type":29,"value":64},{"type":24,"tag":25,"props":3628,"children":3629},{"id":72},[3630],{"type":29,"value":75},{"type":24,"tag":36,"props":3632,"children":3633},{},[3634],{"type":29,"value":80},{"type":24,"tag":57,"props":3636,"children":3637},{"id":83},[3638],{"type":29,"value":83},{"type":24,"tag":42,"props":3640,"children":3642},{"code":88,"language":89,"meta":7,"className":3641},[91],[3643],{"type":24,"tag":47,"props":3644,"children":3645},{"__ignoreMap":7},[3646],{"type":29,"value":88},{"type":24,"tag":57,"props":3648,"children":3649},{"id":99},[3650],{"type":29,"value":99},{"type":24,"tag":42,"props":3652,"children":3654},{"code":104,"language":89,"meta":7,"className":3653},[91],[3655],{"type":24,"tag":47,"props":3656,"children":3657},{"__ignoreMap":7},[3658],{"type":29,"value":104},{"type":24,"tag":25,"props":3660,"children":3661},{"id":113},[3662],{"type":29,"value":116},{"type":24,"tag":36,"props":3664,"children":3665},{},[3666],{"type":29,"value":121},{"type":24,"tag":57,"props":3668,"children":3669},{"id":124},[3670],{"type":29,"value":124},{"type":24,"tag":42,"props":3672,"children":3674},{"code":129,"language":89,"meta":7,"className":3673},[91],[3675],{"type":24,"tag":47,"props":3676,"children":3677},{"__ignoreMap":7},[3678],{"type":29,"value":129},{"type":24,"tag":57,"props":3680,"children":3681},{"id":138},[3682],{"type":29,"value":138},{"type":24,"tag":42,"props":3684,"children":3686},{"code":143,"language":144,"meta":7,"className":3685},[146],[3687],{"type":24,"tag":47,"props":3688,"children":3689},{"__ignoreMap":7},[3690],{"type":29,"value":143},{"type":24,"tag":25,"props":3692,"children":3693},{"id":154},[3694],{"type":29,"value":157},{"type":24,"tag":36,"props":3696,"children":3697},{},[3698],{"type":29,"value":162},{"type":24,"tag":57,"props":3700,"children":3701},{"id":165},[3702],{"type":29,"value":165},{"type":24,"tag":42,"props":3704,"children":3705},{"code":170},[3706],{"type":24,"tag":47,"props":3707,"children":3708},{"__ignoreMap":7},[3709],{"type":29,"value":170},{"type":24,"tag":57,"props":3711,"children":3712},{"id":178},[3713],{"type":29,"value":178},{"type":24,"tag":42,"props":3715,"children":3717},{"code":183,"language":89,"meta":7,"className":3716},[91],[3718],{"type":24,"tag":47,"props":3719,"children":3720},{"__ignoreMap":7},[3721],{"type":29,"value":183},{"type":24,"tag":57,"props":3723,"children":3724},{"id":192},[3725],{"type":29,"value":192},{"type":24,"tag":42,"props":3727,"children":3729},{"code":197,"language":144,"meta":7,"className":3728},[146],[3730],{"type":24,"tag":47,"props":3731,"children":3732},{"__ignoreMap":7},[3733],{"type":29,"value":197},{"type":24,"tag":57,"props":3735,"children":3736},{"id":206},[3737],{"type":29,"value":206},{"type":24,"tag":36,"props":3739,"children":3740},{},[3741],{"type":29,"value":213},{"type":24,"tag":42,"props":3743,"children":3745},{"code":216,"language":89,"meta":7,"className":3744},[91],[3746],{"type":24,"tag":47,"props":3747,"children":3748},{"__ignoreMap":7},[3749],{"type":29,"value":216},{"type":24,"tag":25,"props":3751,"children":3752},{"id":225},[3753],{"type":29,"value":228},{"type":24,"tag":36,"props":3755,"children":3756},{},[3757],{"type":29,"value":233},{"type":24,"tag":42,"props":3759,"children":3761},{"code":236,"language":89,"meta":7,"className":3760},[91],[3762],{"type":24,"tag":47,"props":3763,"children":3764},{"__ignoreMap":7},[3765],{"type":29,"value":236},{"type":24,"tag":25,"props":3767,"children":3768},{"id":245},[3769],{"type":29,"value":248},{"type":24,"tag":36,"props":3771,"children":3772},{},[3773],{"type":29,"value":253},{"type":24,"tag":42,"props":3775,"children":3777},{"code":256,"language":89,"meta":7,"className":3776},[91],[3778],{"type":24,"tag":47,"props":3779,"children":3780},{"__ignoreMap":7},[3781],{"type":29,"value":256},{"type":24,"tag":25,"props":3783,"children":3784},{"id":265},[3785],{"type":29,"value":265},{"type":24,"tag":57,"props":3787,"children":3788},{"id":270},[3789],{"type":29,"value":270},{"type":24,"tag":42,"props":3791,"children":3793},{"code":275,"language":144,"meta":7,"className":3792},[146],[3794],{"type":24,"tag":47,"props":3795,"children":3796},{"__ignoreMap":7},[3797],{"type":29,"value":275},{"type":24,"tag":57,"props":3799,"children":3800},{"id":284},[3801],{"type":29,"value":284},{"type":24,"tag":42,"props":3803,"children":3805},{"code":289,"language":144,"meta":7,"className":3804},[146],[3806],{"type":24,"tag":47,"props":3807,"children":3808},{"__ignoreMap":7},[3809],{"type":29,"value":289},{"type":24,"tag":25,"props":3811,"children":3812},{"id":298},[3813],{"type":29,"value":298},{"type":24,"tag":42,"props":3815,"children":3817},{"code":303,"language":89,"meta":7,"className":3816},[91],[3818],{"type":24,"tag":47,"props":3819,"children":3820},{"__ignoreMap":7},[3821],{"type":29,"value":303},{"type":24,"tag":25,"props":3823,"children":3824},{"id":312},[3825],{"type":29,"value":312},{"type":24,"tag":42,"props":3827,"children":3828},{"code":317},[3829],{"type":24,"tag":47,"props":3830,"children":3831},{"__ignoreMap":7},[3832],{"type":29,"value":317},{"type":24,"tag":36,"props":3834,"children":3835},{},[3836],{"type":29,"value":327},{"title":7,"searchDepth":329,"depth":329,"links":3838},[3839,3840,3841,3844,3848,3852,3858,3859,3860,3864,3865],{"id":8,"depth":332,"text":8},{"id":32,"depth":332,"text":32},{"id":53,"depth":332,"text":53,"children":3842},[3843],{"id":59,"depth":329,"text":59},{"id":72,"depth":332,"text":75,"children":3845},[3846,3847],{"id":83,"depth":329,"text":83},{"id":99,"depth":329,"text":99},{"id":113,"depth":332,"text":116,"children":3849},[3850,3851],{"id":124,"depth":329,"text":124},{"id":138,"depth":329,"text":138},{"id":154,"depth":332,"text":157,"children":3853},[3854,3855,3856,3857],{"id":165,"depth":329,"text":165},{"id":178,"depth":329,"text":178},{"id":192,"depth":329,"text":192},{"id":206,"depth":329,"text":206},{"id":225,"depth":332,"text":228},{"id":245,"depth":332,"text":248},{"id":265,"depth":332,"text":265,"children":3861},[3862,3863],{"id":270,"depth":329,"text":270},{"id":284,"depth":329,"text":284},{"id":298,"depth":332,"text":298},{"id":312,"depth":332,"text":312},1778574600654]