Фронтенд-инжиниринг — модульность JS от принципа к колесу

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

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

  1. Модульная структура JS. (Требуется/Море/Модуль ES6/NEJ)
  2. Фреймворк шаблонов внешнего интерфейса. (Реагировать/Вью/Обычный)
  3. Структура управления государством. (Поток / Редукс)
    В серии статей будут представлены соответствующие принципы из трех вышеперечисленных аспектов, и вы попробуете самостоятельно построить простое колесо.

Эта статья знакомит сМодульный JS.
Модульность JS — это неизбежная мера, предпринятая инженерами после взрывного роста фронтенд-кода с развитием фронтенд-технологий. Текущее модульное мышление делится на CommonJS, AMD и CMD. О разнице между тремя у всех в принципе есть некоторое понимание, и информации много, поэтому я не буду здесь вдаваться в подробности.

Основная идея модульности:

  1. расколоть. Разделите код js на несколько повторно используемых файлов кода js (модулей) в соответствии с функциональной логикой.
  2. нагрузка. Как загружать, выполнять и выводить модули.
  3. инъекция. Возможность внедрить вывод одного модуля js в другой модуль js.
  4. управление зависимостями. Существует большое количество интерфейсных инженерных модулей, и необходимо управлять зависимостями между модулями.

В соответствии с приведенной выше основной идеей видно, что при проектировании модульной инструментальной среды есть две ключевые проблемы: одна заключается в том, как выполнить один модуль и внедрить вывод результата в другой модуль, а другая — в крупномасштабных проектах. зависимости между модулями очень сложны.Как внедрять модули в правильном порядке зависимостей - это управление зависимостями.

Ниже приведен конкретный пример реализации простогона основе браузераизAMDМодульная структура (похожая на NEJ), которая предоставляет функцию определения, внедряет зависимости в функцию обратного вызова и возвращает выходные данные модуля. Чего добиться, показано в коде ниже.

define([
    '/lib/util.js', //绝对路径
    './modal/modal.js', //相对路径
    './modal/modal.html',//文本文件
], function(Util, Modal, tpl) {
    /*
    * 模块逻辑
    */
    return Module;
})

1. Как модули загружаются и выполняются

Не будем рассматривать, как обрабатываются зависимости модуля. Предполагая, что зависимости модуля были внедрены, как загрузить и выполнить модуль и вывести его?
На стороне браузера мы можем использоватьscriptтег для реализацииФайл JS-модуляВнедрение и реализация дляфайл текстового модуляможно использовать напрямуюajaxИсполнение запроса.
Конкретные шаги заключаются в следующем:

  • Первый шаг – получитьАбсолютный путь к файлу модуля.
    Чтобы загрузить файл в браузере, сначала получите полный сетевой абсолютный адрес соответствующего файла модуля. из-заэтикеткаАтрибут href всегда возвращает абсолютный путь, что означает, что он может преобразовывать относительный путь в абсолютный, поэтому эту функцию можно использовать здесь для получения абсолютного сетевого пути модуля. Следует отметить, что для файла зависимого модуля, использующего относительный путь, также необходимо рекурсивно получить абсолютный сетевой адрес текущего модуля, а затем объединить его с относительным путем, чтобы сформировать полный абсолютный адрес. код показывает, как показано ниже:
var a = document.createElement('a');
a.id = '_defineAbsoluteUrl_';
a.style.display = 'none';
document.body.appendChild(a);

function getModuleAbsoluteUrl(path) {
    a.href = path;
    return a.href;
}

function parseAbsoluteUrl(url, parentDir) {
    var relativePrefix = '.',
        parentPrefix = '..',
        result;
    if (parentDir && url.indexOf(relativePrefix) === 0) {
        // 以'./'开头的相对路径
        return getModuleAbsoluteUrl(parentDir.replace(/[^\/]*$/, '') + url);
    }
    if (parentDir && url.indexOf(parentPrefix) === 0) {
        // 以'../'开头的相对路径
        return getModuleAbsoluteUrl(parentDir.replace(/[\/]*$/, '').replace(/[\/]$/, '').replace(/[^\/]*$/, '') + url);
    }
    return getModuleAbsoluteUrl(url);
}
  • Второй шаг,Загружать и выполнять файлы модулей.

Для файлов JS используйтеscriptРеализация ярлыка. код показывает, как показано ниже:

var head = document.getElementsByTagName('head')[0] || document.body;

function loadJsModule(url) {
    var script = document.createElement('script');
    script.charset = 'utf-8';
    script.type = 'text/javascript';
    script.onload = script.onreadystatechange = function() {
        if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
            /*
             * 加载逻辑, callback为define的回调函数, args为所有依赖模块的数组
             * callback.apply(window, args);
             */
            script.onload = script.onreadystatechange = null;
        }  
    };
}

Для текстовых файлов напрямую используйтеajaxвыполнить. код показывает, как показано ниже:

var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),
      textContent = '';

xhr.onreadystatechange = function(){
    var DONE = 4, OK = 200;
    if(xhr.readyState === DONE){
        if(xhr.status === OK){
             textContent = xhr.responseText; // 返回的文本文件
        } else{
            console.log("Error: "+ xhr.status); // 加载失败
        }
    }
}

xhr.open('GET', url, true);// url为文本文件的绝对路径
xhr.send(null);

2. Управление зависимостями модулей

Процесс загрузки модуля показан на следующем рисунке.

  • государственное управление
    Как видно из вышеизложенного, загрузка модуля может иметь следующие возможные состояния.
  1. Состояние загрузки (загрузки), в том числе незагруженное (предзагруженное) состояние, состояние загрузки (загрузки) и загруженное (загруженное) состояние.
  2. Загрузка зависимостей (ожидающих) состояния.
  3. Обратный вызов модуля завершен (завершен).
    Поэтому к каждому загруженному модулю необходимо добавить флаг состояния (status), чтобы идентифицировать состояние текущего модуля.
  • Анализ зависимостей
    После загрузки модуля нам нужно разобрать абсолютный путь (path), зависимый модуль (deps) и функцию обратного вызова (callback) каждого модуля, а затем поместить это в информацию о модуле. Модель данных логики управления объектами модуля показана ниже.

    {
      path: 'http://asdas/asda/a.js',
      deps: [{}, {}, {}],
      callback: function(){ },
      status: 'pending'
    }
  • цикл зависимости
    Модули, скорее всего, будут иметь циклические зависимости. То есть модуль a и модуль b зависят друг от друга. Зависимость делится насильная зависимостьа такжеслабая зависимость.сильная зависимостьОтносится к зависимостям, которые будут использоваться при выполнении обратного вызова модуля; в противном случаеслабая зависимость. длясильная зависимость, вызовет взаимоблокировку, которую невозможно разрешить. нослабая зависимостьВы можете позволить модулю выполняться первым, внедрив пустую ссылку на модуль, а затем заменить ее после выполнения зависимого модуля.сильная зависимостьа такжеслабая зависимостьПример выглядит следующим образом:

//强依赖的例子
//A模块
define(['b.js'], function(B) {
  // 回调执行时需要直接用到依赖模块
   B.demo = 1;
   // 其他逻辑
});
//B模块
define(['a.js'], function(A) {
   // 回调执行时需要直接用到依赖模块
   A.demo = 1;
   // 其他逻辑
});
// 弱依赖的例子
// A模块
define(['b.js'], function(B) {
    // 回调执行时不会直接执行依赖模块
    function test() {
        B.demo = 1;
    }
    return {testFunc: test}
});
//B模块
define(['a.js'], function(A) {
    // 回调执行时不会直接执行依赖模块
    function test() {
        A.demo = 1;
    }
    return {testFunc: test}
});

3. Внешнее воздействиеdefineметод

Для функции определения необходимо просмотреть все необработанные js-скрипты (включаяв линиюа такжеОхват), а затем загрузите модуль. здесь дляв линиюа такжеОхватОпределение в скрипте необходимо обрабатывать отдельно. Есть две основные причины:

  1. Интровертные скрипты не требуют операции загрузки.
  2. Вывод обратного вызова определенного модуля в интровертном сценарии не может использоваться как зависимость от других модулей.
var handledScriptList = [];
window.define = function(deps, callback) {
    var scripts = document.getElementsByTagName('script'),
        defineReg = /s*define\s*\(\[.*\]\s*\,\s*function\s*\(.*\)\s*\{/,
        script;

    for (var i = scripts.length - 1; i >= 0; i--) {
        script = list[i];
        if (handledScriptList.indexOf(script.src) < 0) {
            handledScriptList.push(script.src);
            if (script.innerHTML.search(defineReg) >= 0) {
                // 内敛脚本直接进行模块依赖检查。
            } else {
                // 外联脚本的首先要监听脚本加载
            }
        }
    }
};

Выше приведено описание основных вопросов, связанных с внедрением модульного инструмента. полный коднажми на меня.