[ПОСЛЕДНИЙ РАЗ] Модульность JavaScript объясняется простыми словами

внешний интерфейс JavaScript
[ПОСЛЕДНИЙ РАЗ] Модульность JavaScript объясняется простыми словами

предисловие

The last time, I have learned

[ПОСЛЕДНИЙ РАЗ] всегда был серией, которую я хотел написать, стремясь пережить интерфейс через накопление.

Это также для их собственных недостатков и обмена технологиями.

Приветствуем всех, кто комментирует и указывает на Tucao.

Цикл статей впервые опубликован на официальном аккаунте [Full-Stack Front-End Selection].Сборник статей автора можно посмотреть по адресу GitHub:Nealyang/personalBlog. Содержание и порядок публикации являются предварительными

С развитием Интернета разработка интерфейса становится все более и более сложной.От первоначальной проверки формы до разработки текущего проекта с тысячами строк кода командная работа является нашим неизбежным способом работы.Чтобы лучше управлять функциональной логикой и постепенно появилась концепция модульности.

Хорошая книга 📚 будет разбита на главы, и хороший модуль оценки кода.

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

Модуль — это файл или блок кода, реализующий определенную функцию. По мере того, как построение интерфейсной инженерной системы становится все более и более зрелым, возможно, концепция модульности уже знакома в кругу фронтенда.

Но для многих разработчиков ES6export,import,nodejsсерединаrequire,exports.xx,module.exportsКакая разница? Почему есть другойCommonJS, а такжеAMD,CMD,UMD? В чем разница? Даже когда мы пишем файл ts, нам нужно описать, какой метод модуля в файле конфигурации.Когда мы используем его в проекте, мы действительно знаем, какой вид модуляризации вы используете?

Эта статья совсем не поможет вам написать код, но если у вас все еще есть сомнения по поводу вышеперечисленных вопросов или вы хотите понять прошлое и настоящее модульности JavaScript, тогда давайте начнем~

Официальный аккаунт отвечает на [xmind2], чтобы получить исходный файл.

Значение модульности

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

Лично я считаю, что модульность имеет следующие преимущества:

  • ремонтопригодность, каждый модуль независим. Хороший дизайн может значительно снизить степень связанности проекта. Чтобы его можно было исправить независимо от других функций. Гораздо проще поддерживать хотя бы один независимый функциональный модуль, чем поддерживать беспорядок в коде.
  • Уменьшить глобальное переменное загрязнение, В первые дни фронтенд-разработки у всех нас была головная боль с глобальными переменными, потому что часто возникали какие-то труднодоступные и нетехнические ошибки. Когда какой-то несвязанный код случайно переименовывает глобальную переменную, мы сталкиваемся с раздражающей проблемой «загрязнения пространства имен». На самом деле, мы изо всех сил пытаемся избежать этого, пока не будет определена модульная спецификация. (будет представлено позже)
  • возможность повторного использования, Инкапсуляция функций внешнего модуля значительно повышает возможность повторного использования кода. Не должно быть необходимости подробно останавливаться на этом. думать отnpmПоглядиpackageКогда, что ты делаешь?
  • Легко управлять зависимостями, когда спецификация модуляризации не полностью определена, взаимозависимость между модулями очень расплывчата, и она полностью зависит от порядка, в котором вводятся файлы js. вульгарно! Там вообще нет технического содержания, и оно не только зависит от расплывчатости и сложности в обслуживании.

оригинальный модульный

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

инкапсуляция функций

Вернемся к определению модуля, которое мы только что сказали, модуль — это файл или блок кода, реализующий определенную функцию (это то, что я определил сам). Профессиональное определение в программировании — это программа или подпрограмма, необходимая для выполнения определенной функции, или независимая программная единица, которая может быть обработана компилятором, программой сборки и т. д., или часть большой программной системы. Одной из функций функции является набор операторов, реализующих определенную логику. А область JavaScript основана на функциях. Таким образом, самые примитивные функции должны быть первым шагом в модуляризации.

базовая грамматика

//函数1
function fn1(){
  //statement
}
//函数2
function fn2(){
  //statement
}

преимущество

  • Есть некоторая функциональная изоляция и инкапсуляция...

недостаток

  • загрязняет глобальные переменные
  • Отношения между модулями неоднозначны

инкапсуляция объекта

На самом деле имя переменной набито чуть глубже. . .

базовая грамматика

let module1 = {
  let tag : 1,
  let name:'module1',
  
  fun1(){
    console.log('this is fun1')
  },
  
  fun2(){
    console.log('this is fun2')
  }
}

Когда мы его используем, мы напрямую

module1.fun2();

преимущество

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

недостаток

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

IIFE

IIFEЭто немедленное выполнение функции, и мы можем добиться модульности в виде анонимных замыканий.

базовая грамматика

let global = 'Hello, I am a global variable :)';

(function () {
  // 在函数的作用域中下面的变量是私有的

  const myGrades = [93, 95, 88, 0, 55, 91];

  let average = function() {
    let total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item}, 0);

    return 'Your average grade is ' + total / myGrades.length + '.';
  }

  let failing = function(){
    let failingGrades = myGrades.filter(function(item) {
      return item < 70;});

    return 'You failed ' + failingGrades.length + ' times.';
  }

  console.log(failing());
  console.log(global);
}());

// 控制台显示:'You failed 2 times.'
// 控制台显示:'Hello, I am a global variable :)'

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

похоже на вышеIIFE, есть много эволюционного письма

Например, импорт зависимостей:

// module.js文件
(function(window, $) {
  let data = 'www.baidu.com'
  //操作数据的函数
  function foo() {
    //用于暴露有函数
    console.log(`foo() ${data}`)
    $('body').css('background', 'red')
  }
  function bar() {
    //用于暴露有函数
    console.log(`bar() ${data}`)
    otherFun() //内部调用
  }
  function otherFun() {
    //内部私有的函数
    console.log('otherFun()')
  }
  //暴露行为
  window.myModule = { foo, bar }
})(window, jQuery)
 // index.html文件
  <!-- 引入的js必须有一定顺序 -->
  <script type="text/javascript" src="jquery-1.10.1.js"></script>
  <script type="text/javascript" src="module.js"></script>
  <script type="text/javascript">
    myModule.foo()
  </script>

Существует также так называемый паттерн «выявляющий модуль».Revealing module pattern

var myGradesCalculate = (function () {

   // 在函数的作用域中下面的变量是私有的
  var myGrades = [93, 95, 88, 0, 55, 91];

  var average = function() {
    var total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item;
      }, 0);

    return'Your average grade is ' + total / myGrades.length + '.';
  };

  var failing = function() {
    var failingGrades = myGrades.filter(function(item) {
        return item < 70;
      });

    return 'You failed ' + failingGrades.length + ' times.';
  };

  // 将公有指针指向私有方法

  return {
    average: average,
    failing: failing
  }
})();

myGradesCalculate.failing(); // 'You failed 2 times.' 
myGradesCalculate.average(); // 'Your average grade is 70.33333333333333.'

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

преимущество

  • Реализована базовая инкапсуляция
  • Выставляйте только операции внешнего метода сpublicа такжеprivateКонцепция чего-либо

недостаток

  • Зависимости модуля неоднозначны

CommonJS

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

Хотя каждый метод более эффективен, он также имеет свои недостатки.

С наступлением эры большого интерфейса стандартные спецификации модулей JavaScript также включают:CommonJS,AMD,CMD,UMD,ES6Родной.

основное введение

CommonJSЭто модульная спецификация JavaScript, в основном используемая в Nodejs на стороне сервера.Конечно, она также может работать на стороне браузера путем преобразования и упаковки. Ведь модули, загружаемые сервером, хранятся на локальном диске, поэтому они загружаются быстрее, и нет необходимости рассматривать асинхронный метод.

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

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

определение модуля

CommonJSВ спецификации указано, что один файл является модулем, то есть единой областью видимости. и модуль имеет только одну розетку,module.exports/exports.xxx

// lib/math.js
const NAME='Nealayng';
module.exports.author = NAME;
module.exports.add = (a,b)=> a+b;

загрузочный модуль

Загрузите модуль, используяrequireметод, который читает файл и выполняет его, возвращая файлmodule.exportsобъект

// main.js
const mathLib = require('./lib/math');

console.log(mathLib.author);//Nealyang
console.log(mathLib.add(1,2));// 3

Использование CommonJS в браузере

Потому что браузер не поддерживаетCommonJSнорма, потому что это просто неmodule,exports,requireи другие переменные, вы должны преобразовать формат, если хотите его использовать. Browserify — это наиболее часто используемый инструмент преобразования формата CommonJS, мы можем установить его,browserifyЧтобы его конвертировать.Но нам все равно нужно обратить внимание, так как спецификация CommonJS блокирует загрузку, а файл модуля хранится на стороне сервера, может быть приостановлено состояние ожидания.

npm i browserify -g

Затем используйте следующую команду

browserify main.js -o js/bundle/main.js

Затем вы можете импортировать и использовать его в HTML.

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

Функции

  • Взяв файл как модуль модуля, код выполняется в рамках модуля и не будет загрязнять глобальные переменные.
  • Модуль загружается синхронно, напрямую читать локальный диск на стороне сервера не проблема, для браузеров не подходит
  • Модуль можно загружать несколько раз, но он будет работать только при первой загрузке, а затем при загрузке считывается кэшированный файл. Перед повторным чтением содержимого файла необходимо очистить кеш.
  • Порядок загрузки модулей, порядок их появления в коде
  • Экспорт представляет собой копию значения, которая сильно отличается от ES6 (описано позже).

Дополнительные очки знаний

На самом деле реализация модулей в nodejs не полностью соответствует спецификации CommonJS, а является выбором.

В Node файл — это модуль -> модуль

исходный кодОпределяется следующим образом:

function Module(id = '', parent) {
  this.id = id;
  this.path = path.dirname(id);
  this.exports = {};
  this.parent = parent;
  updateChildren(parent, this, false);
  this.filename = null;
  this.loaded = false;
  this.children = [];
}
//实例化一个模块
var module = new Module(filename, parent);

Модуль CommonJS представляет собой файл сценария. При первой загрузке скрипта командой require выполняется весь скрипт и в памяти создается объект.

{
  id: '...',
  exports: { ... },
  loaded: true,
  ...
}

Приведенный выше код представляет собой объект, сгенерированный после того, как Node загрузит модуль внутри себя. Атрибут id объекта — это имя модуля, атрибут exports — это каждый интерфейс, выводимый модулем, а атрибутload — логическое значение, указывающее, был ли выполнен скрипт модуля. Есть много других атрибутов, которые здесь опущены.

Когда вам понадобится использовать этот модуль в будущем, вы перейдете к атрибуту экспорта, чтобы получить значение. Даже если команда require будет выполнена снова, модуль не будет выполняться снова, а значение будет получено из кеша. То есть независимо от того, сколько раз загружается модуль CommonJS, он будет запускаться только один раз при первой загрузке, а при последующей загрузке он вернет результат первого запуска, если только система не кеш очищается вручную.

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

AMD

Определение асинхронного модуля: определение асинхронного модуля.

Это должно решить фатальную проблему CommonJS на стороне браузера, о которой мы упоминали выше: приостановленная анимация.

представлять

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

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

require.js

GitHub.com/require JS/день…

Более подробные инструкции по require.js можно найти на официальном сайте api:требуется JS.org/docs/API.Contracts…

require.jsВ основном решить две проблемы:

  • Загружать модули асинхронно
  • Неоднозначность зависимости между модулями

Модуль определения

define(id,[dependence],callback)
  • id, необязательный параметр, грубо говоря, должен дать модулю имя, но это уникальный идентификатор модуля. Если не указано, возьмите имя файла скрипта
  • dependence, массив модулей, так как
  • callback, фабричные методы, некоторые операции инициализации модулей. Если это функция, она должна выполняться только один раз. Если объект, то выходное значение модуля

Используйте модули

require([moduleName],callback);
  • moduleName, массив модулей, так как
  • callback, которая является функцией обратного вызова, выполняемой после успешной загрузки зависимого модуля (общее решение для асинхронного интерфейса),

data-main

<script src="scripts/require.js" data-main="scripts/app.js"></script>

data-mainУкажите файл записи, например, указанный здесьscriptsвнизapp.jsфайл, то только прямое или косвенноеapp.jsВ html будут вставлены только модули с зависимостями.

require.config

С помощью этой функции вы можетеrequirejsДля гибкой конфигурации его параметр является объектом конфигурации.Элементы конфигурации и их значения следующие:

  • baseUrl- Корневой путь для загрузки модулей.
  • paths- Используется для сопоставления путей модулей, которые не существуют в корневом пути.
  • shims- Настраивается вне скриптов/модулей и не использует функциональные зависимости RequireJS и функции инициализации. Предполагая, что подчеркивание не используетсяRequireJSопределение, но вы все равно хотите использовать его через RequireJS, тогда вам нужно определить его как прокладку в конфигурации
  • deps- загрузить массив зависимостей
require.config({
//默认情况下从这个文件开始拉去取资源
    baseUrl:'scripts/app',
//如果你的依赖模块以pb头,会从scripts/pb加载模块。
    paths:{
        pb:'../pb'
    },
// load backbone as a shim,所谓就是将没有采用requirejs方式定义
//模块的东西转变为requirejs模块
    shim:{
        'backbone':{
            deps:['underscore'],
            exports:'Backbone'
        }
    }
});

Демо

  • Создать проект
|-js
  |-libs
    |-require.js
  |-modules
    |-article.js
    |-user.js
  |-main.js
|-index.html
  • определить модуль
// user.js文件
// 定义没有依赖的模块
define(function() {
  let author = 'Nealyang'
  function getAuthor() {
    return author.toUpperCase()
  }
  return { getAuthor } // 暴露模块
})

//article.js文件
// 定义有依赖的模块
define(['user'], function(user) {
  let name = 'THE LAST TIME'
  function consoleMsg() {
    console.log(`${name} by ${user.getAuthor()}`);
  }
  // 暴露模块
  return { consoleMsg }
})
// main.js
(function() {
  require.config({
    baseUrl: 'js/', //基本路径 出发点在根目录下
    paths: {
      //映射: 模块标识名: 路径
      article: './modules/article', //此处不能写成article.js,会报错
      user: './modules/user'
    }
  })
  require(['article'], function(article) {
    article.consoleMsg()
  })
})()
// index.html文件
<!DOCTYPE html>
<html>
  <head>
    <title>Modular Demo</title>
  </head>
  <body>
    <!-- 引入require.js并指定js主文件的入口 -->
    <script data-main="js/main" src="js/libs/require.js"></script>
  </body>
</html>

Если нам нужно внедрить сторонние библиотеки, нам нужно ввести их в файл main.js

(function() {
  require.config({
    baseUrl: 'js/',
    paths: {
      article: './modules/article',
      user: './modules/user',
      // 第三方库模块
      jquery: './libs/jquery-1.10.1' //注意:写成jQuery会报错
    }
  })
  require(['article'], function(alerter) {
    article.consoleMsg()
  })
})()

Функции

  • Загружайте модули асинхронно, не вызывая ложной загрузки из-за проблем с сетью
  • Явно перечислить его зависимости и внедрить их как параметры функции (той, которая определяет этот модуль)
  • В начале модуля загрузите все необходимые зависимости

По поводу использования require.js внимательно смотрите документацию, на самом деле там еще очень много знаний. Но так как мы сейчас им мало пользуемся (и я с ним не знаком), обратимся к отличным статьям в интернете и собственной практике, чтобы привлечь больше идей.

CMD

основное введение

CMD был предложен Ю Бо из Али (на историю роста великого бога можно ответить [большому парню] в открытом доступе), а функция js заключается в следующем.sea.js, на самом деле очень похоже на AMD, файл это модуль, но главное отличие в том, что он загружается по требованию. Рекомендуется принцип опоры на близость, а модули выполняются с задержкой, в то время как AMD полагается на модульное раннее выполнение (requireJS 2.0позже также изменено на отложенное выполнение)

//AMD
define(['./a','./b'], function (a, b) {

  //依赖一开始就写好
  a.test();
  b.test();
});
  
//CMD
define(function (requie, exports, module) {
  
  //依赖可以就近书写
  var a = require('./a');
  a.test();
  
  ...
  //按需加载
  if (status) {
    var b = requie('./b');
    b.test();
  }
});

SeaJs

github.com/seajs/seajs

seajs.github.io/seajs/docs/

точноCMDдаSeaJSКанонический продукт определения модуля в процессе продвижения.

Также известен какSeaJSявляется последователемCMDнормативныйJavaScriptПлатформа загрузки модулей может реализовать модульный метод разработки CMD для JavaScript.

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

Не будет преувеличением сказать, что наша текущая страница подробностей — SeaJS+Kissy. . . (вскоре)

SeajsСтремитесь к простому и естественному написанию и организации кода со следующими основными функциями:

  • Простая и удобная спецификация определения модуля:Sea.jsследитьCMDСпецификация, вы можете написать код модуля, например Node.js.
  • Естественная и интуитивно понятная организация кода: автоматическая загрузка зависимостей и краткая и понятная конфигурация позволяют нам получать больше удовольствия от кодирования.

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

Демо

examples/
  |-- sea-modules      存放 seajs、jquery 等文件,这也是模块的部署目录
  |-- static           存放各个项目的 js、css 文件
  |     |-- hello
  |     |-- lucky
  |     `-- todo
  `-- app              存放 html 等文件
        |-- hello.html
        |-- lucky.html
        `-- todo.html

Начнем с hello.html и посмотрим, как организован код с помощью Sea.js.

В конце страницы hello.html, после импорта sea.js через скрипт, есть фрагмент кода конфигурации


// seajs 的简单配置
seajs.config({
  base: "../sea-modules/",
  alias: {
    "jquery": "jquery/jquery/1.10.1/jquery.js"
  }
})

// 加载入口模块
seajs.use("../static/hello/src/main")

После загрузки sea.js входной модуль будет загружен автоматически. Код на странице такой простой.

Эта небольшая игра состоит из двух модулей spinning.js и main.js, написанных по единому методу:

// 所有模块都通过 define 来定义
define(function(require, exports, module) {

  // 通过 require 引入依赖
  var $ = require('jquery');
  var Spinning = require('./spinning');

  // 通过 exports 对外提供接口
  exports.doSomething = ...

  // 或者通过 module.exports 提供整个接口
  module.exports = ...

});

Выше приведен формат записи модуля CMD, рекомендованный Sea.js. Если вы использовали Node.js, все будет происходить само собой.

Приведенные выше примеры взяты с официального сайта Example. Больше демонстрационного просмотра:GitHub.com/sea JS/экзамен P…

Функции

  • Относительно естественный стиль объявления зависимостей и хорошее сообщество
  • файл как модуль
  • Модули загружаются по запросу.
  • Выступайте за принцип опоры на ближайший и откладывайте выполнение модулей

UMD

UMD На самом деле мне лично до сих пор очень нравится. . . . Мне это не нравится.ifElseСразуuniversal. . . .

основное введение

UMDдаAMDа такжеCommonJSкомплексный продукт. Как указано выше,AMDВ дело вступает браузер, неблокирующая загрузка. CommonJS в основном используется в Nodejs на стороне сервера. Вот люди и придумали общую закономерностьUMD(универсальное определение модуля). для решения кроссплатформенных проблем.

Вот так! то естьifElseписьма.

Основная идея такова: сначала определите, поддерживать ли модули Node.js (exports) существует, если он существует, используется режим модуля Node.js.

При решении вопроса о поддержке AMD (defineсуществует), если он существует, используйтеAMDспособ загрузки модуля.

нормальное использование

(function (window, factory) {
    if (typeof exports === 'object') {
     
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
     
        define(factory);
    } else {
     
        window.eventUtil = factory();
    }
})(this, function () {
    //module ...
});

Больше примеров про UMD можно переместить на github:github.com/umdjs/umd

ES6

Если вы дочитали до этого места, поздравляем, мы начинаем представлять наш новейший модуль!

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

основное введение

В ES6 мы можем использоватьimportмодуль импорта ключевых слов черезexportМодуль экспорта ключевых слов, функция по сравнению с предыдущими программами более мощные, мы также уважаем, но потому что ES6 в настоящее время не реализованы во всех браузерах, поэтому нам не нужно будет поддерживаться БабелimportКомпилируется в широко поддерживаемый в настоящее времяrequire.

Модульность ES6 привлекаетCommonJSа такжеAMDПреимущества краткого синтаксиса и асинхронной поддержки. И метод написания очень похож на CommonJS.

Все знакомы с базовым использованием модулей ES6. Здесь мы в основном сравниваем и учимся с CommonJS.

Отличия от CommonJS

Две большие разницы:

  • Модули CommonJS выводят копию значения, модули ES6 выводят ссылку на значение.
  • Модули CommonJS загружаются во время выполнения, модули ES6 представляют собой скомпилированные интерфейсы..

Копия значения и ссылка на значение

// lib/counter.js

var counter = 1;

function increment() {
  counter++;
}

function decrement() {
  counter--;
}

module.exports = {
  counter: counter,
  increment: increment,
  decrement: decrement
};


// src/main.js

var counter = require('../../lib/counter');

counter.increment();
console.log(counter.counter); // 1

Экземпляр в Main.js полностью не имеет значения для оригинального модуля. Это также объясняет, почему вызов Counter.ruement () все еще возвращает 1. Поскольку счетчик счетчиков мы ввели, и тот, который в модуле - два разных экземпляра.

Так что вызывая метод Counter.ruement (), изменят только счетчик в модуле. Если вы хотите изменить импортированный счетчик, вы должны сделать это вручную:

counter.counter++;
console.log(counter.counter); // 2

И с помощью оператора импорта вы можете импортировать модули только для чтения в реальном времени:

// lib/counter.js
export let counter = 1;

export function increment() {
  counter++;
}

export function decrement() {
  counter--;
}


// src/main.js
import * as counter from '../../counter';

console.log(counter.counter); // 1
counter.increment();
console.log(counter.counter); // 2

загрузить и скомпилировать

потому чтоCommonJSзагружен объект (module.exports), объекты могут создаваться только во время выполнения скрипта. И модуль ES6 — это не объект, а просто статическое определение. Генерируется на этапе разбора кода.

Модули ES6 представляют собой выходные интерфейсы времени компиляции, поэтому они обладают следующими двумя характеристиками:

  • Команда импорта будет статически проанализирована движком JS и выполнена перед другим содержимым модуля.
  • Команда экспорта имеет эффект продвижения объявления переменных, поэтому положение команд импорта и экспорта в модуле не влияет на вывод программы.
// a.js
console.log('a.js')
import { foo } from './b';

// b.js
export let foo = 1;
console.log('b.js 先执行');

// 执行结果:
// b.js 先执行
// a.js
// a.js
import { foo } from './b';
console.log('a.js');
export const bar = 1;
export const bar2 = () => {
  console.log('bar2');
}
export function bar3() {
  console.log('bar3');
}

// b.js
export let foo = 1;
import * as a from './a';
console.log(a);

// 执行结果:
// { bar: undefined, bar2: undefined, bar3: [Function: bar3] }
// a.js

Различия в круговой загрузке

Циклическая зависимость означает, что выполнение сценария a зависит от сценария b, а выполнение сценария b зависит от сценария a.

// a.js
var b = require('b');

// b.js
var a = require('a');

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

В CommonJS код скрипта находится вrequire, все будет выполнено.После того, как модуль «циклически загружен», будет выводиться только исполняемая часть, а невыполненная часть не будет выводиться..

// a.js
exports.done = false;
var b = require('./b.js');
console.log('在 a.js 之中,b.done = %j', b.done);
exports.done = true;
console.log('a.js 执行完毕');
// b.js
exports.done = false;
var a = require('./a.js');
console.log('在 b.js 之中,a.done = %j', a.done);
exports.done = true;
console.log('b.js 执行完毕');
// main.js
var a = require('./a.js');
var b = require('./b.js');
console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);

Результат:

在 b.js 之中,a.done = false
b.js 执行完毕
在 a.js 之中,b.done = true
a.js 执行完毕
在 main.js 之中, a.done=true, b.done=true

Из вышеизложенного мы видим, что:

  • существуетb.jsсреди,a.jsВыполнение не завершено, выполняется только первая строка.
  • main.jsКогда вторая строка будет выполнена, она больше не будет выполнятьсяb.js, но вывод кэшируетсяb.jsрезультат выполнения , то есть его четвертая строка

ES6 обрабатывает «циклическую загрузку» принципиально иначе, чем CommonJS**. Модули ES6 имеют динамические ссылки**, если вы используете импорт для загрузки переменных из модуля (т.е. импортируете foo из 'foo'), эти переменные не будут кэшироваться, а станут ссылкой на загруженный модуль, что требует от разработчика обеспечения что значение может быть получено, когда фактическое значение получено.

// a.mjs
import {bar} from './b';
console.log('a.mjs');
console.log(bar);
export let foo = 'foo';

// b.mjs
import {foo} from './a';
console.log('b.mjs');
console.log(foo);
export let bar = 'bar';

Результаты приведены ниже:

b.mjs
ReferenceError: foo is not defined

В приведенном выше коде после выполнения a.mjs будет сообщено об ошибке, а переменная foo не определена.

Конкретные результаты выполнения следующие:

  • После выполнения a.mjs движок обнаруживает, что он загрузил b.mjs, поэтому сначала выполнит b.mjs, а затем выполнит a.mjs
  • Когда b.mjs выполняется, известно, что он получил входной интерфейс foo от a.mjs.В это время он не будет выполнять a.mjs, но будет считать, что этот интерфейс уже существует, и продолжит выполнение.
  • При выполнении третьей строки console.log(foo) обнаруживается, что этот интерфейс вообще не определен, поэтому сообщается об ошибке.

Решение этой проблемы состоит в том, чтобы уже определить foo при запуске b.mjs. Это можно решить, написав foo как функцию.

// a.mjs
import {bar} from './b';
console.log('a.mjs');
console.log(bar());
function foo() { return 'foo' }
export {foo};

// b.mjs
import {foo} from './a';
console.log('b.mjs');
console.log(foo());
function bar() { return 'bar' }
export {bar};

Окончательный результат выполнения:

b.mjs
foo
a.mjs
bar

Функции

  • Каждый модуль загружается несколько раз, а JS выполняется только один раз, если тот же файл в той же директории будет загружен в следующий раз, он будет прочитан прямо из памяти. Модуль — это синглтон или объект
  • Код выполняется в области модуля, а не в глобальной области. Переменные верхнего уровня внутри модуля, не видимые снаружи. Не загрязняет глобальную область видимости;
  • Скрипты модуля автоматически используют строгий режим, независимо от того, объявлено ли использование строгого режима.
  • Среди модулей вы можете использовать команду импорта для загрузки других модулей (суффикс .js нельзя опускать, и вам необходимо указать абсолютный URL-адрес или относительный URL-адрес), или вы можете использовать команду экспорта для вывода внешнего интерфейса.
  • В модулях ключевое слово this на верхнем уровне возвращает undefined вместо указания на окно. То есть бессмысленно использовать ключевое слово this на верхнем уровне модуля.

Для подробного ознакомления с модулями ES6 я настоятельно рекомендую книгу Ruan Yifeng ES6 Introduction and Deep Understanding of ES6.

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

учеба по обмену

  • Номер общественного беспокойства [выбор] полный интерфейс стека, рекомендуется получать хороший текст ежедневно
  • Добавить микросигнал: is_Nealyang (источник комментариев), группа обменивается
Официальный аккаунт [выбор полного интерфейса] Персональный WeChat【is_Nealyang】