Ctrl K
ComunidadGuías…

frontend/test/skeleton-tier-fill.test.ts

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/* 横向骨架铺到右缘:铺满,而且整批只让浏览器布局几次。
 *
 * 改过 DOM 之后再读一次宽度,浏览器就得当场把整页布局一遍。冷启动时资料页那几排骨架逐枚
 * 追加、逐枚量,实测一排一百多毫秒。这里用桩出来的几何数「改了又读」发生了几次。 */
import { afterEach, describe, expect, it } from 'vitest';

import { fillSkeletonTier, fitSkeleton } from '../src/ui-kit';

const ROW = 1000;
const SLOT = 100;
let layouts = 0;
let dirty = false;
const rect = (left: number, right: number) => ({ left, right, top: 0, bottom: 20, width: right - left, height: 20 });
const original = HTMLElement.prototype.getBoundingClientRect;

function row(kind: string): HTMLElement {
  const node = document.createElement('div');
  node.dataset.skeletonTier = kind;
  const read = () => { if (dirty) { layouts++; dirty = false; } };
  Object.defineProperty(node, 'clientWidth', { get: () => { read(); return ROW; } });
  Object.defineProperty(node, 'scrollWidth', { get: () => { read(); return Math.max(ROW, node.children.length * SLOT); } });
  const insert = node.insertAdjacentHTML.bind(node);
  node.insertAdjacentHTML = (where: InsertPosition, html: string) => { dirty = true; insert(where, html); };
  return node;
}

HTMLElement.prototype.getBoundingClientRect = function getBoundingClientRect(this: HTMLElement) {
  const parent = this.parentElement;
  if (this.dataset.skeletonTier) { if (dirty) { layouts++; dirty = false; } return rect(0, ROW) as DOMRect; }
  if (parent?.dataset.skeletonTier) {
    if (dirty) { layouts++; dirty = false; }
    const index = [...parent.children].indexOf(this);
    return rect(index * SLOT, (index + 1) * SLOT) as DOMRect;
  }
  return original.call(this);
};

afterEach(() => { layouts = 0; dirty = false; document.body.replaceChildren(); });

describe('横向骨架补满', () => {
  it('几排一起补:每排都越过右缘,整批只布局两次', () => {
    const root = document.createElement('div');
    const rows = [row('av'), row('brandpill'), row('pill')];
    root.append(...rows);
    document.body.append(root);
    fitSkeleton(root);
    for (const node of rows) expect(node.children.length * SLOT).toBeGreaterThan(ROW);
    expect(layouts).toBe(2);
  });

  it('已经铺满的那排再补一次不追加', () => {
    const root = document.createElement('div');
    const node = row('pill');
    root.append(node);
    document.body.append(root);
    fitSkeleton(root);
    const filled = node.children.length;
    fitSkeleton(root);
    expect(node.children.length).toBe(filled);
  });

  it('单独补一排也越过右缘,多出来的不超过一轮宽度序列', () => {
    const node = row('av');
    document.body.append(node);
    fillSkeletonTier(node, 'av');
    expect(node.children.length * SLOT).toBeGreaterThan(ROW);
    expect(node.children.length).toBeLessThanOrEqual(ROW / SLOT + 2);
  });
});