Обработка модулей в ES6 и CommonJS

Node.js внешний интерфейс JavaScript

И в ES6, и в CommonJS есть собственный набор мер для работы с модульным кодом, а именно взаимные ссылки между файлами JS.

Чтобы облегчить два метода тестирования, используйте среду nodejs для тестирования.

Работа с модулями в CommonJS

Используйте require для импорта кода из других модулей и используйте module.exports для экспорта.

// exportDemo.js
count = 1;
module.exports.count = count;
module.exports.Hello = function() {
  var name;
  this.setName = function(newName) {
    name = newName;
  }
  this.sayHello = function() {
    console.log("hello Mr." + name);
  }
  this.getId = function() {
    return count++
  }
}
// requireDemo.js
var {Hello} = require("./exportDemo")
var hello = new Hello();

hello.setName("Blank");
hello.sayHello();

выполнить в терминалеnode requireDemo.js, возвращаемый результат - "привет, мистер Бланк"

Экспортированная функция Hello является копией исходной функции. Изменение значения атрибута функции Hello не повлияет на другие требуемые места.

var { Hello, count } = require('./exportDemo')
var hello = new Hello();
// 让count自增
console.log(hello.getId());
console.log(hello.getId());
// 发现获取的count还是原值
console.log(count)

// 真正的count其实是已经改了的
var newHello = new Hello();
console.log(newHello.getId())

var { Hello: newHello, count: newCount } = require('./exportDemo')
console.log(newCount, 'newCount');
// 再次require,取得的newHello和之前require的Hello指向同一个拷贝
console.log(newHello === Hello) 

Обработка модулей в ES6

Запуск кода в стиле ES6 в nodejs

Nodejs по умолчанию не поддерживает схему обработки модулей ES6.

Но после 8.5.0, после того как формат файла кода ES6 установлен на mjs, вы можете использоватьnode --experimental-modules xxx.mjsбегать.

// exportDemo.mjs
export let a = 1;
// importDemo.mjs
import {a} from './exportDemo.mjs'
console.log(a)

Различия в обработке модуля CommonJS

  • Модули CommonJS выводят копию значения (проверено в предыдущей главе), а модули ES6 выводят ссылку на значение.

    // exportDemo.mjs
    export let counter = 1;
    export function incCounter() {
      counter ++;
    }
    
    // importDemo.mjs
    import { counter, incCounter } from './exportDemo.mjs'
    
    incCounter();
    console.log(counter)		// 打印结果为2,而不是初始值的1
    
  • Модули CommonJS загружаются во время выполнения, модули ES6 являются выходными интерфейсами во время компиляции.

    • Среды выполнения, такие как Nodejs, будут запускать код модуля CommonJS в замыкании.

      (function(exports, require, module, __filename, __dirname) {
      // Module code actually lives in here
      });
      
    • Модули ES6 не кэшируют результаты, а динамически извлекают значения из загруженных модулей, а переменные всегда привязаны к тому модулю, в котором они находятся.

      // exportDemo.mjs
      export let a = 1;
      export const b = 2;
      export let obj = {};
      
      // importDemo.mjs
      import { a, b } from './exportDemo.mjs'
      console.log(a, b)
      a = 1 // 报错,TypeError: Assignment to constant variable,export出来的值是一个只读引用
      obj.x = 1	// 可以改变属性值
      

      В модулях ES6 мы должны больше думать о синтаксисе.

export default

Иногда мы найдем в кодеexport default objиспользование, то для чего используется это значение по умолчанию?

default — это ключевое слово, введенное в ES6 и используемое вместе с export, для установки имени по умолчанию для анонимных объектов и анонимных функций.

Экспортируемое значение должно иметь имя, иначе будет сообщено об ошибке на грамматическом уровне.

Давайте рассмотрим следующие примеры ошибок, которые вызовут ошибку

  • экспортировать анонимные объекты

    export { x: 1 }	// 报错,SyntaxError:Unexpected token,这是一个编译阶段的错误
    
    // 正确写法
    export default { x: 1 }
    
  • экспортировать анонимную функцию

    export function() {}	// 报错,SyntaxError: Unexpected token (
    
    // 正确写法
    export default function() {}
    

Переработка загрузки

В сложных модулях могут быть межмодульныессылаться друг на друга.

Циклический механизм работы ссылок commonJS

// a.js
exports.loaded = false;
var b = require('./b.js')
console.log("b in a is " + JSON.stringify(b))
exports.loaded = true;
console.log("a complete")
// b.js
exports.loaded = false;
var a = require('./a.js')
console.log("a in b is " + JSON.stringify(a))
exports.loaded = true;
console.log("b complete")
// main.js
var a = require('./a.js')
var b = require('./b.js')

console.log("a in main is" + JSON.stringify(a))
console.log("b in main is" + JSON.stringify(b))

Выполнить инструкцииnodejs main.js

Покомпонентное представление шагов выполнения на диаграмме последовательности выглядит следующим образом:

Циклический эталонный рабочий механизм ES6

  • Пример ошибки
// a.mjs
import { bar } from './b.mjs'

console.log(bar);
export let foo = 'foo from a.mjs'
// b.mjs
import { foo } from './a.mjs'

console.log(foo)

export let bar = 'bar from b.mjs'
// main.mjs
import { foo } from './a.mjs'
import { bar } from './b.mjs'

node main.mjs

Покомпонентное представление шагов выполнения на диаграмме последовательности выглядит следующим образом:

В циклических ссылках ES6 следует обращать особое внимание на то, объявлена ​​ли переменная.Если на необъявленную переменную блочной области ссылаются другие модули, будет сообщено об ошибке.

  • План улучшения: попробуйте экспортировать значения, которые можно определить заранее (например, функции) в циклических ссылках.На самом деле, мы всегда хотим перейти кПеременные, которые дорабатываются после полного выполнения эталонного модуля.

    // a.mjs
    import { bar } from './b.mjs'
    
    console.log(bar());
    export function foo() {
        return 'foo from a.mjs'
    }
    
    // b.mjs
    import { foo } from './a.mjs'
    console.log(foo());
    export function bar() {
        return 'bar from b.mjs'
    }
    
    // main.mjs
    import { foo } from './a.mjs'
    import { bar } from './b.mjs'
    

    node main.mjs

    Возвращаемый результат:

    foo from a.mjs
    bar from b.mjs
    

    Покомпонентное представление шагов выполнения на диаграмме последовательности выглядит следующим образом: