Разница между обработкой цикла загрузки модуля CommonJS и ES6

ECMAScript 6

Спецификация модуля CommonJS использует оператор require для импорта модуля, module.exports экспортирует модуль, а вывод представляет собой копию значения, а импорт модуля также является копией выходного значения. значение выводится, отслеживается изменение значения внутри модуля или нет.

Спецификация модулей ES6 состоит в том, чтобы использовать оператор импорта для импорта модуля, оператора экспорта для экспорта модуля и вывод является ссылкой на значение. Рабочий механизм модулей ES6 отличается от соображения Commonjs. Когда импорт команды модуля загрузки встречается, модуль не будет выполнен, и будет сгенерирована только динамическая ссылка только для чтения. Когда это значение действительно необходимо, значение будет получен из модуля. То есть, если исходное значение изменяется, входное значение также изменится.

Так в чем же разница между спецификацией модуля CommonJS и ES6 для механизма циклической загрузки модулей?

Циклическая загрузка означает, что выполнение сценария a зависит от сценария b, а выполнение сценария b зависит от сценария a.

1. Принцип загрузки модуля CommonJS

Модуль CommonJS представляет собой файл сценария.Когда команда require загружает сценарий в первый раз, выполняется весь сценарий, а затем в памяти создается объект описания модуля.

{
    id: '',  //模块名,唯一
    exports: {  //模块输出的各个接口
        ...
    },
    loaded: true,  //模块的脚本是否执行完毕
    ...
}

Когда этот модуль будет использоваться в будущем, значение будет получено из свойства экспорта объекта. Даже если команда require будет выполнена снова, модуль не будет выполняться снова, а значение будет получено из кеша.

Модули CommonJS выполняются при загрузке, то есть весь код скрипта выполняется, когда это требуется. После того, как модуль "циклически загружен", будет выведена только та его часть, которая была выполнена, а невыполненная часть не будет выведена.

Описание случая:

Дело взято из официального описания Node:узел будет .org/API/modules…

//a.js
exports.done = false;

var b = require('./b.js');
console.log('在a.js中,b.done = %j', b.done);

exports.done = true;
console.log('a.js执行完毕!')
//b.js
exports.done = false;

var a = require('./a.js');
console.log('在b.js中,a.done = %j', a.done);

exports.done = true;
console.log('b.js执行完毕!')
//main.js
var a = require('./a.js');
var b = require('./b.js');

console.log('在main.js中,a.done = %j, b.done = %j', a.done, b.done);

Результат выглядит следующим образом:

//node环境下运行main.js
node main.js

在b.js中,a.done = false
b.js执行完毕!
在a.js中,b.done = true
a.js执行完毕!
在main.js中,a.done = true, b.done = true

Порядок выполнения JS-кода следующий:

1) Сначала загрузите a.js в main.js, скрипт a сначала выводит переменную done, значение равно false, а затем загружает скрипт b, код a прекращает выполнение и ожидает выполнения скрипта b для завершить, прежде чем продолжить выполнение.

2) При выполнении b.js до второй строки будет загружен a.js.В это время будет происходить циклическая загрузка.Система перейдет к атрибуту exports соответствующего объекта модуля a.js для получения Выполненная часть может быть извлечена, а невыполненная часть не возвращается, поэтому полученное значение не является окончательным значением.

3) Существует только одна строка кода, выполняемая a.js, exports.done = false, поэтому для b.js require a.js выводит только переменную done со значением false. Выполните console.log('in b.js, a.done = %j', a.done); консоль выведет:

在b.js中,a.done = false

4) b.js продолжает выполняться, для переменной done устанавливается значение true, console.log('выполнение b.js завершено!'), ждем завершения всех исполнений и возвращаем право выполнения a.js. На данный момент вывод консоли:

b.js执行完毕!

5) После передачи права на выполнение a.js, a.js продолжает выполняться, выполнить console.log('In a.js, b.done = %j', b.done); консоль выводит:

在a.js中,b.done = true

6) a.js продолжает выполняться, и для переменной done устанавливается значение true до тех пор, пока не будет выполнен a.js.

a.js执行完毕!

7) Вторая строка в main.js больше не будет выполнять b.js и выведет кэшированный результат напрямую. Окончательный вывод консоли:

在main.js中,a.done = true, b.done = true

Суммировать:

1) В b.js a.js не дорабатывается, выполняется только первая строка, поэтому в цикле загрузки выводится только исполняемая часть.

2) Вторая строка main.js повторно выполняться не будет, а выведет результат выполнения закешированного b.js. exports.done = истина;

2. Циклическая загрузка модулей ES6

Модули ES6 принципиально отличаются от CommonJS.Модули ES6 представляют собой динамические ссылки на экспортируемые переменные, методы и объекты.При встрече с командой import модуля загрузки модуль не будет выполнен, но будет сгенерирована ссылка на загруженный модуль. необходимо гарантировать, что значение может быть получено, когда значение присутствует, и код может быть выполнен, пока существует ссылка.

Описание случая:

//even.js
import {odd} from './odd';

var counter = 0;
export function even(n){
    counter ++;
    console.log(counter);
    
    return n == 0 || odd(n-1);
}
//odd.js
import {even} from './even.js';

export function odd(n){
    return n != 0 && even(n-1);
}
//index.js
import * as m from './even.js';

var x = m.even(5);
console.log(x);

var y = m.even(4);
console.log(y);

Выполните index.js, и результат будет следующим:

babel-node index.js

1
2
3
false
4
5
6
true

Видно, что значение счетчика кумулятивное, а ES6 является динамической ссылкой. Если приведенная выше ссылка будет изменена на код CommonJS, будет сообщено об ошибке, поскольку в нечетном.js код четного.js не выполняется. Код, загруженный спецификацией CommonJS, изменен на:

//even.js
var odd = require('./odd.js');

var counter = 0;
module.exports = function even(n){
    counter ++;
    console.log(counter);

    return n == 0 || odd(n-1);
}
//odd.js
var even = require('./even.js');

module.exports = function odd(n){
    return n != 0 && even(n-1);
}
//index.js
var even = require('./even.js');

var x = even(5);
console.log(x);

var y = even(5);
console.log(y);

Выполните index.js, и результат будет следующим:

$ babel-node index.js
1
/Users/name/Projects/node/ES6/odd.1.js:6
    return n != 0 && even(n - 1);
                     ^

TypeError: even is not a function
    at odd (/Users/name/Projects/node/ES6/odd.1.js:4:22)

3. Резюме

1)CommonJS模块是加载时执行。 После того, как модуль "циклически загружен", будет выведена только та его часть, которая была выполнена, а невыполненная часть не будет выведена.

2) Модули ES6 имеют динамические ссылки на экспортируемые модули, переменные и объекты.При встрече с командой import модуля загрузки модуль не будет выполнен, но будет сгенерирована ссылка на загруженный модуль.

Спецификация модуля CommonJS в основном применима к серверной части Node.js, а серверная часть Node.js является синхронной загрузкой модуля, поэтому модуль выполняется, когда вводится модульный цикл. Рекомендуется использовать спецификацию модуля ES6 во внешних проектах и ​​поддерживать синтаксис, представленный модулями ES6, путем установки подключаемого модуля транскодирования Babel.

Содержание страницы в основном взято из введения в главу модуля «Введение в стандарт ES6». Если есть какое-либо неясное или неправильное описание, пожалуйста, оставьте сообщение для дачи показаний.

Использованная литература:

Модуль «Введение в стандарты ES6»

Node.js Cycle

ES-Module-Loader