SonTran's avatar
Son Tran
HTML

Tăng tốc độ tải trang khi sử dụng thẻ img

Tăng tốc độ tải trang khi sử dụng thẻ img
0 views
16 min read
#HTML

Giới thiệu

Để đảm bảo hình ảnh trên website của bạn hiển thị một cách mượt mà và ổn định trên mọi kích thước màn hình quả là một nhiệm vụ khó khăn, bạn cần điều chỉnh kích thước của hình ảnh, vị trí bạn muốn đặt khung hình,...Khi mới bắt đầu, hầu hết các lập trình viên mới đều sử dụng cùng một kích thước hình ảnh cho một thẻ img cần hiển thị, điều này khá tệ khi mà trình duyệt sẽ vẫn tải xuống hình ảnh với kích thước đầy đủ(thường là kích thước lớn ban đầu) và dùng nó để hiển thị ngay cả khi nó chỉ cần hiển thị với kích thước nhỏ tương đối. Điều này gây lãng phí tài nguyên người dùng và giảm tốc độ tải trang của bạn.

Giải pháp cho vấn đề này là ta cần cải thiện việc sử dụng hình ảnh phù hợp với kích thước hiển thị, hình ảnh được tối ưu hoá cho kích thước màn hình của người sử dụng. Điều này có nghĩa là hình ảnh sẽ được tải với kích thước và chấy lượng tương thích với thiết bị của người sử dụng. Nó có sẽ góp phần giảm đáng kể dung lương data và làm tăng độ tải cho trang web. Có rất nhiều cách có thể làm điều này, từ đơn giản đến nâng cao, ở bài viết này tôi sẽ giới thiệu cho bạn tất cả các cách có thể tối ưu hoá hình ảnh cho website của bạn.

Img srcset Attribute

Một cách đơn giản nhất để có kích thước hình ảnh tương đối là sử dụng thuộc tính srcset trong thẻ img. Thuộc tính này cho phép bạn định nghĩa nhiều kích thước hình ảnh và trình duyệt sẽ tự động chọn nguồn hình ảnh tương ứng phù hợp với thiết bị của người sử dụng.

<img
  src="tree-1200.jpg"
  alt="A tree"
  srcset="tree-400.jpg 400w, tree-800.jpg 800w, tree-1200.jpg 1200w"
/>

Chúng ta có một thẻ img với thuộc tính srcalt như bạn vẫn thường dùng. Trong một số ít trường hợp, trình duyệt của bạn sẽ không hỗ trợ thuộc tính srcset thì thuộc tính src sẽ được sử dụng để thay thế. Khá lạ đúng không, khi mà thuộc tính srcset đã được hỗ trợ trên tất cả trình duyệt từ 5-10 năm trước cho đến tận bây giờ.

Thuộc tính srcset nhận dấu phẩy để phân tách tập hợp các đường dẫn ảnh và chiều rộng của chúng. Tên đường dẫn của ảnh không quan trọng, nhưng khi bạn sử dụng cùng một hình ảnh với nhiều kích thước khác nhau bạn nên đặt tên chúng gắn liền với kích thước mà chúng hiển thị.

Khi chúng bạn thấy 400w có thể bạn sẽ nhầm lẫn w nó không phải là một đơn vị CSS, thay vào đó, w đại diện cho chiều rộng thực tế của hình ảnh bằng đơn vị pixel. Bạn có thể dễ dàng kiểm tra điều này trên trình duyệt của bạn bằng cách inspect hình ảnh.

Trình duyệt sẽ sử dụng thông tin này để xác định hình ảnh tải tương ứng. Ví dụ, nếu kích thước màn hình của người sử dụng nhỏ hơn 400px thì nó sẽ sử dụng tree-400.jpg và đó cũng là kích thước nhỏ nhất của hình ảnh này mà không bị co kéo hay mờ bởi pixels. Khi trình duyệt rộng hơn 400px thì nó sẽ tự động chuyển sang dùng ảnh tree-800.jpg. Điều này xảy ra bởi vì hình ảnh với 400px giờ đã nhỏ hơn kích thước màn hình hình hiện tại, và nó sẽ làm hình ảnh bị co kéo nếu tiếp tục sử dụng. Việc này sẽ tiếp tục cho tất cả màn hình cho đến lúc mà trình duyệt tìm thấy được hình ảnh có kích thước lớn nhất trong srcset.

Điều này khá là hữu dụng vì giờ với các kích thước màn hình nhỏ thì trình duyệt sẽ download hình ảnh nhỏ và tương tự với các kích thước màn hình có độ phân giải lớn. Nó giúp phần giảm đáng kể lưu lượng data mà trình duyệt cần sử dụng khi load page. Dưới đây là ví dụ bạn có thể thấy, sự thay đổi kích thước trình duyệt nhỏ hơn và nó sẽ tải lại trang, bạn có thể thấy hình ảnh nhỏ hơn đã được tải xuống.

<img
  style="width: 100%; border-radius: 1rem;"
  src="https://placehold.co/3200x800/png"
  srcset="
    https://placehold.co/800x200/png   800w,
    https://placehold.co/1600x400/png 1600w,
    https://placehold.co/3200x800/png 3200w
  "
/>

Khi bạn kiểm tra, bạn sẽ thấy hình ảnh được tải sẽ lớn hơn bạn nghĩ. Ví dụ, nếu kích thước màn hình của bạn là 700px nhưng trình duyệt của bạn sẽ vẫn tải hình ảnh có chiều rộng là 1600px thay vì 800px. Điều này xảy ra vì trình duyệt của bạn sẽ tự động tính toán tỉ lệ pixel trên màn hình của bạn. Nếu màn hình của bạn có độ phân giải cao hoặc bạn đang sử dụng zoom trên trình duyệt, nó sẽ tự động tải hình ảnh kích thước lớn hơn để đảm bảo chất lượng hiển thị vì mỗi đơn vị css pixel tương đương với nhiều đơn vị pixel trên màn hình của bạn. Để kiểm tra độ phân giải của màn hình bạn đang sử dụng, bạn có thể sử dụng window.devicePixelRatio trong tab console

Cách xử lí độ phân giải khác nhau

Đôi khi bạn có một hình ảnh và bạn muốn nó luôn luôn giữ đúng kích thước hiển thị, nhưng bạn cũng muốn đảm bảo nó nhìn ổn với mọi thiết bị có độ phân giải khác nhau. Ví dụ, bạn có một hình ảnh logo luôn có chiều rộng là 100px, nó sẽ bị mờ khi hiển thị trên màn hình có độ phân giải cao nếu bạn chỉ cung cấp duy nhất một kích logo 100px chiều rộng. Để xử lý trường hợp này, bạn cần sử dụng thuộc tính srcset để cung cấp nhiều đường dẫn ảnh có kích thước sử dụng đơn vị x để biểu thị mật độ pixel.

<img
  src="logo-200.jpg"
  alt="Our Logo"
  srcset="logo-100.jpg 1x, logo-150.jpg 1.5x, logo-200.jpg 2x"
/>

Đoạn code trên nhìn rất giống với ví dụ trước về srcset, nhưng thay đổi ở việc ta sử dụng đơn vị 1.5x và 2x thay vì phải hard coded pixels. Đơn vị này tương ứng với mật độ điểm ảnh trên màn hình. Ví dụ, màn hình có độ phân giải là 1.25 trên mỗi điểm ảnh sẽ tương ứng với 1.25 pixel thì hình hảnh logo-150.jpg sẽ được sử dụng.

Bạn không cần phải thêm đơn vị 1x vì nó là mặc định. Nếu bạn chỉ có 2 hình ảnh, bạn có thể sử dụng logo-100.jpg, logo-200.jpg 2x thay vì logo-100.jpg 1x, logo-200.jpg 2x.

Img sizes Attribute

What we have covered so far is the most basic way to implement responsive images, but in many scenarios your image size is not actually the same as the width of your screen. This blog is a good example of that. On small screen sizes the content in my blog (including images) takes up the full width of the screen, but on larger screen sizes I have the content centered on the page with a limited maximum width. If we only used srcset, like above, our images would scale based off the full size of the browser window which would result in the images being larger than needed on large screen sizes. This is where the sizes attribute comes in.

Những gì tôi trình bày ở trên đều là những cách cơ bản để tối ưu hình ảnh của bạn, nhưng trong một số trường hợp, hình ảnh của bạn không thực sự có cùng độ rộng với màn hình. Trang blog này của tôi là một ví dụ, một số màn hình nhỏ phần nội dung chứa hình ảnh có độ rộng hết khung hình, một số màn hình lớn hơn thì nội dung lại có hình ảnh to hơn mức cần thiết. Nếu chỉ sử dụng serset thì chưa đủ để mang lại hình ảnh hiển thị tốt. Đó là lúc chúng ta cần tìm đến thuộc tính size.

The sizes attribute lets you define either a single sizes for your image, such as 50vw, or a list of media queries that will be used to determine which size your image should be. By default when you do not add the sizes attribute to your img it assumes a size of 100vw which is why the images above scaled off the full width of the browser window. Let’s take a look at how we would use the sizes attribute to take into account a blog like this that has a maximum size. Thuộc tính sizes cho phép bạn định nghĩa kích thước duy nhất cho hình ảnh của bạn, ví dụ 50vw, hoặc một media queries để xác định kích thước hình ảnh của bạn. Mặc định, khi bạn không thêm thuộc tính sizes thì nó sẽ tự hiểu là sizes có giá trị là 100vw vì vậy mà hình ảnh ở trên luôn hiển thị hết theo độ rộng của màn hình của trình duyệt. Hãy cùng xem qua cách chúng ta sử dụng thuộc tính sizes

<img
  src="tree-1200.jpg"
  alt="A tree"
  srcset="tree-400.jpg 400w, tree-800.jpg 800w, tree-1200.jpg 1200w"
  sizes="(max-width: 800px) 100vw, 800px"
/>

Đoạn code ở trên nhìn giống với đoạn code về ví dụ srcset ở ví dụ trước đó, nhưng chúng ta đã thêm thuộc tính sizes. Thuộc tính này chấp nhận dấu phẩy để tách rời danh sách các media queries. Để hiểu đoạn code trên làm gì, giờ hãy cùng tách ra từng phần:

Phần đầu tiên danh sách các media queries là (max-width: 800px) 100vw có 2 thành phần. Một là về chiều rộng media query bạn muốn kiểm tra. Trong ví dụ này chúng ta đang kiểm tra với hành hình hiển thị có kích thước nhỏ hơn 800px. Phần thứ hai là kích thước hình ảnh được dùng nếu media query đúng. Trong trường hợp này, khi chúng ta sử dụng 100vw có nghĩa là chúng ta muốn hình hành có kich thước dựa trên độ rộng của cửa sổ trình duyệt.

Phần thứ hai là 800px không được sử dụng cùng với media query. Nó được sử dụng cho trường hợp dự phòng, nếu tất cả các media queries được định nghĩa không hợp lệ, nó sẽ sử dụng hỉnh ảnh có kích thước 800px này. Điều quan trọng là hầu hết các media query sẽ được áp dụng, nhưng điều chúng ta muốn ở đây là hình ảnh khi được tải xuống se có kích thước mong muốn. Trình duyệt sẽ dựa vào thuộc tính sizes này để xác định hình ảnh được tải. Nó sẽ tự động tải các hình ảnh có kích thước lớn hơn 800px nếu thiết bị sử dụng có độ phân giải cao tương ứng. Đây là một cách tốt để bạn có thể đảm bảo tất cả hình ảnh của bạn không lớn quá.

If we take these two items and put them together it is essentially saying that our image should be chosen based on the width of the browser up until 800px. At that point the image will never take up more than 800px on our screen so we should size our image based on that 800px size. This would be how I would write code for adding responsive images to this blog as my blog is limited to a maximum width at larger screen sizes. Let’s look at an example of this in action. Nếu chúng ta sử dụng sizes="(max-width: 800px) 100vw, 800px" điều đó có nghĩa hình ảnh của chúng ta được chọn dựa trên độ rộng của trình duyệt sau đó mới tới 800px.

<img
  style="width: 100%; border-radius: 1rem;"
  src="https://placehold.co/3200x800/png"
  srcset="
    https://placehold.co/400x100/png   400w,
    https://placehold.co/800x200/png   800w,
    https://placehold.co/1200x300/png 1200w,
    https://placehold.co/1600x400/png 1600w,
    https://placehold.co/3200x800/png 3200w
  "
  sizes="(max-width: 800px) 100vw, 800px"
/>

Các bạn đã tìm hiểu qua các ví dụ như px và cách sử dụng các phép đo chia tỷ lệ cửa sổ trình duyệt như vw, nhưng còn kích thước phần trăm thì sao, chẳng hạn như 50%. Rất tiếc, kích thước phần trăm không được hỗ trợ trong thuộc tính kích thước. Lý do cho điều này là trình duyệt không biết thứ gì đó được xác định theo tỷ lệ phần trăm sẽ rộng đến mức nào cho đến khi nó biết chiều rộng của phần tử gốc. Điều này có nghĩa là trình duyệt sẽ phải đợi cho đến khi toàn bộ trang được tải trước khi có thể xác định hình ảnh nào cần tải xuống. Đây sẽ là trải nghiệm không tốt cho người dùng vì người dùng sẽ phải đợi toàn bộ trang tải trước khi họ có thể nhìn thấy bất kỳ hình ảnh nào.

Thẻ Picture

Cho đến thời điểm này, chúng ta đã nói cụ thể về cách hiển thị cùng một hình ảnh ở các kích thước khác nhau để giúp giảm thời gian tải, nhưng điều này không đề cập đến trường hợp bạn muốn hiển thị một hình ảnh khác ở các kích thước màn hình khác nhau. Ví dụ: nếu bạn có tiêu đề lớn trên trang trải dài toàn bộ chiều rộng của trang thì bạn có thể muốn hiển thị hình ảnh trên thiết bị di động khác với hình ảnh trên máy tính để bàn, vì bạn có thể sử dụng hình ảnh có nhiều chi tiết hơn trên máy tính để bàn. Đây là lí do ta cần sử dụng picture

<picture>
  <source media="(max-width: 500px)" srcset="hiking-narrow.jpg" />
  <img src="hiking-wide.jpg" alt="Someone jumping on a hike" />
</picture>
Someone jumping on a hike

Nếu bạn tăng/thu nhỏ kích thước trình duyệt của mình, bạn sẽ thấy hình ảnh thay đổi giữa hai phiên bản khác nhau. Nếu bạn đang sử dụng thiết bị di động, bạn có thể cần phải phóng to/thu nhỏ để xem hình ảnh thay đổi. Ta có phiên bản hình ảnh được cắt xén nhiều hơn cho kích thước màn hình nhỏ hơn do tiêu điểm của hình ảnh, con người, trở nên nhỏ trên kích thước màn hình nhỏ hơn.

Bây giờ hãy xem mã thực tế để xem nó hoạt động như thế nào. Để thẻ img hoạt động, ít nhất bạn cần đặt thẻ img bình thường bên trong the picture ảnh ở cuối.

<picture>
  <img src="hiking-wide.jpg" alt="Someone jumping on a hike" />
</picture>

Làm điều này sẽ chỉ hiển thị hình ảnh giống như thẻ img bình thường. Mỗi thẻ picture của bạn hình ảnh mặc định phải có. Trong trường hợp này, tôi có một nguồn ảnh dự phòng, nhưng bạn có thể có bao nhiêu tùy ý.

<picture>
  <source media="(max-width: 500px)" srcset="hiking-narrow.jpg" />
  <source media="(max-width: 1000px)" srcset="hiking-medium.jpg" />
  <img src="hiking-wide.jpg" alt="Someone jumping on a hike" />
</picture>

Bên trong mỗi thẻ source đó, bạn có hai thuộc tính chính. Thuộc tính srcset hoạt động giống như thuộc tính srcset của thẻ img. Điều này có nghĩa là nếu chúng ta có nhiều độ phân giải cho hình ảnh Hiking-narrow.jpg thì chúng ta có thể đưa chúng vào srcset. Tuy nhiên, khi bạn đang sử dụng thành phần hình ảnh, bạn sẽ chỉ có một độ phân giải trong mỗi thành phần nguồn nên bạn chỉ có thể đặt nó làm url duy nhất trong thuộc tính srcset.

Tại sao sử dụng thẻ Picture cho việc tối ưu tốt nhất?

Lý do chính để sử dụng thẻ picture là nó sẽ luôn hoán đổi thành bất kỳ hình ảnh nào được xác định trong thẻ source phù hợp với kích thước màn hình hiện tại. Điều này có nghĩa là nếu bạn thay đổi kích thước màn hình bằng cách phóng to hoặc thay đổi kích thước cửa sổ trình duyệt, nó sẽ chuyển sang hình ảnh chính xác.

Thuộc tính sizes của hoạt động tương tự, nhưng chỉ khi tăng kích thước màn hình của bạn. Nếu kích thước màn hình của bạn thu nhỏ lại, trình duyệt sẽ không chuyển sang hoặc tải xuống hình ảnh nhỏ hơn vì nó đã có hình ảnh lớn hơn nên nó sẽ tiếp tục hiển thị hình ảnh đó. Điều này thật tuyệt vì nó sẽ tiết kiệm băng thông vì chẳng ích gì khi tải xuống hình ảnh nhỏ hơn khi bạn đã có hình ảnh lớn hơn, nhưng khi bạn muốn hiển thị các hình ảnh khác nhau trên các kích thước màn hình khác nhau thì đây có thể là một vấn đề, đó là lý do tại sao thành phần hình ảnh là sự lựa chọn phù hợp.

Nếu bạn thường xuyên làm việc với CSS, bạn có thể nhận ra rằng ta có thể đạt được kết quả tương tự chỉ bằng cách sử dụng một số thuộc tính CSS đơn giản.

img {
  object-fit: cover;
  object-position: center;
}

Điều này sẽ làm cho hình ảnh lấp đầy toàn bộ chiều rộng của phần tử gốc và sau đó cắt hình ảnh để luôn hiển thị phần giữa của hình ảnh. Điều này sẽ cho chúng ta kết quả rất giống nhau, nhưng nhược điểm là chúng ta vẫn cần tải xuống phiên bản có độ phân giải đầy đủ của hình ảnh ngay cả trên kích thước màn hình nhỏ nơi chúng ta chỉ hiển thị một phần của hình ảnh đó.

Kết luận

Tối ưu hình ảnh có vẻ như là một chủ đề phức tạp nhưng thực tế không nhất thiết phải như vậy. Việc triển khai nó cơ bản cũng đơn giản như thêm thuộc tính srcset vào thẻ img của bạn và sau đó để trình duyệt thực hiện phần còn lại. Nếu bạn muốn nâng cao hơn, bạn có thể sử dụng thuộc tính sizes để giúp trình duyệt chọn hình ảnh chính xác hoặc nếu bạn muốn hiển thị các hình ảnh khác nhau ở các kích thước màn hình khác nhau, bạn có thể sử dụng thẻ picture