Thứ tư, 16/10/2019 | 00:00 GMT+7

Tạo một Plugin webpack tùy chỉnh


Nếu bạn đã từng làm việc với webpack, chắc hẳn bạn đã nghe nói đến các plugin webpack. Plugin là một lựa chọn tuyệt vời để mở rộng việc triển khai và kiến trúc của webpack. Nếu bạn nhìn vào mã nguồn của webpack, khoảng 80% mã của chúng được triển khai bằng các plugin. Nó giúp tách phần cốt lõi của webpack, giúp bảo trì mã tốt hơn.

webpack cũng hỗ trợ các khái niệm về trình loaders giúp mở rộng webpack cũng như hoạt động cùng với resolvers . Chúng chủ yếu được sử dụng để chuyển đổi mã nguồn. Đó là một chủ đề khác cần đề cập và có lẽ tôi sẽ sớm viết một bài về cách tạo trình tải tùy chỉnh.

Trước khi đi vào chi tiết về việc tạo các plugin webpack tùy chỉnh, ta cần biết một số hoạt động cơ bản của gói module và cách thức hoạt động của webpack. Mục tiêu của một gói module cơ bản là đọc mã nguồn, tìm các phần phụ thuộc - đây được gọi là giải pháp phụ thuộc .

Trong quá trình phân giải phụ thuộc, trình gói thực hiện ánh xạ module ( bản đồ module ), gói chúng thành một file , đóng gói nó thành một module . webpack thực hiện các phần này theo cách nâng cao và thêm một số bước khác để làm cho nó hoạt động hiệu quả. Ta có thể phá vỡ kiến trúc của webpack bằng các bước sau:

  • Trình biên dịch : nó có API cấp cao nhất và cung cấp các móc cho chúng để kiểm soát việc thực thi webpack.
  • Biểu đồ biên dịch hoặc phụ thuộc : được trình biên dịch trả về và nó bắt đầu tạo biểu đồ phụ thuộc.
  • Resolver : tạo một đường dẫn tuyệt đối cho đường dẫn nhập được cung cấp và trả về các chi tiết như kết quả, yêu cầu, đường dẫn, ngữ cảnh, v.v.
  • Trình phân tích cú pháp : nó tạo AST (cây cú pháp trừu tượng) và sau đó tìm kiếm những thứ thú vị như yêu cầu và nhập khẩu và sau đó tạo đối tượng phụ thuộc.
  • Mô-đun Factories : Các đối tượng này được chuyển đến chức năng moduleFactory và tạo module .
  • Mẫu : nó thực hiện liên kết dữ liệu của đối tượng module và tạo mã trong file được đóng gói.

webpack cung cấp các hook cho trình biên dịch, phân tích cú pháp và biên dịch. Nó sử dụng một thư viện có tên là tapable , được duy trì bởi group webpack và giúp tạo ra các hook mạnh mẽ, nơi ta có thể khai thác các phương thức.

Hooks và khai thác vào các phương pháp là gì?

Hook tương tự như các sự kiện và việc khai thác chúng giống như người nghe đang lắng nghe một sự kiện để kích hoạt và chạy phương thức thích hợp. Ví dụ: khi ta đặt các trình nghe sự kiện liên quan đến DOM như thế này:

window.addEventListener('load', (event) => {
  loadEventListerner(event)
});

Trong trường hợp này, load là một sự kiện hoặc móc nối mà loadEventListener đang khai thác.

Cách sử dụng webpack có thể chạm & Cách các plugin đi vào hình ảnh?

Hãy lấy một ví dụ trong thế giới thực để giải thích cách webpack sử dụng tapable. Giả sử bạn đang đặt bánh pizza từ một ứng dụng giao bánh pizza. Bây giờ có một loạt các bước liên quan đến quá trình này như kiểm tra menu, tùy chỉnh đơn hàng của bạn và cuối cùng là đặt hàng bằng cách thanh toán. Bây giờ kể từ đây trở đi và cho đến khi giao bánh pizza cho bạn, ứng dụng sẽ gửi cho bạn thông báo về tiến độ đặt hàng.

Trong ví dụ này, bây giờ ta có thể thay thế ứng dụng giao bánh pizza bằng webpack , chính bạn bằng một plugin webpack và thông báo có móc được tạo bằng cách nhấn .

webpack tạo hook cho các giai đoạn biên dịch , biên dịchphân tích cú pháp bằng cách sử dụng tapable và sau đó plugin chạm vào chúng hoặc lắng nghe chúng và hoạt động tương ứng.

Đủ các lý thuyết và khái niệm này, chỉ cho tôi mã số !!

Đối với bài đăng này, ta sẽ tạo một plugin webpack đơn giản để kiểm tra kích thước của file gói được tạo và ghi lại các lỗi hoặc cảnh báo dựa trên giới hạn kích thước. Các giới hạn kích thước đó cũng có thể được thông qua trong các tùy chọn plugin và ta sẽ giữ giới hạn kích thước mặc định là 3KB. Vì vậy, khi nào plugin file kết quả vượt quá giới hạn kích thước, ta sẽ ghi lại thông báo lỗi và nếu nó ở dưới nó, ta sẽ ghi lại thông báo an toàn và nếu nó bằng giới hạn kích thước, ta sẽ chỉ cảnh báo user .

Bạn có thể tìm thấy mã cho plugin tại đây .

Hãy cài đặt dự án đầu tiên.

Trong folder dự án của bạn, hãy cài đặt webpack bằng npm hoặc Yarn:

$ npm init -y
$ npm install webpack webpack-cli --save-dev

Sau đó, tạo một folder src với index.js trong đó, nơi đường dẫn đầu vào hoặc mục nhập của bạn sẽ trỏ đến và tạo file webpack.config.js trong folder root dự án của bạn.

Đến đây bạn có thể tạo một folder cho plugin của bạn và đặt tên cho nó giống như bundlesize-webpack-plugin và tạo một index.js bên trong folder đó.

Cấu trúc dự án của bạn sẽ trông giống như sau:

webpack-Plugin-demo-directory
  |- webpack.config.js
  |- package.json
  |- /src
    |- index.js
  |- /bundlesize-webpack-plugin
    |- index.js

Thêm tập lệnh xây dựng sau vào các scripts trong file package.json của bạn:

"build": "webpack"

Và trong bundlesize-webpack-plugin/index.js hãy viết mã sau:

module.exports = class BundlesizeWebpackPlugin {
 constructor(options) {
   this.options = options;
 }
 apply(compiler) {
   console.log("FROM BUNDLESIZE PLUGIN");
 }
};

Ta sẽ thảo luận điều này sớm.

Bây giờ trong webpack.config.js của bạn, hãy viết mã sau:

const { resolve } = require("path");
const bundlesizeplugin = require("./bundlesize-webpack-plugin");

module.exports = {
 entry: resolve(__dirname, "src/index.js"),
 output: {
   path: resolve(__dirname, "bin"),
   filename: "bundle.js"
 },
 plugins: [new bundlesizeplugin()]
};

Bây giờ chạy npm run build .

Bạn sẽ thấy thông báo “FROM BUNDLESIZE PLUGIN” xuất hiện trong terminal của bạn.

Tuyệt vời, bạn vừa tạo một plugin webpack!

Phá vỡ nó

Mỗi plugin webpack phải có một phương thức apply trong đó được gọi bởi webpack và webpack cung cấp version trình biên dịch làm đối số cho phương thức đó.

Một plugin có thể dựa trên lớp hoặc có thể dựa trên chức năng. Nếu plugin dựa trên hàm, đối số hàm cũng là trình biên dịch. Ta sẽ đi với dựa trên lớp cho bài viết này vì đó là cách được khuyến khích .

Bạn có thể kiểm tra mã nguồn của webpack và cách nó được triển khai tại đây

Trong hàm tạo của lớp, bạn có thể thấy có một đối số options . Điều này được sử dụng khi plugin của bạn chấp nhận một số tùy chọn. Ta sẽ chuyển sizeLimit dưới dạng một tùy chọn và nếu nó không được thông qua, mặc định sẽ là 3KB .

Vì vậy, bây giờ ta có thể thay đổi phương thức hàm tạo thành này:

constructor(options) {
   this.options = options || {
     sizeLimit: 3
   };
 }

Bạn cũng có thể chuyển sizeLimit làm options plugin, như sau:

plugins: [
   new bundlesizeplugin({
     sizeLimit: 4
   })
 ]

Trong webpack.config.js , ta chỉ đề cập đến điểm nhập và yêu cầu webpack xuất file gói trong folder có tên bin trong file bundle.js và yêu cầu webpack sử dụng plugin của ta từ folder bundlesize-webpack-plugin .

Bây giờ ta đã có dự án sẵn sàng, hãy kiểm tra kích thước tài sản và so sánh với sizeLimit . Ta sẽ sử dụng hook compiler.hooks.done được tạo ra khi công việc biên dịch hoàn tất và file gói được tạo. Ta có thể lấy thông tin chi tiết về file được gói theo cách đó.

Lưu ý có một số móc không đồng bộ và ta có thể sử dụng phương pháp khai thác không đồng bộ cho chúng. Bạn có thể tìm hiểu về những điều này tại đây

apply(compiler) {
   compiler.hooks.done.tap("BundleSizePlugin", (stats) => {
     const {
        path,
        filename
     } = stats.compilation.options.output;
   })
 }

Trong phần này, ta đang khai thác sự kiện hoặc hook đã hoàn thành của trình biên dịch, đối số đầu tiên trong phương thức là tên plugin được webpack sử dụng để tham chiếu và phương thức thứ hai là lệnh gọi lại lấy stats làm đối số. Bạn có thể kiểm tra nội dung của thống kê bằng cách sử dụng console.log(stats) , nó sẽ hiển thị một đối tượng lớn với mọi chi tiết có thể về biên dịch và file có sẵn cho hook đó. Ta đang extract đường dẫn và tên file từ thuộc tính kết quả . Từ bây giờ, việc lấy thông tin chi tiết về file tin bằng cách sử dụng path thư viện lõi của Node.js và các module fs :

apply(compiler) {
   compiler.hooks.done.tap("BundleSizePlugin", stats => {
     const { path, filename } = stats.compilation.options.output;
     const bundlePath = resolve(path, filename);
     const { size } = fs.statSync(bundlePath);
     console.log(size); // size in bytes
   });
 }

Đơn giản phải không?

Bây giờ ta có thể chuyển đổi kích thước từ byte sang kb bằng cách sử dụng một hàm như hàm từ câu trả lời StackOverflow này .

Bây giờ chỉ cần so sánh nó với sizeLimitconsole.log thông báo thích hợp:

apply(compiler) {
   compiler.hooks.done.tap("BundleSizePlugin", stats => {
     const { path, filename } = stats.compilation.options.output;
     const bundlePath = resolve(path, filename);
     const { size } = fs.statSync(bundlePath);
     const { bundleSize, fullSizeInfo } = formatBytes(size);
     const { sizeLimit } = this.options;
     if (bundleSize < sizeLimit) {
       console.log(
         "Safe:Bundle-Size",
         fullSizeInfo,
         "\n SIZE LIMIT:",
         sizeLimit
       );
     } else {
       if (bundleSize === sizeLimit) {
         console.warn(
           "Warn:Bundle-Size",
           fullSizeInfo,
           "\n SIZE LIMIT:",
           sizeLimit
         );
       } else {
         console.error(
           "Unsafe:Bundle-Size",
           fullSizeInfo,
           "\n SIZE LIMIT:",
           sizeLimit
         );
       }
     }
   });
 }

Đó là nó! Đến đây bạn có plugin webpack của riêng mình, plugin này sẽ kiểm tra kích thước gói và báo cáo dựa trên giới hạn kích thước.

Đến đây bạn có thể xuất bản điều này trên register npm.

Có một số tiêu chuẩn mà webpack thấy hiệu quả để có trong plugin. Bạn có thể sử dụng webpack-default để có một điểm khởi đầu tốt.

Xin lưu ý plugin packlesize-webpack-plugin mà tôi đã xuất bản cũng đang mở rộng các hook của riêng nó và chúng được tạo bằng cách sử dụng tapable. Bạn có thể tìm thấy việc triển khai trong nhánh chính.

Tóm lược

  • Ta đã xem xét cách hoạt động của webpack và cách kiến trúc của nó được triển khai
  • Ta đã tìm hiểu về móc và ý nghĩa của việc khai thác chúng
  • Ta đã thấy cách các plugin đi vào hệ thống
  • Ta đã tạo một plugin đơn giản để kiểm tra kích thước của file được gói

Tags:

Các tin liên quan