Xoá các đoạn mã không dùng đến

Trong lớp học lập trình này, hãy cải thiện hiệu suất của ứng dụng sau bằng cách xoá mọi phần phụ thuộc không dùng đến và không cần thiết.

Ảnh chụp màn hình ứng dụng

Đo

Trước tiên, bạn nên đo lường hiệu suất của một trang web trước khi thêm các tuỳ chọn tối ưu hoá.

  • Để xem trước trang web, hãy nhấn vào Xem ứng dụng. Sau đó nhấn Toàn màn hình toàn màn hình.

Hãy tiếp tục và nhấp vào chú mèo con yêu thích của bạn! Firebase Cơ sở dữ liệu theo thời gian thực là được sử dụng trong ứng dụng này, đó là lý do tại sao điểm số cập nhật theo thời gian thực và được đồng bộ hoá với người khác bằng ứng dụng. 🐈

  1. Nhấn tổ hợp phím "Control + Shift + J" (hoặc "Command+Option+J" trên máy Mac) để mở Công cụ cho nhà phát triển.
  2. Nhấp vào thẻ Mạng.
  3. Chọn hộp kiểm Tắt bộ nhớ đệm.
  4. Tải lại ứng dụng.

Kích thước gói ban đầu là 992 KB

JavaScript có dung lượng gần 1 MB đang được vận chuyển để tải ứng dụng đơn giản này!

Xem các cảnh báo của dự án trong Công cụ cho nhà phát triển.

  • Nhấp vào thẻ Bảng điều khiển.
  • Hãy đảm bảo bạn đã bật Warnings trong trình đơn thả xuống cấp bên cạnh Đầu vào Filter.

Bộ lọc cảnh báo

  • Hãy xem cảnh báo mà bạn thấy.

Cảnh báo trên bảng điều khiển

Firebase, một trong những thư viện được sử dụng trong ứng dụng này, đang là một samaritan bằng cách đưa ra cảnh báo để cho nhà phát triển biết không nên nhập toàn bộ gói mà chỉ gồm những thành phần được sử dụng. Nói cách khác, có các thư viện không sử dụng có thể được xoá trong ứng dụng này để ứng dụng tải nhanh hơn.

Cũng có những trường hợp khi một thư viện cụ thể được sử dụng, nhưng có thể có một giải pháp thay thế đơn giản hơn. Khái niệm xoá thư viện không cần thiết sẽ được khám phá sau trong hướng dẫn này.

Đang phân tích gói

Có hai phần phụ thuộc chính trong ứng dụng:

  • Firebase: một nền tảng cung cấp một số các dịch vụ hữu ích cho iOS, Android hoặc các ứng dụng web. Đây là Thời gian thực Cơ sở dữ liệu được dùng để lưu trữ và đồng bộ hóa thông tin cho từng chú mèo con theo thời gian thực.
  • Moment.js: một thư viện tiện ích giúp bạn dễ dàng xử lý ngày trong JavaScript. Ngày sinh của mỗi chú mèo con được lưu trữ trong Cơ sở dữ liệu Firebase và moment được dùng để tính tuổi của cơ sở dữ liệu này theo tuần.

Làm cách nào chỉ hai phần phụ thuộc lại đóng góp vào kích thước gói gần 1 MB? Chà, một trong những lý do là bất kỳ phần phụ thuộc nào cũng có thể có phần phụ thuộc, do đó có nhiều hơn chỉ hai nếu mỗi chiều sâu/nhánh của phần phụ thuộc "cây" sẽ được xem xét. Ứng dụng có thể dễ dàng trở nên lớn hơn tương đối nhanh chóng nếu có nhiều phần phụ thuộc.

Phân tích bộ kết hợp để có ý tưởng tốt hơn về nội dung sẽ diễn ra. Có không ít các công cụ khác nhau do cộng đồng tạo có thể giúp làm việc này, chẳng hạn như webpack-bundle-analyzer.

Gói cho công cụ này đã được đưa vào ứng dụng dưới dạng devDependency.

"devDependencies": {
  //...
  "webpack-bundle-analyzer": "^2.13.1"
},

Tức là bạn có thể dùng trực tiếp lớp này trong tệp cấu hình gói web. Nhập tệp này ngay từ đầu webpack.config.js:

const path = require("path");

//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;

Bây giờ, hãy thêm công cụ này dưới dạng một trình bổ trợ ở cuối tệp trong mảng plugins:

module.exports = {
  //...
  plugins: [
    //...
    new BundleAnalyzerPlugin()
  ]
};

Khi ứng dụng tải lại, bạn sẽ thấy hình ảnh trực quan về toàn bộ gói thay vì chính ứng dụng.

Trình phân tích gói webpack

Không đáng yêu như nhìn thấy một vài chú mèo con biểu tượng, nhưng vô cùng hữu ích. Khi di chuột qua một gói bất kỳ, kích thước của gói đó được thể hiện bằng 3 các cách khác nhau:

Kích thước thống kê Kích thước trước khi rút gọn hoặc nén.
Kích thước được phân tích cú pháp Kích thước của gói thực tế trong gói sau khi được biên dịch. Phiên bản 4 của webpack (được dùng trong ứng dụng này) giảm thiểu các tệp được biên dịch tự động, đó là lý do tại sao con số này nhỏ hơn số liệu thống kê kích thước.
Kích thước tệp zip Kích thước của gói sau khi nén bằng mã hoá gzip. Chiến dịch này chủ đề này sẽ được đề cập trong một hướng dẫn riêng.

Với công cụ trình phân tích gói webpack-bundle, sẽ dễ dàng hơn để xác định xem các gói không cần thiết chiếm tỷ lệ lớn trong gói.

Đang xoá các gói không dùng đến

Hình ảnh cho thấy gói firebase bao gồm rất nhiều nội dung khác không chỉ là một cơ sở dữ liệu. Phiên bản này bao gồm các gói bổ sung như:

  • firestore
  • auth
  • storage
  • messaging
  • functions

Đây là tất cả các dịch vụ tuyệt vời do Firebase cung cấp (và tham khảo tài liệu tìm hiểu thêm), nhưng không có thao tác nào trong số đó được sử dụng trong ứng dụng, vì vậy không có lý do gì để nhập tất cả các mục đó.

Huỷ bỏ các thay đổi trong webpack.config.js để xem lại ứng dụng:

  • Xóa BundleAnalyzerPlugin trong danh sách trình bổ trợ:
plugins: [
  //...
  new BundleAnalyzerPlugin()
];
  • Và bây giờ, hãy xoá dữ liệu nhập không dùng đến khỏi đầu tệp:
const path = require("path");

//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

Ứng dụng sẽ tải bình thường ngay bây giờ. Sửa đổi src/index.js để cập nhật Nhập từ Firebase.

import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';

Giờ đây, khi ứng dụng tải lại, cảnh báo trong Công cụ cho nhà phát triển sẽ không xuất hiện. Mở Bảng điều khiển Mạng Công cụ cho nhà phát triển cũng cho thấy kích thước gói giảm tốt:

Kích thước gói giảm xuống còn 480 KB

Hơn một nửa kích thước gói đã bị xoá. Firebase cung cấp nhiều và cung cấp cho nhà phát triển lựa chọn để chỉ đưa vào những dịch vụ cần thiết. Trong ứng dụng này, chỉ có firebase/database được dùng để lưu trữ và đồng bộ hoá tất cả dữ liệu. Lệnh nhập firebase/app giúp thiết lập nền tảng API cho từng dịch vụ khác nhau là bắt buộc.

Nhiều thư viện phổ biến khác, chẳng hạn như lodash, cũng cho phép nhà phát triển nhập có chọn lọc các phần khác nhau trong gói. Nếu không làm nhiều việc, cập nhật các mục nhập thư viện trong một ứng dụng để chỉ đưa vào những dữ liệu đang được dùng có thể giúp cải thiện đáng kể hiệu suất.

Mặc dù kích thước gói đã giảm đi khá nhiều, nhưng vẫn còn nhiều cách khác còn việc cần làm! 😈

Xoá các gói không cần thiết

Không giống như Firebase, bạn không thể nhập các phần của thư viện moment dưới dạng nhưng có thể xoá hoàn toàn được không?

Ngày sinh của mỗi chú mèo con đáng yêu được lưu trữ ở định dạng Unix (mili giây) trong cơ sở dữ liệu Firebase.

Ngày sinh được lưu trữ ở định dạng Unix

Đây là dấu thời gian của một ngày và giờ cụ thể được thể hiện bằng số lượng mili giây đã trôi qua kể từ 00:00 ngày 1 tháng 1 năm 1970 theo giờ UTC. Nếu giá trị ngày và giờ có thể được tính theo cùng định dạng, một hàm nhỏ để tìm tuổi của mỗi chú mèo con tính theo tuần có thể được xây dựng.

Như thường lệ, hãy cố gắng không sao chép và dán khi bạn làm theo ở đây. Bắt đầu bằng xoá moment khỏi lệnh nhập trong src/index.js.

import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';

Có một trình nghe sự kiện Firebase xử lý các thay đổi về giá trị trong cơ sở dữ liệu của chúng tôi:

favoritesRef.on("value", (snapshot) => { ... })

Trên số này, hãy thêm một hàm nhỏ để tính số tuần từ ngày nhất định:

const ageInWeeks = birthDate => {
  const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
  const diff = Math.abs((new Date).getTime() - birthDate);
  return Math.floor(diff / WEEK_IN_MILLISECONDS);
}

Trong hàm này, sự khác biệt theo mili giây giữa ngày hiện tại và thời gian (new Date).getTime() và ngày sinh (đối số birthDate, đã tính bằng mili giây) được tính và chia cho số mili giây trong một một tuần.

Cuối cùng, bạn có thể xoá tất cả các bản sao của moment trong trình nghe sự kiện bằng cách tận dụng hàm này:

favoritesRef.on("value", (snapshot) => {
  const { kitties, favorites, names, birthDates } = snapshot.val();
  favoritesScores = favorites;

  kittiesList.innerHTML = kitties.map((kittiePic, index) => {
    const birthday = moment(birthDates[index]);

    return `
      <li>
        <img src=${kittiePic} onclick="favKittie(${index})">
        <div class="extra">
          <div class="details">
            <p class="name">${names[index]}</p>
            <p class="age">${moment().diff(birthday, 'weeks')} weeks old</p>
            <p class="age">${ageInWeeks(birthDates[index])} weeks old</p>
          </div>
          <p class="score">${favorites[index]} ❤</p>
        </div>
      </li>
    `})
});

Bây giờ, hãy tải lại ứng dụng và xem bảng điều khiển Network (Mạng) một lần nữa.

Kích thước gói giảm xuống còn 225 KB

Kích thước gói của chúng tôi đã giảm hơn một nửa!

Kết luận

Với lớp học lập trình này, bạn đã nắm rõ cách phân tích một gói cụ thể nào và tại sao gói đó lại hữu ích đến vậy khi xoá những gói không sử dụng hoặc không cần thiết . Trước khi bạn bắt đầu tối ưu hoá ứng dụng bằng kỹ thuật này, công nghệ điều quan trọng cần biết rằng điều này có thể phức tạp hơn đáng kể trong ứng dụng của bạn.

Liên quan đến việc xoá các thư viện không dùng đến, hãy thử tìm hiểu xem phần nào của gói đang được sử dụng và phần nào thì không. Để có vẻ ngoài bí ẩn có vẻ như gói đó đang không được sử dụng ở đâu, hãy lùi lại một bước và kiểm tra mà các phần phụ thuộc cấp cao nhất có thể cần đến. Hãy thử tìm cách để có thể tách chúng khỏi nhau.

Khi xoá những thư viện không cần thiết, bạn có thể làm nhiều việc hơn một chút phức tạp. Bạn cần phải hợp tác chặt chẽ với nhóm của mình và xem liệu có giúp đơn giản hoá một số phần của cơ sở mã. Đang xoá moment trong mục này có vẻ như mỗi lần ứng dụng đều là việc làm đúng đắn, nhưng những gì nếu có múi giờ và các ngôn ngữ khác nhau cần được xử lý không? Hoặc nếu có những quy trình chỉnh sửa ngày phức tạp hơn thì sao? Mọi thứ có thể trở nên khó khăn khi thao tác và phân tích cú pháp ngày/giờ cũng như các thư viện như momentdate-fns giúp đơn giản hoá đáng kể việc này.

Mọi thứ đều là sự đánh đổi và cần phải đánh giá xem liệu nó có đáng sự phức tạp và công sức để đưa ra một giải pháp tuỳ chỉnh thay vì dựa vào thư viện của bên thứ ba.