paint-brush
Bây giờ là năm 2023, nhưng chúng ta vẫn cần nói về các kiểu lồng nhau trong CSStừ tác giả@lastcallofsummer
3,720 lượt đọc
3,720 lượt đọc

Bây giờ là năm 2023, nhưng chúng ta vẫn cần nói về các kiểu lồng nhau trong CSS

từ tác giả Olga Stogova8m2023/07/17
Read on Terminal Reader

dài quá đọc không nổi

Các kiểu lồng sâu thường dẫn đến xung đột về kiểu, đặc biệt nếu bạn có một dự án lớn. Điều này có thể dẫn đến sự không nhất quán về hình ảnh không mong muốn và rất nhiều thời gian lãng phí. Khái niệm về tính đặc hiệu (hoặc 'trọng lượng' của các kiểu) trong CSS rất quan trọng để hiểu tại sao việc lồng sâu có thể gây rắc rối.
featured image - Bây giờ là năm 2023, nhưng chúng ta vẫn cần nói về các kiểu lồng nhau trong CSS
Olga Stogova HackerNoon profile picture
0-item

Ngay cả một hình ảnh ngẫu nhiên cho yêu cầu "kiểu CSS" cũng chứa các kiểu lồng nhau.


Là một nhà phát triển giao diện người dùng với gần 15 năm kinh nghiệm, tôi đã trực tiếp chứng kiến sự phát triển của phát triển web. Đối với tôi, đã qua một chặng đường dài kể từ thời tải lên các tệp đã sửa đổi qua FTP (vâng, GitHub đã ra mắt cách đây 15 năm, nhưng tôi chỉ phát hiện ra nó vào năm 2011) đến kỷ nguyên hiện đại của giao diện đáp ứng, thư viện giao diện người dùng và trang web được tạo trực tiếp từ Figma.


Tuy nhiên, tôi vẫn gặp các dự án sử dụng các kiểu lồng nhau như:


 .some-class ul li div a { /* some style */ }


hoặc,


 #nav .nav-link svg { /* some style */ }


Nghe có vẻ gây sốc, nhưng những phương pháp mã hóa như vậy tràn ngập mọi thứ, từ các dự án trị giá hàng triệu đô la, đang phát triển nhanh chóng cho đến các công ty khởi nghiệp khiêm tốn.


Hãy đi sâu vào lý do tại sao phương pháp này có thể gây ra vấn đề.

Xung đột của các kiểu lồng nhau

Các kiểu lồng sâu thường dẫn đến xung đột về kiểu, đặc biệt nếu bạn có một dự án lớn. CSS, dưới dạng biểu định kiểu xếp tầng, xếp tầng xuống và áp dụng cho các thành phần tùy thuộc vào tính đặc hiệu của chúng. Các kiểu được lồng sâu có thể vô tình ghi đè lên các kiểu khác do tính đặc thù của chúng.


Hãy xem xét ví dụ này:


 .some-class ul li div a { color: red; } ... .some-class a { color: blue; }


Bạn có thể mong đợi tất cả các liên kết trong .some-class có màu xanh lam. Tuy nhiên, do tính đặc hiệu cao hơn của quy tắc đầu tiên, bất kỳ liên kết nào được lồng trong ul > li > div sẽ có màu đỏ, không phải màu xanh lam. Điều này có thể dẫn đến sự không nhất quán về hình ảnh không mong muốn và rất nhiều thời gian lãng phí để gỡ lỗi.

Sự phức tạp của tính cụ thể

Hiểu khái niệm về tính đặc hiệu (hoặc 'trọng số' của các kiểu) trong CSS là rất quan trọng để hiểu tại sao việc lồng sâu có thể gây rắc rối. Tính cụ thể xác định quy tắc CSS nào được áp dụng nếu nhiều quy tắc cạnh tranh cho một phần tử. Nó được tính toán dựa trên loại và số lượng bộ chọn.


Độ đặc hiệu được tính toán dựa trên hệ thống trọng số bốn loại:


  1. kiểu nội tuyến
  2. ID
  3. Lớp, thuộc tính và lớp giả
  4. Các phần tử và phần tử giả


Ở đây, hãy xem xét quy tắc:


 body #content .data img:hover { /* some style */ }


Độ đặc hiệu là 0 1 2 2 . Đó là một ID ( #content ), hai lớp ( .data:hover ) và hai phần tử ( bodyimg ).


Bây giờ, hãy xem xét quy tắc:


 #nav .nav-link svg { /* some style */ }


Độ đặc hiệu ở đây là 0 1 1 1 . Đó là một ID ( #nav ), một lớp ( .nav-link ) và một phần tử ( svg ).


Tính đặc hiệu không hoạt động trên hệ thống "chuyển tiếp" như số thập phân truyền thống. Ví dụ: bộ chọn có độ đặc hiệu 0 1 0 11 không bằng độ đặc hiệu 0 1 1 1 mặc dù trong hệ thập phân, 111+1 sẽ tương đương nhau.


Cuối cùng, bộ chọn chung ( * ), bộ kết hợp ( + , > , ~ , '') và lớp giả phủ định ( :not() ) không ảnh hưởng đến tính đặc hiệu. Tuy nhiên, bên trong đối số :not() , bộ chọn được tính như bình thường.


Đối với những người học trực quan, tôi đề xuất video này về Tính đặc hiệu của CSS.


Hiểu tính đặc hiệu của CSS và cách nó được tính toán cho phép bạn viết CSS tốt hơn, dễ dự đoán hơn và gỡ lỗi các vấn đề khi các kiểu không được áp dụng như mong đợi.

Quy tắc !important và tính đặc hiệu

Đôi khi, các nhà phát triển sử dụng quy tắc !important khi gặp khó khăn với các xung đột về tính đặc hiệu của CSS. Quy tắc này làm cho một thuộc tính CSS cực kỳ cụ thể, có nghĩa là nó sẽ ghi đè hầu hết mọi khai báo khác.


Ví dụ:


 #nav .nav-link svg { color: blue; } .nav-link svg { color: red !important; }


Mặc dù quy tắc đầu tiên có độ đặc hiệu cao hơn do bộ chọn ID, nhưng màu của svg sẽ có màu đỏ do !important trong quy tắc thứ hai.

Rủi ro của việc lạm dụng !quan trọng

Mặc dù !important có thể là một cách khắc phục nhanh chóng khi gặp khó khăn với các vấn đề về tính đặc hiệu, nhưng bạn không nên sử dụng nó một cách rộng rãi. Việc sử dụng quá mức có thể ảnh hưởng đến khả năng bảo trì, khả năng dự đoán và hiệu suất. Trong các dự án lớn hơn, việc lạm dụng !important thường cho thấy khó khăn trong việc quản lý tính đặc hiệu của CSS. Thay vì dùng đến !important , tốt hơn hết bạn nên đầu tư thời gian vào việc tái cấu trúc CSS của mình và giảm việc sử dụng các bộ chọn quá cụ thể.


Bạn có thể kiểm tra sản phẩm của mình ngay bây giờ 🙂. Tôi đã kiểm tra của tôi:


111 kết quả trong 55 tệp, không tệ đối với một sản phẩm lớn!


Trong khi !important có thể là một sửa chữa nhanh hấp dẫn; nó giống như dùng búa tạ để đập vỡ hạt. Một cách tiếp cận dễ bảo trì hơn là giữ cho bộ chọn của bạn đơn giản và phẳng nhất có thể, điều này giúp CSS của bạn dễ hiểu, quản lý và mở rộng hơn trong tương lai. Và hãy nhớ rằng, cách tốt nhất để giành chiến thắng trong một cuộc chiến !important không phải là bắt đầu một cuộc chiến ngay từ đầu.

Duyệt qua cây CSS

Một vấn đề khác với các kiểu lồng sâu là tác động hiệu suất đối với kết xuất trình duyệt. Khi một trình duyệt áp dụng các kiểu cho một phần tử, nó sẽ duyệt DOM từ phải sang trái, bắt đầu bằng bộ chọn khóa (trong ví dụ của chúng tôi là asvg ) và di chuyển qua phần tử tổ tiên cho đến khi tìm thấy kết quả khớp hoặc lên đến đỉnh. Kiểu càng lồng nhau, quá trình truyền tải này càng mất nhiều thời gian, có khả năng ảnh hưởng đến hiệu suất và làm chậm thời gian tải trang trong các dự án quy mô lớn.


Khi bạn chỉ định một quy tắc CSS, chẳng hạn như:


 .some-class ul li a { /* some style */ }


Bạn có thể hình dung quy tắc này bắt đầu từ dưới cùng của cây (từ thẻ a ) và đi lên trên cây (thông qua li , ul.some-class ).



chỉ là một phần nhỏ của CSS Object Model



Trước tiên, trình duyệt sẽ tìm kiếm tất cả (ý tôi là TẤT CẢ) phần tử a , sau đó trình duyệt sẽ kiểm tra xem các thẻ a này có nằm trong phần tử li hay không. Sau đó, nó sẽ kiểm tra xem các phần tử li này có nằm trong ul Và cuối cùng, nó sẽ kiểm tra xem các ul ul này có nằm trong một phần tử có lớp là .some-class hay không.


Đây là cách trình duyệt đọc các bộ chọn CSS và lý do tại sao các bộ chọn phức tạp có thể dẫn đến việc hiển thị trang chậm hơn. Trình duyệt phải thực hiện nhiều kiểm tra cho từng thành phần để xem nó có phù hợp với quy tắc đã chỉ định hay không. Quy tắc càng sâu, trình duyệt càng phải thực hiện nhiều kiểm tra, điều này có thể ảnh hưởng đến hiệu suất.

Thực tiễn tốt hơn để quản lý CSS trong các dự án lớn hơn

Mô-đun CSS

Mô-đun CSS cho phép bạn viết CSS trong các mô-đun riêng lẻ, được đặt trong phạm vi cục bộ cho thành phần bạn đang làm việc. Điều này có nghĩa là các kiểu trong mô-đun CSS chỉ áp dụng cho mô-đun cụ thể đó và sẽ không bị rò rỉ hoặc ảnh hưởng đến các thành phần khác trên trang.


Hãy khám phá cách Mô-đun CSS sử dụng tên lớp được băm để đảm bảo đóng gói kiểu. Khi bạn đang sử dụng Mô-đun CSS, tên lớp bạn xác định trong tệp CSS của mình sẽ được băm tại thời điểm biên dịch. Băm này tạo ra một tên lớp duy nhất tương ứng với thành phần của bạn. Hãy xem một ví dụ:


Giả sử bạn có một Mô-đun CSS được định nghĩa như sau:


 /* Button.module.css */ .button { color: white; background-color: blue; }


Và bạn sử dụng nó trong thành phần của mình như thế này (tôi thích nhập đối tượng kiểu dưới dạng s thay vì styles — một mẹo nhanh giúp tiết kiệm thời gian nhập và tăng hiệu quả viết mã):


 import React from 'react'; import s from './Button.module.css'; const Button = () => { return ( <button className={s.button}>Click me</button> ); }; export default Button;


Khi ứng dụng của bạn được biên dịch, HTML được hiển thị của bạn có thể trông giống như sau:


 <button class="Button_button__3FQ9Z">Click me</button>


Trong trường hợp này, Button_button__3FQ9Z là tên lớp băm được tạo từ Mô-đun CSS của bạn. Lưu ý rằng cấu trúc và độ dài chính xác của hàm băm có thể thay đổi tùy theo cấu hình dự án của bạn.


Tên lớp duy nhất này đảm bảo rằng các kiểu bạn đã xác định trong Button.module.css chỉ áp dụng cho nút này và sẽ không ảnh hưởng đến bất kỳ thành phần nào khác trong ứng dụng của bạn. Nó cũng đảm bảo rằng không có kiểu nào khác có thể ảnh hưởng đến nút này trừ khi chúng nhắm mục tiêu rõ ràng đến tên lớp được băm. Việc đóng gói các kiểu này là một trong những lợi ích chính của Mô-đun CSS.

Thư viện CSS-in-JS

Một cách phổ biến khác để xử lý CSS là sử dụng các thư viện CSS-in-JS, chẳng hạn như các thành phần được tạo kiểu hoặc cảm xúc . Các thư viện này cho phép bạn viết CSS trực tiếp trong JavaScript của mình, điều này có một số lợi ích:


  1. Các kiểu có phạm vi : Tương tự như các Mô-đun CSS, các kiểu được đặt trong phạm vi thành phần mà chúng được xác định.
  2. Kiểu động : Thật dễ dàng để tạo kiểu động dựa trên các đạo cụ hoặc trạng thái trong thành phần của bạn.
  3. Cải thiện trải nghiệm của nhà phát triển : Bạn có thể sử dụng logic JavaScript trực tiếp trong các kiểu của mình và mọi thứ liên quan đến một thành phần đều được đặt ở một nơi, điều này có thể giúp mã của bạn dễ hiểu và dễ làm việc hơn.


Đây là một ví dụ về cách bạn có thể sử dụng styled-components trong ứng dụng React:


 import React from 'react'; import styled from 'styled-components'; const Button = styled.button` color: white; background-color: ${(props) => props.primary ? 'blue' : 'gray'}; `; const App = () => { return ( <div> <Button primary>Primary Button</Button> <Button>Secondary Button</Button> </div> ); }; export default App;


Trong ví dụ này, thành phần Button có các kiểu động thay đổi dựa trên chỗ dựa primary của nó.

Phương pháp BEM

Nếu bạn không làm việc với khung JavaScript hỗ trợ Mô-đun CSS hoặc tương tự, bạn vẫn có thể quản lý CSS của mình một cách hiệu quả bằng cách sử dụng các phương pháp đặt tên như BEM (Khối, Phần tử, Công cụ sửa đổi).


BEM là viết tắt của "Block Element Modifier," và đó là một phương pháp giúp bạn tạo các thành phần có thể tái sử dụng và chia sẻ mã trong CSS. Đây là cách bạn có thể cấu trúc CSS của mình bằng BEM:


 /* Block */ .top-menu { } /* Element */ .top-menu__item { } /* Modifier */ .top-menu__item_active { }


Trong BEM, 'Khối' là thực thể độc lập có ý nghĩa riêng, 'Phần tử' là một phần của Khối không có ý nghĩa độc lập và được gắn về mặt ngữ nghĩa với Khối của nó và 'Công cụ sửa đổi' là một cờ trên một Khối hoặc Phần tử được sử dụng để thay đổi hình thức hoặc hành vi.


Sử dụng một phương pháp nhất quán như BEM có thể giúp CSS của bạn dễ hiểu và dễ bảo trì hơn, đặc biệt là trên các dự án lớn hơn.

Tóm lại là

Có một số cách để quản lý CSS trong các dự án lớn hơn, từ Mô-đun CSS và thư viện CSS-in-JS cho đến các phương pháp đặt tên như BEM. Điều quan trọng là tìm ra một cách tiếp cận phù hợp với nhóm và dự án của bạn và áp dụng nó một cách nhất quán. Hãy nhớ rằng, viết CSS cũng giống như viết mã hiệu quả và có hiệu suất cao cũng như viết mã dễ hiểu và có thể bảo trì.


Chúc mừng mã hóa!