«4D Advanced» объясняет Commonjs и модуль Es простыми словами

Node.js внешний интерфейс JavaScript
«4D Advanced» объясняет Commonjs и модуль Es простыми словами

Введение

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

Как обычно, начнём сегодняшний разбор с вопросов🤔🤔🤔:

  • 1 В чем разница между Commonjs и Es Module?
  • 2 Как Commonjs решает проблему циклических ссылок?
  • 3 Так как естьexportsПочемуmodule.exports ? Оба детства, насколько ярко?
  • 4 requireМеханизм поиска модуля?
  • 5 Как модуль Es решает проблему круговой ссылки?
  • 6 exports = {}Почему эта запись недействительна?
  • 7 О насimport()динамический импорт ?
  • 8 Как модуль Es изменяет частные переменные в модуле?
  • 9 ...

ps: Так как автор некоторое время назад писал буклет "React Advanced Practice Guide" то нет времени постоянно выводить качественные статьи.Далее вернусь к созданию качественных технических статей, дарю розы другим, и со стойким ароматом в руках.Друзья, которые надеются прочитать Ставьте автору лайк 👍 и поощряйте меня продолжать творить.

Два модульных

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

<body>
  <script src="./index.js"></script>
  <script src="./home.js"></script>
  <script src="./list.js"></script>
</body>

Как и выше, при условии отсутствия модульности, еслиhtmlЕсли так написать, то обнажится ряд проблем.

  • глобальное загрязнение

без модульности, тоscriptВнутренние переменные могут загрязнять друг друга. Например, есть сценарий, как указано выше./index.jsдокументы и./list.jsФайл был разработан для Xiao A,./home.jsРазработан для малых Б.

маленькая А вindex.jsАтрибут name объявляется как строка.

var name = '我不是外星人'

Тогда маленький А находится вlist.js, ссылаясь на атрибут имени,

console.log(name)

1.jpg

Когда я печатаю его, я обнаруживаю, что это имя стало функцией. Сначала Сяо А был ошеломлен, но позже узнал, что программное обеспечение, разработанное в Сяо Бhome.jsВ документе говорится:

function name(){
    //...
}

И метод имени упоминается несколько раз, что приводит к серии цепных реакций.

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

Просто напишите это в home.js:

(function (){
    function name(){
        //...
    }
})()

2.jpg

Таким образом, маленькое А может быть нормальным вlist.jsПолучите свойство name из . Но это простоdemo, мы не можем гарантировать, что в реальной разработке ситуация будет более сложной. Таким образом, разработка без модулей сопряжена с большим риском.

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

Управление зависимостями также является неразрешимой проблемой. Тем не менее, в приведенном выше примере при нормальных обстоятельствах порядок, в котором выполняется js, совпадает с порядком, в котором расположены теги скрипта. Так как же существует зависимость между тремя js и как с ней обращаться?

Предположим, что в трех js есть публичный методfun1,fun2,fun3. Зависимости между тремя показаны на рисунке ниже.

  • js нижнего уровня могут вызывать методы js верхнего уровня, но js верхнего уровня не могут вызывать методы js нижнего уровня.

3.jpg

Следовательно, модульность необходима для решения вышеуказанных проблем.Сегодня мы сосредоточимся на двух важных решениях модульности интерфейса:Commonjsа такжеEs Module

Три Commonjs

CommonjsПредложение восполняет дефект, заключающийся в том, что в Javascript нет единого стандарта модульности. nodejs заимствован изCommonjsМодуль, в котором реализовано хорошее модульное управление.

В настоящее времяcommonjsОн широко используется в следующих случаях:

  • NodeЭто репрезентативная реализация CommonJS на стороне сервера;
  • BrowserifyЭто реализация CommonJS в браузере;
  • webpackПоддержка и преобразование CommonJS средствами упаковки, то есть фронтенд-приложения также могут разрабатываться с использованием CommonJS перед компиляцией.

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

В спецификации использования есть несколько примечательных особенностей.

  • существуетcommonjsКаждый файл js — это отдельный модуль, мы можем назвать его модулем;
  • Этот модуль содержит основные переменные спецификации CommonJS: exports, module.exports, require;
  • exports и module.exports могут отвечать за экспорт контента в модули;
  • Функция require может помочь нам импортировать контент из других модулей (пользовательские модули, системные модули, модули сторонних библиотек);

Первый опыт использования commonjs

экспорт: Попробуем экспортировать модуль, который:

hello.jsсередина

let name = '《React进阶实践指南》'
module.exports = function sayName  (){
    return name
}

импорт: Следующий простой импорт:

home.js

const sayName = require('./hello.js')
module.exports = function say(){
    return {
        name:sayName(),
        author:'我不是外星人'
    }
}

Выше приведена простейшая реализация Commonjs, поэтому возникают две проблемы:

  • Как решить проблему переменного загрязнения.
  • Как работают module.exports, exports и require? Что это значит?

Принцип реализации Commonjs

Во-первых, наличие каждого из модулей из указанного документа, которыйmodule,exports,requireТри переменные, однако эти три переменные не определены, но мы можем использовать их непосредственно в каждом модуле js в соответствии со спецификацией Commonjs. также существует в nodejs__filenameа также__dirnameПеременная.

Что означает каждая из перечисленных выше переменных:

  • moduleЗапишите текущую информацию о модуле.
  • requireСпособ импорта модуля.
  • exportsСвойства, экспортированные текущим модулем

В процессе компиляции реальный Commonjs оборачивает блоки кода js в начале и в конце.В качестве примера возьмем вышеприведенный home.js🌰, после обёртывания он выглядит следующим образом:

(function(exports,require,module,__filename,__dirname){
   const sayName = require('./hello.js')
    module.exports = function say(){
        return {
            name:sayName(),
            author:'我不是外星人'
        }
    }
})
  • В модуле под спецификацию Commonjs будет сформирована функция-обертка, а написанный нами код будет использоваться как контекст выполнения функции-обертки.require,exports,moduleПо сути, он передается функции-оболочке в виде формальных параметров.

Так как же по сути выглядит функция-оболочка?

function wrapper (script) {
    return '(function (exports, require, module, __filename, __dirname) {' + 
        script +
     '\n})'
}

Выполнение функции-обертки.

const modulefunction = wrapper(`
  const sayName = require('./hello.js')
    module.exports = function say(){
        return {
            name:sayName(),
            author:'我不是外星人'
        }
    }
`)
  • Вышеприведенное имитирует функцию функции-оболочки, сценарий — это то, что мы написали в модуле js, а окончательный результат — это функция, обернутая, как указано выше. Конечно, эта функция пока является строкой.
 runInThisContext(modulefunction)(module.exports, require, module, __filename, __dirname)
  • Когда модуль загружен, он будет выполнен через runInThisContext (что можно понимать как eval )modulefunction, входящийrequire,exports,moduleи другие параметры. В итоге написанный нами файл nodejs выполняется вот так.

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

2 требуется процесс загрузки файла

Выше был упомянут общий принцип реализации спецификации commonjs, далее разберем его.requireКак загружать файлы.

Мы по-прежнему используем nodejs в качестве эталона, например следующий фрагмент кода:

const fs =      require('fs')      // ①核心模块
const sayName = require('./hello.js')  //② 文件模块
const crypto =  require('crypto-js')   // ③第三方自定义模块

Во фрагменте кода выше:

  • ① Это основной модуль в нижней части nodejs.
  • ② Файловые модули, написанные для нас, такие как вышеsayName
  • ③ Для сторонних пользовательских модулей, которые мы загрузили через npm, таких какcrypto-js.

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

принцип требуемого идентификатора нагрузки

Сначала давайте посмотрим на nodejsПринципы обработки идентификаторов в .

  • Во-первых, в качестве nodejs будут использоваться такие идентификаторы, как fs, http, path и т. д.Основной модуль.
  • ./а также../как относительный путьфайловый модуль,/как абсолютный путьфайловый модуль.
  • Модули, которые не находятся в форме пути и не являются основными модулями, будут использоваться какпользовательский модуль.

Обработка основных модулей:

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

Обработка модуля файла в форме пути:

уже./,../а также/Начиная с идентификатора, который рассматривается как файловый модуль.require()Метод преобразует путь в реальный путь и использует реальный путь в качестве индекса для кэширования скомпилированного результата, что будет быстрее при загрузке во второй раз. Что касаетсякак кэшироватьиз? Мы вернемся к этому позже.

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

  • в текущем каталогеnode_modulesПоиск по каталогу.
  • Если нет, то в родительском каталогеnode_modulesНайдите, нет ли родительского каталога в родительском каталогеnode_modulesНайти в.
  • Рекурсивно пройти путь до корневого каталогаnode_modulesсодержание.
  • В процессе поиска он найдетpackage.jsonфайл, на который указывает основной атрибут, если нетpackage.json, в среде узла можно найти здесьindex.js,index.json,index.node.

Блок-схема поиска выглядит следующим образом:

4.jpg

3 требуют введения модуля и обработки

Модули CommonJS синхронно загружают и выполняют файлы модулей, а модули CommonJS анализируют зависимости модулей на этапе выполнения, используяобход в глубину(обход в глубину), порядок выполнения: родительский -> дочерний -> родительский;

Для очистки файлов требуется процесс введения. Затем мы даем еще один пример, обратите внимание на детали здесь:

  • a.js文件
const getMes = require('./b')
console.log('我是 a 文件')
exports.say = function(){
    const message = getMes()
    console.log(message)
}
  • b.jsдокумент
const say = require('./a')
const  object = {
   name:'《React进阶实践指南》',
   author:'我不是外星人'
}
console.log('我是 b 文件')
module.exports = function(){
    return object
}
  • основной файлmain.js
const a = require('./a')
const b = require('./b')

console.log('node 入口文件')

Затем войдите в терминалnode main.jsбегатьmain.js, эффект следующий:

5.jpg

По вышеприведенным результатам работы можно сделать следующие выводы:

  • main.jsа такжеa.jsмодули ссылаютсяb.jsмодуль, ноb.jsМодуль выполняется только один раз.
  • a.jsмодули иb.jsМодули ссылаются друг на друга, но циклические ссылки не создаются.
  • Порядок выполнения: родительский -> дочерний -> родительский;

ТакCommon.jsЭто как регулировать эффекты, описанные выше?

Принцип требуемой загрузки

Прежде всего, для того, чтобы прояснить две вышеуказанные проблемы. Нам нужно понять два чувства, т.moduleа такжеModule.

module: В Node каждый файл js представляет собой модуль.Помимо экспорта и другой информации, сохраняемой в модуле, существует такжеloadedУказывает, загружен ли модуль.

  • дляfalseУказывает, что он не был загружен;
  • дляtrueзначит загружен

Module: Возьмем в качестве примера nodejs, после того как вся система запустится, она будет использоватьModuleКэшировать информацию о загрузке каждого модуля.

Исходный код require выглядит так:

 // id 为路径标识符
function require(id) {
   /* 查找  Module 上有没有已经加载的 js  对象*/
   const  cachedModule = Module._cache[id]
   
   /* 如果已经加载了那么直接取走缓存的 exports 对象  */
  if(cachedModule){
    return cachedModule.exports
  }
 
  /* 创建当前模块的 module  */
  const module = { exports: {} ,loaded: false , ...}

  /* 将 module 缓存到  Module 的缓存属性中,路径标识符作为 id */  
  Module._cache[id] = module
  /* 加载文件 */
  runInThisContext(wrapper('module.exports = "123"'))(module.exports, require, module, __filename, __dirname)
  /* 加载完成 *//
  module.loaded = true 
  /* 返回值 */
  return module.exports
}

Из вышесказанного делаем вывод один разrequireОбщий процесс выглядит следующим образом;

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

  • Если кэша нет, объект модуля будет создан, кэширован в модуле, затем выполнен файл, загружен файл, установлен для свойства load значение true и возвращен объект module.exports. На этом процесс загрузки модуля завершен.

  • Экспорт модуля является возвратом этой переменной.По сути, так же, как и присваивание a = b, базовый тип экспортирует значение, а ссылочный тип экспортирует ссылочный адрес.

  • exports и module.exports содержат одну и ту же ссылку, потому что последним экспортом является module.exports, поэтому присвоение значения exports приведет к тому, что операция exports больше не будет ссылкой на module.exports.

требуют, чтобы избежать повторной загрузки

Сверху мы можем получить его напрямую, как избежать дублирования загрузки, первые файлы нагрузкиmoduleбудет кэшироваться вModuleНапример, модуль должен был ввести модуль, если другой модуль снова ссылается на a, он будет напрямую читать модуль кэшированных значений, поэтому нет необходимости снова выполнять модуль.

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

require избегает циклических ссылок

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

  • ① Выполнить первымnode main.js, затем начните выполнение первой строкиrequire(a.js);
  • ② Затем первый судьяa.jsКеша нет, потому что нет кеша, сначала добавьте кеш, а потом выполните файл a.js (Следует отметить, что сначала добавляется кеш, а затем выполняется содержимое модуля.);
  • ③ Выполните первую строку в a.js, ссылаясь на b.js.
  • ④ Затем судитеb.jsКеша нет, потому что нет кеша, поэтому добавьте кеш, а затем выполните файл b.js.
  • ⑤ b.js снова выполняет первую строку, циклическая ссылкаrequire(a.js)В это время в кеш добавлен a.js, и значение считывается напрямую. печатать дальшеconsole.log('我是 b 文件'), метод экспорта.
  • ⑥ После выполнения b.js вернитесь к файлу a.js и распечатайтеconsole.log('我是 a 文件'), метод экспорта.
  • ⑦ Наконец-то вернулсяmain.js,Распечататьconsole.log('node 入口文件')Завершите этот процесс.

Но здесь мы должны обратить внимание на проблему:

  • Как и в ⑤ выше, когда модуль b.js выполняется, поскольку a.js не был экспортированsayметод, поэтому в контексте синхронизации b.js, скажем, не может быть получен.

Я описываю вышеописанный процесс блок-схемой:

15.jpg

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

const say = require('./a')
const  object = {
   name:'《React进阶实践指南》',
   author:'我不是外星人'
}
console.log('我是 b 文件')
console.log('打印 a 模块' , say)

setTimeout(()=>{
    console.log('异步打印 a 模块' , say)
},0)

module.exports = function(){
    return object
}

распечатать результат:

6.jpg

  • Первый отпечаток say — пустой объект.
  • Во второй раз, когда я печатаю, скажем, я вижу методы, экспортированные b.js.

Итак, как получить сказать, есть два пути:

  • Одним из них является динамическая загрузка a.js, о которой мы вскоре поговорим.
  • Во-вторых - загрузить его асинхронно, как указано выше.

Мы заметили, что A.js используетсяexports.sayЕсли a.js использует module.exports, результат будет другим. Что касается разницы и почему? Я доберусь до этого дальше.

4 требуют динамической загрузки

Мы сказали вышеrequireНайдите файлы и загрузите процессы. СледующийcommonjsЕще одна особенность require по спецификации -динамическая нагрузка.

require может динамически загружать модули в любом контексте. Мои модификации вышеуказанного a.js.

a.js:

console.log('我是 a 文件')
exports.say = function(){
    const getMes = require('./b')
    const message = getMes()
    console.log(message)
}

main.js:

const a = require('./a')
a.say()
  • Как и выше, в функции say модуля a.js используйте require для динамической загрузки модуля b.js. Затем выполните метод say, который запускает модуль a.js в main.js.

Результат печати следующий:

7.jpg

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

5 экспортов и module.exports

системный анализ завершенrequire, а потом анализируем,exportsа такжеmodule.exports, сначала посмотрите на использование двух.

экспорт использует

Первый способ: экспорт a.js

exports.name = `《React进阶实践指南》`
exports.author = `我不是外星人`
exports.say = function (){
    console.log(666)
}

Цитировать

const a = require('./a')
console.log(a)

распечатать результат:

8.jpg

  • exports — это объект, передаваемый в текущий модуль, который, по сути,module.exports.

Вопрос. Почему нельзя напрямую присвоить объект exports={}?Например, мы будемa.jsнемного отремонтировать:

exports={
    name:'《React进阶实践指南》',
    author:'我不是外星人',
    say(){
        console.log(666)
    }
}

распечатать результат:

9.jpg

В идеале пропущено exports = {}Прямое назначение, нет необходимостиexports.a = xxxКаждое свойство, но, как мы видели выше, это не работает таким образом. Почему это происходит? На самом деле это определяется характеристиками самого js.

Благодаря приведенному выше объяснению мы все знаем, что exports, module и require передаются в модуль js как формальные параметры. мы напрямую exports = {}Изменение экспортов эквивалентно переназначению формального параметра, тогда будет переназначена копия, но ссылка на исходный формальный параметр не будет. Возьмем простой пример

function wrap (myExports){
    myExports={
       name:'我不是外星人'
   }
}

let myExports = {
    name:'alien'
}
wrap(myExports)
console.log(myExports)

Распечатать:

10.jpg

Мы ожидаем изменить myExports, но ничего не работает.

ПредположениеwrapЭто функция-оболочка согласно спецификации Commonjs, а наш js-код — это содержимое внутри функции-оболочки. Когда мы передаем объект myExports, но назначаем его напрямую myExports = { name:'我不是外星人' }Не имеет эффекта, эквивалентного внутреннему переобъявлениюmyExportsИ разорвал отношения myExports с внешним миром. Так это объясняет, почему нетexports={...} прямое назначение.

Тогда легко решить вышеописанное, просто напишите в функции как exports.name.

function wrap (myExports){
    myExports.name='我不是外星人'
}

Распечатать:

11.jpg

модуль.экспорт использовать

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

module.exports ={
    name:'《React进阶实践指南》',
    author:'我不是外星人',
    say(){
        console.log(666)
    }
}

module.exports также может экспортировать функцию или класс сам по себе. Например:

module.exports = function (){
    // ...
}

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

exports.name = 'alien' // 此时 exports.name 是无效的
module.exports ={
    name:'《React进阶实践指南》',
    author:'我不是外星人',
    say(){
        console.log(666)
    }
}
  • В приведенных выше случаях имя exports.name недопустимо, оно будетmodule.exportsпокрытие.

Q & A

1 Так вот вопрос?Так как естьexports, почему это вышло сноваmodule.exports ?

Ответ: Если мы не хотим экспортировать объект в commonjs, а экспортируем только одинкласс или функцияили другие свойства, тоmodule.exportsЭто удобнее, как мы знаем вышеexportsБудет инициализирован как объект, то есть мы можем только привязать свойства к объекту, а можем передатьmodule.exportsПользовательский экспорт других типов элементов за пределы объекта.

let a = 1
module.exports = a // 导出函数

module.exports = [1,2,3] // 导出数组

module.exports = function(){} //导出方法

2 сexportsв сравнении с,module.exportsЧто случилось?

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

Четыре модуля Es

NodejsзаимствованныйCommonjsМодульный изES6Начинать,JavaScriptВ истинном смысле он имеет свою собственную модульную спецификацию.

Поколение Es Module имеет много преимуществ, таких как:

  • с помощьюEs ModuleПреимущества статического импорта и экспорта, реализованныеtree shaking.
  • Es ModuleДостаточно хорошоimport()Метод ленивой загрузки реализует разделение кода.

существуетEs ModuleС участиемexportиспользуется для экспорта модулей,importИспользуется для импорта модулей. ноexportСотрудничатьimportКомбинаций будет много, и мы будем разбирать их по порядку.

экспорт экспорт и импорт импорт

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

экспорт экспорт в обычном режиме, импорт импорт

Модуль экспорта:a.js

const name = '《React进阶实践指南》' 
const author = '我不是外星人'
export { name, author }
export const say = function (){
    console.log('hello , world')
}

модуль импорта:main.js

// name , author , say 对应 a.js 中的  name , author , say
import { name , author , say } from './a.js'
  • export { }, связанный с именем переменной, названный export.
  • импортировать {} из 'модуля', импортироватьmoduleИменованный экспорт модуля, как указано выше./a.js
  • В этом случае имя переменной внутри import { } должно точно совпадать с export { } .

экспорт по умолчанию экспорт

Модуль экспорта:a.js

const name = '《React进阶实践指南》'
const author = '我不是外星人'
const say = function (){
    console.log('hello , world')
}
export default {
    name,
    author,
    say
} 

модуль импорта:main.js

import mes from './a.js'
console.log(mes) //{ name: '《React进阶实践指南》',author:'我不是外星人', say:Function }
  • export default anythingИмпортировать экспорт модуля по умолчанию.anythingМожет быть функцией, методом свойства или объектом.
  • Для модулей, которые импортируют экспорт по умолчанию,import anyName from 'module', anyName может быть произвольным именем.

Гибридный импорт|экспорт

Модули ES6 могут импортировать несколько свойств, используя экспорт по умолчанию и экспорт .

Модуль экспорта:a.js

export const name = '《React进阶实践指南》'
export const author = '我不是外星人'

export default  function say (){
    console.log('hello , world')
}

модуль импорта:main.jsСуществует несколько способов импорта:

Первый:

import theSay , { name, author as  bookAuthor } from './a.js'
console.log(
    theSay,     // ƒ say() {console.log('hello , world') }
    name,       // "《React进阶实践指南》"
    bookAuthor  // "我不是外星人"
)

Второй:

import theSay, * as mes from './a'
console.log(
    theSay, // ƒ say() { console.log('hello , world') }
    mes // { name:'《React进阶实践指南》' , author: "我不是外星人" ,default:  ƒ say() { console.log('hello , world') } }
)
  • Экспортированные свойства объединяются вmesНедвижимость на,exportимпортируется в соответствующий атрибут,export defaultСодержимое экспорта привязано кdefaultхарактеристики.theSayтакже может использоваться какexport defaultЭкспорт свойств.

Импортировать повторяющееся имя

import {  name as bookName , say,  author as bookAuthor  } from 'module'
console.log( bookName , bookAuthor , say ) //《React进阶实践指南》 我不是外星人
  • Импортируйте имя из модуля модуля и переименуйте его в bookName , импортируйте автора из модуля модуля и переименуйте его в bookAuthor . Затем под текущим модулем используйте переименованное имя.

перенаправить экспорт

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

export * from 'module' // 第一种方式
export { name, author, ..., say } from 'module' // 第二种方式
export {   name as bookName ,  author as bookAuthor , ..., say } from 'module' //第三种方式
  • Первый способ: перенаправить все атрибуты экспорта в модуль экспорта, но не включатьmoduleвнутриdefaultАтрибуты.
  • Второй способ: импортировать имя, автора, скажем, из модуля и экспортировать их с тем же именем атрибута.
  • Третий способ: импортировать имя из модуля, экспортировать его как bookName, импортировать автора из модуля, экспортировать его как bookAuthor и экспортировать, как обычно.

Не нужно импортировать модуль, просто запустите модуль

import 'module' 
  • Модуль Execute не экспортирует значение. Вызывается несколько раз.moduleТолько запустить один раз.

динамический импорт

const promise = import('module')
  • import('module') , динамический импорт возвращаетPromise. Для поддержки этого метода необходимо выполнить соответствующую обработку конфигурации в webpack.

Возможности модуля ES6

Далее мы сосредоточимся на анализе некоторых важных функций модуля ES6.

1 Статический синтаксис

Введение и экспорт модулей ES6 статичны,importбудет автоматически переведен на верхний уровень кода,import , exportНе может быть помещен в область блока или условные операторы.

🙅 Неверное написание один:

function say(){
  import name from './a.js'  
  export const author = '我不是外星人'
}

🙅 Неправильное написание 2:

isexport &&  export const  name = '《React进阶实践指南》'

Этот статический синтаксис определяет взаимосвязь между импортом и экспортом в процессе компиляции, поэтому более удобно находить зависимости и удобнееtree shaking(дрожание дерева), вы можете использовать инструмент lint для проверки зависимостей модулей, а также выполнять статическую проверку типов при импорте и экспорте, а также информацию о типах.

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

🙅 Неправильное написание три:

import 'defaultExport' from 'module'

let name = 'Export'
import 'default' + name from 'module'

2 Характеристики исполнения

Модуль ES6, как и Common.js, сохранит статические свойства для того же файла js.

Но он отличается от common.js.CommonJS Модули загружают и выполняют файлы модулей синхронно, модули ES6 загружают и выполняют файлы модулей заранее, модули ES6 анализируют зависимости модулей на этапе предварительной обработки и выполняют модули на этапе выполнения.На обоих этапах используется обход в глубину, а порядок выполнения является дочерним. -> родитель.

Чтобы убедиться в этом, взгляните на следующую демонстрацию.

main.js

console.log('main.js开始执行')
import say from './a'
import say1 from './b'
console.log('main.js执行完毕')

a.js

import b from './b'
console.log('a模块加载')
export default  function say (){
    console.log('hello , world')
}

b.js

console.log('b模块加载')
export default function sayhello(){
    console.log('hello,world')
}
  • main.jsа такжеa.jsкотируютсяb.jsмодуль, но модуль b также загружается только один раз.
  • Порядок выполнения дочерний -> родительский

Эффект следующий:

12.jpg

3 Экспорт привязок

Невозможно изменить импортированные свойства

a.js

export let num = 1
export const addNumber = ()=>{
    num++
}

main.jsсередина

import {  num , addNumber } from './a'
num = 2

Если вы измените его напрямую, будет сообщено об ошибке. Следующим образом:

14.jpg привязка свойства

Так что вы можетеmain.jsМодифицирован таким образом.

import {  num , addNumber } from './a'

console.log(num) // num = 1
addNumber()
console.log(num) // num = 2
  • Импорт свойства num выше связан.

Ниже приводится сводка атрибута импорта:

  • Модули, импортированные с помощью импорта, выполняются в строгом режиме.
  • Переменные, импортированные с помощью import, доступны только для чтения, по понятным причинам оформлены как const по умолчанию и им нельзя присвоить значение.
  • Переменная, импортируемая с помощью импорта, связана/ссылается на исходную переменную, что можно понять, поскольку переменная, импортированная с помощью импорта, передается по ссылке независимо от того, является ли она базовым типом или нет.

import() динамический импорт

import()вернутьPromiseобъект, возвращенныйPromiseВ обратном вызове then Success вы можете получить информацию об успешной загрузке модуля. Давайте кратко рассмотримimport()как это используется.

main.js

setTimeout(() => {
    const result  = import('./b')
    result.then(res=>{
        console.log(res)
    })
}, 0);

b.js

export const name ='alien'
export default function sayhello(){
    console.log('hello,world')
}

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

13.jpg

Из распечатанных результатов видно, чтоimport()основные характеристики.

  • import()Может использоваться динамически, загружая модули.
  • import()вернутьPromise, информация, соответствующая модулю, может быть получена при успешном обратном вызове then.nameВ соответствии с атрибутом имени,defaultпредставлятьexport default.__esModuleЭто идентификатор модуля es.

import() может что-то сделать

динамическая нагрузка

  • первыйimport()Динамически загружать некоторый контент, который можно поместить в контекст выполнения условного оператора или функции.
if(isRequire){
    const result  = import('./b')
}

ленивая загрузка

  • import()Ленивая загрузка может быть реализована, например, маршрутизация ленивой загрузки в vue;
[
   {
        path: 'home',
        name: '首页',
        component: ()=> import('./home') ,
   },
]

Динамическая загрузка в React

const LazyComponent =  React.lazy(()=>import('./text'))
class index extends React.Component{   
    render(){
        return <React.Suspense fallback={ <div className="icon"><SyncOutlinespin/></div> } >
               <LazyComponent />
           </React.Suspense>
    }

React.lazyа такжеSuspenseПри совместном использовании это может иметь эффект динамической загрузки компонентов.React.lazyПринимает функцию, которую необходимо вызвать динамическиimport().

import()Этот эффект загрузки может быть легко достигнутразделение кода. Избегайте одновременной загрузки большого количества js-файлов, что приводит к долгой загрузке белого экрана в первый раз.

реализация встряхивания дерева

Реализация Tree Shaking в Webpack заключается в максимально возможном удалении неиспользуемого кода, кода, который был импортирован, но фактически не используется. Например, следующие сценарии:

a.js:

export let num = 1
export const addNumber = ()=>{
    num++
}
export const delNumber = ()=>{
    num--
}

main.js:

import {  addNumber } from './a'
addNumber()
  • как указано вышеa.jsраскрывает два метода вaddNumberа такжеdelNumber, но во всем приложении толькоaddNumber, то при сборке и упаковке,delNumberБудет использоваться как нессылочный метод и не будет упакован.

Резюме модуля Five Commonjs и Es

Далее через весь текст расскажите оCommonjsа такжеEs Moduleхарактеристики.

Резюме Commonjs

CommonjsХарактеристики следующие:

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

резюме модуля es

Es moduleХарактеристики следующие:

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

Шесть резюме

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

Если эта статья была вам полезна, я надеюсь передать ее автору.Нравится + ИзбранноеТаким образом, автору предлагается продолжать создавать хардкорные статьи. Вы также можете подписаться на официальный аккаунт автораСовместное использование внешнего интерфейсаПервый раз, чтобы нажать интерфейс хороший текст.

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

После того, как буклет закончен, это конец и отправная точка~

Буклет Nuggets для систематического изучения React — «Расширенное практическое руководство по React» закончен~

1.jpg

Это конечная точка и отправная точка.Содержимое буклета будет постоянно обновляться.С обновлением версии React он будет продолжать поддерживаться, и главы будут постоянно обновляться~

раскрывается заранее,В буклете будут добавлены:React contextОсновная часть, содержание дополняется главой 8.

Скидка 30% на несколько брошюр с кодом купонаF3Z1VXtv, первый пришел первый обслужен~