Статья, которая поможет вам понять модульность ES6

внешний интерфейс ECMAScript 6

Модульность интерфейса является краеугольным камнем разработки интерфейса. В настоящее время использование модулей в эпоху большого внешнего интерфейса является повсеместным, и, заглядывая в будущее, форма импорта и экспорта, предложенная в es6, похоже, унифицирует модульную загрузку внешнего и внутреннего интерфейса.

Обзор модульности

Изоляция различных js-файлов и предоставление только других модулей, требуемых текущим модулем, — это идея модульности.

Прежде чем изучать модульность ES6, давайте рассмотрим модульность, появившуюся ранее.Есть три часто используемых стандартных определения: CommonJS, AMD и CMD.

Их характеристики и отличия друг от друга заключаются в следующем:

  1. CommonJS для серверной части, записывается как:

    var clock = require('clock.js')
    clock.start();
    

    В приведенном выше примере говорится:clockВызов должен ждатьclock.jsЗапрос был успешно загружен, другими словами, даСинхронизироватьоперации, и это также привело к тому, что CommonJS стал широко использоваться на стороне сервера, а не на стороне клиента (сервер считывает модули на локальном диске и загружается быстро, а если он находится на стороне клиента, он подвержен «зависанию» death'), то можно ли его использовать? А как насчет асинхронной загрузки модулей?

  2. AMD (определение асинхронного модуля) — это модуль асинхронной загрузки, который в основном используется в браузерах.(requireJs применяет эту спецификацию), записывается как:

    require([module],callback);
    
    // eg
    require(['clock.js'],function(clock){
      clock.start();
    })
    

    Хотя асинхронная загрузка реализована, чтобы избежать проблемы «притворной смерти» браузера, есть и недостатки:Не логично выписывать все зависимости в самом начале. Итак, может ли это быть какЭто требуется только тогда, когда CommonJS используется таким же образом, и тогда он также может поддерживать асинхронную загрузку и выполнение.?

  3. CMD (Общее определение модуля) зависит от ближайшего, а затем требуется при использовании(Спецификации, соблюдаемые Seajs), записывается как:

    define(function(require,exports,module){
      var clock = require('clock.js');
      clock.start();
    })
    

Разница между AMD и CMD заключается в том, что время выполнения зависимых модулей отличается., вместо того, чтобы загружать модуль по-разному, оба загружают модули асинхронно.

AMD полагается на предварительное,js может легко понять, какие зависимые модули, загрузить немедленно;

Ближайшая зависимость CMD, разработчики могут потребовать, когда им нужно использовать зависимости, но для процессоров js им необходимо обрабатывать код как строку, чтобы проанализировать его, чтобы узнать, какие модули зависимы, то естьЖертвовать производительностью ради удобства разработки, хотя время парсинга на самом деле настолько мало, что им можно пренебречь, многие люди критикуют за это CMD.

Идея модульного дизайна ES6 состоит в том, чтобы быть как можно более статичным, чтобы зависимости модулей можно было определить во время компиляции.

Сравнение модулей CommonJS и ES6:

// CommonJS
let { start, exists, readFile } = require('fs')
// 相当于
// let _fs = require('fs')
// let start = _fs.start, exists = _fs.exists, readFile = _fs.readFile

// ES6
import { start, exists, readFile } from 'fs'

В приведенном выше примереСуть CommonJS в общей загрузкеМодуль fs генерирует_fsобъект, а затем прочитать три метода из объекта, называемые "загружать во время выполнения".а такжеМодули ES6 загружаются тремя способами., называется "загружать во время компиляции"

2. Спецификация модульного синтаксиса ES6

строгий режим

Строгий режим автоматически применяется в модулях ES6. Регулирование:

  • Переменные должны быть объявлены первыми
  • Параметры функции не могут иметь свойств с одинаковыми именами.
  • нельзя использоватьwith
  • назначение свойства только для чтения,deleteНеудаляемый атрибут напрямую сообщает об ошибке
  • Неудаляемые переменныеdelete prop, удалить только свойстваdelete global[prop]
  • evalНикакие переменные не будут введены во внешнюю область видимости.
  • evalа такжеargumentsне переназначаемый
  • argumentsНе реагирует автоматически на изменения параметров функции
  • запретитьthisуказать на глобальное
  • Увеличение зарезервированных слов: статические, интерфейсные, защищенные и т. Д.

Примечание. В модулях ES6 верхний уровеньthisдляundefined, не следует использовать.

команда экспорта

Первый:

export var a = '123';
export const _b = '2323'
export let c = '2222' 

Второй:

var a = '123';
const _b = '2323'
let c = '2222' 
export {a, _b, c}; // 推荐

Третий тип (второй основан на переименовании ключевого слова as)

var a = '123';
const _b = '2323'
let c = '2222' 
export {
  a as stream1,
  _b as stream2,
  c as stream3 };

Уведомление:

  1. Вывод интерфейса оператором экспорта является ссылкой на соответствующее значение., этодинамическая привязкавзаимосвязь, с помощью которой можно получить значение в реальном времени внутри модуля.

    Сравните спецификации CommonJS:Модуль CommonJS выводит кеш значений, динамического обновления нет.

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

команда импорта

Первый:

import {a, _b ,c} from './profile'

Второй:

import {stream1 as firstVal} from './profile'

импортстатическое выполнение, выражения, переменные и если конструкции не могут быть применены.

if(x == 1){
  import { foo } from 'module1'
}else{
  //...
}

Оператор import представляет собой шаблон Singleton: хотяfooа такжеbarЗагружается в два оператора, но соответствует одному и тому жеmy_moduleпример.

import { foo } from './module1'
import { bar } from './module1'

// 相当于
import {foo,bar} from './module1'

Общая загрузка модулей

Вы можете использовать * для указания объекта, на который загружаются все выходные значения:

import * as circle from './module1'
circle.foo();
circle.bar();

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

import * as circle from './module1'
// 下面两行都是不允许的
circle.foo = 123;
circle.bar = function(){}

вывод по умолчанию

Команда экспорта по умолчанию может выводить данные по умолчанию для модуля.

// module2.js
export default function(){
  console.log('123')
}
// 相当于
function a(){
  console.log('123')
}
export {a as default}; 

Команда импорта может указывать произвольные имена для анонимных функций.

import defaultFn from './module2'
// 相当于
import {default as defaultFn} from './module2'

Составное написание экспорта и импорта

export { foo, bar} from 'my_module';
// 等同于
import {foo,bar} from 'my_module';
export{foo,bar};
export {es6 as default} from './someModule'
// 等同于
import {es6} from './someModule'
export default es6;

метод импорта()

Как упоминалось ранее, require — это динамическая загрузка, то есть она может потребоваться при ее использовании, тогда как import — это статическое выполнение, которое может быть только на верхнем уровне кода и не может существовать в блочной области видимости. Это приводит к тому, что импорт не выполняется на лету (аналогично недостатку AMD). Итак, есть предложение: ввести функцию import(), которая похожа на функцию Node require (CommonJS), но реализует асинхронную загрузку.

Определение: Функция import() получает те же параметры, что и import, возвращает объект Promise и загружает полученное значение в качестве параметра обратного вызова метода then.

const main = document.querySelector('main')

import(`./section-modules/${someVariable}.js`)
	.then(module => {
  	module.loadPageInto(main);
	})
	.catch(err => {
    main.textContext = err.message;
  })
// 加载获得接口参数:
import('./module1.js')
.then(({default:defaultFn,foo,bar}) => {
  console.log(defaultFn)
})
// 同时加载多个模块并应用于async函数中
async function main() {
  const myModule = await import('./myModule.js');
  const {export1, export2} = await import('./myModule.js');
  const [module1, module2, module3] = 
        await Promise.all([
          import('./module1,js'),
          import('./module2.js'),
          import('./module3.js')
        ])
}
main();

3. Загрузка между различными спецификациями

импорт загружает модули CommonJS

  • Используйте команду import для загрузки модуля CommonJS, Node автоматически использует свойство module.exports в качестве вывода модуля по умолчанию, что эквивалентно экспорту по умолчанию.

    // a.js
    module.exports = {
      foo: 'hello',
      bar: 'world'
    }
    
    // 在import引入时等同于
    export default {
      foo: 'hello',
      bar: 'world'
    }
    
  • Модуль CommonJS определяет выходной интерфейс во время выполнения, поэтому при использовании команды импорта для загрузки модуля CommonJS можно использовать только общий ввод (*).

    import {readfile} from 'fs' //当'fs'为CommonJS模块时错误
    // 整体输入
    import * as express from 'express'
    const app = express.default();
    

требуют загрузки модулей ES6

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

    // es.js
    let foo = {bar : 'my-default'};
    exxport default foo;
    foo = null;
    
    // cjs.js
    const es_namespace = require('./es')
    console.log(es_namespace.default);// {bar:'my-default'}
    

В-четвертых, сравните CommonJS

Если у вас есть новая любовь, вы не можете забыть свою старую любовь.Давайте продолжим сравнивать различия между модульностью CommonJS и ES6 и разбираться в характеристиках модульности ES6.

Копия выходного значения

Модули CommonJS выводят копию значения, вывод ES6 — это ссылка на значение

// lib.js 
let num = 3;
function changeNum() {
  num = 4;
}
module.exports = {
  num: num,
  changeNum: changeNum,
};

//main.js
var mod = require('./lib.js')
console.log(mod.num); // 3
mod.changeNum();
console.log(mod.num); // 3

Это связано с тем, что mod.num является примитивным значением и будет кэшироваться. Вы можете получить внутренне измененное значение, написав его как функцию:

// lib.js 
let num = 3;
function changeNum() {
  num = 4;
}
module.exports = {
  get num(){
    return num
  },
  changeNum: changeNum,
};

//main.js
var mod = require('./lib.js')
console.log(mod.num); // 3
mod.changeNum();
console.log(mod.num); // 3

Сравните модули ES6:

// lib.js 
export let num = 3;
export function changeNum() {
  num = 4;
}

//main.js
import {num,changeNum} from './lib.js'
console.log(num); // 3
changeNum();
console.log(num); // 4

Загрузка цикла CommonJS

Принцип загрузки

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

циклическая загрузка

// a.js
exports.done = false;
var b = require('./b.js'); // 1. a.js暂停执行,转到执行b.js ; b.js完毕后回来,b:{done:true}
console.log('在a.js中,b.done=%j',b.done); // 5. '在a.js中,b.done=true'
exports.done = true;
console.log('a.js执行完毕') // 6. 'a.js执行完毕'

// b.js
exports.done = false;
var a = require('./b.js') // 2. a:{done:false}
console.log('在b.js中,a.done=%j',a.done); // 3. '在b.js中,a.done=false'
exports.done = true;
console.log('b.js执行完毕') // 4. 'b.js执行完毕',继续执行a.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); // 7.'在main.js中,a.done=true,b.done=true'

Из вышеприведенного кода видно: во-первых, в b.js не был выполнен a.js, во-вторых, когда main.js выполняется до второй строки, b.js не будет выполняться повторно, а закешированный b. Результат выполнения js, который является его четвертой строкой:exports.done = true

Подводя итог: 1. Поскольку модуль CommonJS сталкивается с циклической загрузкой, он возвращает значение текущей выполняемой части, а не значение после выполнения всего кода (комментарий к шагу 2 выше) 2. Вход CommonJS — это кеш выходного значения. (копия), а не динамическая ссылка.

Контраст: на модули ES6 ссылаются динамически, переменные не кэшируются.

// a.js
import {bar} from './b.js';
export function foo(){
  console.log('foo')
  bar();
  console.log('执行完毕')
}
foo();

// b.js
import {foo} from './a.js' // 如果为CommonJS,这里直接就返回undefined值且不会再更改
export function bar(){
  console.log('bar')
  if(Math.random() > 0.5){
    foo();
  }
}

// 执行结果可能为:foo bar 执行完毕
// 执行结果也可能为: foo bar foo bar 执行完毕 执行完毕