JavaScript中的命名空间

Java和PHP等编程语言中的名称空间给我留下了深刻的印象。如此之多,以至于我什至以某种方式在哈布雷(Habré)上了一篇有关它们的文章从那时起已经过去了将近两年,但是在这段时间内,名称空间尚未出现在JavaScript中。如果我自己在JS中创建名称空间,它们将是什么? ”-我想。切记-我的想法是,JavaScript需要什么名称空间。

介绍性

下面我所有的道理也适用于ES6模块和不接触其他格式(AMD,UMD,CommonJS的),只是因为我很感兴趣,看这里的JavaScript是怎么回事,而不是它另外,在我的实践中,我以某种方式非常接近GWT,此后,我对各种编译器(以及堆,缩小器和混淆器)产生了持久的拒绝。因此,香草JS,没有TS。好吧,我有这样的东西。

ES6模块

ES模块是一个单独的源文件,它明确定义了模块外部可用的元素:

export function fn() {/*...*/}

因此,首先,您需要以某种方式解决整个应用程序中的各个ES模块。

配套

现代应用程序由单独的程序包组成,它们之间的依赖关系由程序包管理器管理。程序包本身包含单独的模块。单个供应商可以在其各种应用程序中使用相同的程序包,将其代码分成多个程序包。模块的源代码通常放置在包内的单独目录中(例如./src)。

软件包管理器将一个应用程序的所有软件包放入目录node_modules因此,从某个开发人员的程序包编译而来的nodejs应用程序的结构可能看起来像这样:

* node_modules
    * @vendor
        * package1
            * src
                * module1.js
                * ...
                * moduleN.js
        * ...
        * packageN
            * src
                * module1.mjs
                * ...
                * moduleN.mjs

模块寻址

在文件结构中,任何ES模块的源代码都是通过相对于应用程序根目录的文件路径来寻址的:

./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   // browser

并使用它动态导入模块。当然,不需要映射包中的每个模块-您只需要映射包的根即可。输出是这样的:

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');

模块的导入必须是异步的,因为 内部将使用异步函数import()

概要

以这种优雅的方式,有可能从导入期间ES模块的物理寻址转移到其逻辑寻址(命名空间),并为nodejs应用程序和浏览器使用相同的模块。这里没有发明新的东西在PHP中已经做了类似的事情,这个想法从那里被偷走了)。




All Articles