YouTube 多标签页优化 -- YouTube Smart Resource Booster (Dynamic & Lazy)

快如闪电!!提前预加载 YouTube 视频页的关键资源(JS/CSS/字体/懒加载图片等),并优化连接与加载顺序。LightHouse 评分由 29 提升至 35,【单标签性能】LCP 耗时 9.2 秒 ==> 3.8 秒,提升 58.7%;FCP 耗时 1.7 秒 ==> 1.1 秒 ,提升 35.3%;Speed Index 耗时 8.8 秒 ==> 7.0 秒,提升 20.5% 。

  1. // ==UserScript==
  2. // @name YouTube 多标签页优化 -- YouTube Smart Resource Booster (Dynamic & Lazy)
  3. // @namespace https://greatest.deepsurf.us/users/1171320
  4. // @description 快如闪电!!提前预加载 YouTube 视频页的关键资源(JS/CSS/字体/懒加载图片等),并优化连接与加载顺序。LightHouse 评分由 29 提升至 35,【单标签性能】LCP 耗时 9.2 秒 ==> 3.8 秒,提升 58.7%;FCP 耗时 1.7 秒 ==> 1.1 秒 ,提升 35.3%;Speed Index 耗时 8.8 秒 ==> 7.0 秒,提升 20.5% 。
  5. // @version 1.02
  6. // @match *://www.youtube.com/watch*
  7. // @author yzcjd
  8. // @author2 ChatGPT4 辅助
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. const MAX_RESOURCES = 100;
  18. const loaded = new Set();
  19. const lazyImages = new Set();
  20. const head = document.head || document.getElementsByTagName('head')[0];
  21.  
  22. // ✅ 1. CDN 提前连接
  23. const preconnectDomains = [
  24. 'https://www.youtube.com',
  25. 'https://i.ytimg.com',
  26. 'https://vi.ytimg.com',
  27. 'https://fonts.googleapis.com',
  28. 'https://fonts.gstatic.com',
  29. 'https://googleads.g.doubleclick.net',
  30. 'https://www.google.com',
  31. 'https://yt3.ggpht.com'
  32. ];
  33. preconnectDomains.forEach(domain => {
  34. ['preconnect', 'dns-prefetch'].forEach(rel => {
  35. const link = document.createElement('link');
  36. link.rel = rel;
  37. link.href = domain;
  38. if (rel === 'preconnect') link.crossOrigin = 'anonymous';
  39. head.appendChild(link);
  40. });
  41. });
  42.  
  43. // ✅ 类型判断
  44. function getAsType(url) {
  45. if (url.endsWith('.js')) return 'script';
  46. if (url.endsWith('.css')) return 'style';
  47. if (url.includes('fonts') || url.endsWith('.woff2')) return 'font';
  48. if (/\.(jpg|jpeg|png|webp)/.test(url)) return 'image';
  49. return 'fetch';
  50. }
  51.  
  52. // ✅ 通用资源 preload
  53. function preloadCommonResources() {
  54. const nodes = Array.from(document.querySelectorAll('link[rel=stylesheet], script[src], link[href*="fonts"]'));
  55. let count = 0;
  56. for (const node of nodes) {
  57. const href = node.href || node.src;
  58. if (!href || loaded.has(href)) continue;
  59. const preload = document.createElement('link');
  60. preload.rel = 'preload';
  61. preload.href = href;
  62. preload.as = getAsType(href);
  63. preload.crossOrigin = 'anonymous';
  64. if (preload.as === 'font') preload.type = 'font/woff2';
  65. head.appendChild(preload);
  66. loaded.add(href);
  67. if (++count >= MAX_RESOURCES) break;
  68. }
  69. }
  70.  
  71. // ✅ 播放器核心 preload(base.js, www-player.css)
  72. function preloadPlayerAssets() {
  73. const links = Array.from(document.querySelectorAll('script[src*="/s/player/"], link[href*="/s/player/"]'));
  74. links.forEach(link => {
  75. const href = link.src || link.href;
  76. if (!href || loaded.has(href)) return;
  77. const preload = document.createElement('link');
  78. preload.rel = 'preload';
  79. preload.href = href;
  80. preload.as = getAsType(href);
  81. preload.crossOrigin = 'anonymous';
  82. head.appendChild(preload);
  83. loaded.add(href);
  84. });
  85. }
  86.  
  87. // ✅ 提高资源优先级
  88. function elevatePriority() {
  89. document.querySelectorAll('img, iframe, video').forEach(el => {
  90. if (!el.getAttribute('fetchpriority')) {
  91. el.setAttribute('fetchpriority', 'high');
  92. }
  93. });
  94. }
  95.  
  96. // ✅ preload 封面图和推荐图(主动探测 ytimg)
  97. function preloadVideoImages() {
  98. document.querySelectorAll('img[src*="ytimg.com"]').forEach(img => {
  99. const src = img.src;
  100. if (!src || lazyImages.has(src)) return;
  101. const preload = document.createElement('link');
  102. preload.rel = 'preload';
  103. preload.as = 'image';
  104. preload.href = src;
  105. head.appendChild(preload);
  106. lazyImages.add(src);
  107. });
  108. }
  109.  
  110. // ✅ debounced 执行(避免频繁触发)
  111. function debounce(fn, delay = 300) {
  112. let timer;
  113. return () => {
  114. clearTimeout(timer);
  115. timer = setTimeout(fn, delay);
  116. };
  117. }
  118.  
  119. // ✅ 播放器加载监测(调试用,可关闭)
  120. function watchPlayerReady() {
  121. const start = performance.now();
  122. const interval = setInterval(() => {
  123. const player = document.getElementById('movie_player');
  124. if (player && typeof player.getPlayerState === 'function') {
  125. const now = performance.now();
  126. console.log('[✅ Player Ready]', (now - start).toFixed(0) + 'ms');
  127. clearInterval(interval);
  128. }
  129. }, 100);
  130. }
  131.  
  132. // ✅ 初始预加载 & DOM 观察器
  133. const runAll = () => {
  134. preloadCommonResources();
  135. preloadPlayerAssets();
  136. preloadVideoImages();
  137. elevatePriority();
  138. };
  139.  
  140. const observer = new MutationObserver(debounce(runAll, 500));
  141. observer.observe(document.documentElement, { childList: true, subtree: true });
  142.  
  143. // ✅ 初始执行 + 滚动时懒图 preload + 播放器监测
  144. runAll();
  145. window.addEventListener('scroll', () => {
  146. requestIdleCallback(preloadVideoImages, { timeout: 1000 });
  147. });
  148. watchPlayerReady(); // 可注释掉调试用日志
  149.  
  150. })();
  151.