add-lazy-load.js 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/usr/bin/env node
  2. /**
  3. * E 批:图片 lazy-load 治理
  4. * 在长列表页的 <image> 标签上补 lazy-load(若尚未有 lazy-load 属性)
  5. *
  6. * 策略:
  7. * - 对 <image ...> 不含 lazy-load 的标签补上 lazy-load
  8. * - 跳过已有 lazy-load 的标签
  9. * - 仅处理已识别的长列表页(discover / article-center / shop / activity / growth 等)
  10. */
  11. const fs = require('fs')
  12. const path = require('path')
  13. const ROOT = path.resolve(__dirname, '..')
  14. const EXCLUDE_DIRS = new Set(['node_modules', 'dist', 'unpackage', '.git', 'docs'])
  15. // 需要治理的长列表页面/组件(按父目录分组,walk 时过滤)
  16. const TARGET_PATTERNS = [
  17. 'pages/discover/',
  18. 'pages/article-center/',
  19. 'pages/shop/',
  20. 'pages/activity/',
  21. 'pages/growth/',
  22. 'pages/promotion/',
  23. 'components/DimensionActivities.vue',
  24. 'components/DimensionProducts.vue',
  25. 'components/RecommendedFeed.vue',
  26. 'components/ArticleBookshelf.vue',
  27. 'components/FamilyFeed.vue',
  28. 'components/daily-task-card.vue',
  29. ]
  30. // 模式:找到 <image ...> 中不含 lazy-load 的,加上 lazy-load
  31. // 匹配:<image ...attrs...>(不含 lazy-load),在后面某个位置补
  32. const IMG_TAG_RE = /<image\s([^>]*)>/gi
  33. function processFile(filePath) {
  34. const src = fs.readFileSync(filePath, 'utf8')
  35. // 只处理包含长列表 image 的文件(启发式:有 v-for 或 list/data 字样的文件优先)
  36. const hasLoop = /v-for\s*=/.test(src) || /v_loop/.test(src)
  37. if (!hasLoop) return { changed: false, count: 0 }
  38. let result = src
  39. let count = 0
  40. result = result.replace(IMG_TAG_RE, (fullMatch, attrs) => {
  41. if (/lazy-load/.test(attrs)) return fullMatch // 已有 lazy-load,跳过
  42. // 在标签闭合 `>`(可能为 `/>`)前插入 lazy-load
  43. if (fullMatch.endsWith('/>')) {
  44. return fullMatch.slice(0, -2) + ' lazy-load />'
  45. }
  46. return fullMatch.slice(0, -1) + ' lazy-load>'
  47. })
  48. count = (result.match(/lazy-load/g) || []).length - (src.match(/lazy-load/g) || []).length
  49. if (count > 0) {
  50. fs.writeFileSync(filePath, result, 'utf8')
  51. }
  52. return { changed: count > 0, count }
  53. }
  54. function walk(dir, out = []) {
  55. for (const name of fs.readdirSync(dir)) {
  56. if (EXCLUDE_DIRS.has(name)) continue
  57. const p = path.join(dir, name)
  58. const st = fs.statSync(p)
  59. if (st.isDirectory()) walk(p, out)
  60. else if (name.endsWith('.vue')) out.push(p)
  61. }
  62. return out
  63. }
  64. function main() {
  65. const allFiles = walk(ROOT)
  66. const targets = allFiles.filter(f => {
  67. const rel = path.relative(ROOT, f).split(path.sep).join('/')
  68. return TARGET_PATTERNS.some(p => rel.startsWith(p))
  69. })
  70. let grandTotal = 0
  71. const log = []
  72. for (const f of targets) {
  73. const rel = path.relative(ROOT, f)
  74. const r = processFile(f)
  75. if (r.changed) {
  76. grandTotal += r.count
  77. log.push(`${r.count.toString().padStart(3)} ${rel}`)
  78. }
  79. }
  80. console.log(`\n=== E 批 lazy-load 补全完成 ===`)
  81. console.log(`新增 lazy-load: ${grandTotal} 处`)
  82. log.sort((a, b) => parseInt(b.split(' ')[0]) - parseInt(a.split(' ')[0]))
  83. for (const line of log.slice(0, 30)) console.log(line)
  84. }
  85. main()