TopGit
Đánh giá repo GitHub

Pretext: đo văn bản không chạm DOM cho JS

chenglou/pretext
PTopGit review image for chenglou/pretext
Review by Topgit.dev for chenglou/pretext, with GitHub repository stats and README context.
Nhận định nhanh

Pretext làm tốt đúng một việc: thay getBoundingClientRect và reflow của DOM bằng cặp prepare()/layout(), biến việc đo chiều cao thành phép tính đã cache sẵn. Đây là lợi ích thực sự cho danh sách virtualized, textarea tự giãn, và bong bóng chat kiểu shrinkwrap. Nó chưa phải một engine CSS đầy đủ, nên các tính năng font đặc biệt vẫn cần DOM.

Sao
★ 49.7k
Fork
⑂ 2.7k
Ngôn ngữ
TypeScript
Giấy phép
MIT
Chủ đề
UI / UX
Cập nhật
Jun 2026
Trang chủ
GitHub

Pretext là gì?

Pretext là thư viện TypeScript đo và bố trí văn bản nhiều dòng mà không chạm DOM. Một lượt prepare() chạy một lần sẽ phân đoạn văn bản và đo bằng canvas measureText dựa trên font engine thật của trình duyệt; sau đó layout() biến kết quả thành chiều cao và số dòng chỉ bằng phép tính số học — không cần getBoundingClientRect, không gây reflow.

Vì sao nên tránh đo văn bản qua DOM?

Cách đọc chiều cao văn bản thông thường là gọi getBoundingClientRect hoặc offsetHeight, việc này buộc trình duyệt chạy layout reflow — một trong những thao tác tốn kém nhất mà trình duyệt phải làm — mỗi lần bạn cần một con số cho virtualization, một textarea tự giãn, hay một resize handler. Chính README của Pretext coi đây là lý do nó ra đời: đo một lần bằng canvas dựa trên font engine thật, rồi từ đó mỗi lần đổi chiều rộng hay resize chỉ còn là phép tính, không phải một lượt đọc DOM mới.

Các API cốt lõi: prepare, layout và line walkers

  • prepare() thực hiện phần việc một lần duy nhất — chuẩn hoá khoảng trắng, phân đoạn văn bản, đo từng đoạn bằng canvas — rồi trả về một handle opaque để bạn tái sử dụng.
  • layout() biến handle đó thành chiều cao và số dòng bằng phép tính số học thuần túy, nên đủ rẻ để gọi lại ở mỗi lần resize.
  • prepareWithSegments() cùng layoutWithLines() trả về chuỗi văn bản từng dòng thật, chiều rộng và cursor bắt đầu/kết thúc để tự render lên canvas hoặc SVG.
  • walkLineRanges() và measureLineStats() tính số dòng và chiều rộng lớn nhất mà không cấp phát bất kỳ chuỗi dòng nào — hữu ích khi cần binary-search một chiều rộng 'shrinkwrap'.
  • layoutNextLineRange() phát từng dòng một, cho phép chiều rộng tối đa thay đổi giữa đoạn văn, ví dụ khi văn bản chảy quanh một ảnh được float.
  • Điểm vào riêng @chenglou/pretext/rich-inline xử lý font theo từng item, các chip nguyên khối với break: 'never', và extraWidth do caller tự khai báo cho phần viền của pill.
  • Soft hyphen chèn trước prepare() trở thành điểm ngắt tuỳ chọn; khi được chọn, nó hiện thành dấu '-' ở cuối dòng.
  • setLocale() và clearCache() quản lý locale phân đoạn và cache dùng chung cho các app xoay vòng qua nhiều font.
Số sao GitHub của repo này thay đổi thế nào theo thời gian. Nguồn: star-history.com.Xem lịch sử sao

Đo chiều cao văn bản mà không đụng đến DOM

Gọi prepare() một lần cho mỗi cặp văn bản, font và config, rồi tái sử dụng kết quả. Nó chuẩn hoá khoảng trắng, phân đoạn chuỗi, và đo từng đoạn bằng canvas — phần việc tốn kém chỉ chạy đúng một lần. Sau đó gọi layout() trên handle đó bất cứ khi nào cần chiều cao: ```ts import { prepare, layout } from '@chenglou/pretext' const prepared = prepare('Hello, world 你好', '16px Inter') const { height, lineCount } = layout(prepared, 320, 20) ``` layout() rất rẻ. Gọi lại nó ở mỗi lần resize — chỉ đừng gọi lại prepare() cho cùng một văn bản và font, vì như vậy sẽ lặp lại lượt đo. Truyền { whiteSpace: 'pre-wrap' } vào prepare() nếu bạn cần xử lý kiểu textarea, nơi space, tab và newline vẫn hiển thị. Muốn tự bố trí từng dòng thay vì chỉ lấy chiều cao, hãy đổi sang prepareWithSegments() và layoutWithLines(), hai hàm này trả về chuỗi và chiều rộng thật của từng dòng.

Cài đặt

Cài package từ npm: `npm install @chenglou/pretext`. Đó là toàn bộ bước cài đặt để dùng thư viện trong app của bạn — README không nhắc tới bước build, file config hay plugin bundler nào khác. Nếu muốn xem thư viện chạy thực tế, README mô tả một luồng riêng để chạy các demo đi kèm: clone repo, chạy `bun install`, rồi `bun start` và mở /demos/index trong trình duyệt (dùng `bun run start:windows` trên Windows). Các demo dựng sẵn cũng được link trực tiếp từ README.

Điểm mạnh của Pretext

  • Cặp prepare()/layout() nghĩa là lượt phân đoạn và đo tốn kém chỉ chạy một lần, còn mỗi lần resize sau đó chỉ là phép tính.
  • Các line walker cấp thấp (walkLineRanges, layoutNextLineRange) không cấp phát chuỗi dòng, nên đủ rẻ để binary-search một chiều rộng shrinkwrap.
  • rich-inline là một API riêng, hẹp, dành cho chip và mention, thay vì bắt bạn giả lập item nguyên khối bằng API cho đoạn văn thông thường.
  • Các chuỗi ví dụ trong README trộn tiếng Trung, tiếng Ả Rập và emoji trong cùng một lần gọi, và segLevels tồn tại riêng cho việc render bidi tuỳ chỉnh.
  • Giấy phép MIT, bề mặt hàm nhỏ — vài export có tên rõ ràng, không phải một framework với định dạng config riêng phải học.

Lưu ý và những tính năng chưa hỗ trợ

  • Render phía server được ghi là 'sắp có' trong README, chưa triển khai — hiện tại nó chỉ nhắm tới DOM, Canvas và SVG trong trình duyệt.
  • Không có tự động gạch nối (hyphenation) sẵn; bạn phải tự chèn soft hyphen trước khi gọi prepare().
  • system-ui bị chính README gắn cờ là không an toàn cho độ chính xác của layout() trên macOS, do các lỗi Chrome/Firefox mà README dẫn link tới — bạn cần dùng một font có tên cụ thể thay vào đó.
  • Các tính năng văn bản CSS nằm ngoài canvas font shorthand — font-optical-sizing, font-feature-settings, font-variation-settings độc lập — chưa được mô hình hoá.
  • Yêu cầu Intl.Segmenter và đo văn bản Canvas 2D lúc runtime, nên các môi trường JS cũ hoặc không chuẩn sẽ không được hỗ trợ.
Xem trên GitHubTrang chủ

Lựa chọn thay thế cho Pretext

text-layout — dự án trước đó của chenglou, được ghi công trong phần Credits của README như tổ tiên thiết kế mà Pretext phát triển tiếp.Đo DOM gốc (getBoundingClientRect/offsetHeight) — cách tiếp cận mặc định của trình duyệt mà Pretext sinh ra để tránh, đổi lại là gây reflow ở mỗi lần đọc.Tự xây một lớp cache canvas measureText riêng — khả thi, nhưng bạn mất các API phân đoạn, xử lý bidi và line-walking của Pretext, và phải tự dựng lại chúng.

Câu hỏi thường gặp

Does Pretext work on the server or in Node.js?

Hiện tại Pretext nhắm tới trình duyệt — README liệt kê việc render phía server là 'sắp có', chưa triển khai. Runtime của nó cần Intl.Segmenter và đo văn bản Canvas 2D, những thứ mà một tiến trình Node.js thuần không có sẵn nếu không polyfill cả hai.

Can Pretext render text to HTML or only Canvas/SVG?

Pretext không chỉ giới hạn ở Canvas/SVG — README liệt kê DOM, Canvas và SVG là các đích render được hỗ trợ ngay bây giờ, còn render phía server được ghi là sắp ra mắt. Bản thân thư viện chỉ trả về số đo và dữ liệu dòng; bạn vẫn tự vẽ văn bản bằng renderer của mình.

What happens with CJK languages and bidirectional RTL text in Pretext?

Pretext phân đoạn và đo văn bản CJK cũng như văn bản right-to-left — chính các ví dụ trong README trộn tiếng Trung và tiếng Ả Rập trong cùng một chuỗi. Handle PreparedTextWithSegments phong phú hơn còn có trường segLevels dành cho việc render bidi tùy chỉnh, dù các API line-breaking dựng sẵn không đọc trường này.

Is Pretext safe to use with system-ui font on macOS?

Chính Pretext ghi rõ trong phần caveats rằng system-ui không an toàn cho độ chính xác của layout() trên macOS, và trỏ tới một platform bug ledger ghi lại các lỗi liên quan trên Chrome và Firefox. Hãy dùng một font có tên cụ thể như 'Inter' thay vì system-ui khi cần độ chính xác.

How does Pretext compare to calling getBoundingClientRect directly?

Gọi getBoundingClientRect buộc trình duyệt phải chạy layout reflow — một trong những thao tác tốn kém nhất mà nó thực hiện — chỉ để đọc lại một kích thước. Pretext bỏ qua bước đó: prepare() đo văn bản một lần qua canvas dựa trên font engine của trình duyệt, còn layout() sau đó chỉ là phép tính số học thuần túy.

Does Pretext support CSS letter-spacing or word-break options?

Pretext hỗ trợ cả hai: prepare() và prepareWithSegments() đều nhận option letterSpacing (giá trị pixel kiểu CSS) và option wordBreak với normal và keep-all, khớp với hành vi word-break của CSS cho văn bản CJK, Hangul và văn bản trộn ngôn ngữ không dấu cách.

Trường hợp sử dụng tốt nhất

  • Đo chiều cao dòng trong danh sách hay feed virtualized trước khi render, để vị trí scroll không nhảy khi chiều cao đoán trước bị sai.
  • Textarea tự giãn chiều cao và ô soạn tin chat cần chiều cao chính xác từng pixel khi người dùng gõ, kể cả giữ nguyên tab và newline qua whiteSpace: 'pre-wrap'.
  • Kiểm tra ở dev-time hoặc CI rằng một nút hay nhãn không bị tràn xuống dòng thứ hai — không cần trình duyệt để chạy kiểm tra.
  • Layout kiểu masonry tuỳ chỉnh hay layout điều khiển bằng JS cần số dòng và chiều rộng thật của đoạn văn, thay vì phải trigger CSS reflow để biết.
  • Cho văn bản chảy quanh một ảnh được float, nơi chiều rộng khả dụng của mỗi dòng thu hẹp dần cho tới khi hết ảnh.
  • Giao diện shrinkwrap như bong bóng chat hay tooltip cần co về đúng chiều rộng hẹp nhất mà vẫn vừa văn bản, thông qua walkLineRanges().

Nên dùng và không nên dùng khi nào

Kỹ sư frontend đang xây danh sách virtualized, giao diện chat, hay ô nhập tự giãn chiều cao — nơi một con số chiều cao sai gây layout shift — là đối tượng phù hợp nhất, nhất là khi việc profiling đã chỉ ra getBoundingClientRect là điểm nóng. Những đội cần render văn bản CSS đầy đủ (trục variable font, font-feature-settings, độ chính xác vị trí glyph cho RTL) hoặc phải đo văn bản trên Node ngay bây giờ nên khoan vội — README ghi rõ phía server vẫn đang 'sắp có', và một số tính năng CSS vẫn nằm ngoài phạm vi hỗ trợ.

Repo liên quan

Nguồn & ghi công

Thông tin và chi tiết API lấy từ repository GitHub chenglou/pretext và README của nó (https://github.com/chenglou/pretext).

Dữ liệu GitHub · đồng bộ lần cuối 14 thg 8, 2026Đánh giá bởi Henry
Về TopGit

Muốn nghe thêm một ý kiến về pretext?

Hỏi một AI đọc được trang này — một cú bấm là có ngay nhận định về pretext.

GitHub