Vite-plugin-Reac-css-mô-đun

Rung cây là một thuật ngữ thường được sử dụng trong ngữ cảnh JavaScript để loại bỏ mã chết. Nó dựa trên cấu trúc tĩnh của cú pháp mô-đun ES2015, tôi. e.

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
0 và
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
1. Tên và khái niệm đã được phổ biến bởi bản tổng hợp gói mô-đun ES2015

Bản phát hành webpack 2 đi kèm với hỗ trợ tích hợp cho các mô-đun ES2015 (mô-đun hài hòa bí danh) cũng như phát hiện xuất mô-đun không sử dụng. Bản phát hành webpack 4 mới mở rộng khả năng này bằng cách cung cấp gợi ý cho trình biên dịch thông qua thuộc tính

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
2
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
3 để biểu thị tệp nào trong dự án của bạn là "thuần túy" và do đó an toàn để cắt bớt nếu không sử dụng

Thêm tiện ích

Hãy thêm một tệp tiện ích mới vào dự án của chúng ta,

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
4, xuất hai hàm

dự án

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
  |- bundle.js
  |- index.html
|- /src
  |- index.js
+ |- math.js
|- /node_modules

src/toán học. js

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}

Đặt tùy chọn cấu hình

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
5 thành phát triển để đảm bảo rằng gói không bị thu nhỏ

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

________số 8

Với điều đó, hãy cập nhật tập lệnh nhập của chúng tôi để sử dụng một trong những phương thức mới này và xóa

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
6 để đơn giản

src/chỉ mục. js

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
0

Lưu ý rằng chúng tôi đã không

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
0 phương pháp
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
8 từ mô-đun
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
4. Chức năng đó được gọi là "mã chết", nghĩa là một
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
1 không được sử dụng sẽ bị loại bỏ. Bây giờ, hãy chạy tập lệnh npm của chúng ta,
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
+ mode: 'development',
+ optimization: {
+   usedExports: true,
+ },
};
1 và kiểm tra gói đầu ra

dist/bó. js (khoảng dòng 90 - 100)

Lưu ý nhận xét

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
+ mode: 'development',
+ optimization: {
+   usedExports: true,
+ },
};
2 ở trên. Nếu bạn nhìn vào mã bên dưới, bạn sẽ nhận thấy rằng
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
8 không được nhập, tuy nhiên, nó vẫn được bao gồm trong gói. Chúng tôi sẽ khắc phục điều đó trong phần tiếp theo

Đánh dấu tệp là không có tác dụng phụ

Trong thế giới mô-đun 100% ESM, việc xác định tác dụng phụ rất đơn giản. Tuy nhiên, chúng tôi vẫn chưa hoàn thành, vì vậy trong thời gian đó, cần cung cấp gợi ý cho trình biên dịch của webpack về "độ tinh khiết" của mã của bạn

Cách này được thực hiện là gói

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
2. tài sản json

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
9

Tất cả các mã được lưu ý ở trên không chứa tác dụng phụ, vì vậy chúng tôi có thể đánh dấu thuộc tính là

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
+ mode: 'development',
+ optimization: {
+   usedExports: true,
+ },
};
5 để thông báo cho webpack rằng nó có thể cắt bớt các lần xuất không sử dụng một cách an toàn

Tuy nhiên, nếu mã của bạn có một số tác dụng phụ, thì có thể cung cấp một mảng thay thế

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
1

Mảng chấp nhận các mẫu hình cầu đơn giản cho các tệp có liên quan. Nó sử dụng glob-to-regexp dưới mui xe (Hỗ trợ.

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
+ mode: 'development',
+ optimization: {
+   usedExports: true,
+ },
};
6,
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
+ mode: 'development',
+ optimization: {
+   usedExports: true,
+ },
};
7,
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
+ mode: 'development',
+ optimization: {
+   usedExports: true,
+ },
};
8,
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
+ mode: 'development',
+ optimization: {
+   usedExports: true,
+ },
};
9). Các mẫu như
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
00, không bao gồm
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
01, sẽ được xử lý như
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
02

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
9

Cuối cùng, cũng có thể đặt

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
2 từ tùy chọn cấu hình
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
04

Làm rõ rung cây và export function square(x) { return x * x; } export function cube(x) { return x * x * x; }05

Tối ưu hóa

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
05 và
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
07 (còn được gọi là rung cây) là hai điều khác nhau

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
05 hiệu quả hơn nhiều vì nó cho phép bỏ qua toàn bộ mô-đun/tệp và toàn bộ cây con

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
07 dựa vào terser để phát hiện tác dụng phụ trong các câu lệnh. Đây là một nhiệm vụ khó khăn trong JavaScript và không hiệu quả bằng cờ
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
05 đơn giản. Nó cũng không thể bỏ qua cây con/phụ thuộc vì thông số kỹ thuật nói rằng các tác dụng phụ cần được đánh giá. Mặc dù chức năng xuất hoạt động tốt, nhưng các Thành phần bậc cao hơn (HOC) của React có vấn đề về vấn đề này

Hãy làm một ví dụ

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
8

Phiên bản đi kèm trông như thế này

Khi

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
91 không được sử dụng, bạn có thể xóa
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
92 một cách hiệu quả, để lại tất cả mã còn lại. Vì vậy, câu hỏi đặt ra là "Mã này có bất kỳ tác dụng phụ nào không hoặc nó có thể được gỡ bỏ một cách an toàn không?". Khó nói, đặc biệt là vì dòng này
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
93.
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
94 được gọi và giá trị trả về cũng được gọi. Có bất kỳ tác dụng phụ nào khi gọi
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
95 hoặc
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
96 không?

Terser thực sự cố gắng tìm ra nó, nhưng nó không biết chắc chắn trong nhiều trường hợp. Điều này không có nghĩa là terser không làm tốt công việc của mình bởi vì nó không thể tìm ra. Quá khó để xác định nó một cách đáng tin cậy trong một ngôn ngữ động như JavaScript

Nhưng chúng ta có thể giúp ngắn gọn hơn bằng cách sử dụng chú thích

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
99. Nó gắn cờ một tuyên bố là không có tác dụng phụ. Vì vậy, một thay đổi nhỏ sẽ có thể làm rung cây mã

Điều này sẽ cho phép loại bỏ đoạn mã này. Nhưng vẫn còn những câu hỏi về hàng nhập khẩu cần được đưa vào/đánh giá vì chúng có thể chứa tác dụng phụ

Để giải quyết vấn đề này, chúng ta sử dụng thuộc tính

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
2 trong
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
3

Nó tương tự như

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
99 nhưng ở cấp độ mô-đun thay vì cấp độ câu lệnh. Nó nói (
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
2 tài sản). "Nếu không sử dụng xuất trực tiếp từ mô-đun được gắn cờ không có tác dụng phụ, trình đóng gói có thể bỏ qua việc đánh giá mô-đun để tìm tác dụng phụ. "

Trong ví dụ về Polaris của Shopify, các mô-đun ban đầu trông như thế này

mục lục. js

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
  |- bundle.js
  |- index.html
|- /src
  |- index.js
+ |- math.js
|- /node_modules
2

thành phần/chỉ mục. js

bưu kiện. json

Đối với

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
14, điều này có ý nghĩa như sau

  • bao gồm nó. bao gồm mô-đun, đánh giá nó và tiếp tục phân tích các phụ thuộc
  • bỏ qua. không bao gồm nó, không đánh giá nó nhưng tiếp tục phân tích các phụ thuộc
  • loại trừ nó. không bao gồm nó, không đánh giá nó và không phân tích các phụ thuộc

Cụ thể trên (các) tài nguyên phù hợp

  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    15. Không sử dụng xuất trực tiếp, nhưng được gắn cờ với sideEffects -> bao gồm nó
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    16. Không xuất được sử dụng, nhưng được gắn cờ với sideEffects -> bao gồm nó
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    17. Không xuất được sử dụng, không được gắn cờ với sideEffects -> loại trừ nó
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    18. Không sử dụng xuất trực tiếp, không gắn cờ với sideEffects, nhưng sử dụng xuất tái xuất -> bỏ qua
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    19. Không xuất được sử dụng, không được gắn cờ với sideEffects -> loại trừ nó. Điều này cũng loại trừ tất cả các phụ thuộc như
    export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    90 ngay cả khi chúng được gắn cờ với sideEffects
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    91. Xuất trực tiếp được sử dụng, không được gắn cờ với sideEffects -> bao gồm nó
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    92. Không xuất được sử dụng, nhưng được gắn cờ với sideEffects -> bao gồm nó

Trong trường hợp này, chỉ có 4 mô-đun được bao gồm trong gói

  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    15. khá nhiều trống rỗng
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    16
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    91
  • export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    92

Sau tối ưu hóa này, các tối ưu hóa khác vẫn có thể áp dụng. Ví dụ. Các bản xuất

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
97 và
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
98 từ
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
99 cũng không được sử dụng. Tối ưu hóa
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
07 sẽ chọn nó và terser có thể loại bỏ một số câu lệnh khỏi mô-đun

Ghép nối mô-đun cũng được áp dụng. Vì vậy, 4 mô-đun này cộng với mô-đun đầu vào (và có thể nhiều phụ thuộc hơn) có thể được nối với nhau.

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
15 cuối cùng không có mã nào được tạo

Đánh dấu một cuộc gọi chức năng là không có tác dụng phụ

Có thể nói với webpack rằng một lệnh gọi hàm không có tác dụng phụ (thuần túy) bằng cách sử dụng chú thích

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
99. Nó có thể được đặt trước các lời gọi hàm để đánh dấu chúng là không có tác dụng phụ. Các đối số được truyền cho hàm không được đánh dấu bằng chú thích và có thể cần được đánh dấu riêng lẻ. Khi giá trị ban đầu trong khai báo biến của một biến không sử dụng được coi là không có tác dụng phụ (thuần túy), nó sẽ bị đánh dấu là mã chết, không được thực thi và bị bộ thu nhỏ loại bỏ. Hành vi này được kích hoạt khi
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
83 được đặt thành
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
84

tập tin. js

Giảm thiểu đầu ra

Vì vậy, chúng tôi đã chuẩn bị sẵn sàng để loại bỏ "mã chết" bằng cách sử dụng cú pháp

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
0 và
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
1, nhưng chúng tôi vẫn cần loại bỏ nó khỏi gói. Để làm điều đó, hãy đặt tùy chọn cấu hình
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
5 thành
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
88

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

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}
8

Với bình phương đó, chúng ta có thể chạy một

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
+ mode: 'development',
+ optimization: {
+   usedExports: true,
+ },
};
1 khác và xem có gì thay đổi không

Nhận thấy bất cứ điều gì khác nhau về

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
  |- bundle.js
  |- index.html
|- /src
  |- index.js
+ |- math.js
|- /node_modules
20? . Với việc thu nhỏ và rung cây, gói của chúng tôi hiện nhỏ hơn một vài byte. Mặc dù điều đó có vẻ không nhiều trong ví dụ giả định này, nhưng việc lắc cây có thể làm giảm đáng kể kích thước gói khi làm việc trên các ứng dụng lớn hơn với các cây phụ thuộc phức tạp

Sự kết luận

Những gì chúng tôi đã học được là để tận dụng lợi thế của việc rung cây, bạn phải

  • Sử dụng cú pháp mô-đun ES2015 (i. e.
    export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    0 và
    export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    1)
  • Đảm bảo không có trình biên dịch nào chuyển đổi cú pháp mô-đun ES2015 của bạn thành các mô-đun CommonJS (đây là hành vi mặc định của cài đặt sẵn Babel phổ biến @babel/preset-env - xem tài liệu để biết thêm chi tiết)
  • Thêm thuộc tính
    export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    2 vào tệp
    export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    3 của dự án của bạn
  • Sử dụng tùy chọn cấu hình
    export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    88
    export function square(x) {
      return x * x;
    }
    
    export function cube(x) {
      return x * x * x;
    }
    5 để kích hoạt các tối ưu hóa khác nhau bao gồm thu nhỏ và rung cây

Bạn có thể tưởng tượng ứng dụng của mình như một cái cây. Mã nguồn và các thư viện mà bạn thực sự sử dụng đại diện cho những chiếc lá xanh tươi của cây. Mã chết đại diện cho những chiếc lá chết màu nâu của cây được tiêu thụ vào mùa thu. Để loại bỏ những chiếc lá chết, bạn phải rung cây, khiến chúng rơi xuống

Nếu bạn quan tâm đến các cách khác để tối ưu hóa đầu ra của mình, vui lòng chuyển sang hướng dẫn tiếp theo để biết chi tiết về xây dựng cho sản xuất