Sự đánh đổi của CSS-in-JS

Ảnh của Artem Bali

Gần đây tôi đã viết một tổng quan cấp cao hơn về CSS-in-JS, chủ yếu nói về các vấn đề mà phương pháp này đang cố gắng giải quyết. Các tác giả thư viện hiếm khi đầu tư thời gian vào việc mô tả sự đánh đổi của giải pháp của họ. Đôi khi, nó rất khó vì họ quá thiên vị và đôi khi họ chỉ biết cách người dùng áp dụng công cụ này. Vì vậy, đây là một nỗ lực để mô tả sự đánh đổi mà tôi đã thấy cho đến nay. Tôi nghĩ điều quan trọng là phải đề cập rằng tôi là tác giả của JSS, vì vậy tôi nên được coi là thiên vị.

Tác động xã hội

Có một lớp người làm việc trên nền tảng web và không biết bất kỳ JavaScript nào. Những người đó được trả tiền để viết HTML và CSS. CSS-in-JS đã tạo ra một tác động lớn đến quy trình làm việc của các nhà phát triển. Một sự thay đổi thực sự không bao giờ có thể được thực hiện mà không có một số người bị bỏ lại phía sau. Tôi không biết rằng liệu CSS-in-JS có phải là cách duy nhất hay không, nhưng việc áp dụng đại trà là một dấu hiệu rõ ràng về các vấn đề khi sử dụng CSS trong các ứng dụng hiện đại.

Một phần lớn của vấn đề là chúng tôi không có khả năng giao tiếp chính xác các trường hợp sử dụng trong đó CSS-in-JS tỏa sáng và cách sử dụng nó đúng cách cho một nhiệm vụ. Nhiều người đam mê CSS-in-JS đã thành công trong việc quảng bá công nghệ, nhưng không nhiều nhà phê bình nói về sự đánh đổi một cách xây dựng, mà không có sự thay đổi giá rẻ ở các công cụ. Do đó, chúng tôi đã để lại nhiều sự đánh đổi và didn đã nỗ lực mạnh mẽ để đưa ra lời giải thích và cách giải quyết.

CSS-in-JS là một nỗ lực để làm cho các trường hợp sử dụng phức tạp dễ xử lý hơn, do đó, don đẩy nó ở nơi không cần thiết!

Chi phí thời gian chạy

Khi CSS được tạo từ JavaScript khi chạy, trong trình duyệt, có một chi phí cố hữu. Thời gian chạy khác nhau từ thư viện để thư viện. Đây là một điểm chuẩn chung tốt, nhưng hãy chắc chắn để thực hiện các bài kiểm tra của riêng bạn. Sự khác biệt chính trong thời gian chạy xuất hiện tùy thuộc vào nhu cầu phải phân tích cú pháp CSS đầy đủ của chuỗi mẫu, số lượng tối ưu hóa, chi tiết triển khai kiểu động, thuật toán băm và chi phí tích hợp khung. *

Bên cạnh chi phí thời gian chạy tiềm năng, bạn cần xem xét 4 chiến lược gói khác nhau, bởi vì một số thư viện CSS-in-JS hỗ trợ nhiều chiến lược và tùy thuộc vào người dùng để áp dụng chúng. *

Chiến lược 1: Chỉ tạo thời gian chạy

Tạo CSS thời gian chạy là một kỹ thuật tạo ra một chuỗi CSS trong JavaScript và sau đó tiêm chuỗi đó bằng cách sử dụng thẻ kiểu vào tài liệu. Kỹ thuật này tạo ra một Style Sheet, KHÔNG phải kiểu nội tuyến.

Sự đánh đổi của việc tạo thời gian chạy là không thể cung cấp nội dung theo kiểu ở giai đoạn đầu, khi tài liệu bắt đầu tải. Cách tiếp cận này thường phù hợp với các ứng dụng không có nội dung có thể hữu ích ngay lập tức. Thông thường, các ứng dụng như vậy yêu cầu tương tác của người dùng trước khi chúng thực sự có thể trở nên hữu ích cho người dùng. Thông thường các ứng dụng như vậy hoạt động với nội dung năng động đến mức nó trở nên lỗi thời ngay khi bạn tải nó, vì vậy bạn cần thiết lập một đường dẫn cập nhật sớm, ví dụ như Twitter. Ngoài ra, khi người dùng đăng nhập, không cần cung cấp HTML cho SEO.

Nếu tương tác yêu cầu JavaScript, gói cần phải được tải trước khi ứng dụng sẵn sàng. Ví dụ: bạn có thể hiển thị nội dung của một kênh mặc định khi tải Slack trong tài liệu, nhưng có khả năng người dùng sẽ muốn thay đổi kênh ngay sau đó. Vì vậy, nếu bạn tải các nội dung ban đầu chỉ để ném chúng đi ngay lập tức.

Hiệu suất cảm nhận của các ứng dụng như vậy có thể được cải thiện với trình giữ chỗ và các thủ thuật khác để cho ứng dụng cảm thấy tức thì hơn thực tế. Các ứng dụng như vậy thường là dữ liệu nặng, vì vậy chúng đã thắng được hữu ích nhanh như một bài viết.

Chiến lược 2: Tạo thời gian chạy với CSS quan trọng

CSS quan trọng là số lượng CSS tối thiểu được yêu cầu để định kiểu trang ở trạng thái ban đầu. Nó được kết xuất bằng cách sử dụng thẻ kiểu trong phần đầu của tài liệu. Kỹ thuật này được sử dụng rộng rãi có và không có CSS-in-JS. Trong cả hai trường hợp, bạn có khả năng tải gấp đôi các quy tắc CSS, một lần là một phần của CSS quan trọng và một lần là một phần của gói JavaScript hoặc CSS. Kích thước của CSS quan trọng có thể khá lớn tùy thuộc vào số lượng nội dung. Thông thường, tài liệu won won được lưu trữ.

Nếu không có CSS ​​quan trọng, một ứng dụng trang đơn nặng nội dung tĩnh với CSS-in-JS thời gian chạy sẽ phải hiển thị giữ chỗ thay vì nội dung. Điều này là xấu vì nó có thể hữu ích cho người dùng sớm hơn, cải thiện khả năng truy cập trên các thiết bị cấp thấp và cho các kết nối băng thông thấp.

Với CSS quan trọng, việc tạo CSS thời gian chạy có thể được thực hiện ở giai đoạn sau mà không chặn UI trong giai đoạn ban đầu. Mặc dù được cảnh báo, trên các thiết bị di động cấp thấp, có tuổi đời trên 5 năm, việc tạo CSS từ JavaScript có thể có tác động tiêu cực đến hiệu suất. Nó phụ thuộc rất nhiều vào số lượng CSS được tạo và thư viện được sử dụng, do đó, nó có thể được khái quát hóa.

Sự đánh đổi của chiến lược này là chi phí trích xuất CSS quan trọng và chi phí tạo CSS thời gian chạy.

Chiến lược 3: Chỉ trích xuất thời gian xây dựng

Chiến lược này là chiến lược mặc định trên web không có CSS-in-JS. Một số thư viện CSS-in-JS cho phép bạn trích xuất CSS tĩnh khi xây dựng. * Trong trường hợp này, không có chi phí thời gian chạy nào được tham gia, CSS được hiển thị trên trang bằng thẻ liên kết. Chi phí tạo ra CSS được trả trước một lần.

Có 2 sự đánh đổi lớn ở đây:

  1. Bạn có thể sử dụng một số API động mà CSS-in-JS cung cấp khi chạy vì bạn không có quyền truy cập vào trạng thái. Thường thì bạn vẫn có thể sử dụng các thuộc tính tùy chỉnh CSS, bởi vì chúng không được hỗ trợ trong mọi trình duyệt và không thể được điền vào lúc xây dựng. Trong trường hợp này, bạn sẽ phải thực hiện các cách giải quyết cho chủ đề động và kiểu dáng dựa trên trạng thái. *
  2. Nếu không có CSS ​​quan trọng và với bộ đệm trống, bạn sẽ chặn lớp sơn đầu tiên, cho đến khi gói CSS của bạn được tải. Một phần tử liên kết trong phần đầu của tài liệu sẽ chặn kết xuất HTML.
  3. Độ đặc hiệu không xác định với phân chia gói dựa trên trang trong các ứng dụng trang đơn. *

Chiến lược 4: Trích xuất thời gian xây dựng với CSS quan trọng

Chiến lược này cũng không phải là duy nhất đối với CSS-in-JS. Trích xuất hoàn toàn tĩnh với CSS quan trọng mang lại hiệu suất tốt nhất khi làm việc với ứng dụng tĩnh hơn. Cách tiếp cận này vẫn có sự đánh đổi nói trên của một CSS tĩnh, ngoại trừ việc thẻ liên kết chặn có thể được di chuyển xuống dưới cùng của tài liệu.

Có 4 chiến lược kết xuất CSS chính. Chỉ có 2 trong số đó là dành riêng cho CSS-in-JS và không ai trong số chúng áp dụng cho tất cả các thư viện.

Khả năng tiếp cận

CSS-in-JS có thể giảm khả năng truy cập khi sử dụng sai cách. Điều này sẽ xảy ra khi một trang web nội dung phần lớn tĩnh được triển khai mà không cần trích xuất CSS quan trọng để HTML có thể được vẽ trước khi gói JavaScript được tải và đánh giá. Điều này cũng có thể xảy ra khi một tệp CSS khổng lồ được hiển thị bằng cách sử dụng thẻ liên kết chặn trong phần đầu của tài liệu, đây là vấn đề phổ biến nhất hiện nay với việc nhúng truyền thống và không dành riêng cho CSS-in-JS.

Các nhà phát triển cần chịu trách nhiệm về khả năng tiếp cận. Vẫn còn một ý tưởng sai lầm mạnh mẽ rằng kết nối internet không ổn định là vấn đề của các nước yếu về kinh tế. Chúng ta có xu hướng quên rằng chúng ta có vấn đề kết nối mỗi ngày khi chúng ta bước vào một hệ thống đường sắt ngầm hoặc một tòa nhà lớn. Một kết nối di động không có cáp ổn định là một huyền thoại. Thậm chí không dễ để có kết nối WiFi ổn định, ví dụ, mạng WI-FI 2,4 GHz có thể bị nhiễu từ lò vi sóng!

Chi phí CSS quan trọng với kết xuất phía máy chủ

Để có được trích xuất CSS quan trọng cho CSS-in-JS, chúng ta cần SSR. SSR là một quá trình tạo HTML cuối cùng cho trạng thái nhất định của một ứng dụng trên máy chủ. Trong thực tế, nó có thể là một quá trình khá phức tạp và tốn kém. Nó đòi hỏi một số chu kỳ CPU nhất định trên máy chủ cho mỗi yêu cầu HTTP.

CSS-in-JS thường thúc đẩy thực tế rằng nó được nối vào đường dẫn kết xuất HTML. * Nó biết HTML nào được kết xuất và CSS cần gì để có thể tạo ra số lượng tối thiểu tuyệt đối của nó. CSS quan trọng bổ sung thêm chi phí bổ sung cho kết xuất HTML trên máy chủ vì CSS cũng cần được biên dịch thành chuỗi CSS cuối cùng. Trong một số trường hợp, thật khó hoặc thậm chí không thể lưu trữ trên máy chủ.

Kết xuất hộp đen

Bạn cần lưu ý về cách thư viện CSS-in-JS bạn đang sử dụng đang hiển thị CSS của bạn. Ví dụ, mọi người thường không biết làm thế nào các Thành phần được tạo kiểu và Cảm xúc thực hiện các kiểu động. Kiểu động là một cú pháp cho phép sử dụng các hàm JavaScript bên trong khai báo kiểu của bạn. Các hàm này chấp nhận đạo cụ và trả về một khối CSS.

Để giữ tính đặc hiệu của thứ tự nguồn nhất quán, cả hai thư viện có tên ở trên đều tạo quy tắc CSS mới nếu nó chứa khai báo động và cập nhật thành phần với các đạo cụ mới. Để chứng minh điều tôi muốn nói, tôi đã tạo ra hộp cát này. Trong JSS, chúng tôi đã quyết định thực hiện một sự đánh đổi khác, cho phép chúng tôi cập nhật các thuộc tính động mà không tạo ra các quy tắc CSS mới. *

Dốc học

Đối với những người quen thuộc với CSS, nhưng chưa quen với JavaScript, khối lượng công việc ban đầu để tăng tốc với CSS-in-JS có thể khá lớn.

Bạn không cần phải là một nhà phát triển JavaScript chuyên nghiệp để viết CSS-in-JS, cho đến khi logic phức tạp được tham gia. Chúng ta có thể nói chung về sự phức tạp của kiểu dáng, vì nó thực sự phụ thuộc vào trường hợp sử dụng. Trong trường hợp CSS-in-JS trở nên phức tạp, có khả năng việc triển khai với vanilla CSS sẽ còn phức tạp hơn nữa.

Để tạo kiểu CSS-in-JS cơ bản, người ta cần biết cách khai báo các biến, cách sử dụng chuỗi mẫu và nội suy các giá trị JavaScript. Nếu ký hiệu đối tượng được sử dụng, người ta cần biết cách làm việc với các đối tượng JavaScript và cú pháp dựa trên đối tượng dành riêng cho thư viện. Nếu liên quan đến kiểu dáng động, người ta cần biết cách sử dụng các hàm và điều kiện JavaScript.

Nhìn chung, có một đường cong học tập, chúng ta có thể từ chối nó. Tuy nhiên, đường cong học tập này thường không lớn hơn nhiều so với học Sass. Trong thực tế, tôi đã tạo ra khóa học egghead này để chứng minh điều này.

Không có khả năng tương tác

Hầu hết các lib CSS-in-JS không thể tương tác. Điều này có nghĩa là các kiểu được viết bằng một thư viện có thể được kết xuất bằng thư viện khác. Thực tế, điều đó có nghĩa là bạn có thể chuyển đổi toàn bộ ứng dụng của mình một cách dễ dàng từ cách triển khai này sang cách khác. Điều đó cũng có nghĩa là bạn có thể dễ dàng chia sẻ UI của mình trên NPM mà không cần đưa thư viện CSS-in-JS vào gói của người tiêu dùng trừ khi bạn có trích xuất tĩnh thời gian xây dựng cho CSS của mình.

Chúng tôi đã bắt đầu làm việc với định dạng ISTF được cho là sẽ khắc phục vấn đề này, nhưng thật không may, chúng tôi đã không có thời gian để đưa nó về trạng thái sẵn sàng sản xuất. *

Tôi nghĩ rằng việc chia sẻ các thành phần UI bất khả tri có thể tái sử dụng trong phạm vi công cộng vẫn là một vấn đề thường khó giải quyết.

Rủi ro bảo mật

Có thể giới thiệu các rò rỉ bảo mật với CSS-in-JS. Giống như với bất kỳ ứng dụng phía máy khách nào, bạn cần phải thoát đầu vào của người dùng trước khi kết xuất nó.

Bài viết này sẽ cung cấp cho bạn cái nhìn sâu sắc hơn và một số ví dụ sai lệch.

Tên lớp không thể đọc được

Một số người vẫn nghĩ rằng điều quan trọng là chúng tôi giữ các tên lớp có thể đọc có ý nghĩa trên web. Hiện tại, nhiều thư viện CSS-in-JS cung cấp các tên lớp có ý nghĩa dựa trên tên khai báo hoặc tên thành phần trong chế độ phát triển. Một số trong số họ thậm chí cho phép bạn tùy chỉnh chức năng tạo tên lớp.

Trong chế độ sản xuất, hầu hết trong số họ tạo ra các tên ngắn hơn cho tải trọng nhỏ hơn. Đây là một sự đánh đổi người dùng của thư viện phải thực hiện và tùy chỉnh thư viện nếu cần.

Phần kết luận

Sự đánh đổi tồn tại, và tôi có lẽ đã không đề cập đến tất cả chúng. Nhưng hầu hết trong số họ không áp dụng phổ biến cho tất cả CSS-in-JS. Chúng phụ thuộc vào thư viện bạn sử dụng và cách bạn sử dụng nó.

* Nó sẽ mất một bài viết dành riêng để giải thích câu này. Hãy cho tôi biết trên Twitter (@ oleg008) về cái nào bạn muốn đọc thêm.