Модули Es6 позволяют нам структурировать наш код и совместно использовать функцию, переменную и классы внутри модуля с другими файлами или модулями в проекте.

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

Давайте создадим 2 файла javascript: somefile и main. В somefile.js мы создаем 2 переменные и присваиваем им значения, консольируем их в main.js и добавляем эти файлы в файл index.html в правильном порядке.

// somefile.js,
let firstVariable = 10;
let secondVariable = 20;
// main.js
console.log(firstVariable,secondVariable);
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
   <script src="somefile.js"></script> 
   <script src="main.js"></script>
</body>
</html>

Если вы проверите свою консоль в браузере, вы получите 10 20, как и ожидалось.

На вкладке сети для каждого файла JS он должен вызывать запрос на получение

Теперь все выглядит нормально, так как есть только 2 файла JS, но при создании большого проекта может быть 5 файлов, поэтому нам нужно добавить 5 тегов script в наш index.html, и он загружает каждый файл JS отдельно, что увеличивает время загрузки приложение

Можно сказать, что если у него проблема с несколькими JS-файлами, почему мы не можем добавить все в один JS-файл во время разработки?

Мы не можем этого сделать, потому что:

  1. Снижает читабельность кода
  2. Становится трудно поддерживать код

Но если мы изменим порядок, это выдаст ошибку ссылки, поскольку мы пытаемся утешить их перед их определением.

<script src="main.js"></script> 
<script src="somefile.js"></script>

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

Используя модули ES6, мы можем решить эту проблему, но прежде чем переходить к ним, мы должны иметь представление о том, какие подходы предшествуют модулям ES6.

Эволюция модулей Javascript

  • Встроенный скрипт
  • Внешние теги сценария
  • Выявление шаблона модуля
  • CommonJS
  • Определение асинхронного модуля (AMD) и RequireJS
  • Сборщики модулей — Browserify, Webpack
  • Синтаксис модуля ES6

Встроенный скрипт

При использовании встроенного сценария мы добавляем весь наш javascript в тег ‹script›‹/script› в файле HTML, как показано ниже.

У него 3 основные проблемы:

1. Отсутствие возможности повторного использования кода: мы не можем повторно использовать какой-либо код javascript в любом другом HTML-файле, мы должны написать его снова.

2. Отсутствие разрешения зависимостей: порядок функций и переменных следует учитывать при написании кода javascript, как вы видите в приведенном выше примере, если мы вызовем функцию displayResult() до объявления temp, она даст NaN

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

Внешние теги сценария

Он разделит ваш код на несколько файлов, ссылаясь на внешний файл JS, используя атрибут src в теге script.

Он решает проблему отсутствия повторного использования кода, но все еще имеет оставшиеся 2 проблемы:

1. Отсутствие разрешения зависимостей: add.js всегда должен стоять перед display.js, иначе он не сможет получить значение temp и отобразить NaN в консоли.

2. Загрязнение глобального пространства имен: все переменные и функции остаются доступными в глобальной области видимости.

Выявление шаблона модуля

Он использует IIFE (немедленно вызываемое функциональное выражение: (function(){})();) для переноса функции вашего модуля, чтобы один объект содержал все методы и значения и открывал этот единственный объект для глобальной области видимости.

Используя этот подход, вы можете один объект myObj содержать все переменные и функции, а также сделать функцию displayResult() общедоступной и доступной только с myObj и сделать другие функции и переменные закрытыми

Это уменьшает загрязнение глобального пространства имен, поскольку мы добавляем только 1 объект в глобальное пространство имен, но, тем не менее, у нас есть проблема разрешения зависимостей, как в нашем index.html, поскольку app.js всегда должен стоять перед main.js, иначе он не будет работать.

ОбщиеJS

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

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

Теперь, если мы запустим файл main.js в nodejs с помощью команды node main.js, он отобразит 20 в консоли, как и ожидалось. Это решает проблему разрешения зависимостей, поскольку наш JS-код импортируется и экспортируется как модули, поэтому мы можем импортировать любую функцию, экспортированную module.exports, из любого JS-файла, используя require, также мы не добавляли никаких дополнительных объектов в глобальное пространство имен, но это работает только на nodejs, а не в браузерах.

Определение асинхронного модуля (AMD) и RequireJS

CommonJS создала AMD для передачи синтаксиса модуля с сервера в браузер. Чтобы использовать его в браузере, требуется requireJS, который загружает модуль асинхронно. Используя синтаксис AMD, модули и их зависимости можно загружать в браузер асинхронно.

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

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

Он решает все 3 основные проблемы, но его синтаксис слишком многословен, а список зависимостей в массиве в define должен совпадать со списком аргументов в функции.

Чтобы заставить AMD работать, мы должны добавить атрибут data-main в тег скрипта в index.html, где мы указываем основной файл, из которого начинается выполнение, а также мы должны создать файл require.js и загрузить последнюю версию requireJS из https:/ /requirejs.org/docs/download.html и вставьте его в наш файл require.js и используйте его в теге скрипта, как показано выше.

Сборщики модулей — Browserify, Webpack

Просмотреть:

Это сборщик модулей, который объединяет несколько JS-файлов, которые импортируют модули с помощью функции require, в один JS-файл в вашем приложении, чтобы обслуживать его в браузере с помощью одного тега <script>. По сути, он разработан для использования синтаксиса CommonJS в браузере.

Веб-пакет:

Это также сборщик модулей, такой как browserify, но он более гибкий и предлагает достаточно возможностей из коробки, чем browserify. Мы узнаем больше о webpack позже в этом блоге.

Хотя эти сборщики модулей решают все 3 основные проблемы и имеют четко определенный синтаксис при написании кода модульным способом, но это внешние утилиты, которые мы должны добавить в наше приложение.

Синтаксис модуля ES6

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

Теперь мы знаем, что нам нужны модули ES6, чтобы:

  1. Сделайте код повторно используемым.
  2. Обработка разрешения зависимостей.
  3. Изолируйте код, чтобы избежать конфликтов имен в глобальном объекте окна.
  4. Управление кодом в нескольких файлах

Чтобы использовать модуль ES6, мы должны добавить атрибут type="module" в тег скрипта и связать основной файл, куда мы можем импортировать экспортированные модули.

В этом примере мы экспортируем firstVariable из somefile.js и импортируем в наш файл main.js, используя оператор импорта.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>ES6 modules</h1>
    <script type="module" src="main.js"></script> 
</body>
</html>
// somefile.js
export let firstVariable = 10;
// main.js
import  { firstVariable } from "./somefile.js"
console.log(firstVariable);

Для запуска модулей ES6 нам нужен сервер, поэтому мы можем использовать расширение живого сервера из Visual Studio Code, а в консоли браузера мы получим значение firstVariable как 10.

Есть видов экспорта

  • Именованный экспорт:
  • Экспорт по умолчанию
// somefile.js

// named export
export let firstVariable = 10;

export function usefulFunction() {
    console.log("this is a useful func")
}

// default export 
function defaultExport() {
    console.log("this is a default export")
}

export default defaultExport;
// main.js
import  { firstVariable,usefulFunction } from "./somefile.js"
import defaultExport from "./somefile.js"

console.log(firstVariable);

usefulFunction()
defaultExport()

Мы можем выполнить экспорт по умолчанию, написав «export default» перед вашей функцией или вашей переменной. Мы можем дать любое имя экспорту по умолчанию во время импорта, и его не нужно заключать в {}, и у нас может быть только один экспорт по умолчанию в файле, тогда как у нас может быть несколько именованных экспортов в одном файле, и он должен быть заключен в {}.

// If change default export name from defaultExport to abc
// it will still give same output in console
import  { firstVariable,usefulFunction } from "./somefile.js"
import abc from "./somefile.js"

console.log(firstVariable);

usefulFunction()
abc()

Если в 2 файлах есть функция с одинаковым именем, например полезная функция, и мы импортируем оба файла в main.js, мы можем переименовать любую из функций, используя ключевое слово as, как показано ниже, чтобы другой файл полезной функции не заменял какой-то файл полезной функции.

// anotherfile.js
function usefulFunction() {
    console.log("useful function from anotherfile");
}
export { usefulFunction }
// main.js
import defaultExport from "./somefile.js"
import {usefulFunction} from "./somefile.js"
import { usefulFunction as usefullFromAnother } from "./anotherfile.js";


defaultExport()
usefulFunction()
usefullFromAnother()

Мы можем импортировать все из somefile.js с помощью * и переименовать его как все и использовать все экспортированные функции и переменные этого файла.

// main.js
import * as everything from "./somefile.js"

console.log(everything);
everything.default()
everything.usefulFunction()

Вебпак

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

Прежде чем изучать реализацию webpack. мы должны четко понимать, почему существует необходимость в объединении?

Мы должны сделать комплектацию:

  • Чтобы создать один исполняемый файл
  • Для обслуживания одного файла JS по сети

Более фундаментальная причина заключается в том, что браузеры могут загружать только от 4 до 6 файлов за раз, и если у вас большое количество модулей, поставляемых с изображениями, файлы css, вам придется подождать, так как браузер может загрузить максимальное количество файлов, потому что у него есть несколько потоков и он будет ждать, пока один из них завершит загрузку, чтобы начать следующий, поэтому нам нужно связывание. Итак, после объединения у нас есть 1 гигантский файл, что хорошо для эффективной загрузки. Он позволяет предварительно загружать модули и может знать, что это все модули, которые вам нужны, и связывать их.

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

Когда доступна новая функция javascript, она доступна на языке, но браузерам требуется некоторое время для реализации этой функции, зависящей от языка. Иногда это реализовано в Chrome, Firefox не реализовал эту новую функцию, может она работает в Chrome и Firefox, но не в Safari. Это относится и к модулям, поэтому было время, когда мы полагались на внешние инструменты, такие как веб-пакеты.

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

Создайте проект npm, создав файл package.json. Добавьте -y, чтобы автоматически выбирать значения по умолчанию для файла package.json.

npm init -y

Установите пакет webpack, пакет webpack-cli. Добавьте -D или — save-dev, чтобы установить пакет как зависимость dev.

npm i -D webpack webpack-cli

Удалите тег скрипта с типом = «модуль» из index.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Webpack</h1>
</body>
</html>

Создайте папку src, переместите в нее файл somefile.js и создайте файл index.js.

// index.js
import { usefulFunction } from "./somefile"
usefulFunction()

Теперь структура папок должна выглядеть так

Создайте веб-пакет для объединения всех js-файлов, зависящих от index.js, поскольку веб-пакет по умолчанию использует index.js в качестве точки входа при связывании.

// Command to build webpack
npx webpack

Он создает папку dist и генерирует связанный код в файле main.js. Webpack по умолчанию создает выходной файл с именем main.js, как показано ниже.

(()=>{"use strict";console.log("this is a useful func")})();

В Webpack есть 2 типа режимов:

  • Режим разработки
  • Режим производства

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

Теперь создайте файл webpack.config.js, с помощью которого информация о конфигурации веб-пакета становится доступной для веб-клиента.

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

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

Мы импортируем модуль пути с помощью NodeJS (использует синтаксис CommonJS) и экспортируем ввод и вывод свойств, настроив путь и имя файла.

// webpack.config.js 
const path = require("path");

module.exports = {
    entry: "./src/main.js",
    output: {
        filename: "app.js",
        path: path.join(__dirname, "dist"),
    }
}

Создайте файл main.js, поскольку мы устанавливаем новую точку входа в конфигурации веб-пакета.

// main.js
import * as everything from "./somefile.js"

everything.default()

everything.usefulFunction()

Если мы снова собираем с помощью npx webpack, мы получаем связанный файл app.js

Используя webpack, мы больше не беспокоимся о порядке скриптов.

Webpack сжал все в один файл и избавился от всех пробелов. Этот процесс известен как минификация. Он также переименовывает некоторые переменные, чтобы имя переменной не занимало много места. Он выполняет такие оптимизации в производственном режиме.

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

Добавить режим разработки в webpack.config.js

const path = require("path");

module.exports = {
    mode: "development",
    entry: "./src/main.js",
    output: {
        filename: "app.js",
        path: path.join(__dirname, "dist"),
    }
}

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

Мы также можем собрать веб-пакет, добавив для него скрипт в package.json.

  "scripts": {
    "build": "webpack --mode production",
    "dev": "webpack --mode development"
  }

Мы можем запустить его, используя npm run build для производства и npm run dev для режима разработки для сборки.

Теперь переместите index.html в папку dist и добавьте этот скрипт в файл index.html, чтобы использовать связанный файл JS.

<script src="app.js"></script>

В консоли браузера мы видим main.js все используемые функции

Установите библиотеку lodash, импортируйте ее и создайте статью об элементе, используйте метод соединения из lodash и установите текст элемента как объединенное строковое значение и добавьте этот элемент в dom.

npm i lodash
// main.js 
import _ from "lodash"

const element = document.createElement("article")

element.textContent = _.join(["Hello","Webpack"]," ");

document.body.appendChild(element)

Если мы создадим его с помощью npm run build, вы увидите большой связанный файл. Но мы использовали только метод соединения, но он объединяет всю библиотеку lodash.

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

import join from "lodash/join"

const element = document.createElement("article")

element.textContent = join(["Hello","Webpack"]," ");

document.body.appendChild(element) 

если мы построим его сейчас,

CSS в Webpack

Мы можем импортировать css как модуль, используя webpack. Создайте style.css внутри папки src и создайте класс blue и blue для элемента в main.js.

/*style.css*/

.blue {
    color: royalblue
}
// main.js
import join from "lodash/join";
import "./style.css"

const element = document.createElement("article")

element.textContent = join(["Hello","Webpack"]," ");

element.classList.add("blue");

document.body.appendChild(element) 

Мы импортируем файл css в main.js, но вебпак не понимает, как загрузить этот файл. По умолчанию веб-пакет понимает только файлы javascript и json. Поэтому для этого нам нужны загрузчики, загрузчики позволяют веб-пакету понимать файлы других типов, такие как импорт текстовых файлов, звуковых файлов, файлов изображений, файлов svg.

npm i -D style-loader css-loader

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

Нам нужно добавить свойство модуля в webpack.config.js и добавить правила, например, всякий раз, когда веб-пакет получает файл css, он использует стиль и загрузчик css.

// webpack.config.js
const path = require("path");

module.exports = {
    mode: "development",
    entry: "./src/main.js",
    output: {
        filename: "app.js",
        path: path.join(__dirname, "dist"),
    },
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: ['style-loader', 'css-loader']
            }
        ]
    },
}

Теперь, если мы создадим его с помощью npm run build, он добавит css в наш связанный файл. Если мы отметим, что он применил стиль к нашему элементу

мини-css-экстракт-плагин

Как вы заметили на изображении выше, он добавляет css как встроенный css, используя тег стиля, а не как внешний файл css. Если нам нужно использовать внешний файл css, мы должны использовать плагин mini-css-extract-plugin. Этот плагин берет все css и генерирует объединенный оптимизированный css.

Плагины помогают создавать файл, копировать файл из одного места в другое и т. д.

npm i -D mini-css-extract-plugin

В webpack.config.js импортируйте модуль MiniCssExtractPlugin и используйте его в правилах css вместо загрузчика стилей, добавьте массив плагинов и создайте новый экземпляр MiniCssExtractPlugin().

// webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');


module.exports = {
    mode: "development",
    entry: "./src/main.js",
    output: {
        filename: "app.js",
        path: path.join(__dirname, "dist"),
    },
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin(),
    ],
}

Теперь создайте веб-пакет, он создаст main.css в папке dist, и мы должны связать этот файл css с нашим файлом index.html. Для этого нам нужно добавить ‹link href="main.css" rel="stylesheet"› в тег заголовка.

HTML-плагин веб-пакета

Мы должны вручную создать этот файл index.html и связать его с каждым связанным файлом javascript и файлом css. В webpack есть способ автоматизировать все эти вещи с помощью HtmlWebpackPlugin.

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

npm i -D html-webpack-plugin

В webpack.config.js импортируйте модуль HtmlWebpackPlugin и создайте новый экземпляр HtmlWebpackPlugin() и настройте свойства, такие как установка заголовка для связанного html-файла.

// webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");


module.exports = {
   mode: "development",
    entry: "./src/main.js",
    output: {
        filename: "app.js",
        path: path.join(__dirname, "dist"),
    },
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "Webpack demo"
        }),
         new MiniCssExtractPlugin(),
    ],
}

Теперь удалите файл index.html и снова соберите его. Он сгенерирует html-файл со ссылкой на связанные файлы js и css.

Мы также можем добавить HTML-файл шаблона в HtmlWebpackPlugin, добавив путь к шаблону в HtmlWebpackPlugin.

plugins: [
    new HtmlWebpackPlugin({
      title: 'Webpack App',
      template: 'src/template.html',
    }),
  ],

Добавьте теги h1 и p с текстом в файл template.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
    <h1>Template Heading</h1>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit</p>
</body>
</html>

Когда мы создадим его с помощью npm run build, он создаст index.html с содержимым из template.html и связан с файлом bundle.js и css.

Ниже показан файл шаблона, созданный веб-пакетом при сборке.

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Webpack demo</title>
<script defer="defer" src="app.js"></script>
<link href="main.css" rel="stylesheet">
</head>
<body>
<h1>Template Heading</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit</p>
</body>
</html>

SaSS в Webpack

Используя webpack, мы также можем загрузить файл sass и преобразовать его в файл CSS.

Установить sass и загрузчик sass

npm i -D sass-loader sass

Создайте style.scss и импортируйте его в main.js

$color: royalblue;
.blue {
  color: $color;
}
// main.js
import join from "lodash/join";
import "./style.scss"

const element = document.createElement("article")

element.textContent = join(["Hello","Webpack"]," ");

element.classList.add("blue");

document.body.appendChild(element) 

Добавить sass-loader в массив использования scss в конфигурации webpack

Change test regex to check for sass/scss files
test:/\.s[ac]ss$/i
// webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");


module.exports = {
   mode: "development",
    entry: "./src/main.js",
    output: {
        filename: "app.js",
        path: path.join(__dirname, "dist"),
    },
    module: {
        rules: [
            {
                test:/\.s[ac]ss$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader','sass-loader']
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "Webpack demo",
            template: 'src/template.html',
        }),
         new MiniCssExtractPlugin(),
    ],
}

Выполните команду сборки npm run и проверьте в браузере, она преобразует файл scss в css.

Исходные карты

Он обеспечивает отображение между исходным и преобразованным исходным кодом. Это помогает в отладке кода. Например, на изображении выше класс .blue показывает строку № 1 в файле css, но в фактическом файле scss, но это другой номер строки, поэтому исходная карта показывает, что это файл scss с точным номером строки.

Чтобы использовать его, добавьте devtool : ‘source-map’ в webpack.config.js.

// webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
   mode: "development",
    entry: "./src/main.js",
    output: {
        filename: "app.js",
        path: path.join(__dirname, "dist"),
    },
    module: {
        rules: [
            {
                test:/\.s[ac]ss$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader','sass-loader']
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "Webpack demo",
            template: 'src/template.html',
        }),
         new MiniCssExtractPlugin(),
    ],
    devtool : 'source-map'
}

Выполните команду сборки npm run, и в папке dist пакета мы заметим, что она создает дополнительные файлы .map вместе с файлами пакетов.

Теперь он показывает точную строку no в scss для синего класса.

Точки входа

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

 entry: {
    main: "./src/main.js",
    some: "./src/somefile.js",
  },

Чтобы использовать пользовательское имя для точек входа, мы должны изменить конфигурацию в выходном объекте, добавив [имя] в имя файла. Также нет необходимости добавлять .bundle перед .js, мы просто добавляем, чтобы различать файлы в папке dist.

    output: {
        filename: "[name].bundle.js",
        path: path.join(__dirname, "dist"),
    },

Когда вы строите, он создаст файл пакета в соответствии с именем файла записи, которое мы укажем, например, в этом случае он создаст 2 связанных файла main.bundle.js и some.bundle.js

Каждый раз, когда мы связываем, он не удаляет предыдущий связанный файл, который больше не нужен. Чтобы очистить нашу сборку, чтобы она сохранила пригодный для использования пакетный файл, укажите в выводе clean true

output: {
        filename: "[name].bundle.js",
        path: path.join(__dirname, "dist"),
        clean:true
    },

Сервер разработки Webpack

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

Это позволяет запускать локальный сервер и использовать веб-пакет для размещения всех файлов в нашем проекте.

Установите сервер разработки веб-пакета и добавьте devServer в конфигурацию веб-пакета.

npm i -D webpack-dev-server
  devServer: {
        static: "./dist",
        port: 3000,
        open:true
    },

В package.json добавьте этот скрипт для обслуживания веб-пакета на локальном хосте: 3000.

"start": "webpack serve"

Выполните команду npm start для запуска сервера

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

const button = document.createElement("button");
button.textContent = "Click button";
button.addEventListener("click", () => {
    console.log("button clicked");
})
document.body.appendChild(button)

Но мы не можем найти этот код в нашем связанном файле, так как сервер разработки webpack не записывает в файловую систему, значит, он собирается и показывается в браузере, но вы не можете найти его в папке dist.

Сотрясение дерева

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

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

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

Экспортировать неиспользуемую функцию FN, но мы ее не импортировали

// somefile.js

export function unusedFN() {
    console.log("unused");
}


export function usefulFunction() {
    console.log("this is a useful func")
}
// main.js

import { usefulFunction } from "./somefile.js";

usefulFunction()

Когда мы собираем его с помощью npm run build, он запускается в производственном режиме и добавляет полезную функцию в пакет, но не добавляет unusedFN, поскольку в производственном режиме веб-пакет по умолчанию выполняет встряхивание дерева, поэтому он игнорирует unusedFN

Но когда мы создаем его с помощью npm run dev, он запускается в режиме разработки и добавляет в пакет как useFunction, так и unusedFN, а также вызывает обе функции, как показано в гармонии экспорта.

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

 optimization: {
        usedExports: true
    }

Выполните команду npm run dev

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

Модули активов

Чтобы использовать файлы активов в веб-пакете, такие как изображение svg, мы должны настроить веб-пакет и добавить правило для изображений, а также использовать модуль актива/ресурса. Также добавьте assetsModuleFilename:’[name][ext]’ в выходной объект, чтобы назвать связанное изображение таким же, как исходное изображение.

// webpack.config.js
const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");


module.exports = {
    mode: "development",
    entry: {
       main: "./src/main.js",
    },
    output: {
        filename: "[name].bundle.js",
        path: path.join(__dirname, "dist"),
        clean: true,
        assetModuleFilename:'[name][ext]'
    },
    module: {
        rules: [
            {
                test:/\.s[ac]ss$/i,
                use: [MiniCssExtractPlugin.loader,'css-loader','sass-loader']
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif)$/i,
                type: 'asset/resource',
              },
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "Webpack demo",
            template: 'src/template.html',
        }),
        new MiniCssExtractPlugin()
    ],
    devServer: {
        static: "./dist",
        port: 3000,
    },
    optimization: {
        usedExports: true
    }
}

Добавьте тег img с id="reactImg" в template.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
    <h1>Template Heading</h1>
    <img id="reactImg" alt="" />
</body>
</html>

Создайте папку с ресурсами, добавьте файл react-icon.svg и импортируйте его в main.js.

// main.js
import reactIcon from './assets/react-icon.svg'

const reactImg = document.getElementById('reactImg')
reactImg.src = reactIcon

Выполните команду сборки и запустите сервер

npm run build
npm start

ReactJS с Webpack

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

Давайте воспользуемся реагирующим кодом js без create-реагировать-приложения с помощью веб-пакета, поэтому для этого нам нужно установить babel и его загрузчик для преобразования JSX в код JS, поскольку браузер не понимает JSX.

Предустановленная реакция Babel сообщает Babel, если вы столкнетесь с jsx, транспилируете его в код реакции

npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react

Добавьте правила в конфигурацию веб-пакета для проверки файлов js/jsx и используйте для них загрузчик babel. Также мы не хотели тестировать файлы javascript модулей узлов, поэтому мы их исключаем.

const path = require("path");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    mode: "development",
    entry: {
       main: "./src/main.js",
    },
    output: {
        filename: "[name].bundle.js",
        path: path.join(__dirname, "dist"),
        clean: true,
        assetModuleFilename:'[name][ext]'
    },
    module: {
        rules: [
            {
                test:/\.s[ac]ss$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader','sass-loader']
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif)$/i,
                type: 'asset/resource',
              },
              {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: {
                  loader: 'babel-loader',
                  options: {
                     presets: ['@babel/preset-env','@babel/preset-react'],
                  },
                },
              },
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: "Webpack demo",
            template: 'src/template.html',
        }),
        new MiniCssExtractPlugin()
    ],
    devServer: {
        static: "./dist",
        port: 3000,
    },
    optimization: {
        usedExports: true
    }
}

Установить реакцию и реакцию-дом

npm i react react-dom

Внутри папки src создайте App.js

// App.js
import React from 'react';

export function App() {
    return (
        <div>
            <h1>React App</h1>
        </div>
    );
}

Замените index.js приведенным ниже кодом, чтобы добавить appElement в реагирующий дом и отобразить компонент приложения.

// index.js
import React from 'react';
import { createRoot } from 'react-dom/client'
import { App } from './App';

const appElement = document.getElementById('app');
createRoot(appElement).render(<App/>)

Создайте div с id="app" в template.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
    <div id="app"></div>
</body>
</html>

Добавьте точку входа как index.js в объект записи конфигурации веб-пакета, чтобы связать код реакции.

  entry: {
       main: "./src/main.js",
       index: "./src/index.js"
    },
npm run build 
npm run start

Теперь в папке src измените App.js на App.jsx, затем выдает ошибку

Module not found: Error: Can't resolve './App' 

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

 resolve:{
        extensions:['.js','.jsx']
    },

Альтернативы Webpack:

  • ESBuild: это сборщик модулей, который написан на golang с учетом производительности при его сборке, что позволяет иметь простую конфигурацию для замены веб-пакета для большинства наших приложений, и это невероятно быстро.
  • SWC:этотакже транспилятор для JavaScript, он написан на Rust и работает намного быстрее, чем Babel. Он может объединять несколько файлов JavaScript или TypeScript в один. Его конфигурация очень похожа на машинописный текст.
  • Vite:этосовременный инструмент для создания внешнего интерфейса, который обеспечивает чрезвычайно быструю среду разработки и объединяет ваш код для производства.

Вите

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

Под капотом используется роллап. В процессе локальной разработки он использует ESBuild для компиляции всех зависимостей.

В процессе локальной разработки он использует eSBuild для компиляции всех зависимостей. Он имеет временную сложность o(1) для компиляции, что означает, что если у нас есть, например, 1000 компонентов, и если мы изменим только один из них, вам не нужно пересобирать все компоненты, как вы это делаете в веб-пакете, вы будете перестраивать только тот, который изменился.

Vite использует модули ecmascript вместо модулей commonjs

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

Создайте новый проект vite (выберите vanillaJS и Javascript)

npm create vite@latest
cd vite-project
npm install

Ваша структура папок выглядит так

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

Большинство вещей, которые нам нужно настроить в webpack, по умолчанию используются в vite. Например, он имеет devserver по умолчанию, поддерживает файлы svg, модульный CSS и т. д.

Чтобы запустить сервер разработки

npm run dev

Модуль CSS в Vite

создайте style.module.css, это особый тип файла, который позволяет нам использовать css как модуль

.red{
    color: darkred;
}

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

import classes from "./style.module.css"
Add class red from classes module
class="${classes.red}"

Добавьте его в main.js

// main.js
import './style.css'
import javascriptLogo from './javascript.svg'
import { setupCounter } from './counter.js'
import classes from "./style.module.css"

document.querySelector('#app').innerHTML = `
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank">
      <img src="${javascriptLogo}" class="logo vanilla" alt="JavaScript logo" />
    </a>
    <h1>Hello Vite!</h1>
    <div class="card">
      <button id="counter" type="button"></button>
    </div>
    <p class="${classes.red}">
      Click on the Vite logo to learn more
    </p>
  </div>
`

setupCounter(document.querySelector('#counter'))

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

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

Вы можете проверить полный код для веб-пакета в моем репозитории github.