Что такое модульность веб-интерфейса?

Node.js внешний интерфейс Webpack RequireJS
Что такое модульность веб-интерфейса?

предисловие

Когда речь заходит о модульности интерфейса, что первое приходит вам на ум? Вебпак? Модуль ES6? Больше? Давайте посмотрим на картинку ниже.

模块化关键词
Я полагаю, что все знакомы со словами на картинке выше и, возможно, использовали, видели или просто слышали их. Можете ли вы использовать картинку, чтобы разобраться в отношениях между всеми вышеперечисленными словами? Когда мы ежедневно пишем код, есть ли у нас отношения с тем, кто и кто находится между ними?

Один, бесчисленное множество

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

1. По платформе

Платформа Технические характеристики характеристика
браузер АМД, ЦМД Есть узкое место в сети, используйте асинхронную загрузку
не браузер CommonJS Прямая работа IO, синхронная загрузка

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

характеристика Технические характеристики
синхронная загрузка CommonJS
Асинхронная загрузка АМД, ЦМД

2. Две основные спецификации AMD и CMD

Сначала игнорируйте CommonJS, давайте сначала представим некогда популярные спецификации AMD и CMD.

Технические характеристики Ограничения шедевр
AMD Зависит от фронта requirejs
CMD Ближайшая зависимость seajs

AMD и CMD предоставляют метод инкапсуляции модулей, а синтаксис реализации аналогичен.Даже requirejs молча поддерживает метод написания CMD на более позднем этапе. Давайте используем пример, чтобы проиллюстрировать самую большую разницу между двумя спецификациями: префикс зависимости и зависимость близости.

AMD:

// hello.js
define(function() {
    console.log('hello init');
    return {
        getMessage: function() {
            return 'hello';
        }
    };
});
// world.js
define(function() {
    console.log('world init');
});

// main
define(['./hello.js', './world.js'], function(hello) {
    return {
        sayHello: function() {
            console.log(hello.getMessage());
        }
    };
});

// 输出
// hello init
// world init

CMD:

// hello.js
define(function(require, exports) {
    console.log('hello init');
    exports.getMessage = function() {
        return 'hello';
    };
});

// world.js
define(function(require, exports) {
    console.log('world init');
    exports.getMessage = function() {
        return 'world';
    };
});

// main
define(function(require) {
    var message;
    if (true) {
        message = require('./hello').getMessage();
    } else {
        message = require('./world').getMessage();
    }
});

// 输出
// hello init

В заключение:В выводе CMD "world init" не печатается. но,Обратите внимание, что CMD не печатает «world init» и не означает, что файл world.js не загружен. И AMD, и CMD загружают все модули при инициализации страницы, разница лишь в том, что ближайшая зависимость выполняется, когда модуль требуется.

Конкретная реализация requirejs и seajs здесь не будет подробно рассматриваться, заинтересованные студенты могут зайти на официальный сайт, чтобы узнать об этом, ведь сейчас должно быть очень мало использования requirejs и seajs.

3. CommonJS

Возвращаясь к CommonJS, студенты, которые писали NodeJS, наверняка не будут незнакомы с ним. CommonJS определяет, что файл является модулем. В реализации node.js каждому файлу также дается объект модуля, этот объект включает в себя всю информацию, описывающую текущий модуль, мы пытаемся напечатать объект модуля.

// index.js
console.log(module);

// 输出
{
    id: '/Users/x/Documents/code/demo/index.js',
    exports: {},
    parent: { module }, // 调用该模块的模块,可以根据该属性查找调用链
    filename: '/Users/x/Documents/code/demo/index.js',
    loaded: false,
    children: [...],
    paths: [...]
}

То есть в CommonJS модули представлены объектами. Давайте рассмотрим пример с циклической загрузкой, чтобы углубить наше понимание.

// a.js
exports.x = 'a1';
console.log('a.js ', require('./b.js').x);
exports.x = 'a2';

//b.js
exports.x = 'b1';
console.log('b.js ', require('./a.js').x);
exports.x = 'b2';

//main
console.log('index.js', require('./a.js').x);

// 输出
b.js  a1
a.js  b2
index.js  a2

Нашей теоретической основой является модульный объект, по которому мы проводим следующий анализ.

1、 a.js准备加载,在内存中生成module对象moduleA
2、 a.js执行exports.x = 'a1'; 在moduleA的exports属性中添加x
3、 a.js执行console.log('a.js', require('./b.js').x); 检测到require关键字,开始加载b.js,a.js执行暂停
4、 b.js准备加载,在内存中生成module对象moduleB
5、 b.js执行exports.x = 'b1'; 在moduleB的exports属性中添加x
6、 b.js执行console.log('b.js', require('./a.js').x); 检测到require关键字,开始加载a.js,b.js执行暂停
7、 检测到内存中存在a.js的module对象moduleA,于是可以将第6步看成console.log('b.js', moduleA.x); 在第二步中moduleA.x赋值为a1,于是输出b.js, a1
8、 b.js继续执行,exports.x = 'b2',改写moduleBexports的x属性
9、 b.js执行完成,回到a.js,此时同理可以将第3步看成console.log('a.js', modulerB.x); 输出了a.js, b2
10、 a.js继续执行,改写exports.x = 'a2'
11、 输出index.js a2

До сих пор «модуль CommonJS — это объект.» Каждый должен быть в состоянии понять эту концепцию, верно?

Возвращаясь к этому примеру, в нем также есть зарезервированное слово exports. На самом деле экспорт — это ссылка на module.exports. Пример может проиллюстрировать отношения между ними двумя.

const myFuns = { a: 1 };
let moduleExports = myFuns;
let myExports = moduleExports;

// moduleExports 重新指向
moduleExports = { b: 2 };
console.log(myExports);
// 输出 {a : 1}

// 也就是说在module.exports被重新复制时,exports与它的关系就gg了。解决方法就是重新指向
myExports = modulerExports;
console.log(myExports);
// 输出 { b: 2 }

4. ES6 module

Товарищи, знакомые с ES6, должны знать, что до ES6 модуляризация веб-интерфейса не была спецификацией языка, в отличие от других языков java, php и т. д., в которых есть понятие namespace или package. Упомянутые выше спецификации AMD, CMD и CommonJS предназначены для модуляции на основе спецификации, а не для поддержки синтаксиса JavaScript. Давайте кратко рассмотрим пример модульного написания ES6:

// a.js
export const a = 1;

// b.js
export const b = 2;

// main
import { a } from './a.js';
import { b } from './b.js';
console.log(a, b);
//输出 1 2

Эммм, да, зарезервированное слово export похоже на экспорт CommonJS? Попробуем сравнить ES6 и CommonJS по зарезервированным словам.

зарезервированное слово CommonJS ES6
require служба поддержки служба поддержки
export / import не поддерживается служба поддержки
exports / module.exports служба поддержки не поддерживается

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

Давайте сначала сравним различия между модулями ES6 и CommonJS.

выход модуля Способ загрузки
CommonJS копия значения объект
ES6 ссылка (символическая ссылка) статический анализ

Есть еще несколько новых слов, давайте сначала представим разницу между копией значения и ссылкой на примере.

// 值拷贝 vs 引用

// CommonJS
let a = 1;
exports.a = a;
exports.add = () => {
    a++;
};

const { add, a } = require('./a.js');
add();
console.log(a); // 1

// ES6
export const a = 1;
export const add = () => {
    a++;
};

import { a, add } from './a.js';
add();
console.log(a); // 2
// 显而易见CommonJS和ES6之间,值拷贝和引用的区别吧。

статический анализчто такое статический парсинг? В отличие от модульной реализации CommonJS, модуль ES6 — это не объект, а просто набор кода. То есть ES6 не обязательно должен быть таким же, как CommonJS, вам нужно загрузить весь файл в объект, чтобы знать, что у вас есть, но в процессе написания кода, что такое код, это то, что есть. .

PS:

  1. В настоящее время модульная поддержка ES6 в различных браузерах и node.js на самом деле не дружит, более практичные товарищи заинтересованы и могут сами поднять волну.
  2. Используйте слово require в ES6, и возможность статического разрешения будет потеряна!

5. UMD

В модульной спецификации также есть UMD, о котором следует упомянуть. Что такое УМД?

UMD = AMD + CommonJS

Да, UMD это просто. Распространенный сценарий — когда модуль, который вы инкапсулируете, должен быть адаптирован к различным платформам (браузер, node.js). Например, вы написали вторичную инкапсуляцию на основе объекта Date. Для класса инструмента обработки времени вы хотите продвигать его. на интерфейсную страницу, отвечающую за Для студентов А, которые разрабатывали Node.js, и студентов Б, которые разрабатывали Node.js в фоновом режиме, нужно ли вам учитывать модули, которые вы инкапсулируете, которые могут адаптироваться как к протоколу CommonJS Node.js, так и к AMD протокол, используемый передовыми студентами?

Эпоха инструментов

1. webpack

После появления веб-пакетов AMD, CMD, CommonJS и UMD кажутся неактуальными. Потому что модульность webpack действительно сильна.

При определении модулей webpack может поддерживать методы объявления модулей CommonJS, AMD и ES6. Другими словами, если ваши модули написаны с синтаксисом CommonJS, AMD или ES6, webpack их поддерживает! Давайте посмотрим на пример:

//say-amd.js
define(function() {
    'use strict';
    return {
        sayHello: () => {
            console.log('say hello by AMD');
        }
    };
});
//say-commonjs.js
exports.sayHello = () => {
    console.log('say hello by commonjs');
};
//say-es6.js
export const sayHello = () => {
    console.log('say hello in es6');
};

//main
import { sayHello as sayInAMD } from './say-amd';
import { sayHello as sayInCommonJS } from './say-commonjs';
import { sayHello as sayInES6 } from './say-es6';

sayInAMD();
sayInCommonJS();
sayInES6();

Мало того, после того, как вебпак распознает ваш модуль, его можно будет запаковать в модули UMD, AMD и т.п. для повторного вывода. Например, как упоминалось выше, вам нужно инкапсулировать модуль Date в формат UMD. Просто добавьте libraryTarget: 'UMD' в вывод webpack.

2. more...

Суммировать

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

总结

@Author: _Jay