Thư viện lớp tiện ích css

Bạn còn nhớ lần đầu bạn viết CSS giống như thế nào không?

a {
  text-decoration: none;
}

p {
  color: rainbow;
}

Sau đó bạn biết thêm về ID và lớp học

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}

Và bạn học được cách xử lý pseudo selector, hoặc khai báo cho các phần tử anh chị em con cháu họ hàng, v. v

#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}

Rồi khi bạn đã quen với CSS và bắt tay vào làm dự án thực tế, bạn bàng hoàng nhận ra kẻ thù không ở đâu xa, chúng đang loang lổ vởn quanh ta mà. Bạn bước vào cuộc chiến gọi là "đặc công chiến", đánh nhau tơi bời khói lửa với đẳng cấp được viết bởi các chiến hữu trong team. Quả là một trận đấu kinh hoàng khi ai cũng muốn lật ngược cổ (đè) người đi trước. Kẻ nắm giữ

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
4 trong tay cứ ngỡ đã gần với chiến thắng, nào ngờ xuất hiện tiểu nhân dùng
#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
5. Tình anh em sứt mẻ, chiến hữu quay đầu không nhìn nhau. Bạn ức chế và gào lên "đậu khổ CSS 🥜"

Cụ thể là gì?

Tính cụ thể là một số quan trọng mà trình duyệt sử dụng để quyết định kiểu CSS nào sẽ được áp dụng cho các thành phần. Tính đặc hiệu được tính toán dựa trên bộ chọn loại phân loại và bộ chọn số lượng áp dụng lên một phần tử. Bạn có thể đọc thêm về chủ đề này tại đây hoặc trên trang MDN. http. // tính đặc hiệu của css. com minh họa khá cụ thể về tính đặc hiệu

#header { margin: 10px; text-align: center; color: fabulous; } .text { font-weight: 700; font-size: 20px; }6

Dân tay đối mặt đọc đến đây có lẽ đang âm thầm (hoặc công khai) cười khẩy. "Ai kêu gà, xài BEM là được rồi". Chíp chíp 🐥

Cho những bạn chưa biết

BEM - Block Element Modifier là một phương pháp đặt tên lớp CSS được phát triển bởi Yandex. Theo lý thuyết, BEM giúp bạn xây dựng các lớp theo từng khối, mỗi khối lại có phần tử con, và các phần tử này có thể sẽ có giao diện khác nhau tùy thuộc vào công cụ sửa đổi của nó

Ví dụ đây là CSS

/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}

Áp dụng vào HTML

<button class="btn btn--big btn--green">
  <span class="btn__price">$9.99span>
button>

Hoàn toàn không sai. BEM là một phương pháp hiệu quả để chia nhỏ trang thành từng thành phần, và bạn hoàn toàn có thể thoát khỏi cuộc chiến thánh ở trên bằng cách chỉ sử dụng lớp được đặt tên theo BEM. Ngoài ra, khi một thành phần không còn được sử dụng nữa, bạn có thể tự động xóa lớp của nó mà không sợ ảnh hưởng đến các thành phần khác

Tuy nhiên BEM cũng có những vấn đề "khó chịu" mà bạn có thể xem thêm tại bài viết Chiến BEM CSS. 10 vấn đề thường gặp và cách tránh chúng. Kinh nghiệm cá nhân là khi làm việc với BEM, có thể bạn sẽ bỏ qua khoảng thời gian khác chỉ để suy nghĩ về ngữ nghĩa (ngữ nghĩa) của lớp. Bạn sẽ phải cân nhắc việc chặn này nên đặt tên là gì, những thành phần con của nó nên là phần tử hay là một thành phần khác, rồi phần tử này nên có tên chi, nên gọi nó là

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
7,
#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
8, hay
#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
9, v. v. Đừng coi thường việc đặt tên nhé, một trong những vấn đề khó nhai nhất của khoa học máy tính đấy. Ngoài ra, tên lớp thường xuất hiện liền kề với cấu trúc/nội dung HTML mà nó được sử dụng, dẫn đến việc khi mã cấu trúc lại (chuyển thành thành phần tổng quan hơn), chúng ta phải tốn thời gian suy nghĩ tên khác để hợp lý

Với mình, việc suy nghĩ thêm về ngữ nghĩa cho lớp CSS không đánh lại hiệu quả đáng kể. Vì không giống như HTML, trình duyệt và trình thu thập dữ liệu không quan tâm nên bạn đặt tên lớp có ý nghĩa hay không. Chúng chỉ có giá trị với lập trình viên, và thông thường thì chúng ta chỉ muốn viết HTML/CSS sao cho giống với thiết kế từ designer nhất mà thôi

Tính năng tái sử dụng và kích thước tệp CSS

Mặt khác, tính chất "cascading" của CSS vốn được sinh ra để hỗ trợ tái sử dụng mã lại là một con dao 2 trò chơi và mang đến sự phụ thuộc cho nhiều người biết. Xếp tầng cùng với tính đặc hiệu làm cho CSS trở nên khó dự đoán và lời khuyên là hạn chế xếp tầng được bao nhiêu hay nhiều ít. Điều này dẫn đến tệp CSS chứa nhiều cảnh báo bị trùng lặp

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
1

Khi dự án của bạn phát triển, nhiều thành phần xuất hiện đồng nghĩa với kích thước tệp CSS ngày càng tăng. Đáng buồn là, không phải tất cả CSS được gửi xuống cho người dùng sẽ thực sự được sử dụng trong trang

Thư viện lớp tiện ích css
Kích thước tệp CSS của các trang web lớn nhất, tính đến tháng 11 năm 2016. Nguồn. [1]

Vậy thì Atomic CSS đã giải quyết được những vấn đề trên à?

Có thể. Nhưng trước hết hãy xem Atomic CSS là gì đã

CSS nguyên tử là cách khai báo các lớp sao cho mỗi lớp chỉ mô tả một tính năng duy nhất. Để xây dựng thành phần lớn hơn, chúng ta sẽ kết hợp các nguyên tử lớp này lại với nhau. các hạn định như

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
2

Trong ví dụ trên, lớp

#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
0 chỉ làm duy nhất một công việc là đổi chữ sang màu trắng,
#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
1 sẽ thiết lập nền sang màu xanh, trong khi
#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
2 chỉnh sửa phần đệm ở bên trái và phải (trục x/ hoành) thành 10px. Một thành phần được viết theo CSS nguyên tử sẽ giống như thế này

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
6

Xem Pen BEM so với CSS nguyên tử của Ehkoo (@ehkoo) trên CodePen

Atomic CSS đang được sử dụng bởi các công ty như npm, StackOverflow, Heroku, v. v

Chuyện bên lề. CSS chức năng, CSS nguyên tử, hay CSS ưu tiên tiện ích?

"Functional CSS" là cái tên đầu tiên mình bắt gặp khi tìm hiểu về cách viết CSS này. Từ "chức năng" ngoài nghĩa như trong "lập trình chức năng" còn có nghĩa là "hoạt động" (trích từ điển Oxford). Do đó "CSS chức năng" có thể hiểu là "CSS hoạt động được", hoàn toàn không liên quan đến ý tưởng chính. set CSS class to each small function

Một tên gọi khác là "CSS nguyên tử", theo nghĩa mỗi lớp là một "nguyên tử" độc lập. Tên gọi này rất phù hợp với tiêu chí chia lớp nhỏ, nhưng thật đáng tiếc khi nó "có thể" nhầm lẫn là có liên quan đến phương thức Atomic Design. Ngoài ra, còn có một thư viện của Yahoo. cũng có tên là Atomic CSS

Cuối cùng, "CSS đầu tiên của tiện ích" có thể là tên gọi mô tả chính xác nhất. "Utility-first" mang nghĩa "tập trung xây dựng các lớp hỗ trợ". Điểm trừ của thuật ngữ này là…tên dài quá

Suy đi xét lại thiệt hơn thì trong bài viết này mình chọn "atomic CSS" ( chữ "a" viết thường) vì…gõ nhanh thôi. But you to mean is 3 thuật ngữ này đều sử dụng chung cho một cách viết CSS nhé

Vì vậy có gì hay?

Cảnh binh đao

Lợi ích đầu tiên cũng giống như BEM, Atomic CSS chỉ cho phép khai báo các lớp nên chúng không xảy ra tranh chấp đặc thù. Đồng thời vì mỗi lớp chỉ mô tả một tính năng duy nhất, việc các tính năng thuộc tính phơi chân của nhau được giới hạn ở mức thấp nhất

Còn vẫn ghét nhau quá, muốn đạp nhau cho chết thì đây

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
7

Tín dụng. @huytd

File CSS nhẹ hơn

Bằng cách xây dựng thành phần bằng các lớp nguyên tử, bạn không cần phải lặp lại các khai báo đã có. Khi nhận được thiết kế cho thành phần mới, bạn chỉ cần kết hợp các lớp đã có sẵn với nhau hoặc viết thêm các lớp nguyên tử. Những lớp mới xuất hiện sẽ tiếp tục được tái sử dụng nên số lượng lớp mà bạn thêm vào sẽ ngày càng giảm đi khi tuổi thọ của dự án dài ra. Trong bài viết By The Numbers. Một năm rưỡi với Atomic CSS, John Polacek đã thử nghiệm chuyển đổi từ CSS truyền thống sang CSS nguyên tử và kết quả là dung lượng file giảm từ 123. 1KB xuống còn 72. 7KB (chưa nén gzip)

Thư viện lớp tiện ích css

Kết quả cuối cùng là càng ngày bạn sẽ càng viết lại ít CSS

Không được suy nghĩ chuyện đặt tên lớp

Vì các lớp nguyên tử được đặt tên gần với thuộc tính của chúng, bạn không cần phải suy nghĩ nên đặt tên gì. Một lợi ích nữa là nhìn vào HTML mà bạn có thể tương đối biết được phong cách của một phần tử. Lấy ví dụ

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
8

Với BEM, bạn có thể phỏng đoán "à cái nút này là cái nút màu xanh để chơi nè", nhưng "bự" là cái gì "bự",

#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
3 hay
#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
4, và cái gì "xanh", chữ xanh, nền xanh hay . Khi đi đến
#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
5 thì hoàn toàn không thể biết được phong cách của nó. So sánh với CSS nguyên tử, bạn có thể đọc ngay là "nút này có viền 1px, viền màu xanh, nền xanh, chữ trắng, bo tròn góc 5px, lề ở mặt bóng 10px, phông chữ ở mức 3, chữ được chuyển thành chữ . Tương tự với thẻ SPAN, bạn có thể đọc là "nền đen có độ mờ 0. 4, chữ trắng, padding trái phải 12px, font đậm 600, bo tròn góc bên trái 5px"

Liệu việc đọc phong cách như thế này có cần thiết không?

Xây dựng nguyên mẫu nhanh hơn

Prototyping, hay là chuyện làm những giao diện giả để kiểm tra UI/UX, không còn xa lạ với dân làm frontend nữa. Một trong những lý do khiến bạn chọn Bootstrap, Foundation, hay Bulma. cho dự án vì đơn giản chúng tôi cung cấp các thành phần được xây dựng sẵn, cùng với lưới hệ thống và các lớp hỗ trợ. Nhưng rồi bạn sẽ lâm vào cảnh đánh nhau với các class của framework để tùy biến cho phù hợp với thiết kế chuẩn. Kết quả ra sao thì bạn biết rồi đấy

Các framework được thiết kế theo hướng nguyên tử không có nhiều thành phần, nhưng bù lại chúng không đặt quá nhiều ý kiến ​​riêng và buộc bạn phải làm theo. Hầu hết tất cả đều cho phép bạn tùy chỉnh màu sắc, kích thước theo ý muốn, từ đó bạn có thể xây dựng lên những thành phần cần thiết

Un mode

Lớp Bùng nổ

Tachyons là một trong những framework CSS nguyên tử phổ biến nhất. In version 4. 10. 0, Tachyons hỗ trợ 37 màu. Giả sử mỗi màu sẽ có lớp tương ứng với chữ màu, màu nền, màu viền, hover đổi màu chữ, hover đổi màu nền và hover đổi màu viền. Tachyons có 3 điểm dừng. Như vậy tổng số lớp được tạo ra là 37 x 8 (thuộc tính) x 3 = 888 lớp. Nếu bạn phải sử dụng nhiều màu hơn, ví dụ như bảng màu của Material Design, hỗ trợ nhiều thuộc tính và bộ chọn giả hơn, thêm một số điểm ngắt nữa, bạn cũng có thể đoán được số lượng lớp vật liệu ra như thế nào

Thực tế là không phải tất cả các màu lớp đều được sử dụng, nên việc tạo lớp để bao gồm tất cả các trường hợp là không cần thiết. Cách giải quyết ở đây chỉ là viết thêm lớp khi bạn thực sự cần đến nó

Tìm và thay đổi lớp theo yêu cầu mới khó hơn

Giả sử một ngày đẹp trời nào đó, đồng chí Nguyễn Văn Xài Không quyết định nền màu xanh của tất cả các nút phải đậm hơn một chút, nút to nghĩa là cỡ chữ phải ở mức 2. Với BEM, bạn chỉ cần thay đổi giá trị của lớp

#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
6 và
#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
7 là xong. Trong khi đó, bạn không thể thay đổi mã màu của
#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
8 bởi vì thay đổi này chỉ được áp dụng trên nút và biết đâu là màu cũ vẫn được sử dụng ở nơi khác. Bạn cũng không thể tùy chỉnh tăng cỡ chữ của
#header > a:first-child {
  color: unicorn;
}

#header p > a.text::before {
  content: '⛓';
  font-family: Comic Sans;
}
9. Giải pháp an toàn nhất là tìm tất cả các nút, xóa lớp cũ và thêm vào
/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
0. Bạn có tìm thấy các nút bằng cách nào không?

Nếu dự án của bạn sử dụng thành phần React, Vue, hay các thư viện hỗ trợ (web) khác, việc thay đổi này không thành vấn đề. Còn với HTML thuần thì…coi bộ cực đó. Bạn có thể đặt tên cho thành phần giao diện người dùng bằng

/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
4, nhưng như vậy việc đánh dấu sẽ gặp rắc rối và ở phía người dùng, các thuộc tính này hoàn toàn không được sử dụng đến

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
0

Nghi ngờ cộng đồng

Thành thật mà nói, CSS nguyên tử rất không tự nhiên khi tiếp xúc lần đầu tiên. Chúng ta đã quá quen với cách viết CSS truyền thống/ BEM, và khi nhìn thấy một stack class đi chung với những cái tên xa lạ như

/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
5,
/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
6, hay
/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
7, nghi ngờ là phản ứng rất dễ hiểu

Vì vậy, có bất kỳ phong cách nội tuyến nào khác?

Nhìn sơ qua thì đúng là giống như inline style vậy, và ai cũng biết inline style is bad practice. Nhưng các lớp nguyên tử khác hoàn toàn và mạnh mẽ hơn kiểu nội tuyến nhiều. Các lớp nguyên tử cho phép bạn viết truy vấn phương tiện,

/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
8, bộ chọn giả hoặc sử dụng hoạt ảnh, những điều mà kiểu nội tuyến không làm được. Và vì CSS nguyên tử được lưu trong tệp CSS, trình duyệt có thể lưu vào bộ nhớ đệm, không giống như kiểu nội tuyến

Tên lớp nhìn thấy

Kiểu viết tắt

/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
5 (lề tất cả),
/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
6 (đệm x),
<button class="btn btn--big btn--green">
  <span class="btn__price">$9.99span>
button>
1 (đệm ngang) hay
<button class="btn btn--big btn--green">
  <span class="btn__price">$9.99span>
button>
2 rất phổ biến trong cộng đồng Atomic CSS. Ban đầu bạn có thể thấy khó hiểu và tốn thời gian để học, nhưng sau một khoảng thời gian chúng sẽ trở nên tự nhiên

Ngoài ra còn một số ý kiến ​​phản đối Atomic CSS nữa mà nếu muốn, bạn có thể đọc bài viết The Problem with Atomic CSS rồi tự đưa ra nhận xét. cảnh báo spoiler. không phải tất cả các điểm trong bài đều hợp lý

Tích hợp vào dự án

Nếu bạn đọc đến đây và không cảm thấy Atomic CSS là một ý tưởng tưởng dị hợm thì bài viết này coi như đã thành công. Để sử dụng CSS nguyên tử trong dự án, bạn có thể chọn cách sử dụng các thư viện có sẵn hoặc tự xây dựng thư viện riêng

Dùng hàng ăn sẵn

Cách này phù hợp với những dự án mới hoặc bạn muốn thử nghiệm với CSS nguyên tử. Hiện tại thì hai thư viện phổ biến nhất là Tachyons và Tailwind. Tachyons có lượng người dùng đông đảo vì xuất hiện trước, nhưng Tailwind lại mạnh mẽ hơn vì cho phép bạn thay đổi màu sắc, kích thước, v. v. hoàn toàn theo ý muốn. Không thì bạn có thể sử dụng hệ thống thiết kế của StackOverflow hay Heroku cũng được

Sự thật là các lớp nguyên tử quá nhỏ nên chúng gần như giống nhau ở tất cả các framework, có khác chăng chỉ là tên gọi mà thôi

autocad

Trong một dự án đang chạy, nếu muốn áp dụng CSS nguyên tử thì không gì tốt hơn là tự động lấy một khung. Các lớp nguyên tử rất nhỏ và đơn giản nên hoàn toàn không có gì khó để tự viết cả. Bạn có thể tách rời các thuộc tính của các lớp cũ và chuyển chúng thành các lớp nguyên tử. Dự án của bạn có thể sắp xếp các tập tin (S)CSS như thế này

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
1

Về việc đặt tên, bạn có thể chọn kiểu viết tắt

/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
6,
/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
5 hay
/* Block */
.btn {
}

/* Element */
.btn__price {
}

/* Modifier */
.btn--big {
}

.btn--green {
}
7 như trong Tachyons, hoặc kiểu đầy đủ
<button class="btn btn--big btn--green">
  <span class="btn__price">$9.99span>
button>
6,
<button class="btn btn--big btn--green">
  <span class="btn__price">$9.99span>
button>
7,
<button class="btn btn--big btn--green">
  <span class="btn__price">$9.99span>
button>
8. Cái này tùy thuộc vào sở thích của từng nhóm

Ngoài ra, việc viết vòng lặp đi lặp lại nhóm lớp có thể gây chán nản, bạn đừng quên tận dụng các công cụ tiền xử lý CSS như SASS, LESS, hay Stylus để cuộc sống dễ thở hơn. Không giới hạn như đoạn mã dưới đây để tạo ra các lớp liên quan đến chiều cao

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
2

Khi cần thêm một giá trị mới, bạn chỉ cần bỏ nó vào

<button class="btn btn--big btn--green">
  <span class="btn__price">$9.99span>
button>
9. Bạn cũng có thể sử dụng mixin để tạo ra các lớp đáp ứng

#header {
  margin: 10px;
  text-align: center;
  color: fabulous;
}

.text {
  font-weight: 700;
  font-size: 20px;
}
3

Kết quả

Nếu nhóm của bạn đang sử dụng BEM hoặc các phương pháp phát triển CSS khác và hài lòng với họ, bạn không thể cần CSS nguyên tử. CSS nguyên tử không phải sinh ra là để triệt tiêu BEM, bổ sung và giúp bạn làm việc với CSS một cách hiệu quả hơn

Cuối cùng, hãy xem video này và quyết định có nên sử dụng CSS nguyên tử không nhé ;)

Trân trọng cảm ơn Huy Tran, Duc Nguyen Huu và các bạn trong kênh #frontend @ WeBuild đã đóng góp ý kiến ​​cho bài viết hoàn thiện hơn

Tham khảo

[1] Philip Ardeljan. 15kb CSS là tất cả những gì bạn cần ⚡️. https. //vừa phải. com/@philipardeljan/15kb-of-css-is-all-youll-ever-need-%EF%B8%8F-634da7258338

[2] John Polacek. Bởi các con số. Một năm rưỡi với Atomic CSS. https. //vừa phải. com/@johnpolacek/theo-số-năm-rưỡi-với-nguyên-tử-css-39d75b1263b4

[3] Ferdy Christant. Bảo vệ CSS biểu cảm. https. //ferdychristant. com/in-defence-of-expressive-css-5d44862d5c56

[4] John Polacek. Trường hợp CSS nguyên tử. https. // johnpolacek. github. io/the-case-for-atomic-css/

Bạn thích bài viết này?

Đăng ký ngay để nhận những tin tức mới nhất về lập trình frontend trong tuần. Bản tin sẽ được gửi đến mỗi sáng thứ Bảy ✌️