So sánh & So sánh phụ đề

Thuộc tính ngôn ngữ chỉ được liên kết với một ngôn ngữ. Tức là thuộc tính <html> chỉ có thể có một ngôn ngữ, ngay cả khi có nhiều ngôn ngữ trên trang. Đặt lang thành ngôn ngữ chính của trang.

Không nên
<html lang="ar,en,fr,pt">...</html>
Chúng tôi không hỗ trợ nhiều ngôn ngữ.
Nên
<html lang="ar">...</html>
Chỉ đặt ngôn ngữ chính cho trang. Trong trường hợp này, ngôn ngữ là tiếng Ả Rập.

Tương tự như các nút, đường liên kết chủ yếu lấy tên dễ tiếp cận qua nội dung văn bản. Một mẹo hay khi tạo đường liên kết là đặt đoạn văn bản có ý nghĩa nhất vào chính đường liên kết đó, thay vì chèn các từ đệm như "Đây" hoặc "Đọc thêm".

Không đủ mô tả
Check out our guide to web performance <a href="/guide">here</a>.
Nội dung hữu ích!
Check out <a href="/guide">our guide to web performance</a>.

Kiểm tra xem ảnh động có kích hoạt bố cục hay không

Ảnh động di chuyển một phần tử bằng thao tác khác transform có thể sẽ bị chậm. Trong ví dụ sau, tôi đã đạt được cùng một kết quả trực quan ảnh động topleft bằng transform.

Không nên
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     top: calc(90vh - 160px);
     left: calc(90vw - 200px);
  }
}
Nên
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     transform: translate(calc(90vw - 200px), calc(90vh - 160px));
  }
}

Bạn có thể kiểm thử điều này trong 2 ví dụ về Sự cố sau đây và khám phá hiệu suất bằng Công cụ cho nhà phát triển.

Với cùng một mã đánh dấu, chúng ta có thể thay thế: padding-top: 56.25% bằng aspect-ratio: 16 / 9, đặt aspect-ratio thành tỷ lệ width / height đã chỉ định.

Sử dụng khoảng đệm-top
.container {
  width: 100%;
  padding-top: 56.25%;
}
Sử dụng tỷ lệ khung hình
.container {
  width: 100%;
  aspect-ratio: 16 / 9;
}

Việc sử dụng aspect-ratio thay vì padding-top sẽ rõ ràng hơn nhiều và không đại diện cho thuộc tính khoảng đệm để thực hiện điều gì đó bên ngoài phạm vi thông thường.

Vâng, đúng vậy, tôi đang sử dụng reduce để xâu chuỗi một chuỗi lời hứa. Tôi rất thông minh. Tuy nhiên, đây là một phương pháp lập trình rất thông minh.

Tuy nhiên, khi chuyển đổi nội dung trên thành một hàm không đồng bộ, bạn sẽ thực hiện thao tác quá tuần tự:

Không nên – quá tuần tự
async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}
Trông gọn gàng hơn nhiều, nhưng lần tìm nạp thứ hai của tôi chỉ bắt đầu cho đến khi lần tìm nạp đầu tiên được đọc đầy đủ và cứ tiếp tục như vậy. Phương thức này chậm hơn nhiều so với ví dụ hứa hẹn về việc thực hiện các tìm nạp song song. Rất may là có một vị trí giữa lý tưởng.
Được đề xuất - đẹp và song song
function markHandled(...promises) {
  Promise.allSettled(promises);
}

async function logInOrder(urls) {
  // fetch all the URLs in parallel
  const textPromises = urls.map(async (url) => {
    const response = await fetch(url);
    return response.text();
  });

  markHandled(...textPromises);

  // log them in sequence
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}
Trong ví dụ này, các URL được tìm nạp và đọc song song, nhưng bit reduce "thông minh" được thay thế bằng một vòng lặp tiêu chuẩn, nhàm chán và dễ đọc.

Viết thuộc tính tuỳ chỉnh Houdini

Sau đây là ví dụ về cách đặt thuộc tính tuỳ chỉnh (chẳng hạn như biến CSS), nhưng hiện tại thuộc tính này có cú pháp (loại), giá trị ban đầu (dự phòng) và boolean kế thừa (thuộc tính này có kế thừa giá trị của thành phần mẹ hay không?). Hiện tại, bạn có thể làm việc này thông qua CSS.registerProperty() trong JavaScript, nhưng trong Chromium 85 trở lên, cú pháp @property sẽ được hỗ trợ trong các tệp CSS của bạn:

Tệp JavaScript riêng biệt (Chromium 78)
CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '',
  initialValue: 'magenta',
  inherits: false
});
Có trong tệp CSS (Chromium 85)
@property --colorPrimary {
  syntax: '';
  initial-value: magenta;
  inherits: false;
}

Giờ đây, bạn có thể truy cập --colorPrimary như mọi thuộc tính tuỳ chỉnh CSS khác thông qua var(--colorPrimary). Tuy nhiên, sự khác biệt ở đây là --colorPrimary không chỉ được đọc dưới dạng chuỗi. Ứng dụng này có dữ liệu!

CSS backdrop-filter áp dụng một hoặc nhiều hiệu ứng cho phần tử trong suốt hoặc trong suốt. Để hiểu rõ điều này, hãy xem xét những hình ảnh dưới đây.

Không có nền trong suốt
Một hình tam giác xếp chồng lên một đường tròn. Không thể nhìn thấy vòng tròn qua hình tam giác.
.frosty-glass-pane {
  backdrop-filter: blur(2px);
}
Độ trong suốt của nền trước
Một hình tam giác xếp chồng lên một đường tròn. Hình tam giác trong suốt, cho phép có thể nhìn thấy vòng tròn.
.frosty-glass-pane {
  opacity: .9;
  backdrop-filter: blur(2px);
}

Hình ảnh ở bên trái cho thấy cách kết xuất các phần tử chồng chéo nếu backdrop-filter không được sử dụng hoặc hỗ trợ. Hình ảnh ở bên phải áp dụng hiệu ứng làm mờ bằng backdrop-filter. Xin lưu ý rằng thuộc tính này sử dụng opacity ngoài backdrop-filter. Nếu không có opacity, thì sẽ không có gì để áp dụng tính năng làm mờ. Hầu như không có gì xảy ra nếu bạn đặt opacity thành 1 (mờ hoàn toàn) thì sẽ không có ảnh hưởng nào đến nền.

Tuy nhiên, không giống như sự kiện unload, beforeunload được sử dụng hợp pháp. Ví dụ: khi bạn muốn cảnh báo người dùng rằng họ có các thay đổi chưa lưu, họ sẽ mất nếu rời khỏi trang. Trong trường hợp này, bạn chỉ nên thêm trình nghe beforeunload khi người dùng chưa lưu các thay đổi, rồi xoá các thay đổi đó ngay sau khi lưu các thay đổi chưa lưu.

Không nên
window.addEventListener('beforeunload', (event) => {
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return event.returnValue = 'Are you sure you want to exit?';
  }
});
Đoạn mã trên đã thêm một trình nghe beforeunload một cách vô điều kiện.
Nên
function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});
Đoạn mã trên chỉ thêm trình nghe beforeunload khi cần thiết (và xoá trình nghe khi không cần thiết).

Hạn chế tối đa việc sử dụng Cache-Control: no-store

Cache-Control: no-store là một tiêu đề HTTP mà máy chủ web có thể thiết lập trên những phản hồi hướng dẫn trình duyệt không lưu trữ phản hồi trong bất kỳ bộ nhớ đệm HTTP nào. Bạn nên sử dụng tính năng này cho các tài nguyên chứa thông tin nhạy cảm của người dùng, ví dụ như các trang yêu cầu phải đăng nhập.

Phần tử fieldset, chứa từng nhóm đầu vào (.fieldset-item), đang sử dụng gap: 1px để tạo đường viền gọn gàng giữa các phần tử. Không có giải pháp đường viền phức tạp!

Khoảng trống được lấp đầy
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
Hiệu ứng đường viền
.grid {
  display: grid;

  & > .fieldset-item {
    background: var(--bg-surface-2);

    &:not(:last-child) {
      border-bottom: 1px solid var(--bg-surface-1);
    }
  }
}

Tự nhiên bọc lưới

Bố cục phức tạp nhất cuối cùng là bố cục macro, hệ thống bố cục logic giữa <main><form>.

input
<input
  type="checkbox"
  id="text-notifications"
  name="text-notifications"
>
label
<label for="text-notifications">
  <h3>Text Messages</h3>
  <small>Get notified about all text messages sent to your device</small>
</label>

Phần tử fieldset, chứa từng nhóm đầu vào (.fieldset-item), đang sử dụng gap: 1px để tạo đường viền gọn gàng giữa các phần tử. Không có giải pháp đường viền phức tạp!

Khoảng trống được lấp đầy
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
Hiệu ứng đường viền
.grid {
  display: grid;

  & > .fieldset-item {
    background: var(--bg-surface-2);

    &:not(:last-child) {
      border-bottom: 1px solid var(--bg-surface-1);
    }
  }
}

Bố cục <header> thẻ

Bố cục tiếp theo cũng gần giống: Tôi sử dụng flex để tạo thứ tự dọc.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

.snap-indicator phải di chuyển theo chiều ngang cùng với nhóm các đường liên kết và bố cục tiêu đề này sẽ giúp thiết lập giai đoạn đó. Không có phần tử có vị trí tuyệt đối nào ở đây!

Gentle Flex là chiến lược chỉ tập trung vào trung tâm thực tế hơn. Hàm này rất mềm và nhẹ nhàng, vì không giống như place-content: center, kích thước hộp của phần tử con không thay đổi trong quá trình căn giữa. Nhẹ nhàng nhất có thể, tất cả các mục sẽ được xếp chồng, căn giữa và giãn cách.

.gentle-flex {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1ch;
}
Ưu điểm
  • Chỉ xử lý căn chỉnh, hướng và phân phối
  • Chỉnh sửa và bảo trì đều ở cùng một nơi
  • Khoảng trống đảm bảo khoảng cách bằng nhau giữa n phần tử con
Nhược điểm
  • Hầu hết các dòng mã

Phù hợp với cả bố cục macro và vi mô.

Cách sử dụng

gap chấp nhận mọi độ dài hoặc phần trăm của CSS làm giá trị.

.gap-example {
  display: grid;
  gap: 10px;
  gap: 2ch;
  gap: 5%;
  gap: 1em;
  gap: 3vmax;
}


Khoảng trống có thể được chuyển 1 độ dài, sẽ được sử dụng cho cả hàng và cột.

Viết tắt
.grid {
  display: grid;
  gap: 10px;
}
Đặt cả hàng và cột cùng nhau cùng một lúc
Mở rộng
.grid {
  display: grid;
  row-gap: 10px;
  column-gap: 10px;
}


Khoảng trống có thể được chuyển đi 2 chiều, sẽ được sử dụng cho hàng và cột.

Viết tắt
.grid {
  display: grid;
  gap: 10px 5%;
}
Đặt cả hàng và cột riêng biệt cùng một lúc
Mở rộng
.grid {
  display: grid;
  row-gap: 10px;
  column-gap: 5%;
}