Mô-đun so với Không gian tên - Nhập so với Chỉ số loại yêu cầu


102

Tôi đang nhận được rất nhiều sự nhầm lẫn với module/namespace/exportvà import, require, referencecách sử dụng. Xuất thân từ nền tảng Java, ai đó có thể giải thích ngắn gọn cho tôi khi nào thì sử dụng cái gì và thiết kế phù hợp là gì không? Tôi cảm thấy mình đang lộn xộn khi viết dự án mẫu

Cho đến nay đây là hiểu biết của tôi 1. moduledành cho các gói bên ngoài 2. namespacedành cho các gói bên trong

  • Tôi không hiểu cách chúng tôi phân loại chúng?
  • Khi nào xuất một lớp hoặc không gian tên hoặc gó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
  • Làm thế nào mỗi một trong số chúng có thể được nhập / yêu cầu?

Theo bác sĩ , nếu tôi đang tạo mỗi tệp "ts" cho mỗi trình quản lý / mô hình, thì Typescript không khuyên bạn nên sử dụng "không gian tên"? Sử dụng trực tiếp các đường dẫn tham chiếu?

Vui lòng giải thích chi tiết vì tôi đến từ nền tảng khác và không chắc chắn về ES6 / ES5, v.v. ''.

Tôi đã thấy một số người nêu ra / bối rối với những câu hỏi tương tự. Tôi hy vọng ai đó có thể giải thích chi tiết với kịch bản thế giới thực


này có rất nhiều sự chú ý mà trả lời câu hỏi của bạn, không chắc chắn lý do tại sao nó đã bountied
— Dan Pantry

@DanPantry, trước khi bắt đầu tiền thưởng, không có câu trả lời duy nhất :(
— Reddy

sai lầm của tôi - tôi đã không so sánh dấu thời gian.
— Dan Pantry

Câu trả lời:


80

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);
})();

30

Có hai điều:

  • Một mô-đun trong TypeScript là một khái niệm tiêu chuẩn của ES6, nó sử dụng import/ exporttừ khóa ở cấp cao nhất của mã;
  • Không gian tên là một khái niệm dành riêng cho TypeScript để giúp tổ chức mã theo kiểu lỗi thời.

Không gian tên

Nó gần như là một quan niệm lỗi thời. Trước mô-đun ES6, cách phổ biến để tách mã JavaScript trong trình duyệt là tạo các biến toàn cục. Ví dụ: tất cả các chức năng của một API như dấu gạch dưới đều nằm trong một biến toàn cục có tên _.

Cách tiếp tục cũ này giống như các gói Java hoặc không gian tên PHP. Nó không phù hợp với Web. Tiêu chuẩn ECMAScript mới giải quyết các vấn đề như: làm thế nào để sử dụng hai thư viện trùng tên? Làm thế nào để sử dụng hai phiên bản khác nhau của cùng một thư viện?

Lưu ý: Trong các phiên bản của TypeScript trước định nghĩa "mô-đun" của ECMAScript (mùa hè 2014), không gian tên được gọi là "mô-đun nội bộ" và mô-đun được gọi là "mô-đun bên ngoài".

Lưu ý 2: Từ khóa exportbên trong a namespacelà cách sử dụng từ khóa TypeScript không chuẩn. Nó là phương tiện để khai báo một thứ có thể truy cập công khai từ bên ngoài không gian tên.

Mô-đun ES6

Mô-đun là một tệp chứa các từ khóa importhoặc exportở cấp cao nhất của mã.

TypeScript tuân theo tiêu chuẩn từ ECMAScript. Tôi khuyên bạn nên đọc phần giới thiệu hay về các mô-đun ES6 trong một bài báo từ Mozilla.

Nếu bạn muốn sử dụng các mô-đun trong một ứng dụng front-end (trong một trình duyệt), thì bạn sẽ phải sử dụng một gói ( Webpack [ tài liệu tại đây ], Browserify) hoặc một trình tải ( SystemJS [ một hướng dẫn tại đây ], RequestJS) và để cấu hình TypeScript với môi trường này.

Nếu mã của bạn được thực thi trong Node.js, chỉ cần cấu hình trình biên dịch TypeScript để tạo định dạng CommonJS.

Lưu ý: Một vùng tên có thể được khai báo trong một mô-đun. Trong trường hợp đó, nó sẽ không thể truy cập được dưới dạng biến toàn cục từ bên ngoài mô-đun. Tuy nhiên, nó có thể được xuất từ ​​mô-đun.


Không phải trình biên dịch typecript tự động gói các mô-đun vào một tệp .js?
— Kokodoko

@Kokodoko, Không. Nó có thể ghép lại.
— Paleo

Erm ... sự khác biệt là gì? .... Tôi đang sử dụng mô-đun và chỉ tsc để xây dựng một tệp .js duy nhất. Tôi không phải sử dụng systemJS, webpack hoặc Browserify.
— Kokodoko

1
@Kokodoko Bạn nói đúng! Lỗi của tôi! Có vẻ như trình biên dịch TypeScript có thể xây dựng một gói kể từ TS 1.8 , tôi đã bỏ lỡ tính năng này.
— Paleo

Điều đó đang được nói ... Các mô-đun của tôi biên dịch nhưng sẽ không thực thi sau SystemJS.import ('/ js / main.js'); Bất kỳ ý tưởng làm thế nào để 'khởi động' ứng dụng?
— Kokodoko

13
  1. mô-đun dành cho các gói bên ngoài 2. không gian tên dành cho các gói bên trong

Trên thực tế moduletừ khóa đã được thay thế bằng namespacetừ khóa.

Một tuyên bố tốt hơn là như vậy Mô-đun là những gì được sử dụng để được gọi là mô-đun bên ngoài, không gian tên là những gì được sử dụng để được gọi là mô-đun bên trong.

Hơn

Hy vọng điều này sẽ hữu ích hơn: https://basarat.gitbooks.io/typescript/content/docs/project/modules.html


8
xin lỗi để thông báo, các câu hỏi chưa được trả lời. Tôi dự kiến ​​sẽ giải thích chi tiết bằng ví dụ trong thế giới thực hơn là định nghĩa sách văn bản.
— Reddy

Ý bạn là bây giờ không có bàn phím mô-đun trong TypeScript?
— Mohammad Kermani

7

" request " và " import " tương đương nhau về chức năng. chúng ta có thể sử dụng chúng thay thế cho nhau vì chúng ta có các bộ chuyển đổi mà không thực sự quan tâm đến việc liệu trình duyệt có hỗ trợ chúng nguyên bản hay không. nhưng, trong khi " request " có nguồn gốc từ phong cách mã hóa cũ từ CommonJS trở lại năm 2009, thì "import" lại bắt nguồn từ cú pháp của nó từ cú pháp ES6 (ES2015) được chấp nhận rộng rãi. vì vậy bạn nên sử dụng " nhập khẩu " cho các dự án mới chứ không phải " yêu cầu ".


1
Chính xác. Thật khó hiểu, và quá ít thông tin về nó, nói rằng nó về cơ bản là giống nhau. Tự hỏi tại sao điều này lại bị bỏ phiếu?
— Shai Petel

0

Đặt tên nhầm lẫn

Trong những ngày đầu của Typecript, không gian tên được gọi là mô-đun bên trong và Mô-đun ES6 được gọi là mô-đun bên ngoài .

Bây giờ để khai báo không gian tên, nhóm Typecript khuyên bạn nên sử dụng cú pháp namespace { }thay vì module { }cú pháp để tránh nhầm lẫn đặt tên với các mô-đun bên ngoài. Bởi vì các mô-đun bên ngoài bây giờ chỉ đơn giản là 'mô-đun' và mô-đun bên trong là 'không gian tên'.


Không gian tên

Tờ khai

Một không gian tên trong nguyên cảo có thể được khai báo sử dụng một trong hai namespacehoặc moduletừ khóa. Cả hai từ khóa đều làm điều tương tự. Sau đó, chúng tôi có thể quyết định phần nào của không gian tên của chúng tôi sẽ được công khai bằng cách sử dụng exporttừ khóa.

// LivingThings.ts
export namespace Animals {
    export class Dog { }
    export class Cat { }
}
export namespace Plants {
    export class Orchid { }
    export class Bamboo { }
}

// LivingThingsUser.ts
import { Animals, Plants } from "./LivingThings"

Nhóm lôgic

Trước ES6, không gian tên đã được sử dụng trong Typecript để đóng gói các giao diện, lớp, hàm và biến để hỗ trợ một nhóm các chức năng liên quan và ẩn chi tiết triển khai. Bằng cách này, chúng tôi có thể ngăn các biến rò rỉ vào không gian chung. Điều này đã giúp tổ chức mã tốt hơn và ngăn chặn xung đột tên. Bây giờ bạn nên sử dụng các mô-đun ES6 để đạt được điều này.

Các không gian tên hiện được sử dụng cho các khai báo không gian tên xung quanh.

Sử dụng tệp đơn

Chúng ta có thể khai báo không gian tên trên nhiều tệp và chúng có thể được nối bằng --outFilecờ. Sau đó, chúng tôi có thể sử dụng tệp được nối đó bên trong <script>thẻ trong trang HTML của mình. Điều này cho phép chúng tôi cấu trúc mã của mình theo cách tốt trong ứng dụng web phía máy khách với tất cả các phụ thuộc được bao gồm.


Mô-đun

Tờ khai

Mô-đun còn được gọi là mô-đun ES6. Chúng tôi sử dụng nhiều tệp để nhóm các chức năng liên quan và chỉ sử dụng exporttừ khóa để hiển thị công khai đối tượng mong muốn.

// Animals.ts
export class Dog { }
export class Cat { }

// Plants.ts
export class Orchid { }
export class Bamboo { }

// LivingThingsUser.ts
import { Dog, Cat } from "./Animals"
import { Orchid, Bamboo } from "./Plants"

Nhóm lôgic

Nhóm hợp lý trong các mô-đun đạt được bằng cách sử dụng các tệp riêng biệt để nhóm các chức năng liên quan. Vì lý do này, các mô-đun bên ngoài còn được gọi là mô-đun tệp .

Sử dụng tệp đơn

Chúng tôi không tải các mô-đun của ứng dụng web phía máy khách bằng <script>thẻ, vì trình duyệt có thể bị chậm khi tải xuống nhiều tệp và hiển thị trang cùng một lúc. Đối với điều này, chúng tôi sử dụng các trình tải mô-đun như CommonJS, AMD, SystemJS cho phép chúng tôi tải tệp không đồng bộ hoặc nối các tệp mô-đun bên ngoài thành một tệp được tối ưu hóa duy nhất.

Đối với phía máy chủ, đặc biệt là trong Node.js, các mô-đun được khuyến khích sử dụng.

Đó là nó!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.