Ich bin sehr beeindruckt von Namespaces in Programmiersprachen wie Java und PHP. So sehr, dass ich sogar irgendwie einen Artikel über sie auf Habré geschrieben habe. Seitdem sind fast zwei Jahre vergangen, aber Namespaces wurden in dieser Zeit nicht in JavaScript angezeigt. " Und wenn ich Namespaces in JS für mich selbst machen würde, welche wären das? " - dachte ich. Unter dem Strich - meine Gedanken, welche Namespaces ich in JavaScript brauche.
Einleitend
Alle meine nachstehenden Überlegungen gelten für ES6-Module und berühren keine anderen Formate (AMD, UMD, CommonJS), nur weil ich daran interessiert bin, wohin JavaScript geht und nicht dorthin, wo es war . Außerdem bin ich in meiner Praxis irgendwie ziemlich eng auf GWT gestoßen, woraufhin ich eine anhaltende Ablehnung verschiedener Transpiler (sowie eines Haufens von Minifierern und Verschleiern) entwickelte. Daher Vanille JS und kein TS. Nun, ich habe solche Gegenstände.
ES6-Module
Ein ES-Modul ist eine separate Quelldatei, die die außerhalb des Moduls verfügbaren Elemente explizit definiert:
export function fn() {/*...*/}Daher müssen Sie zunächst einzelne ES-Module in der gesamten Anwendung ansprechen.
Pakete
, . . (vendor) , . (, ./src).
node_modules. , nodejs-, , :
* node_modules
* @vendor
* package1
* src
* module1.js
* ...
* moduleN.js
* ...
* packageN
* src
* module1.mjs
* ...
* moduleN.mjsES- :
./node_modules/@vendor/package1/src/module1.js
...
./node_modules/@vendor/packageN/src/moduleN.mjs nodejs- ./node_modules/ :
import SomeThing from '@vendor/package1/src/module1.js';, , :
import SomeThing from './module1.js'; web- , web- node_modules, web- ES-, , nodejs:
<script type="module">
import {fn} from './@vendor/package1/src/module1.js'
fn();
</script>:
<script>
import('./@vendor/package1/src/module1.js').then((mod) => {
mod.fn();
});
</script> , web' ./ . :
import {fn} from '@vendor/package1/src/module1.js':
Uncaught TypeError: Failed to resolve module specifier "@vendor/package1/src/module1.js". Relative references must start with either "/", "./", or "../".
, ES-:
( ):
./module1.js(nodejs):
@vendor/package1/src/module1.js(web):
./@vendor/package1/src/module1.js
./ nodejs-, ./ .
, JS- , , ( - ) , ( ).
" " ( , namespace'), ES- ( ), ES- , , nodejs, .
, ./, , ( , ):
@vendor/package1/src/module1 - : ./src/, ./lib/, ./dist/. - , , :
@vendor/package1/module1, , .
Namespace mapping
, , . - web-, node_modules web- ( - ./packages/):
const node = {
'@vendor/package1': {path: '/.../node_modules/@vendor/package1/src', ext: 'js'},
'@vendor/packageN': {path: '/.../node_modules/@vendor/packageN/src', ext: 'mjs'},
};
const browser = {
'@vendor/package1': {path: 'https://.../packages/@vendor/package1/src', ext: 'js'},
'@vendor/packageN': {path: 'https://.../packages/@vendor/packageN/src', ext: 'mjs'},
};Module loader
, '' ( @vendor/package1/module1) ( - ) (node ):
@vendor/package1/module1 => /.../node_modules/@vendor/package1/src/module1.js // node
@vendor/packageN/moduleN => https://.../packages/@vendor/packageN/src/moduleN.mjs // browserund verwenden Sie es, um Module dynamisch zu importieren. Natürlich müssen Sie nicht jedes Modul im Paket zuordnen - Sie müssen nur das Stammverzeichnis des Pakets zuordnen. Die Ausgabe ist ungefähr so:
const loader = new ModuleLoader();
loader.addNamespace('@vendor/package1', {path: '/.../node_modules/@vendor/package1/src', ext: 'js'});
// ...
loader.addNamespace('@vendor/packageN', {path: '/.../node_modules/@vendor/packageN/src', ext: 'js'});
const module1 = await loader.import('@vendor/package1/module1');Der Import von Modulen muss asynchron sein, da Im Inneren wird eine asynchrone Funktion verwendet import().
Zusammenfassung
Auf solch elegante Weise wäre es möglich, von der physischen Adressierung von ES-Modulen während des Imports zu ihrer logischen Adressierung (Namespaces) zu wechseln und dieselben Module sowohl für NodeJS-Anwendungen als auch im Browser zu verwenden. Hier wurde nichts Neues erfunden ( etwas Ähnliches wurde bereits in PHP gemacht, von wo diese Idee gestohlen wird).