Как достигается модульность в Js?

Node.js JavaScript

Из-за изначального позиционирования Js (сначала я не ожидал, что его будут использовать в слишком сложных сценариях) он не предоставляет самой модульной системы.По мере усложнения приложения,модульныйпревратилась в проблему, которую необходимо решить. В соответствии с ФимойУглубленный принципВпринципе надо приоткрыть завесу модульности

Модульная проблема, которая должна быть решена

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

Лучшая организация кода проекта без глобального загрязнения

В качестве простого примера у нас теперь есть следующий код:

function doSomething () {
  const a = 10;
  const b = 11;
  const add = function (a, b) {
    return a + b
  }
  add (a + b)
}

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

// doSomething.js 文件
const add = require('add.js');
const a = 10;
const b = 11;
add(a+ b);
// add.js 文件
function add (a, b) {
  return a + b;
}
module.exports = add;

Целью этого очевидна, лучшая организация проекта, обратите внимание, что два файлаrequireа такжеmodule.exports, с текущей точки зрения Бога, это происходит из ключевых слов в спецификации CommonJS (о спецификации будет рассказано позже), которые представляют импорт и экспорт соответственно.Помимо спецификации, это на самом деле наш способ модуляризация вопросы, требующие решения. Кроме того, хотя модуль add необходимо использовать повторно, мы не хотим вызывать глобальное загрязнение при введении add

Во-вторых, как запустить модуль введен

В приведенном выше примере мы разделили файлы кода на два модуля, которые, не вызывая глобального загрязнения, как достичьrequireДля того, чтобы сделать пример кода для достижения нормальной работы?

requirerequireМожет быть достигнут

function require (path) {
   // lode 方法读取 path 对应的文件模块的代码字符串
   // let code = load(path);
   // 不考虑 load 的过程,直接获得模块 add 代码字符串
   let code = 'function add(a, b) {return a+b}; module.exports = add';
   // 封装成闭包
   code = `(function(module) {${code}})(context)`
   // 相当于 exports,用于导出对象
   let context = {};
   // 运行代码,使得结果影响到 context
   const run = new Function('context', code);
   run(context);
   //返回导出的结果
   return context.exports;
}

Здесь есть несколько ключевых моментов:
1) Чтобы не вызвать глобальное загрязнение, необходимо инкапсулировать строку кода в виде закрытия, а также экспортировать ключевые словаmodule.exports, модуль является единственным носителем для связи с внешним миром, его необходимо использовать в качестве входного параметра анонимной функции закрытия, а контекст передается рефереромcontextсоздать ассоциацию
2) использоватьnew FunctionПредполагается, что для выполнения строки кода большинство учащихсяnew FunctionЭто незнакомо, потому что не обязательно определять функцию вообще.Вы должны знать, что вы можете создавать функции непосредственно с классом Function.Синтаксис следующий:

var function_name = new function(arg1, arg2, ..., argN, function_body)

在上面的形式中,每个 arg 都是一个参数,最后一个参数是函数主体(要执行的代码)。这些参数必须是字符串。 То есть,Его можно использовать для выполнения строкового кода, аналогичногоeval, и по сравнению сevalЗначение определенных переменных, параметры могут быть переданы в виде строки кода
3) Если вы когда-нибудь задумывались, почему ключевое слово канонического экспортаexportsmodule.exportsexportsПолучатьcontextcontextЭто не будет иметь никакого эффекта (передача параметра адреса), буквенный код не изменится на следующий вид без косточек:

演示结果
Результаты демонстрации

3. Способ загрузки кода

После решения проблемы запуска кода нам также необходимо решить проблему загрузки кода файла модуля.Согласно приведенному выше примеру, наша цель - загрузить код файла модуля в виде строки.

В контейнере Node все файлы модулей являются локальными, просто прочитайте файл модуля с локального диска и загрузите строковый код, а затем выполните описанный выше процесс. Оказывается, невстроенные модули Node, core и c++ загружаются и выполняются примерно одинаково (хотя и не с использованием новой функции, а с помощью аналогичного метода).

В контейнере RN / Weex для загрузки удаленного Bundle.js вы можете запросить удаленный файл JS через возможность нажимания, а затем прочитать его в строковый код для загрузки (в соответствии с этой логикой, узел считывает удаленный файл JS Модули также могут быть необходимы, хотя в большинстве случаев нам не нужно)

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

Вот почему существует спецификация CommonJs и спецификация AMD/CMD.

Так как же это делает браузер? Чтобы динамически загрузить файл удаленного модуля Js через элемент управления Js в браузере, вам необходимо динамически вставить<script>узел:

// 摘抄自 require.js 的一段代码
var node = config.xhtml ?
                document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
                document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
node.addEventListener('load', context.onScriptLoad, false);
node.addEventListener('error', context.onScriptError, false);

Знаешь, поставил<script>После src тега, как только код скачается, он сразу же выполнится. Вы не можете инкапсулировать его в замыкание. Поэтому файловому модулю нужно делать статью в начале его определения. Это то, что мы скажем, в известной спецификации AMD/CMD.define, открытие add.js нужно переписать

// add.js 文件
define ('add',function () {
    function add (a, b) {
      return a + b;
    }
    return add;
})

Для реализации define самое главное — зарегистрировать результат выполнения обратного вызова в модульный массив контекста:

    context.modules = {}
    function define(name, callback) {
        context.modules[name] = callback && callback()
    }

Таким образом, require может загружать модули в соответствии с именем модуля из context.modules У вас есть желание написать «requirejs» самостоятельно?

Конкретная реализация AMD, конечно, намного сложнее, и ей также нужно контролировать время загрузки модулей, зависимости модулей и т. д., но после понимания сути этого нетрудно прочитать исходный код require.js. интенсивно.

4. Модульность в Webpack

WebPack также может настроить асинхронный модуль. При настройке в качестве асинхронного модуля среда браузера основана на динамической вставке.<script>

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

Модульная спецификация Js возникла из идеи расширения Js на серверную часть.Чтобы у Js была базовая возможность разработки крупномасштабных приложений, подобных Python, Ruby и Java, модульная спецификация необходима.Предложение спецификации CommonJS сформулировало прекрасное видение Js, надеясь, что Js может работать где угодно.,в том числе, но не ограничивается:

  • Js-приложение на стороне сервера
  • инструмент командной строки
  • настольное приложение
  • Гибридное приложение

Определение модулей в CommonJS несложное и в основном делится на справку по модулям, определение модуля и идентификацию модуля.

模块示意
Схема модуля

Спецификация CommonJs сияет в Node и продвигает друг друга, но на стороне браузера из-за сети явно нецелесообразно загружать модули синхронно.После периода споров спецификация AMD, наконец, победила. конечная сцена (полное название «Определение асинхронного модуля» или «определение асинхронного модуля»)

Что такое AMD и зачем вам AMD? В процессе вывода вышеупомянутой модульной реализации вы сможете найти ответ.

Кроме того, есть спецификация CMD, предложенная Юбо в Китае. Основное отличие между AMD и CMD заключается в том, что прежние должны объявить все зависимости в начале определения, а последние могут динамически импортировать модули в любое время. CMD ближе к Commonjs

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

V. Резюме

Если вам интересно, вы можете обратиться к выводам в этой статье, чтобы реализовать "yourRequireJs". Ничто так не ускоряет получение знаний, как повторение колеса~~

Передняя часть FIMcЭто сообщество знаний, которое углубляет знания в принципы. У нас есть планета знаний, официальный аккаунт и группа. Добро пожаловать, чтобы добавить микрокрючки: facemagic2014