Tôi không hiểu cách chúng tôi phân loại chúng?
Không gian tên được sử dụng để tổ chức / đóng gói mã của bạn. Các mô-đun bên ngoài được sử dụng để tổ chức / đóng gói mã của bạn VÀ để định vị mã của bạn trong thời gian chạy. Trên thực tế, bạn có hai lựa chọn trong thời gian chạy: 1) kết hợp tất cả mã đã được chuyển thành một tệp hoặc 2) sử dụng các mô-đun bên ngoài và có nhiều tệp và yêu cầu một số cơ chế khác để truy cập các tệp đó.
Khi nào xuất một lớp hoặc không gian tên hoặc gói?
Để làm cho một loại hoặc giá trị hiển thị bên ngoài tệp chứa nó, bạn phải xuất nó nếu nó nằm trong không gian tên. Việc bạn xuất nó ở cấp cao nhất hay trong một không gian tên sẽ quyết định xem nó hiện đang ở trong một mô-đun bên ngoài.
Nếu chúng tôi xuất gói / không gian tên, tất cả các lớp trong đó được xuất hoặc cần được xuất rõ ràng
Các lớp trong một không gian tên sẽ luôn cần được xuất một cách rõ ràng để lớp có thể hiển thị tại thời điểm biên dịch bên ngoài tệp mà nó được định nghĩa.
Làm thế nào mỗi một trong số chúng có thể được nhập / yêu cầu?
Điều này phụ thuộc vào việc bạn đang sử dụng các mô-đun bên ngoài. Một mô-đun bên ngoài sẽ luôn cần được nhập để "sử dụng" nó. Nhập một không gian tên không có trong mô-đun bên ngoài thực sự chỉ là cung cấp một bí danh cho không gian tên - bạn vẫn phải thêm tiền tố kiểu / bất cứ thứ gì với bí danh (và đây là lý do tại sao bạn thường không muốn sử dụng không gian tên với các mô-đun bên ngoài; làm như vậy có nghĩa là bạn luôn phải sử dụng tiền tố khi tham chiếu bất kỳ thứ gì được cung cấp bởi mô-đun bên ngoài.) Không gian tên không có trong mô-đun bên ngoài có thể kéo dài tệp, vì vậy nếu bạn đang ở trong cùng một không gian tên, bạn có thể tham chiếu đến bất kỳ thứ gì được xuất bởi không gian tên mà không cần bất kỳ loại nhập nào.
Để thực sự hiểu những điều trên bạn cần một số kiến thức nền tảng. Điều quan trọng cần hiểu với các tham chiếu / không gian tên / mô-đun bên ngoài là những gì các cấu trúc này làm trong thời gian biên dịch và chúng làm gì trong thời gian chạy.
Các chỉ thị tham chiếu được sử dụng tại thời điểm biên dịch để định vị thông tin kiểu. Nguồn của bạn có một biểu tượng cụ thể trong đó. Trình biên dịch TypeScript định vị định nghĩa cho biểu tượng đó như thế nào? Chỉ thị tham chiếu phần lớn đã được cộng gộp bởi cơ chế tsconfig.json - bằng cách sử dụng tsconfig.json, bạn cho trình biên dịch biết tất cả các nguồn của bạn ở đâu.
Không gian tên có thể chứa định nghĩa kiểu và / hoặc triển khai. Nếu một vùng tên chỉ chứa thông tin kiểu thì nó không có biểu hiện thời gian chạy nào cả - bạn có thể kiểm tra điều này bằng cách xem đầu ra JS và tìm một tệp JS trống. Nếu một không gian tên có mã thực thi, thì mã được bao bọc bên trong một bao đóng được gán cho một biến toàn cục có cùng tên với không gian tên. Với không gian tên lồng nhau, sẽ có một biến toàn cục cho không gian tên gốc. Một lần nữa, hãy kiểm tra đầu ra JS. Không gian tên về mặt lịch sử là cách mà các thư viện phía máy khách JS đã cố gắng tránh vấn đề xung đột tên. Ý tưởng là gói toàn bộ thư viện của bạn thành một bao đóng và sau đó để lộ dấu vết toàn cục càng nhỏ càng tốt - chỉ một biến toàn cục tham chiếu đến bao đóng. Chà, vấn đề vẫn là bạn đã khẳng định được tên tuổi trong không gian toàn cầu. Điều gì sẽ xảy ra nếu bạn muốn, giả sử, hai phiên bản của một thư viện? Không gian tên TypeScript vẫn có vấn đề về cách xác định nguồn cho không gian tên. Có nghĩa là, mã nguồn tham chiếu AB vẫn có vấn đề trong việc cho trình biên dịch biết cách xác định vị trí AB - bằng cách sử dụng các lệnh tham chiếu hoặc bằng cách sử dụng tsconfig.json. Hoặc bằng cách đặt không gian tên vào một mô-đun bên ngoài và sau đó nhập mô-đun bên ngoài.
Mô-đun bên ngoài bắt nguồn bằng JS phía máy chủ. Có sự tương ứng 1-1 giữa mô-đun bên ngoài và tệp trên hệ thống tệp. Bạn có thể sử dụng cấu trúc thư mục hệ thống tệp để tổ chức các mô-đun bên ngoài thành một cấu trúc lồng nhau. Việc nhập một mô-đun bên ngoài nói chung sẽ luôn giới thiệu phụ thuộc thời gian chạy vào mô-đun bên ngoài đó (ngoại lệ là khi bạn nhập một mô-đun bên ngoài nhưng sau đó không sử dụng bất kỳ xuất nào của nó ở vị trí giá trị - tức là bạn chỉ nhập mô-đun bên ngoài để nhận thông tin loại của nó). Một mô-đun bên ngoài hoàn toàn nằm trong một bao đóng và đây là chìa khóa: người dùng mô-đun có thể gán bao đóng cho bất kỳ biến cục bộ nào họ muốn. TypeScript / ES6 bổ sung thêm cú pháp xung quanh việc ánh xạ các bản xuất của các mô-đun bên ngoài sang tên cục bộ, nhưng đây chỉ là một điều tốt đẹp. Về phía máy chủ, định vị mô-đun bên ngoài tương đối dễ hiểu: chỉ cần xác định vị trí tệp đại diện cho mô-đun bên ngoài trên hệ thống tệp cục bộ. Nếu bạn muốn sử dụng các mô-đun bên ngoài ở phía máy khách, trong trình duyệt, nó sẽ phức tạp hơn vì không có hệ thống tệp nào tương đương với hệ thống tệp có sẵn mô-đun để tải. Vì vậy, bây giờ ở phía máy khách, bạn cần một cách để gói tất cả các tệp đó thành một biểu mẫu có thể được sử dụng từ xa trong trình duyệt - đây là nơi mà các trình gói mô-đun như Webpack (Webpack thực hiện rất nhiều thứ hơn gói mô-đun) và Browserify phát huy tác dụng. Các gói mô-đun cho phép giải quyết thời gian chạy của các mô-đun bên ngoài của bạn trong trình duyệt. trong trình duyệt, nó trở nên phức tạp hơn vì không có hệ thống tệp nào tương đương với hệ thống tệp có sẵn mô-đun để tải. Vì vậy, bây giờ ở phía máy khách, bạn cần một cách để gói tất cả các tệp đó thành một biểu mẫu có thể được sử dụng từ xa trong trình duyệt - đây là nơi mà các trình gói mô-đun như Webpack (Webpack thực hiện rất nhiều thứ hơn gói mô-đun) và Browserify phát huy tác dụng. Các gói mô-đun cho phép giải quyết thời gian chạy của các mô-đun bên ngoài của bạn trong trình duyệt. trong trình duyệt, nó trở nên phức tạp hơn vì không có hệ thống tệp nào tương đương với hệ thống tệp có sẵn mô-đun để tải. Vì vậy, bây giờ ở phía máy khách, bạn cần một cách để gói tất cả các tệp đó thành một biểu mẫu có thể được sử dụng từ xa trong trình duyệt - đây là nơi mà các trình gói mô-đun như Webpack (Webpack thực hiện rất nhiều thứ hơn gói mô-đun) và Browserify phát huy tác dụng. Các gói mô-đun cho phép giải quyết thời gian chạy của các mô-đun bên ngoài của bạn trong trình duyệt.
Kịch bản thế giới thực: AngularJS. Giả sử các mô-đun bên ngoài không tồn tại, sử dụng một không gian tên duy nhất để hạn chế ô nhiễm không gian toàn cầu (trong ví dụ dưới đây, một biến MyApp là tất cả những gì có trong không gian toàn cầu), chỉ xuất giao diện và sử dụng tiêm phụ thuộc AngularJS để thực hiện triển khai có sẵn để sử dụng. Đặt tất cả các lớp trong thư mục gốc, thêm tsconfig.json vào thư mục gốc, cài đặt các kiểu chữ anglejs dưới cùng thư mục gốc để tsconfig.json cũng chọn nó, kết hợp tất cả đầu ra trong một tệp JS. Điều này sẽ hoạt động tốt cho hầu hết các dự án nếu việc sử dụng lại mã không phải là điều đáng lo ngại.
MyService.ts:
namespace MyApp {
// without an export the interface is not visible outside of MyService.ts
export interface MyService {
....
}
// class is not exported; AngularJS DI will wire up the implementation
class MyServiceImpl implements MyService {
}
angular.module("MyApp").service("myService", MyServiceImpl);
}
MyController.ts:
namespace MyApp {
class MyController {
// No import of MyService is needed as we are spanning
// one namespace with multiple files.
// MyService is only used at compile time for type checking.
// AngularJS DI is done on the name of the variable.
constructor(private myService: MyService) {
}
}
angular.module("MyApp").controller("myController", MyController);
}
Sử dụng IIFE để tránh gây ô nhiễm phạm vi thời gian chạy toàn cầu. Trong ví dụ này, không có biến toàn cục nào được tạo. (Giả sử là tsconfig.json.)
Foo.ts:
namespace Foo {
// without an export IFoo is not visible. No JS is generated here
// as we are only defining a type.
export interface IFoo {
x: string;
}
}
interface ITopLevel {
z: string;
}
(function(){
// export required above to make IFoo visible as we are not in the Foo namespace
class Foo1 implements Foo.IFoo {
x: string = "abc";
}
// do something with Foo1 like register it with a DI system
})();
Bar.ts:
// alias import; no external module created
import IFoo = Foo.IFoo;
(function() {
// Namespace Foo is always visible as it was defined at
// top level (outside of any other namespace).
class Bar1 implements Foo.IFoo {
x: string;
}
// equivalent to above
class Bar2 implements IFoo {
x: string;
}
// IToplevel is visible here for the same reason namespace Foo is visible
class MyToplevel implements ITopLevel {
z: string;
}
})();
Sử dụng IIFE, bạn có thể loại bỏ việc giới thiệu MyApp như một biến toàn cục trong ví dụ đầu tiên.
MyService.ts:
interface MyService {
....
}
(function() {
class MyServiceImpl implements MyService {
}
angular.module("MyApp").service("myService", MyServiceImpl);
})();
MyController.ts:
(function() {
class MyController {
constructor(private myService: MyService) {
}
}
angular.module("MyApp").controller("myController", MyController);
})();