Vấn đề bên trong html

Tách mã là một trong những tính năng hấp dẫn nhất của webpack. Tính năng này cho phép bạn chia mã của mình thành nhiều gói khác nhau, sau đó có thể được tải theo yêu cầu hoặc song song. Nó có thể được sử dụng để đạt được các gói nhỏ hơn và kiểm soát mức độ ưu tiên tải tài nguyên, nếu được sử dụng đúng cách, có thể có tác động lớn đến thời gian tải

Có ba cách tiếp cận chung để tách mã có sẵn

  • các điểm nhập cảnh. Tách mã thủ công bằng cách sử dụng cấu hình
    import _ from 'lodash';
    
    console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
    5
  • Ngăn chặn trùng lặp. Sử dụng hoặc
    import _ from 'lodash';
    
    console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
    6 để loại bỏ và tách khối
  • Nhập động. Tách mã thông qua các lệnh gọi hàm nội tuyến trong các mô-đun

các điểm nhập cảnh

Đây là cách dễ nhất và trực quan nhất để tách mã. Tuy nhiên, nó thủ công hơn và có một số cạm bẫy, chúng ta sẽ xem qua. Hãy xem cách chúng ta có thể tách một mô-đun khác khỏi gói chính

dự định

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
+ |- another-module.js
|- /node_modules

mô-đun khác. js

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];

gói web. cấu hình. js

 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };

Điều này sẽ mang lại kết quả xây dựng sau

...
[webpack-cli] Compilation finished
asset index.bundle.js 553 KiB [emitted] [name: index]
asset another.bundle.js 553 KiB [emitted] [name: another]
runtime modules 2.49 KiB 12 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./src/another-module.js 84 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 245 ms

Như đã đề cập, có một số cạm bẫy đối với phương pháp này

  • Nếu có bất kỳ mô-đun trùng lặp nào giữa các khối mục nhập, chúng sẽ được bao gồm trong cả hai gói
  • Nó không linh hoạt và không thể được sử dụng để phân tách động mã với logic ứng dụng cốt lõi

Điểm đầu tiên trong hai điểm này chắc chắn là một vấn đề đối với ví dụ của chúng tôi, vì

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
7 cũng được nhập trong
import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
8 và do đó sẽ được sao chép trong cả hai gói. Hãy loại bỏ sự trùng lặp này trong phần tiếp theo

phụ thuộc đầu vào

Cho phép chia sẻ các mô-đun giữa các khối

gói web. cấu hình. js

ngày thứ 8

Nếu chúng tôi định sử dụng nhiều điểm vào trên một trang HTML, thì cũng cần có ____ ______, nếu không chúng tôi có thể gặp rắc rối được mô tả tại đây

gói web. cấu hình. js

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
0

Và đây là kết quả của việc xây dựng

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
1

Như bạn có thể thấy, có một tệp

 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
1 khác được tạo bên cạnh
 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
2,
 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
3 và
 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
4

Mặc dù cho phép sử dụng nhiều điểm nhập trên mỗi trang trong webpack, nhưng nên tránh sử dụng khi có thể để ưu tiên một điểm nhập có nhiều lần nhập.

 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
5. Điều này dẫn đến việc tối ưu hóa tốt hơn và thứ tự thực thi nhất quán khi sử dụng thẻ tập lệnh
 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
6

SplitChunksPlugin

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
6 cho phép chúng tôi trích xuất các phụ thuộc phổ biến vào một đoạn mục nhập hiện có hoặc một đoạn hoàn toàn mới. Hãy sử dụng điều này để khử trùng lặp phụ thuộc
import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
7 từ ví dụ trước

gói web. cấu hình. js

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
+ |- another-module.js
|- /node_modules
0

Với tùy chọn cấu hình tại chỗ, bây giờ chúng ta sẽ thấy phần phụ thuộc trùng lặp đã bị xóa khỏi

 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
3 và
 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
4 của chúng ta. Plugin sẽ lưu ý rằng chúng tôi đã tách
import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
7 thành một đoạn riêng biệt và loại bỏ trọng lượng chết khỏi gói chính của chúng tôi. Hãy làm một
...
[webpack-cli] Compilation finished
asset index.bundle.js 553 KiB [emitted] [name: index]
asset another.bundle.js 553 KiB [emitted] [name: another]
runtime modules 2.49 KiB 12 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./src/another-module.js 84 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 245 ms
3 để xem nó có hoạt động không

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
+ |- another-module.js
|- /node_modules
5

Dưới đây là một số plugin và trình tải hữu ích khác do cộng đồng cung cấp để tách mã

Nhập động

Hai kỹ thuật tương tự được webpack hỗ trợ khi phân tách mã động. Cách tiếp cận đầu tiên và được đề xuất là sử dụng phương pháp phù hợp với đề xuất ECMAScript để nhập động. Cách tiếp cận kế thừa dành riêng cho webpack là sử dụng. Hãy thử sử dụng cách đầu tiên trong hai cách tiếp cận này

Trước khi chúng tôi bắt đầu, hãy loại bỏ thêm

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
5 và
 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
9 khỏi cấu hình của chúng tôi trong ví dụ trên vì chúng sẽ không cần thiết cho phần trình diễn tiếp theo này

gói web. cấu hình. js

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
+ |- another-module.js
|- /node_modules
8

Chúng tôi cũng sẽ cập nhật dự án của mình để xóa các tệp hiện không sử dụng

dự định

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
0

Bây giờ, thay vì nhập tĩnh

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
7, chúng tôi sẽ sử dụng nhập động để tách một đoạn

src/chỉ mục. js

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
1

Lý do chúng tôi cần

...
[webpack-cli] Compilation finished
asset index.bundle.js 553 KiB [emitted] [name: index]
asset another.bundle.js 553 KiB [emitted] [name: another]
runtime modules 2.49 KiB 12 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./src/another-module.js 84 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 245 ms
9 là vì webpack 4, khi nhập mô-đun CommonJS, quá trình nhập sẽ không còn phân giải thành giá trị của
 const path = require['path'];

 module.exports = {
   mode: 'development',
   entry: {
-    index: './src/index.js',
-    another: './src/another-module.js',
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
0, thay vào đó, nó sẽ tạo một đối tượng không gian tên nhân tạo cho mô-đun CommonJS. Để biết thêm thông tin về lý do đằng sau điều này, hãy đọc webpack 4. nhập[] và CommonJs

Hãy chạy webpack để xem

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
7 được tách ra thành một gói riêng

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
2

Khi

...
[webpack-cli] Compilation finished
asset index.bundle.js 553 KiB [emitted] [name: index]
asset another.bundle.js 553 KiB [emitted] [name: another]
runtime modules 2.49 KiB 12 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./src/another-module.js 84 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 245 ms
4 trả về một lời hứa, nó có thể được sử dụng với các hàm của
 const path = require['path'];

 module.exports = {
-  entry: './src/index.js',
+  mode: 'development',
+  entry: {
+    index: './src/index.js',
+    another: './src/another-module.js',
+  },
   output: {
-    filename: 'main.js',
+    filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
6. Đây là cách nó sẽ đơn giản hóa mã

src/chỉ mục. js

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
3

Tìm nạp trước/Tải trước mô-đun

Gói web 4. 6. 0+ thêm hỗ trợ cho tìm nạp trước và tải trước

Sử dụng các lệnh nội tuyến này trong khi khai báo quá trình nhập của bạn cho phép gói web xuất ra “Gợi ý tài nguyên” cho trình duyệt biết rằng đối với

  • tìm nạp trước. tài nguyên có lẽ là cần thiết cho một số điều hướng trong tương lai
  • tải trước. tài nguyên cũng sẽ cần thiết trong quá trình điều hướng hiện tại

Một ví dụ về điều này là có thành phần

 const path = require['path'];

 module.exports = {
   mode: 'development',
   entry: {
-    index: './src/index.js',
-    another: './src/another-module.js',
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
4, thành phần này hiển thị thành phần
 const path = require['path'];

 module.exports = {
   mode: 'development',
   entry: {
-    index: './src/index.js',
-    another: './src/another-module.js',
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
5, sau đó theo yêu cầu sẽ tải thành phần
 const path = require['path'];

 module.exports = {
   mode: 'development',
   entry: {
-    index: './src/index.js',
-    another: './src/another-module.js',
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
6 sau khi được nhấp

nút đăng nhập. js

Điều này sẽ dẫn đến việc

 const path = require['path'];

 module.exports = {
   mode: 'development',
   entry: {
-    index: './src/index.js',
-    another: './src/another-module.js',
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
7 được thêm vào phần đầu của trang, điều này sẽ hướng dẫn trình duyệt tìm nạp trước tệp
 const path = require['path'];

 module.exports = {
   mode: 'development',
   entry: {
-    index: './src/index.js',
-    another: './src/another-module.js',
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
8 trong thời gian nhàn rỗi

Chỉ thị tải trước có nhiều điểm khác biệt so với tìm nạp trước

  • Một đoạn được tải trước bắt đầu tải song song với đoạn gốc. Đoạn tìm nạp trước bắt đầu sau khi đoạn gốc tải xong
  • Một đoạn được tải trước có mức độ ưu tiên trung bình và được tải xuống ngay lập tức. Một đoạn tìm nạp trước được tải xuống trong khi trình duyệt không hoạt động
  • Một đoạn được tải trước phải được yêu cầu ngay lập tức bởi đoạn gốc. Một đoạn tìm nạp trước có thể được sử dụng bất cứ lúc nào trong tương lai
  • Hỗ trợ trình duyệt là khác nhau

Một ví dụ về điều này có thể là có một

 const path = require['path'];

 module.exports = {
   mode: 'development',
   entry: {
-    index: './src/index.js',
-    another: './src/another-module.js',
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
9 luôn phụ thuộc vào một thư viện lớn nên nằm trong một đoạn riêng biệt

Hãy tưởng tượng một thành phần

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
00 cần một
import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
01 khổng lồ. Nó hiển thị một
import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
02 khi được kết xuất và ngay lập tức thực hiện nhập khẩu theo yêu cầu của
import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
01

Thành phần biểu đồ. js

Khi một trang sử dụng

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
00 được yêu cầu, chart-library-chunk cũng được yêu cầu thông qua
 const path = require['path'];

 module.exports = {
   mode: 'development',
   entry: {
-    index: './src/index.js',
-    another: './src/another-module.js',
+    index: {
+      import: './src/index.js',
+      dependOn: 'shared',
+    },
+    another: {
+      import: './src/another-module.js',
+      dependOn: 'shared',
+    },
+    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve[__dirname, 'dist'],
   },
 };
7. Giả sử đoạn trang nhỏ hơn và kết thúc nhanh hơn, trang sẽ được hiển thị với
import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
02, cho đến khi kết thúc
import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
07 đã được yêu cầu. Điều này sẽ giúp tăng thời gian tải một chút vì nó chỉ cần một chuyến khứ hồi thay vì hai. Đặc biệt là trong môi trường có độ trễ cao

Đôi khi bạn cần có quyền kiểm soát tải trước của riêng mình. Ví dụ: tải trước bất kỳ quá trình nhập động nào có thể được thực hiện thông qua tập lệnh async. Điều này có thể hữu ích trong trường hợp kết xuất phía máy chủ phát trực tuyến

Nếu quá trình tải tập lệnh không thành công trước khi webpack bắt đầu tự tải tập lệnh đó [webpack chỉ tạo thẻ tập lệnh để tải mã của nó, nếu tập lệnh đó không có trên trang], trình xử lý bắt đó sẽ không bắt đầu cho đến khi không được thông qua. Hành vi này có thể bất ngờ. Nhưng điều đó có thể giải thích được - webpack không thể đưa ra bất kỳ lỗi nào, vì webpack không biết, tập lệnh đó không thành công. Webpack sẽ thêm trình xử lý lỗi vào tập lệnh ngay sau khi xảy ra lỗi

Để ngăn chặn sự cố như vậy, bạn có thể thêm trình xử lý onerror của riêng mình, trình xử lý này sẽ xóa tập lệnh trong trường hợp có bất kỳ lỗi nào

import _ from 'lodash';

console.log[_.join[['Another', 'module', 'loaded!'], ' ']];
4

Trong trường hợp đó, tập lệnh bị lỗi sẽ bị xóa. Webpack sẽ tạo tập lệnh riêng và mọi lỗi sẽ được xử lý mà không có bất kỳ thời gian chờ nào

Phân tích gói

Khi bạn bắt đầu tách mã của mình, có thể hữu ích khi phân tích đầu ra để kiểm tra xem các mô-đun đã kết thúc ở đâu. Công cụ phân tích chính thức là một nơi tốt để bắt đầu. Ngoài ra còn có một số tùy chọn khác được cộng đồng hỗ trợ

  • biểu đồ webpack. Biểu đồ hình tròn tương tác cho số liệu thống kê webpack
  • trình hiển thị webpack. Trực quan hóa và phân tích các gói của bạn để xem mô-đun nào đang chiếm dung lượng và mô-đun nào có thể trùng lặp
  • trình phân tích gói webpack. Tiện ích plugin và CLI đại diện cho nội dung gói dưới dạng sơ đồ cây có thể thu phóng tương tác thuận tiện
  • trình trợ giúp tối ưu hóa gói gói web. Công cụ này sẽ phân tích gói của bạn và cung cấp cho bạn các đề xuất khả thi về những điều cần cải thiện để giảm kích thước gói của bạn
  • thống kê bó. Tạo báo cáo gói [kích thước gói, nội dung, mô-đun] và so sánh kết quả giữa các bản dựng khác nhau

bước tiếp theo

Xem Lazy Loading để biết ví dụ cụ thể hơn về cách sử dụng

...
[webpack-cli] Compilation finished
asset index.bundle.js 553 KiB [emitted] [name: index]
asset another.bundle.js 553 KiB [emitted] [name: another]
runtime modules 2.49 KiB 12 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./src/another-module.js 84 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 245 ms
4 trong ứng dụng thực và bộ nhớ đệm để tìm hiểu cách phân tách mã hiệu quả hơn

Chủ Đề