Анализ принципа модульности JavaScript

JavaScript Webpack
Анализ принципа модульности JavaScript

Статья впервые опубликована на:GitHub.com/US TB-Вуд, умри, о ты…

написать впереди

Модульность просто означает разложение сложной системы на несколько модулей для облегчения кодирования. Общий процесс модульности JS: CommonJS (сервер) -> AMD (браузер) -> UMD (совместимый с CommonJS и AMD) -> Модуль ES (стандарт ES6). В этой статье будет представлено их использование и просто реализованы их принципы. И простой в реализации модульный инструмент для упаковки webpack.

Эта статья будет заключаться из следующих разделов:

  1. Использование и принцип CommonJS
  2. Использование и принцип AMD
  3. Стандартизация ES2015
  4. Простой и удобный веб-пакет

CommonJS

CommonJS — это широко используемая спецификация модульности JavaScript.Основная идея состоит в том, чтобы синхронно загружать другие зависимые модули с помощью метода require и экспортировать интерфейсы, которые необходимо предоставить, с помощью module.exports.

Использование Commonjs

// 用require方法导入
const someFun= require('./moduleA');
someFun();

// 用module.exports导出
module.exports = someFunc;

Принцип CommonJS

a.js:

console.log('aaa');
exports.name = '这是a模块的内容';

b.js:

let fs = require('fs');
let path = require('path');
let b = req('./a.js');
// req即使CommonJS中的require方法
function req(mod) {
    let filename = path.join(__dirname, mod);
    let content = fs.readFileSync(filename, 'utf8');
    /**
       * 最后一个参数是函数的内容体,相当于以下函数
       *function fn(exports, module, require, __dirname, __filename) {
       *  module.exports = '这是另外一个文件导出的内容'
       *  return module.exports
       *}
    */
    let fn = new Function('exports', 'require', 'module', '__filename', '__dirname', content + '\n return module.exports;');
    let module = {
        exports: {}
    };

    return fn(module.exports, req, module, __filename, __dirname);
}

AMD

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

Преимущества АМД:

  • Запускается прямо в браузере без транскодирования
  • Может загружать несколько зависимостей
  • Код может работать в среде браузера и среде Node.js.

Недостатки АМД:

  • Среда выполнения JavaScript изначально не поддерживает AMD, и ей необходимо импортировать библиотеку, реализующую AMD, прежде чем ее можно будет использовать в обычном режиме.
// 使用define方法定义一个模块
define('a', [], function () {
    return 'a';
});
define('b', ['a'], function (a) {
    return a + 'b';
});
// 使用require来导入和使用
require(['b'], function (b) {
    console.log(b);
});

Принцип AMD

let factories = {};
/**
 * 实现AMD的define方法
 * @param moduleName 模块的名字
 * @param dependencies 依赖
 * @param factory 工厂函数
*/
function define(modName, dependencies, factory) {
    factory.dependencies = dependencies;
    factories[modName] = factory;
}
/**
 * 实现AMD的require方法
 * @param mods 引入的模块
 * @param callback 回调函数
 */
function require(modNames, callback) {
    let loadedModNames = modNames.map(function (modName) {
        let factory = factories[modName];
        let dependencies = factory.dependencies;
        let exports;
        require(dependencies, function (...dependencyMods) {
            exports = factory.apply(null, dependencyMods);
        });
        return exports;
    })
    callback.apply(null, loadedModNames);
}

Модульность ES2015

ES2015 Modularity — это спецификация модульности JavaScript, предложенная ECMA, которая реализует модульность на уровне языка. И поставщики браузеров, и Node.js объявили о встроенной поддержке спецификации. Он постепенно заменит спецификации CommonJS и AMD в качестве общего модульного решения для браузеров и серверов. Код при использовании модульного импорта и экспорта ES2015 выглядит следующим образом:

// 使用import导入
import { name } from './person.js';

// 使用export导出
export const name = 'musion';

Хотя модули ES2015 являются окончательным модульным решением, их недостатком является то, что они не могут быть запущены напрямую в большинстве сред выполнения JavaScript и должны быть преобразованы в стандартные инструменты ES5 с помощью инструментов сборки, прежде чем они смогут нормально работать.

Автоматизация сборки

  • Оптимизация файлов: сжатие JavaScript, CSS, сжатие HTML-кода, слияние изображений.
  • Разделение кода: Извлеките общий код нескольких страниц, извлеките код, который не нужно выполнять на первом экране, и дайте ему возможность загружаться асинхронно.
  • Модуль Merge: есть много модулей и файлов в модульном элементе, а функция сборки встроена в один файл.
  • Автоматическое обновление: отслеживайте изменения локального исходного кода, автоматически перестраивайте и обновляйте браузер.
  • Проверка кода: перед отправкой кода на склад необходимо проверить, соответствует ли код спецификации и проходит ли модульный тест.
  • Автоматический выпуск: после обновления кода код онлайн-релиза создается автоматически и передается в систему выпуска.

webpack

Webpack — это инструмент для упаковки и модуляции JavaScript.В webpack все файлы являются модулями, файлы конвертируются через загрузчик, хуки внедряются через плагин, и, наконец, выводится файл, состоящий из нескольких модулей. Webpack фокусируется на создании модульных проектов.

Все файлы:JavaScript, CSS, SCSS, изображения и шаблоны — все это модули в глазах Webpack.Преимущество этого заключается в том, что зависимости между модулями могут быть четко описаны, чтобы упростить Webpack для объединения и упаковки модулей. После обработки Webpack в конечном итоге выводит статические ресурсы, которые может использовать браузер.

Простая демистификация веб-пакета

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

(function (modules) {
    function require(moduleId) {
        var module = {
            exports: {}
        };
        // moduleId -> 模块的名字
        modules[moduleId].call(module.exports, module, module.exports, require);
        return module.exports;

    }
    return require("./index.js");
}) ({
    "./index.js":
    (function (module, exports, require) {
        eval("console.log('hello');\n\n");
    })
});

Простая реализация веб-пакета

#! /usr/bin/env node
// 这个文件用来描述如何打包
const pathLib = require('path');
const fs = require('fs');
let ejs = require('ejs');
let cwd = process.cwd();
let { entry, output: { filename, path } } = require(pathLib.join(cwd, './webpack.config.js'));
let script = fs.readFileSync(entry, 'utf8');
let bundle = `
(function (modules) {
    function require(moduleId) {
        var module = {
            exports: {}
        };
        modules[moduleId].call(module.exports, module, module.exports, require);
        return module.exports;

    }
    return require("<%-entry%>");
})
    ({
        "<%-entry%>":
            (function (module, exports, require) {
                eval("<%-script%>");
            })
    });
`
let bundlejs = ejs.render(bundle, {
    entry,
    script
});
try {
    fs.writeFileSync(pathLib.join(path, filename), bundlejs);
} catch (e) {
    console.error('编译失败 ', e);
}
console.log('compile sucessfully!');

Зависимость от других модулей

#! /usr/bin/env node
// 这个文件用来描述如何打包
const pathLib = require('path');
const fs = require('fs');
let ejs = require('ejs');
let cwd = process.cwd();
let { entry, output: { filename, path } } = require(pathLib.join(cwd, './webpack.config.js'));
let script = fs.readFileSync(entry, 'utf8');
let modules = [];
script.replace(/require\(['"](.+?)['"]\)/g, function () {
    let name = arguments[1];
    let script = fs.readFileSync(name, 'utf8');
    modules.push({
        name,
        script
    });
});
let bundle = `
(function (modules) {
    function require(moduleId) {
        var module = {
            exports: {}
        };
        modules[moduleId].call(module.exports, module, module.exports, require);
        return module.exports;
    }
    return require("<%-entry%>");
})
    ({
        "<%-entry%>":
            (function (module, exports, require) {
                eval(\`<%-script%>\`);
            })
       <%if(modules.length>0){%>,<%}%>
        <%for(let i=0;i<modules.length;i++){
            let module = modules[i];%>   
            "<%-module.name%>":
            (function (module, exports, require) {
                eval(\`<%-module.script%>\`);
            })
        <% }%>    
    });
`
let bundlejs = ejs.render(bundle, {
    entry,
    script,
    modules
});
try {
    fs.writeFileSync(pathLib.join(path, filename), bundlejs);
} catch (e) {
    console.error('编译失败 ', e);
}
console.log('compile sucessfully!');

использованная литература

Интенсивное чтение js модульная разработка

Курсы Everest Front-End архитектор курсов

Дополнительное чтение

Семь дней модульности JavaScript

Краткая история модульного программирования в JavaScript (2009-2016)

Можете обратить внимание на мой паблик-аккаунт «Muchen Classmate», фермера на гусиной фабрике, который обычно записывает какие-то банальные мелочи, технологии, жизнь, инсайты и срастается.