Phân mảnh khối là việc chia một hộp cấp khối CSS (chẳng hạn như một phần hoặc đoạn văn bản) thành nhiều mảnh khi hộp đó không vừa với một vùng chứa mảnh, được gọi là vùng chứa mảnh. Fragmentainer không phải là một phần tử, mà là một cột trong bố cục nhiều cột hoặc một trang trong nội dung nghe nhìn được phân trang.
Để việc phân mảnh xảy ra, nội dung cần phải nằm trong ngữ cảnh phân mảnh. Ngữ cảnh phân mảnh thường được thiết lập bởi vùng chứa nhiều cột (nội dung được chia thành các cột) hoặc khi in (nội dung được chia thành các trang). Một đoạn dài có nhiều dòng có thể cần được tách thành nhiều mảnh để các dòng đầu tiên được đặt trong mảnh đầu tiên, còn các dòng còn lại được đặt trong các mảnh tiếp theo.
Phân mảnh khối tương tự như một loại phân mảnh khác cũng rất phổ biến: phân mảnh dòng, còn được gọi là "đoạn dòng". Mọi phần tử nội tuyến bao gồm nhiều từ (mọi nút văn bản, mọi phần tử <a>
, v.v.) và cho phép ngắt dòng đều có thể được chia thành nhiều mảnh. Mỗi mảnh được đặt vào một hộp dòng khác nhau. Hộp dòng là mảnh nội tuyến tương đương với fragmentainer (vùng chứa mảnh) cho các cột và trang.
Phân mảnh khối LayoutNG
LayoutNGBlockFragmentation là bản viết lại của công cụ phân mảnh cho LayoutNG, ban đầu được phân phối trong Chrome 102. Về cấu trúc dữ liệu, phiên bản này đã thay thế nhiều cấu trúc dữ liệu trước NG bằng mảnh NG được biểu thị trực tiếp trong cây mảnh.
Ví dụ: chúng tôi hiện hỗ trợ giá trị "tránh" cho các thuộc tính CSS "break-before" và "break-after", để cho phép tác giả tránh bị ngắt ngay sau tiêu đề. Việc tiêu đề là nội dung cuối cùng trên một trang, trong khi nội dung của mục đó bắt đầu trên trang tiếp theo, thường sẽ gây khó chịu. Bạn nên ngắt trước tiêu đề.
Chrome cũng hỗ trợ tràn phân mảnh, để nội dung nguyên khối (được cho là không thể bị hỏng) không bị cắt thành nhiều cột và các hiệu ứng vẽ như đổ bóng và biến đổi được áp dụng chính xác.
Quá trình phân mảnh khối trong LayoutNG đã hoàn tất
Phân mảnh cốt lõi (các vùng chứa khối, bao gồm cả bố cục dòng, phần nổi và vị trí ngoài luồng) được phân phối trong Chrome 102. Tính năng phân mảnh Flex và lưới được cung cấp trong Chrome 103, còn tính năng phân mảnh bảng được cung cấp trong Chrome 106. Cuối cùng, tính năng in được xuất bản trong Chrome 108. Phân mảnh khối là tính năng cuối cùng phụ thuộc vào công cụ cũ để thực hiện bố cục.
Kể từ Chrome 108, công cụ cũ không còn được dùng để thực hiện bố cục nữa.
Ngoài ra, các cấu trúc dữ liệu LayoutNG hỗ trợ vẽ và kiểm thử lượt nhấp, nhưng chúng tôi vẫn dựa vào một số cấu trúc dữ liệu cũ cho các API JavaScript đọc thông tin bố cục, chẳng hạn như offsetLeft
và offsetTop
.
Khi triển khai mọi thứ với NG, bạn có thể triển khai và chuyển các tính năng mới chỉ triển khai LayoutNG (và không có phiên bản công cụ cũ), chẳng hạn như truy vấn vùng chứa CSS, vị trí neo, MathML và bố cục tuỳ chỉnh (Houdini). Đối với các truy vấn vùng chứa, chúng tôi đã gửi trước một chút, kèm theo cảnh báo cho nhà phát triển rằng tính năng in chưa được hỗ trợ.
Chúng tôi đã phát hành phần đầu tiên của LayoutNG vào năm 2019, bao gồm bố cục vùng chứa khối thông thường, bố cục nội tuyến, phần nổi và vị trí ngoài luồng, nhưng không hỗ trợ flex, lưới hoặc bảng và hoàn toàn không hỗ trợ phân mảnh khối. Chúng ta sẽ quay lại sử dụng công cụ bố cục cũ cho tính năng linh hoạt, lưới, bảng và mọi thứ liên quan đến việc phân mảnh khối. Điều này đúng ngay cả đối với các phần tử khối, nội tuyến, nổi và ngoài luồng trong nội dung bị phân mảnh – như bạn có thể thấy, việc nâng cấp công cụ bố cục phức tạp như vậy tại chỗ là một công việc rất tinh tế.
Ngoài ra, vào giữa năm 2019, phần lớn chức năng cốt lõi của bố cục phân mảnh khối LayoutNG đã được triển khai (phía sau một lá cờ). Vậy tại sao đơn đặt hàng của tôi lại mất nhiều thời gian để vận chuyển đến vậy? Câu trả lời ngắn gọn là: phân mảnh phải cùng tồn tại đúng cách với các phần cũ của hệ thống. Bạn không thể xoá hoặc nâng cấp các phần này cho đến khi tất cả phần phụ thuộc được nâng cấp.
Tương tác với công cụ cũ
Các cấu trúc dữ liệu cũ vẫn chịu trách nhiệm về các API JavaScript có chức năng đọc thông tin bố cục, vì vậy, chúng ta cần ghi lại dữ liệu vào công cụ cũ theo cách mà công cụ đó hiểu được. Điều này bao gồm việc cập nhật chính xác các cấu trúc dữ liệu đa cột cũ, chẳng hạn như LayoutMultiColumnFlowThread.
Phát hiện và xử lý dự phòng cho công cụ cũ
Chúng tôi phải quay lại công cụ bố cục cũ khi có nội dung bên trong chưa được phân mảnh khối LayoutNG xử lý. Tại thời điểm phân mảnh khối LayoutNG cốt lõi, bao gồm cả flex, lưới, bảng và mọi nội dung được in. Điều này đặc biệt khó khăn vì chúng ta cần phát hiện nhu cầu sử dụng tính năng dự phòng cũ trước khi tạo các đối tượng trong cây bố cục. Ví dụ: chúng tôi cần phát hiện trước khi biết liệu có đối tượng cấp trên của vùng chứa nhiều cột hay không, cũng như trước khi biết được nút DOM nào sẽ trở thành ngữ cảnh định dạng hay không. Đây là vấn đề gà và trứng không có giải pháp hoàn hảo, nhưng miễn là hành vi sai trái duy nhất là dương tính giả (sử dụng lại giao diện cũ khi thực sự không cần thiết), thì không sao, vì mọi lỗi trong hành vi bố cục đó đều là lỗi mà Chromium đã có, chứ không phải lỗi mới.
Đi bộ trước khi vẽ cây
Sơn trước là việc chúng ta làm sau khi sắp xếp bố cục, nhưng trước khi tô màu. Thách thức chính là chúng ta vẫn cần đi qua cây đối tượng bố cục, nhưng hiện tại chúng ta có các mảnh NG. Vậy làm cách nào để giải quyết vấn đề đó? Chúng ta đi bộ cả đối tượng bố cục và cây mảnh NG cùng một lúc! Việc này khá phức tạp vì việc ánh xạ giữa 2 cây không phải là một việc đơn giản.
Mặc dù cấu trúc cây đối tượng bố cục rất giống với cấu trúc cây DOM, nhưng cây mảnh là đầu ra của bố cục, chứ không phải đầu vào của bố cục. Ngoài việc thực sự phản ánh hiệu ứng của mọi hoạt động phân mảnh, bao gồm cả phân mảnh nội tuyến (mảnh dòng) và phân mảnh khối (mảnh cột hoặc trang), cây mảnh cũng có mối quan hệ mẹ con trực tiếp giữa khối chứa và các phần tử con cháu DOM có mảnh đó làm khối chứa. Ví dụ: trong cây mảnh, một mảnh do phần tử được định vị tuyệt đối tạo ra là một mảnh con trực tiếp của mảnh khối chứa, ngay cả khi có các nút khác trong chuỗi gốc giữa phần tử con được định vị ngoài luồng và khối chứa của phần tử đó.
Việc này có thể còn phức tạp hơn khi có một phần tử được đặt vị trí ngoài luồng bên trong quá trình phân mảnh, vì sau đó các mảnh ngoài luồng sẽ trở thành phần tử con trực tiếp của fragmentainer (và không phải là phần tử con của phần tử mà CSS cho là khối chứa). Đây là vấn đề cần giải quyết để có thể cùng tồn tại với công cụ cũ. Trong tương lai, chúng ta có thể đơn giản hoá mã này vì LayoutNG được thiết kế để linh hoạt hỗ trợ tất cả các chế độ bố cục hiện đại.
Vấn đề với công cụ phân mảnh cũ
Công cụ cũ (được thiết kế ở thời kỳ sơ khai của web) không thực sự có khái niệm phân mảnh, ngay cả khi về mặt kỹ thuật sự phân mảnh cũng tồn tại vào thời điểm đó (để hỗ trợ hoạt động in). Tính năng hỗ trợ phân mảnh chỉ là một tính năng được gắn vào trên cùng (in) hoặc được trang bị lại (nhiều cột).
Khi bố trí nội dung có thể phân mảnh, công cụ cũ bố trí mọi nội dung thành một dải cao có chiều rộng bằng kích thước cùng dòng của cột hoặc trang, và chiều cao cũng tương đương chiều cao cần thiết để chứa nội dung. Dải cao này không được kết xuất cho trang – hãy coi đó là kết xuất cho một trang ảo, sau đó được sắp xếp lại để hiển thị cuối cùng. Về mặt lý thuyết, cách này tương tự như việc in toàn bộ một bài báo trên giấy thành một cột, sau đó dùng kéo để cắt thành nhiều bài báo ở bước thứ hai. (Ngày xưa, một số tờ báo thực sự đã sử dụng các kỹ thuật tương tự như thế này!)
Công cụ cũ theo dõi ranh giới của trang hoặc cột ảo trong dải. Điều này cho phép nó đẩy nội dung không vừa với ranh giới vào trang hoặc cột tiếp theo. Ví dụ: nếu chỉ nửa trên của một dòng vừa với trang mà công cụ cho là trang hiện tại, thì công cụ này sẽ chèn một "cột phân trang" để đẩy dòng đó xuống vị trí mà công cụ giả định là đầu trang tiếp theo. Sau đó, hầu hết công việc phân mảnh thực tế ("cắt bằng kéo và đặt") diễn ra sau khi bố cục trong quá trình vẽ trước và vẽ, bằng cách cắt dải nội dung cao thành các trang hoặc cột (bằng cách cắt và dịch các phần). Điều này khiến một số việc về cơ bản là không thể, chẳng hạn như áp dụng các phép biến đổi và định vị tương đối sau khi phân mảnh (đây là yêu cầu của thông số kỹ thuật). Hơn nữa, mặc dù có một số tính năng hỗ trợ phân mảnh bảng trong công cụ cũ, nhưng không có tính năng hỗ trợ phân mảnh flex hoặc lưới nào cả.
Dưới đây là hình minh hoạ cách bố cục ba cột được thể hiện nội bộ trong công cụ cũ, trước khi sử dụng kéo, vị trí và keo (chúng ta có chiều cao được chỉ định, vì vậy chỉ có 4 dòng vừa vặn, nhưng có một số không gian thừa ở dưới cùng):
Vì công cụ bố cục cũ không thực sự phân mảnh nội dung trong quá trình bố cục, nên có nhiều cấu phần phần mềm lạ, chẳng hạn như vị trí tương đối và các phép biến đổi áp dụng không chính xác, cũng như bóng hộp bị cắt ở các cạnh cột.
Sau đây là ví dụ về text-shadow:
Công cụ cũ không xử lý tốt vấn đề này:
Bạn có thấy cách bóng văn bản trên dòng trong cột đầu tiên bị cắt bớt và được đặt ở đầu cột thứ hai không? Đó là do công cụ bố cục cũ không hiểu được tính năng phân mảnh.
Mã sẽ có dạng như sau:
Tiếp theo, hãy làm cho chủ đề phức tạp hơn một chút bằng cách biến đổi và đổ bóng. Hãy lưu ý cách trong công cụ cũ, có lỗi cắt và tràn cột không chính xác. Đó là do các phép biến đổi theo thông số kỹ thuật được áp dụng dưới dạng hiệu ứng sau bố cục, sau khi phân mảnh. Với tính năng phân mảnh LayoutNG, cả hai đều hoạt động chính xác. Điều này làm tăng khả năng tương tác với Firefox, trình duyệt đã có khả năng hỗ trợ phân mảnh tốt trong một thời gian với hầu hết các bài kiểm thử trong khu vực này cũng vượt qua.
Công cụ cũ cũng gặp vấn đề với nội dung nguyên khối cao. Nội dung là thống nhất nếu không đủ điều kiện để chia thành nhiều mảnh. Các phần tử có tính năng cuộn tràn là nguyên khối, vì người dùng không thể cuộn trong một vùng không phải hình chữ nhật. Hộp dòng và hình ảnh là các ví dụ khác về nội dung nguyên khối. Ví dụ:
Nếu đoạn nội dung nguyên khối quá cao để vừa trong một cột, công cụ cũ sẽ cắt phần nội dung đó một cách tàn bạo (dẫn đến hành vi rất "thú vị" khi cố gắng cuộn vùng chứa có thể cuộn):
Thay vì để nó tràn sang cột đầu tiên (như khi phân mảnh khối LayoutNG):
Công cụ cũ hỗ trợ các điểm ngắt bắt buộc. Ví dụ: <div style="break-before:page;">
sẽ chèn một dấu ngắt trang trước DIV. Tuy nhiên, thuộc tính này chỉ hỗ trợ một phần để tìm các dấu ngắt không bắt buộc tối ưu. Phương thức này hỗ trợ break-inside:avoid
và các dòng đơn và dòng cuối, nhưng không hỗ trợ tránh các khoảng nghỉ giữa các khối, ví dụ: nếu được yêu cầu thông qua break-before:avoid
. Hãy xem xét ví dụ sau:
Tại đây, phần tử #multicol
có đủ chỗ cho 5 dòng trong mỗi cột (vì chiều cao là 100px và chiều cao dòng là 20px), vì vậy, tất cả #firstchild
đều có thể vừa với cột đầu tiên. Tuy nhiên, phần tử đồng cấp #secondchild
có break-before:avoid, nghĩa là nội dung không muốn có khoảng nghỉ giữa các phần tử này. Vì giá trị của widows
là 2, nên chúng ta cần đẩy 2 dòng #firstchild
vào cột thứ hai để thực hiện tất cả các yêu cầu tránh ngắt. Chromium là công cụ trình duyệt đầu tiên hỗ trợ đầy đủ tổ hợp tính năng này.
Cách hoạt động của tính năng phân mảnh NG
Công cụ bố cục NG thường bố trí tài liệu bằng cách duyệt qua cây hộp CSS theo chiều sâu trước. Khi tất cả các phần tử con của một nút được bố trí, bạn có thể hoàn tất bố cục của nút đó bằng cách tạo một NGPhysicalFragment và quay lại thuật toán bố cục mẹ. Thuật toán đó sẽ thêm mảnh vào danh sách mảnh con và sau khi tất cả mảnh con đã hoàn tất, sẽ tạo một mảnh cho chính nó với tất cả mảnh con bên trong. Bằng phương thức này, nó sẽ tạo một cây mảnh cho toàn bộ tài liệu. Tuy nhiên, đây là một cách đơn giản hoá quá mức: ví dụ: các phần tử được định vị ngoài luồng sẽ phải chuyển từ vị trí hiện tại trong cây DOM đến khối chứa trước khi có thể bố trí. Tôi sẽ bỏ qua thông tin chi tiết nâng cao này để đơn giản hoá.
Cùng với hộp CSS, LayoutNG cung cấp một không gian ràng buộc cho thuật toán bố cục. Việc này cung cấp cho thuật toán những thông tin như không gian có sẵn cho bố cục, ngữ cảnh định dạng mới có được thiết lập hay không và kết quả thu gọn lề trung gian từ nội dung trước đó. Không gian ràng buộc cũng biết kích thước khối được bố trí của trình phân mảnh và độ lệch khối hiện tại trong không gian đó. Điều này cho biết vị trí ngắt.
Khi có phân mảnh khối, bố cục của các phần tử con phải dừng lại ở một điểm ngắt. Lý do ngắt dòng bao gồm hết không gian trong trang hoặc cột hoặc ngắt dòng bắt buộc. Sau đó, chúng ta tạo các mảnh cho các nút đã truy cập và trả về tất cả các mảnh đến gốc ngữ cảnh phân mảnh (vùng chứa nhiều cột hoặc trong trường hợp in, gốc tài liệu). Sau đó, ở gốc ngữ cảnh phân mảnh, chúng ta chuẩn bị cho một trình phân mảnh mới và tiếp tục xuống cây, tiếp tục tại nơi chúng ta đã dừng lại trước khi nghỉ.
Cấu trúc dữ liệu quan trọng để cung cấp phương tiện tiếp tục bố cục sau khi nghỉ có tên là NGBlockBreakToken. Tệp này chứa tất cả thông tin cần thiết để tiếp tục bố cục một cách chính xác trong trình phân mảnh tiếp theo. NGBlockBreakToken được liên kết với một nút và tạo thành một cây NGBlockBreakToken để mỗi nút cần tiếp tục được biểu thị. NGBlockBreakToken được đính kèm vào NGPhysicalBoxFragment được tạo cho các nút bị ngắt bên trong. Các mã thông báo ngắt được truyền đến các thành phần mẹ, tạo thành một cây mã thông báo ngắt. Nếu chúng ta cần ngắt trước một nút (thay vì bên trong nút đó), thì sẽ không có mảnh nào được tạo, nhưng nút mẹ vẫn cần tạo mã thông báo ngắt "break-before" cho nút đó để chúng ta có thể bắt đầu bố trí nút đó khi đến cùng một vị trí trong cây nút trong trình phân mảnh tiếp theo.
Các điểm ngắt được chèn khi chúng ta hết không gian fragmentainer (điểm ngắt không bắt buộc) hoặc khi có yêu cầu điểm ngắt bắt buộc.
Có các quy tắc trong quy cách để ngắt dòng không bắt buộc một cách tối ưu và không phải lúc nào bạn cũng nên chèn dấu ngắt dòng chính xác tại vị trí hết khoảng trống. Ví dụ: có nhiều thuộc tính CSS như break-before
ảnh hưởng đến lựa chọn vị trí ngắt dòng.
Trong quy trình bố cục, để triển khai chính xác phần thông số kỹ thuật không bắt buộc chèn, chúng ta cần theo dõi các điểm ngắt có thể phù hợp. Bản ghi này có nghĩa là chúng ta có thể quay lại và sử dụng điểm ngắt tốt nhất có thể tìm thấy gần đây nhất, nếu chúng ta hết không gian tại một điểm vi phạm các yêu cầu tránh ngắt (ví dụ: break-before:avoid
hoặc orphans:7
). Mỗi điểm ngắt có thể có một điểm số, từ "chỉ làm việc này khi không còn cách nào khác" đến "điểm ngắt hoàn hảo", với một số giá trị ở giữa. Nếu một vị trí chèn quảng cáo nhận được điểm "hoàn hảo", thì tức là không có quy tắc nào bị vi phạm nếu chúng ta chèn quảng cáo tại vị trí đó (và nếu chúng ta nhận được điểm này chính xác tại điểm hết không gian, thì không cần phải tìm kiếm một vị trí tốt hơn). Nếu điểm số là "last-resort" (phương án cuối cùng), thì điểm ngắt thậm chí không phải là điểm ngắt hợp lệ, nhưng chúng ta vẫn có thể ngắt ở đó nếu không tìm thấy điểm nào tốt hơn để tránh tình trạng tràn fragmentainer.
Điểm ngắt hợp lệ thường chỉ xảy ra giữa các điểm ngắt đồng cấp (hộp dòng hoặc khối) và không xảy ra, ví dụ: giữa nhà xuất bản mẹ và con đầu tiên của nó (điểm ngắt lớp C là một ngoại lệ, nhưng chúng ta không cần thảo luận về các điểm ngắt đó ở đây). Ví dụ: có một điểm ngắt hợp lệ trước một khối đồng cấp có break-before:avoid, nhưng điểm ngắt này nằm ở đâu đó giữa "hoàn hảo" và "cuối cùng".
Trong quá trình bố cục, chúng ta theo dõi điểm ngắt tốt nhất được tìm thấy cho đến nay trong một cấu trúc có tên là NGEarlyBreak. Điểm ngắt sớm là một điểm ngắt có thể xảy ra trước hoặc bên trong nút khối hoặc trước một dòng (dòng vùng chứa khối hoặc dòng flex). Chúng ta có thể tạo một chuỗi hoặc đường dẫn của các đối tượng NGEarlyBreak, trong trường hợp điểm ngắt tốt nhất nằm đâu đó sâu bên trong thứ gì đó mà chúng ta đã đi qua trước đó tại thời điểm hết không gian. Ví dụ:
Trong trường hợp này, chúng ta hết không gian ngay trước #second
, nhưng nó có "break-before:avoid", nhận được điểm vị trí ngắt là "vi phạm break avoid". Khi đó, chúng ta có một chuỗi NGEarlyBreak "bên trong #outer
> bên trong #middle
> bên trong #inner
> trước "dòng 3"", với trạng thái "hoàn hảo". Vì vậy, chúng ta muốn dừng ở đó. Vì vậy, chúng ta cần quay lại và chạy lại bố cục từ đầu #outer (và lần này truyền NGEarlyBreak mà chúng ta tìm thấy) để có thể ngắt trước "dòng 3" trong #inner. (Chúng ta ngắt trước "dòng 3" để 4 dòng còn lại kết thúc trong trình phân mảnh tiếp theo và để tuân thủ widows:4
.)
Thuật toán được thiết kế để luôn ngắt ở điểm ngắt tốt nhất có thể (như được xác định trong thông số kỹ thuật) bằng cách loại bỏ các quy tắc theo đúng thứ tự, nếu không thể đáp ứng tất cả các quy tắc. Xin lưu ý rằng chúng ta chỉ phải bố trí lại tối đa một lần cho mỗi luồng phân mảnh. Khi chúng ta ở lượt truyền bố cục thứ hai, vị trí ngắt tốt nhất đã được truyền đến các thuật toán bố cục. Đây là vị trí ngắt được phát hiện trong lượt truyền bố cục đầu tiên và được cung cấp dưới dạng một phần của đầu ra bố cục trong vòng đó. Trong lượt truyền bố cục thứ hai, chúng ta sẽ không bố trí cho đến khi hết không gian – thực tế là chúng ta không được phép hết không gian (đó thực sự là một lỗi), vì chúng ta đã được cung cấp một vị trí siêu đẹp (tốt, đẹp nhất có thể) để chèn một điểm ngắt sớm, nhằm tránh vi phạm bất kỳ quy tắc ngắt nào không cần thiết. Vì vậy, chúng ta chỉ cần trình bày đến điểm đó rồi nghỉ.
Lưu ý rằng đôi khi chúng ta cần vi phạm một số yêu cầu tránh ngắt nếu điều đó giúp tránh tình trạng tràn fragmentainer. Ví dụ:
Ở đây, chúng ta hết dung lượng ngay trước #second
, nhưng lại có "break-before:tránh". Tương tự như ví dụ cuối cùng, thông báo này được dịch là "vi phạm break avoid". Chúng tôi cũng có một NGEarlyBreak với nội dung "góa phụ và trẻ mồ côi vi phạm" (bên trong #first
> trước "dòng 2"). Tuy nhiên, tình trạng này vẫn chưa hoàn hảo, nhưng hiệu quả hơn là "tránh ngắt lời vi phạm". Vì vậy, chúng ta sẽ ngắt dòng trước "dòng 2", vi phạm yêu cầu về dòng đơn độc / dòng góa. Quy cách này xử lý vấn đề này trong 4.4. Unforced Breaks (Điểm ngắt không bắt buộc), trong đó xác định những quy tắc ngắt nào sẽ bị bỏ qua trước tiên nếu chúng ta không có đủ điểm ngắt để tránh tình trạng tràn fragmentainer.
Kết luận
Mục tiêu chức năng của dự án phân mảnh khối LayoutNG là cung cấp việc triển khai hỗ trợ cấu trúc LayoutNG cho mọi thứ mà công cụ cũ hỗ trợ và ít nhất có thể, ngoài việc sửa lỗi. Trường hợp ngoại lệ chính là hỗ trợ tránh ngắt tốt hơn (ví dụ: break-before:avoid
), vì đây là phần cốt lõi của công cụ phân mảnh, vì vậy, bạn phải có từ đầu, vì việc thêm sau này sẽ có nghĩa là viết lại.
Giờ đây, khi quá trình phân mảnh khối LayoutNG đã hoàn tất, chúng ta có thể bắt đầu thêm chức năng mới, chẳng hạn như hỗ trợ các kích thước trang hỗn hợp khi in, hộp lề @page
khi in, box-decoration-break:clone
, v.v. Và như với LayoutNG nói chung, chúng tôi dự kiến tỷ lệ lỗi và gánh nặng bảo trì của hệ thống mới sẽ giảm đáng kể theo thời gian.
Lời cảm ơn
- Una Kravets vì "ảnh chụp màn hình thủ công" rất đẹp.
- Chris Harrelson đã giúp hiệu đính, đưa ra ý kiến phản hồi và đề xuất.
- Philip Jägenstedt về ý kiến phản hồi và đề xuất.
- Rachel Andrew đã chỉnh sửa và cung cấp hình ảnh ví dụ đầu tiên có nhiều cột.