Pretext.js: ไลบรารี 15KB เร่งความเร็วการจัดวางข้อความ 500 เท่า

Ashley Innocent

Ashley Innocent

31 March 2026

Pretext.js: ไลบรารี 15KB เร่งความเร็วการจัดวางข้อความ 500 เท่า

Apidog สำหรับองค์กร

การติดตั้งแบบ On-Premises

SSO & RBAC

รองรับมาตรฐาน SOC 2

สำรวจ Apidog Enterprise

สรุปสั้นๆ (TL;DR)

Pretext.js เป็นไลบรารี TypeScript ที่ไม่มีการพึ่งพาใดๆ ซึ่งใช้ในการวัดและจัดตำแหน่งข้อความหลายบรรทัดด้วยการคำนวณทางคณิตศาสตร์ล้วนๆ แทนที่จะใช้การดำเนินการของ DOM มันช่วยขจัดปัญหาการจัดเรียงเลย์เอาต์ (reflow) แบบซิงโครนัสโดยไม่จำเป็น, ให้การวัดข้อความเร็วกว่า getBoundingClientRect() ประมาณ 500 เท่า และรองรับระบบการเขียนหลักๆ ทั่วโลก หากคุณสร้าง Virtual Scroller, Chat UI หรือ Data Grid ไลบรารีนี้ช่วยแก้ปัญหาที่เบราว์เซอร์ละเลยมา 30 ปี

บทนำ

ทุกครั้งที่ JavaScript ของคุณเรียกใช้ getBoundingClientRect() หรืออ่านค่า offsetHeight เบราว์เซอร์จะหยุดทุกอย่าง มันจะล้างการเปลี่ยนแปลงสไตล์ที่รอดำเนินการ, คำนวณเลย์เอาต์ใหม่ และบังคับให้มีการเรนเดอร์ใหม่ทั้งหมด สิ่งนี้เรียกว่า Forced Synchronous Reflow และเป็นการดำเนินการที่มีค่าใช้จ่ายสูงที่สุดที่เบราว์เซอร์สามารถทำได้

ลองคูณสิ่งนี้ด้วยฟองข้อความแชท 1,000 อันในรายการเสมือน (virtual list) หรือ 10,000 แถวในตารางข้อมูล (data grid) ผลลัพธ์คืออะไร? เฟรมตก, กระตุก และผู้ใช้ที่คิดว่าแอปของคุณเสีย

💡
ทีม Apidog ที่สร้างส่วนหน้า (frontend) ที่ขับเคลื่อนด้วย API เข้าใจความเจ็บปวดนี้เป็นอย่างดี; การสตรีมข้อมูลการตอบกลับเข้าสู่ UI แบบไดนามิกในขณะที่ยังคงความราบรื่นอยู่เสมอนั้นเป็นการต่อสู้ที่ไม่สิ้นสุด เมื่อเอนจินเลย์เอาต์ของคุณต่อต้านคุณในทุกขั้นตอน
button

เฉิง โหลว (Cheng Lou) ผู้พัฒนาเบื้องหลัง react-motion (ดาว GitHub กว่า 21,700 ดวง) และผู้มีส่วนร่วมหลักของ React และ ReasonML ที่ Meta ได้สร้าง Pretext.js ขึ้นมาเพื่อแก้ไขปัญหานี้ ไลบรารีนี้เปิดตัวในเดือนมีนาคม 2026 ได้รับดาว GitHub กว่า 14,000 ดวงภายในไม่กี่วัน และจุดประกายให้เกิดการสนทนาครั้งใหญ่ที่สุดครั้งหนึ่งใน Hacker News ของปีนั้น

บทความนี้จะอธิบายว่า Pretext.js ทำอะไร, ทำงานอย่างไรเบื้องหลัง, เมื่อไหร่ที่คุณควรใช้มัน และข้อจำกัดของมัน คุณจะได้รับความรู้ว่าไลบรารีนี้เหมาะสมกับเทคโนโลยีที่คุณใช้หรือไม่

Pretext.js คืออะไร?

Pretext.js คือเอนจินจัดเรียงข้อความ (text layout engine) ที่เขียนด้วย JavaScript/TypeScript ล้วนๆ มันวัดและจัดตำแหน่งข้อความหลายบรรทัดทั้งหมดโดยใช้การคำนวณทางคณิตศาสตร์; ไม่มี getBoundingClientRect(), ไม่มี offsetHeight, ไม่มี reflow, ไม่มี thrashing

แนวคิดหลักนั้นเรียบง่าย แทนที่จะถามเบราว์เซอร์ว่า “ข้อความนี้สูงเท่าไหร่?” (ซึ่งบังคับให้เบราว์เซอร์ต้องเรนเดอร์ก่อน) Pretext.js จะคำนวณคำตอบทางคณิตศาสตร์โดยใช้ข้อมูลเมตริกของฟอนต์จาก Canvas API

นี่คือ API ทั้งหมด:

import { prepare, layout } from '@chenglou/pretext';

// ขั้นตอนที่ 1: เตรียมข้อความ (ครั้งเดียว, สามารถแคชได้)
const handle = prepare('Hello, pretext.js', '16px "Inter"');

// ขั้นตอนที่ 2: จัดเรียงเลย์เอาต์ที่ความกว้างใดก็ได้ (คณิตศาสตร์ล้วนๆ, ระดับไมโครวินาที)
const { height, lineCount } = layout(handle, 400, 24);

แค่นั้นแหละ สองฟังก์ชัน ฟังก์ชันหนึ่งวัดส่วนของข้อความและแคชไว้ อีกฟังก์ชันหนึ่งทำการคำนวณเพื่อหาเลย์เอาต์ การเรียกใช้ prepare() เป็นการดำเนินการเดียวที่เกี่ยวข้องกับเบราว์เซอร์ (ผ่าน Canvas measureText()) หลังจากนั้น layout() เป็นการคำนวณทางคณิตศาสตร์ล้วนๆ

ทำไมสิ่งนี้ถึงสำคัญสำหรับแอปพลิเคชันที่ใช้ API จำนวนมาก

หากคุณกำลังสร้างแอปพลิเคชันที่ต้องใช้การตอบกลับของ API แบบสตรีมมิ่ง; เช่น ผู้ช่วย AI, แดชบอร์ดแบบเรียลไทม์ หรือโปรแกรมแก้ไขเอกสารร่วมกัน; คุณจำเป็นต้องทราบความสูงของข้อความที่เข้ามา ก่อนที่คุณจะเรนเดอร์มัน หากไม่มีสิ่งนี้ Virtual Scroller ของคุณจะกระตุก, Chat UI ของคุณจะกระโดด และผู้ใช้ของคุณจะสังเกตเห็นได้

Pretext.js จะให้ความสูงนั้นแก่คุณในระดับไมโครวินาที แทนที่จะเป็นมิลลิวินาที ความแตกต่างนี้จะเพิ่มขึ้นอย่างรวดเร็ว

ปัญหาที่ Pretext.js แก้ไข

เพื่อให้เข้าใจว่าทำไมไลบรารีนี้จึงมีอยู่ คุณต้องเข้าใจสิ่งที่เกิดขึ้นเมื่อ JavaScript อ่านคุณสมบัติเลย์เอาต์

คำอธิบายเกี่ยวกับ Forced Synchronous Reflow

เมื่อคุณเขียนโค้ดนี้:

const elements = document.querySelectorAll('.text-block');
elements.forEach(el => {
  const height = el.getBoundingClientRect().height; // REFLOW!
  // ใช้ความสูงในการจัดตำแหน่ง...
});

การเรียกใช้ getBoundingClientRect() แต่ละครั้งจะบังคับให้เบราว์เซอร์ต้อง:

  1. หยุดการทำงานของ JavaScript ชั่วคราว
  2. ล้างการเปลี่ยนแปลงสไตล์ที่รอดำเนินการทั้งหมด
  3. คำนวณเลย์เอาต์ใหม่สำหรับเอกสารทั้งหมด (หรือ subtree)
  4. ส่งคืนค่าที่คำนวณได้

สิ่งนี้เรียกว่า “layout thrashing” ในการวนลูปเพื่อวัดองค์ประกอบ 1,000 รายการ เบราว์เซอร์จะทำการคำนวณเลย์เอาต์ใหม่ทั้งหมด 1,000 ครั้ง มีค่าใช้จ่ายเท่าไหร่? ประมาณ 94 มิลลิวินาที ซึ่งหมายถึงเฟรมตก 6 เฟรมที่ 60fps

ปัญหาของ Virtual Scrolling

ไลบรารี Virtual Scrolling (เช่น react-window หรือ tanstack-virtual) จำเป็นต้องทราบความสูงของแต่ละรายการเพื่อคำนวณตำแหน่งการเลื่อน สำหรับรายการที่มีความสูงคงที่ สิ่งนี้เป็นเรื่องง่าย แต่สำหรับเนื้อหาข้อความที่มีความสูงแปรผัน มันคือฝันร้าย

วิธีแก้ปัญหามาตรฐานคือการเรนเดอร์รายการนอกหน้าจอ, วัดขนาด แล้วจึงจัดตำแหน่ง วิธีนี้ได้ผล แต่ขัดกับวัตถุประสงค์ของ Virtual Scrolling; คุณกำลังเรนเดอร์โหนด DOM ที่คุณพยายามหลีกเลี่ยงการเรนเดอร์

ไลบรารีบางตัวประมาณความสูงและแก้ไขหลังจากเรนเดอร์ ซึ่งทำให้เกิดการกระโดดที่มองเห็นได้ ส่วนไลบรารีอื่นๆ บังคับให้นักพัฒนาต้องระบุความสูงคงที่ ซึ่งจำกัดสิ่งที่คุณสามารถแสดงได้

Pretext.js ขจัดวิธีแก้ปัญหาประเภทนี้ทั้งหมด คุณจะคำนวณความสูงของข้อความที่แน่นอนก่อนที่โหนด DOM ใดๆ จะถูกสร้างขึ้น

ตัวเลขจริง

Pretext.js ได้เผยแพร่ผลการทดสอบประสิทธิภาพบนเว็บไซต์ของพวกเขา:

วิธีการ ข้อความ 1,000 บล็อก ข้อความ 500 บล็อก
DOM (getBoundingClientRect) ~94ms (6 เฟรมตก) ~47ms
Pretext.js (layout()) ~2ms ~0.09ms
ความแตกต่างด้านความเร็ว เร็วกว่า ~47 เท่า เร็วกว่า ~500 เท่า

การปรับปรุงความเร็วจะเห็นได้ชัดเจนยิ่งขึ้นกับชุดข้อมูลขนาดเล็ก เนื่องจากโอเวอร์เฮดต่อการเรียกใช้ของการวัด DOM ยังคงที่ ในขณะที่ค่าใช้จ่ายในการคำนวณทางคณิตศาสตร์ของ Pretext จะเพิ่มขึ้นตามสัดส่วนโดยตรง

Pretext.js ทำงานอย่างไรเบื้องหลัง

ไลบรารีนี้ทำงานในสามขั้นตอนที่แตกต่างกัน การทำความเข้าใจสิ่งเหล่านี้จะช่วยให้คุณปรับปรุงการใช้งานให้เหมาะสมที่สุด

ขั้นตอนที่ 1: การแบ่งส่วนข้อความ (Text Segmentation)

เมื่อคุณเรียกใช้ prepare() Pretext.js จะทำการปรับปรุงข้อความที่คุณป้อนให้เป็นมาตรฐานก่อน มันจะจัดการช่องว่าง, ใช้กฎการขึ้นบรรทัดใหม่ของ Unicode (UAX #14) และแบ่งส่วนข้อความออกเป็นหน่วยที่สามารถขึ้นบรรทัดใหม่ได้

นี่คือจุดที่การรองรับหลายภาษาเข้ามาเกี่ยวข้อง เอนจินการแบ่งส่วนข้อความจะจัดการได้อย่างถูกต้องสำหรับ:

ขั้นตอนที่ 2: การวัดด้วย Canvas

หลังจากการแบ่งส่วนข้อความ Pretext.js จะส่งแต่ละส่วนผ่าน Canvas measureText() API นี่เป็นการเรียกใช้เบราว์เซอร์เพียงครั้งเดียวที่ไลบรารีนี้ทำ และมันรวดเร็วเนื่องจากการวัดข้อความของ Canvas ไม่ได้กระตุ้นให้เกิด layout reflow

// ภายใน: Pretext วัดข้อความอย่างไร
const ctx = offscreenCanvas.getContext('2d');
ctx.font = '16px "Inter"';
const metrics = ctx.measureText('Hello'); // ไม่มี reflow!
const width = metrics.width; // Glyph advance width

การวัดจะถูกแคชตามส่วนของข้อความและการรวมฟอนต์ หากคุณเรียกใช้ prepare() ด้วยข้อความและฟอนต์เดียวกันสองครั้ง การเรียกใช้ครั้งที่สองจะใช้ข้อมูลที่แคชไว้ซ้ำ

ขั้นตอนที่ 3: การจัดเรียงเลย์เอาต์ด้วยการคำนวณล้วนๆ

ฟังก์ชัน layout() จะรับความกว้างของส่วนข้อความที่แคชไว้และความกว้างของคอนเทนเนอร์ จากนั้นจะคำนวณการขึ้นบรรทัดใหม่โดยใช้อัลกอริทึมแบบ greedy:

  1. รวมความกว้างของส่วนข้อความจนกว่าผลรวมจะเกินความกว้างของคอนเทนเนอร์
  2. ขึ้นบรรทัดใหม่
  3. ทำซ้ำจนกว่าทุกส่วนจะถูกจัดวาง
  4. คูณจำนวนบรรทัดด้วย line-height เพื่อให้ได้ความสูงทั้งหมด

ไม่มี DOM. ไม่มี Canvas. มีแต่การบวกและการเปรียบเทียบล้วนๆ

นี่คือเหตุผลที่ layout() รวดเร็วมาก; มันกำลังทำการคำนวณแบบเดียวกับที่คุณเขียนลงบนกระดาษโดยใช้ไม้บรรทัดและเครื่องคิดเลข

รูปแบบ Reusable Handle

หนึ่งในการตัดสินใจออกแบบที่ดีที่สุดใน Pretext.js คือ prepare() จะส่งคืน handle ที่สามารถนำกลับมาใช้ใหม่ได้ การเรียกใช้ prepare() เพียงครั้งเดียวสามารถใช้ได้กับความกว้างของคอนเทนเนอร์ทุกขนาด:

const handle = prepare(longArticleText, '16px "Inter"');

// คำนวณความสูงสำหรับมือถือ, แท็บเล็ต และเดสก์ท็อปในระดับไมโครวินาที
const mobile = layout(handle, 375, 24);   // { height: 2400, lineCount: 100 }
const tablet = layout(handle, 768, 24);   // { height: 1200, lineCount: 50 }
const desktop = layout(handle, 1200, 24); // { height: 720, lineCount: 30 }

รูปแบบนี้สมบูรณ์แบบสำหรับการคำนวณการออกแบบที่ตอบสนอง (responsive design) คุณวัดครั้งเดียวและสามารถจัดเรียงเลย์เอาต์ที่ความกว้างใดก็ได้ทันที

กรณีการใช้งานจริง

1. Virtual Scrolling พร้อมข้อความที่มีความสูงแปรผัน

นี่คือกรณีการใช้งานหลัก นี่คือวิธีที่คุณจะผสานรวม Pretext.js เข้ากับ Virtual Scroller:

import { prepare, layout } from '@chenglou/pretext';

interface TextItem {
  id: string;
  content: string;
}

function computeHeights(items: TextItem[], containerWidth: number) {
  return items.map(item => {
    const handle = prepare(item.content, '14px "Inter"');
    const { height } = layout(handle, containerWidth, 20);
    return { id: item.id, height: height + 32 }; // +32 สำหรับระยะห่าง (padding)
  });
}

// รายการ 10,000 รายการถูกวัดในเวลาประมาณ ~4ms
const heights = computeHeights(chatMessages, 600);

ไม่มีการเรนเดอร์นอกหน้าจอ ไม่มีการประมาณความสูง ไม่มีการกระโดดที่มองเห็นได้เมื่อรายการเลื่อนเข้ามาในมุมมอง

2. ส่วนต่อประสานแชท AI (AI Chat Interfaces)

ผู้ช่วย AI จะสตรีมการตอบกลับทีละโทเค็น โทเค็นใหม่แต่ละตัวสามารถเปลี่ยนจำนวนบรรทัดได้ ทำให้ทุกอย่างที่อยู่ด้านล่างเลื่อนตำแหน่ง ด้วยการวัด DOM แบบดั้งเดิม การอัปเดตโทเค็นทุกครั้งจะกระตุ้นให้เกิด reflow

ด้วย Pretext.js คุณสามารถคำนวณความสูงใหม่หลังจากแต่ละส่วนข้อมูลมาถึง โดยไม่ต้องยุ่งกับ DOM:

let streamedText = '';
const font = '15px "SF Pro"';

socket.on('token', (token: string) => {
  streamedText += token;
  const handle = prepare(streamedText, font);
  const { height } = layout(handle, bubbleWidth, 22);
  
  // อัปเดตตำแหน่ง Virtual Scroller โดยไม่ต้องวัด DOM
  scroller.updateItemHeight(messageId, height + padding);
});

3. ตารางข้อมูล (Data Grids) ที่มีคอลัมน์ข้อความ

แอปพลิเคชันสไตล์สเปรดชีตต้องการการปรับขนาดคอลัมน์อัตโนมัติ การวัดค่าเซลล์หลายพันรายการผ่าน DOM มีค่าใช้จ่ายสูง Pretext.js ทำให้มันรวดเร็วขึ้น:

function computeColumnWidth(values: string[], font: string, padding: number) {
  let maxWidth = 0;
  for (const value of values) {
    const handle = prepare(value, font);
    // จัดเรียงเลย์เอาต์ด้วยความกว้างไม่จำกัด = บรรทัดเดียว = ความกว้างข้อความตามธรรมชาติ
    const { height } = layout(handle, Infinity, 20);
    // ใช้การติดตามความกว้างภายในของ handle สำหรับการปรับขนาดคอลัมน์
    maxWidth = Math.max(maxWidth, /* computed width */);
  }
  return maxWidth + padding;
}

4. ฟีดเนื้อหาหลายภาษา

ฟีดโซเชียลมีเดียที่มีเนื้อหาหลายภาษาปะปนกัน (โพสต์ภาษาจีนตามด้วยการตอบกลับภาษาอาหรับ ตามด้วยความคิดเห็นภาษาเกาหลี) เป็นเรื่องที่ยากในการทำให้เป็น Virtualize เนื่องจากแต่ละภาษาการเขียนมีกฎการขึ้นบรรทัดใหม่ที่แตกต่างกัน

Pretext.js จัดการทั้งหมดได้ด้วย API เดียวกัน:

const posts = [
  { text: 'This library changed everything', lang: 'en' },
  { text: 'RTL text with correct bidirectional layout', lang: 'ar' },
  { text: 'CJK text gets proper character-level breaks', lang: 'zh' },
];

// API เดียวกัน, ให้ผลลัพธ์ที่ถูกต้องสำหรับทุกภาษา
posts.forEach(post => {
  const handle = prepare(post.text, '16px system-ui');
  const { height } = layout(handle, 400, 24);
});

การทดสอบเลย์เอาต์ข้อความของคุณด้วย Apidog

เมื่อคุณกำลังสร้าง UI ที่เน้นข้อความซึ่งขับเคลื่อนด้วย API การจัดเลย์เอาต์ให้ถูกต้องเป็นเพียงครึ่งหนึ่งของการต่อสู้เท่านั้น คุณยังต้องตรวจสอบการตอบกลับของ API ที่ส่งไปยังส่วนประกอบข้อความของคุณว่าส่งมอบข้อมูลที่ถูกต้อง ในรูปแบบที่ถูกต้อง ด้วยความเร็วที่เหมาะสม

Apidog ทำให้สิ่งนี้ง่ายดาย คุณสามารถจำลองการตอบกลับของ API แบบสตรีมมิ่งเพื่อทดสอบว่าการผสานรวม Pretext.js ของคุณจัดการการโหลดข้อความแบบต่อเนื่องได้อย่างไร ตั้งค่าสถานการณ์ทดสอบด้วยความยาวข้อความ ภาษา และกรณีขอบ Unicode ที่แตกต่างกัน จากนั้นตรวจสอบว่า Virtual Scroller ของคุณทำงานได้อย่างถูกต้องก่อนที่จะนำไปใช้งานจริง

สำหรับทีมที่สร้างผลิตภัณฑ์แชท AI สภาพแวดล้อมการทดสอบ API ของ Apidog ช่วยให้คุณสามารถ:

สิ่งนี้สำคัญเนื่องจากไลบรารีการจัดเรียงข้อความจะดีได้ก็ต่อเมื่อข้อมูลที่ไหลเข้าสู่มันดีเท่านั้น การตอบกลับของ API ที่ไม่ถูกต้องจะสร้างเลย์เอาต์ที่ไม่ถูกต้อง ไม่ว่าเอนจินการวัดของคุณจะทำงานเร็วแค่ไหนก็ตาม

button

ข้อจำกัดและข้อวิจารณ์ที่ทราบ

Pretext.js ไม่ได้สมบูรณ์แบบ การสนทนาใน Hacker News ได้เผยให้เห็นข้อกังวลที่สมเหตุสมผลหลายประการที่คุณควรรู้ก่อนนำไปใช้

กรณีขอบของความแม่นยำในการเรนเดอร์

ผู้ใช้หลายรายรายงานว่าข้อความเกินขอบกล่อง (bounding box) ในการสาธิตของ Safari และ Chrome การคำนวณของไลบรารีอาจแตกต่างจากการจัดเรียงเลย์เอาต์ดั้งเดิมของเบราว์เซอร์ในสถานการณ์เฉพาะบางอย่าง:

กรณีขอบเหล่านี้มีความสำคัญน้อยกว่าสำหรับ Virtual Scrolling (ซึ่งข้อผิดพลาดไม่กี่พิกเซลไม่สามารถมองเห็นได้) แต่มีความสำคัญมากกว่าสำหรับการจัดพิมพ์ที่ต้องการความแม่นยำระดับพิกเซล

การวัดด้วย Canvas ไม่ใช่ไม่มีค่าใช้จ่าย

การเรียกใช้ prepare() ยังคงเข้าถึงเอนจินข้อความของ Canvas ในเบราว์เซอร์ สำหรับแอปพลิเคชันที่สร้าง handle ของ prepare() ที่ไม่ซ้ำกันหลายพันรายการต่อเฟรม สิ่งนี้อาจกลายเป็นคอขวดได้ วิธีแก้ปัญหาคือการแคชและการจัดกลุ่ม แต่ไลบรารีไม่ได้บังคับใช้สิ่งใดสิ่งหนึ่ง

ไม่รองรับคุณสมบัติ CSS

Pretext.js วัดข้อความดิบด้วยการระบุฟอนต์ มันไม่ได้พิจารณาคุณสมบัติ CSS ที่ส่งผลต่อเลย์เอาต์ดังต่อไปนี้:

หากสไตล์ข้อความของคุณอาศัยคุณสมบัติ CSS เหล่านี้ ความสูงที่คำนวณได้จะไม่ตรงกับสิ่งที่เบราว์เซอร์เรนเดอร์ คุณจะต้องพิจารณาสิ่งเหล่านี้ด้วยตนเอง หรือยอมรับความแตกต่าง

มันไม่ได้เข้ามาแทนที่การเรนเดอร์ DOM

Pretext.js บอกคุณว่าข้อความจะสูงเท่าไหร่ มันไม่ได้เรนเดอร์ข้อความให้คุณ คุณยังคงต้องการโหนด DOM (หรือการเรนเดอร์ Canvas/SVG) เพื่อแสดงข้อความ คุณค่าของไลบรารีนี้อยู่ในขั้นตอนการวัด ไม่ใช่ขั้นตอนการเรนเดอร์

Pretext.js เทียบกับวิธีการแบบดั้งเดิม

คุณสมบัติ Pretext.js การวัด DOM ความสูงโดยประมาณ
ความเร็ว (1,000 รายการ) ~2ms ~94ms ~0ms (ไม่มีการวัด)
ความแม่นยำ สูง (อิง Canvas) สมบูรณ์แบบ (ความจริงพื้นฐาน) ต่ำ (อาศัยการคาดคะเน)
การพึ่งพา DOM ไม่มีหลังจาก prepare() เต็มรูปแบบ ไม่มี
ตัวกระตุ้น Reflow ศูนย์ หนึ่งครั้งต่อการวัด ศูนย์
หลายภาษา รองรับ Unicode เต็มรูปแบบ เต็มรูปแบบ (เบราว์เซอร์พื้นฐาน) แย่ (อัตราส่วนที่กำหนดไว้ตายตัว)
รองรับคุณสมบัติ CSS จำกัด (เฉพาะฟอนต์) เต็มรูปแบบ ไม่มี
โอเวอร์เฮดหน่วยความจำ ส่วนข้อความที่แคช โหนด DOM น้อยที่สุด
เลย์เอาต์ที่ตอบสนอง หนึ่ง prepare(), หลาย layout() วัดใหม่ทุกความกว้าง ประมาณใหม่ทุกความกว้าง

การเลือกที่ถูกต้องขึ้นอยู่กับข้อจำกัดของคุณ หากคุณต้องการความแม่นยำระดับพิกเซลและรองรับคุณสมบัติ CSS การวัด DOM ก็ยังคงเป็นความจริงพื้นฐาน หากคุณต้องการความเร็วในการทำงานกับรายการหลายพันรายการและยอมรับความแตกต่างระดับซับพิกเซลเล็กน้อยได้ Pretext.js จะเป็นผู้ชนะอย่างขาดลอย

เริ่มต้นใช้งาน

การติดตั้ง

npm install @chenglou/pretext
# or
pnpm add @chenglou/pretext
# or
bun add @chenglou/pretext

การใช้งานพื้นฐาน

import { prepare, layout } from '@chenglou/pretext';

// วัดย่อหน้า
const handle = prepare(
  'Pretext.js computes text layout without touching the DOM.',
  '16px "Inter"'
);

// รับความสูงที่ความกว้างคอนเทนเนอร์ที่กำหนด
const result = layout(handle, 600, 24);
console.log(result.height);    // เช่น 48 (2 บรรทัด x 24px)
console.log(result.lineCount); // เช่น 2

การผสานรวมกับ React

import { prepare, layout } from '@chenglou/pretext';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useMemo, useRef } from 'react';

function VirtualChat({ messages }: { messages: string[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const containerWidth = 600;
  const font = '14px "Inter"';
  const lineHeight = 20;

  const heights = useMemo(() => {
    return messages.map(msg => {
      const handle = prepare(msg, font);
      const { height } = layout(handle, containerWidth, lineHeight);
      return height + 24; // ระยะห่าง (padding)
    });
  }, [messages]);

  const virtualizer = useVirtualizer({
    count: messages.length,
    getScrollElement: () => parentRef.current,
    estimateSize: (index) => heights[index],
  });

  return (
    <div ref={parentRef} style={{ height: '100vh', overflow: 'auto' }}>
      <div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
        {virtualizer.getVirtualItems().map(virtualRow => (
          <div
            key={virtualRow.key}
            style={{
              position: 'absolute',
              top: virtualRow.start,
              width: containerWidth,
            }}
          >
            {messages[virtualRow.index]}
          </div>
        ))}
      </div>
    </div>
  );
}

สิ่งนี้จะช่วยให้คุณมีแชทเสมือน (virtual chat) ที่มีความสูงของรายการที่แม่นยำ ซึ่งคำนวณไว้ก่อนที่ข้อความใดๆ จะถูกเรนเดอร์ไปยัง DOM ไม่มีการประมาณค่า, ไม่มีการกระโดดแก้ไข, ไม่มี reflow

สนามทดลองแบบอินเทอร์แอกทีฟ

เว็บไซต์ Pretext.js มีสนามทดลองแบบอินเทอร์แอกทีฟที่ pretextjs.dev/playground ซึ่งคุณสามารถวางข้อความ, เลือกฟอนต์, ปรับความกว้างของคอนเทนเนอร์ และดูการคำนวณเลย์เอาต์ได้แบบเรียลไทม์ เป็นวิธีที่เร็วที่สุดในการตรวจสอบพฤติกรรมก่อนที่จะนำไปผสานรวม

เมื่อใดที่คุณไม่ควรใช้ Pretext.js

Pretext.js ไม่ใช่เครื่องมือที่เหมาะสมสำหรับปัญหาการวัดข้อความทุกประเภท:

คำถามที่พบบ่อย

Pretext.js พร้อมใช้งานใน Production แล้วหรือยัง?

ไลบรารีนี้เปิดตัวในเดือนมีนาคม 2026 และได้รับดาว GitHub กว่า 14,000 ดวงภายในไม่กี่วัน เฉิง โหลว (Cheng Lou) ผู้สร้าง เป็นผู้ดูแลส่วนหน้า (frontend) ของ Midjourney ซึ่งเป็นระบบ Production ที่ให้บริการผู้ใช้หลายล้านคน ชุดการทดสอบของไลบรารีครอบคลุมภาษาและกรณีขอบจำนวนมาก อย่างไรก็ตาม มันเป็นเวอร์ชันใหม่ ควรตรึงเวอร์ชันของคุณไว้และทดสอบกับฟอนต์และเนื้อหาเฉพาะของคุณ

Pretext.js ใช้งานได้กับ React, Vue และ Svelte หรือไม่?

ได้ Pretext.js เป็นไลบรารีที่ไม่ขึ้นกับเฟรมเวิร์ก (framework-agnostic) มันเป็นไลบรารี TypeScript ล้วนๆ ที่มีสองฟังก์ชัน คุณเรียกใช้ prepare() และ layout() ได้ทุกที่ที่คุณต้องการการวัดข้อความ ไม่ว่าจะเป็นภายใน React hooks, Vue composables, Svelte stores หรือ JavaScript ธรรมดา

Pretext.js จัดการ Web Font อย่างไร?

ฟังก์ชัน prepare() จะวัดข้อความโดยใช้ฟอนต์ที่เบราว์เซอร์โหลดไว้ ณ เวลาที่เรียกใช้ หาก Web Font ของคุณยังไม่ได้โหลด การวัดจะใช้ฟอนต์สำรองและให้ผลลัพธ์ที่ไม่ถูกต้อง ตรวจสอบให้แน่ใจว่าฟอนต์ของคุณโหลดเสร็จแล้วก่อนเรียกใช้ prepare() ใช้ Font Loading API (document.fonts.ready) เพื่อตรวจสอบ

ฉันสามารถใช้ Pretext.js สำหรับการเรนเดอร์ Canvas หรือ SVG ได้หรือไม่?

ได้ ไลบรารีนี้คำนวณเลย์เอาต์ข้อความที่ไม่ขึ้นกับเป้าหมายการเรนเดอร์ คุณสามารถใช้ความสูงและการขึ้นบรรทัดใหม่ที่คำนวณได้เพื่อจัดตำแหน่งข้อความใน Canvas 2D, WebGL, SVG หรือ DOM เว็บไซต์ Pretext.js แสดงตัวอย่างของเป้าหมายการเรนเดอร์เหล่านี้ทั้งหมด

รองรับภาษา RTL (จากขวาไปซ้าย) หรือไม่?

ได้ Pretext.js จัดการภาษาอาหรับ ฮีบรู และภาษา RTL อื่นๆ ได้อย่างถูกต้องด้วยการรองรับข้อความแบบสองทิศทาง นอกจากนี้ยังจัดการข้อความที่มีทิศทางผสมกัน (เช่น ข้อความภาษาอาหรับที่มีคำภาษาอังกฤษแทรกอยู่) ได้อย่างถูกต้อง

ขนาด Bundle เป็นเท่าไหร่?

15KB แบบย่อ (minified) โดยไม่มีการพึ่งพาใดๆ ไม่จำเป็นต้องใช้ polyfills ไลบรารีนี้ใช้เพียง API มาตรฐานของเบราว์เซอร์เท่านั้น (Canvas measureText() และ Intl.Segmenter หากมี)

มีความแม่นยำแค่ไหนเมื่อเทียบกับการวัด DOM?

สำหรับเนื้อหาส่วนใหญ่ Pretext.js จัดเรียงเลย์เอาต์ได้ตรงกับ DOM ภายใน 1-2 พิกเซล ความแม่นยำขึ้นอยู่กับฟอนต์และคุณสมบัติ CSS ที่คุณใช้ คุณสมบัติเช่น letter-spacing และ word-spacing ไม่ได้ถูกนำมาพิจารณา ดังนั้นหากคุณใช้สิ่งเหล่านี้ คาดว่าจะมีความแตกต่างที่มากขึ้น สำหรับการทำ Virtual Scrolling ซึ่งข้อผิดพลาดเพียงไม่กี่พิกเซลไม่สามารถมองเห็นได้ ความแม่นยำก็เพียงพอแล้ว

Pretext.js สามารถวัดข้อความที่มีสไตล์ (ตัวหนา, ตัวเอียง, ขนาดผสม) ได้หรือไม่?

การเรียกใช้ prepare() แต่ละครั้งจะรับการระบุฟอนต์เพียงครั้งเดียว สำหรับข้อความที่มีการผสมผสานสไตล์ (คำตัวหนาในข้อความปกติ) คุณจะต้องแบ่งส่วนข้อความด้วยตนเองและสร้าง handle แยกต่างหากสำหรับแต่ละส่วนสไตล์ นี่เป็นข้อจำกัดที่ทราบกันดี ซึ่งอาจได้รับการแก้ไขในเวอร์ชันในอนาคต

สรุป

Pretext.js แก้ปัญหาที่เบราว์เซอร์ละเลยมาสามทศวรรษ: การวัดข้อความที่รวดเร็วและแม่นยำโดยไม่มี DOM reflow สำหรับนักพัฒนาที่สร้าง Virtual Scroller, Chat UI, Data Grid หรืออินเทอร์เฟซใดๆ ที่ต้องการวัดบล็อกข้อความหลายพันรายการ ไลบรารีนี้เข้ามาแทนที่วิธีแก้ปัญหาทั้งหมดด้วยการเรียกใช้ฟังก์ชันเพียงสองครั้ง

ไลบรารีนี้ไม่ใช่ยาวิเศษครอบจักรวาล มันไม่รองรับคุณสมบัติ CSS สำหรับข้อความนอกเหนือจากการระบุฟอนต์, มีความแตกต่างด้านความแม่นยำระดับซับพิกเซลเล็กน้อย และยังไม่ทำงานฝั่งเซิร์ฟเวอร์ แต่สำหรับกรณีการใช้งานหลักของมัน นั่นคือการคำนวณความสูงของข้อความล่วงหน้าสำหรับ Virtualized List ก็ไม่มีสิ่งใดเทียบได้

พร้อมที่จะสร้าง UI ที่เน้นข้อความให้เร็วขึ้นแล้วหรือยัง? เริ่มต้นด้วยการทดสอบ API endpoints ของคุณด้วย Apidog เพื่อให้แน่ใจว่าเลเยอร์ข้อมูลของคุณแข็งแกร่ง จากนั้นนำ Pretext.js ไปใช้ในขั้นตอนการเรนเดอร์ของคุณ

button

ฝึกการออกแบบ API แบบ Design-first ใน Apidog

ค้นพบวิธีที่ง่ายขึ้นในการสร้างและใช้ API