javascript-модули "из прошлого в будущее"
TRANSCRIPT
Что дает модульная система?
● Ограничение области видимости
● Реиспользование частей приложения
● Структуризация приложения
● Наглядный код
Простейшие модули
● Разделение на файлы
● Паттерн с самовызывающейся ф-цией
● Глобальная область видимости
//iMath.js(function(myNameSpace){
function square(x){ return x*x; }
myNameSpace.iMath = { square: square };
}(window.myNamespace = window.myNamespace || {}))
//main.js(function(){ alert(window.myNamespace.iMath.square(2));}())
Плюсы:- ограничение области видимости- это уже почти модули
Минусы:- нет поддержки зависимостей- для асинхронной загрузки модуля нужно
реализовывать обертку- нужно следить за порядком загрузки модулей
- подход к организации модулей, который определяет API, следуя которому можно организовать асинхронную загрузку модулей и их зависимостей.
Asynchronous Module Definition (AMD)
Краткие характеристики
● работает без eval() или этапа компиляции, за счет синтаксиса;
● предназначен для асинхронной загрузки;
● преимущественно используется на стороне клиента.
define - определение модуля
define( [module_name] /*опционально*/, [dependencies] /*зависимости - опционально*/, definition /*функция инстанцирования модуля или объект*/);
define('iMath', function(){ function square(x){ return x*x; }
return { square: square };});
define('anotherModule', ['iMath'], function(iMath){ alert(iMath.square(2));});
require - загрузка модулей
require( [modules] /*список модулей*/, callback /*функция вызываемая при успешной загрузке модулей*/ errback /*функция вызываемая при ошибке загрузки модулей*/);
require( ['anotherModule', 'yetAnotherModule'], function(aM, yAM){ //do something with modules }, function(error){ console.error(error); } );
Система плагинов
require(['tpl!main'], function(tplFn){ var html = tplFn(data);});
//плагин 'tpl!'define('tpl', ['templateEngine'], function(templateEngine){ return { load: function(name, parentRequire, onload, config){ parentRequire(['text!' + name], function(text){ onload(templateEngine.createTemplate(text)); }); } };});
Почему AMD - это лучший выбор для написания модульного js на клиенте?
● Имеет четкое представление о том как нужно подходить к определению гибких модулей.
● Значительно чище и безопасней, чем использование глобального нэймспейса● Определение модуля инкапсулировано, что помогает избежать загрязнения
глобальной области видимости.● Работает лучше чем альтернативные решения (например CommonJS, который
мы рассмотрим позже)● Не имеет проблем с кросс-доменностью, отладкой. Не нуждается в
инструменте на стороне сервера. Большенство загрузчиков AMD поддерживают загрузку модулей без процесса сборки.
● Обеспечивает подход «transport» для включения нескольких модулей в один файл.
● Поддерживает ленивую загрузку, если необходимо.
CommonJS
- рабочая группа, работающая над разработкой и стандартизацией JavaScript API. На сегодняшний день они работают над ратификацией стандарта для модулей и пакетов. Модули предложенные CommonJS определяют простой API для работы js на стороне сервера.
Краткие характеристики:
● компактный синтаксис;
● предназначен для синхронной загрузки;
● преимущественно используется на стороне сервера.
//iMath.jsfunction square(x){ return x*x;}exports.square = square;
//anotherModule.jsvar iMath = require('./iMath');console.log(iMath.square(2));
//iMath.jsdefine('iMath', function(){ function square(x){ return x*x; } return { square: square };});
//anotherModule.jsdefine('anotherModule', ['iMath'], function(iMath){ alert(iMath.square(2));});
Общие модули для клиента и сервера
● Загрузчики и фреймворки поддерживащие CommonJS модули в браузере:○ curl.js - загрузчик модулей на клиенте○ cajon - загрузчик разработанный поверх require.js
Общие модули для клиента и сервера
● Загрузчики и фреймворки поддерживащие CommonJS модули в браузере:○ curl.js - загрузчик модулей на клиенте○ cajon - загрузчик разработанный поверх require.js
● Использование Browserify, утилиты которая позволяет запускать любые node.js модули в браузере
Общие модули для клиента и сервера
● Загрузчики и фреймворки поддерживащие CommonJS модули в браузере:○ curl.js - загрузчик модулей на клиенте○ cajon - загрузчик разработанный поверх require.js
● Использование Browserify, утилиты которая позволяет запускать дюбые node.js модули в браузере
● Использование на сервере node-require (AMD на клиенте и на сервере)
Общие модули для клиента и сервера
● Загрузчики и фреймворки поддерживащие CommonJS модули в браузере:○ curl.js - загрузчик модулей на клиенте○ cajon - загрузчик разработанный поверх require.js
● Использование Browserify, утилиты которая позволяет запускать дюбые node.js модули в браузере
● Использование на сервере node-require (AMD на клиенте и на сервере)
● Использование оберток для модулей
Стандарт ES6 module состоит из двух частей:
● декларативный синтаксис (для импорта и экспорта);
● программной загрузки (чтобы задать конфигурацию загрузки модулей и для условной загрузки модулей).
Экспорт
let notExported = 'abc';export function square(x) { return x * x;}export const MY_CONSTANT = 123;
Импорт
import { square } from './iMath';console.log(square(3));
import { square, MY_CONSTANT } from './iMath';
Импорт
import { square } from './iMath';console.log(square(3));
import { square, MY_CONSTANT } from './iMath';
import './iMath' as c;console.log(c.square(3));
Импорт
import { square } from './iMath';console.log(square(3));
import { square, MY_CONSTANT } from './iMath';
import './iMath' as c;console.log(c.square(3));
import { square as squ } from './iMath';console.log(squ(3));
Экспорт по умолчанию
// myapp/models/Customer.jsexport default class { // анонимный класс constructor(id, name) { this.id = id; this.name = name; }};
Экспорт по умолчанию
// myapp/models/Customer.jsexport default class { // анонимный класс constructor(id, name) { this.id = id; this.name = name; }};
// myapp/myapp.jsimport Customer from 'models/Customer';let c = new Customer(0, 'Jane');
API программной загрузки
Позволяет делать две вещи:
● настраивать загрузку модулей
● программно работать с модулями и скриптами
normalize
System.normalize = function(moduleName, options){ //преобразуем 'core!superLib' --> 'core/lib/superLib.coffee' return moduleName};
resolve
System.resolve = function(moduleName, options){ // преобразуем 'core/lib/superLib.coffee' --> // '/var/www/myProject/resources/core/lib/superLib.coffee' return moduleName};
link
System.link = function(jsSource, options){ //можем вернуть объект, с зависимостями и колбэком
//можем вернуть модуль
//можем вернуть undefined тогда система решит, что мы загрузили // настоящий ES6 модуль и предоставит наружу все что он экспортирует};
Используем модули ECMAScript 6
● ES6 Module Transpiler: позволяет писать свои модули,
используя некую часть стандарта ECMAScript 6 (грубо говоря,
ECMAScript 5 + экспорт + импорт), и компилирует их в
модули AMD или CommonJS. Статья Райана Флоренца (Ryan
Florence) детально объясняет этот подход.
● ES6 Module Loader: позволяет использовать API загрузки
модулей ECMAScript 6 в современных браузерах.
Ещё:
● require-hm: плагин для RequireJS, позволяющий загружать модули ECMAScript 6
(только ECMAScript 5 + экспорт + импорт). Статья Каолана Макмахона (Caolan
McMahon) объясняет, как он работает. Предупреждение: плагин использует
более старый синтаксис.
● Traceur (компилятор ECMAScript 6 в ECMAScript 5): частично поддерживает
модули, возможно, в конечном счете будет поддерживать их полностью.
● TypeScript TypeScript (грубо говоря, ECMAScript 6 и поддержка статической
типизации): компилирует модули из внешних файлов (которые могут
использовать большую часть ECMAScript 6) в AMD или CommonJS.
Материалы
● Статья про es6 модули на frontender.info
● Варианты использования es6 модулей - Иегуда Кац(Yehuda Katz)
● Блог Эдди Османи(Addy Osmani)
● Статья “Путь js модуля” - Михаил Давыдов (Mikhail Davydov)