Webpack с Typescript

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

С помощью webpack код TypeScript компилируется в файл JavaScript, удобный для браузера. С помощью загрузчиков веб-пакетов вы также можете конвертировать файлы SASS и LESS в один файл пакета CSS.

В этой статье мы узнаем, как использовать webpack для компиляции TypeScript в JavaScript, объединения исходного кода в один файл JavaScript и использования исходной карты для отладки. Мы также рассмотрим, как использовать плагины webpack.

Чтобы следовать этому руководству, вам понадобится следующее:

  • npm
  • Node.js: если у вас уже установлен Node.js, убедитесь, что он ≥v8.x.
  • Базовые знания TypeScript

Webpack loaders

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

Загрузчики сообщают вебпаку, как компилировать и связывать статические ресурсы. Они используются для компиляции модулей TypeScript в JavaScript, обработки стилей приложений и даже анализа кода с помощью ESLint.

Несколько загрузчиков включают ts-loader, css-loader, style-loader и другие; мы обсудим их позже в этом уроке.

Настройка Webpack и TypeScript

Начнем с настройки нашего проекта. Во-первых, на вашем компьютере должен быть установлен TypeScript. Чтобы установить TypeScript глобально, используйте следующую команду:

npm install -g typescript

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

Далее мы установим пакеты webpack и ts-loader в качестве зависимостей в нашем проекте:

npm init -y
npm install -D webpack webpack-cli ts-loader webpack-dev-server>

Конфигурация Webpack

По умолчанию webpack не нуждается в файле конфигурации. Предполагается, что точка входа для вашего src/index.js проекта есть и будет выводить минимизированный и оптимизированный результат в dist/ main.js во время production.

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

Давайте добавим файл конфигурации webpack в наш проект. В корневой папке проекта создайте файл webpack.config.js со следующими конфигурациями:

const path = require('path');

module.exports = {
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.ts?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  devServer: {
    static: path.join(__dirname, "dist"),
    compress: true,
    port: 4000,
  },
};

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

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

Конфигурация TypeScript

Файл конфигурации TypeScript управляет компиляцией TypeScript в JavaScript и определяет различные параметры компилятора, необходимые для транспиляции TypeScript.

В корневой папке проекта создайте файл tsconfig.json и добавьте следующие конфигурации:

{
    "compilerOptions": {
        "noImplicitAny": true,
        "target": "ES5",
        "module": "ES2015"
    }
}

Параметр target— это версия JavaScript, в которую вы хотите транспилировать TypeScript, а module формат используемого оператора импорта. Вы можете установить для модуля CommonJS, ES6 или UMD, поскольку webpack будет обрабатывать все модульные системы.

Конфигурация пакета

Теперь нам нужно добавить скрипт вебпака, который будет запускать файл webpack.config.js для нас.

Чтобы добавить сценарий webpack, откройте файл package.json и добавьте следующие сценарии в параметр сценария:

"develop": "webpack-dev-server --mode development",
"build" : "webpack --mode production"

Теперь файл package.json будет содержать следующие параметры конфигурации:

{
    "name": "webpack-setup",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "develop": "webpack-dev-server --mode development",
    "build": "webpack --mode production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.7.1",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.6.1",
    "ts-loader": "^9.4.1",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1"
  }
}

Теперь давайте создадим простую программу на TypeScript, которая будет вычитать два числа. Внутри src папки создайте файл index.ts и добавьте следующий код TypeScript:

import { subtract } from "./app";

function init() {
    const form = document.querySelector("form");
    form?.addEventListener("submit", submitHandler);
  }

  function submitHandler(e: Event) {
    e.preventDefault();
    const num1 = document.querySelector("input[name='firstnumber']") as HTMLInputElement;
    const num2 = document.querySelector("input[name='secondnumber']") as HTMLInputElement;
    const result = subtract(Number(num1.value), Number(num2.value));
    const resultElement = document.querySelector("p");
    if (resultElement) {
      resultElement.textContent = result.toString();
    }
  }

  init();

Затем создайте еще один файл app.ts и добавьте следующий код:

export function subtract(firstnumber: number, secondnumber: number): number {
  return firstnumber - secondnumber;
}

Запуск develop скрипта запустит приложение в режиме разработки:

npm run develop 

Запуск buildскрипта запустит приложение в рабочем режиме:

npm run build

После запуска команды сборки webpack преобразует два файла TypeScript в код JavaScript и сгенерирует файл bundle.js внутри папки dist.

Создание HTML-страниц с помощью HtmlWebpackPlugin

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

Когда имя файла пакета изменяется или хэшируется, HTMLWebpackPlugin обновляются имена файлов на HTML-странице. Во-первых, для установки HtmlWebpackPlugin выполните следующую команду:

npm install html-webpack-plugin --save-dev

Далее нам нужно импортировать и добавить HtmlWebpackPlugin в плагин конфигурации webpack следующий параметр:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require('path');

module.exports = {
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.ts?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      }
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },

  plugins: [
    new HtmlWebpackPlugin({
        title: 'our project', 
        template: 'src/custom.html' }) 
   ],

  devServer: {
    static: path.join(__dirname, "dist"),
    compress: true,
    port: 4000,
  },
};

Шаблон представляет собой пользовательский HTML-файл, сгенерированный HtmlWebpackPluginдля внедрения на HTML-страницу. Чтобы создать собственный HTML-код, внутри srcпапки создайте файл custom.html и добавьте следующий HTML-код:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <div class="cal">
      <center>
     <form><br>
      <p>Result : <span id="display"></span></p>
      <input type="number" class="input" placeholder="Enter first number" name="firstnumber" value="1" min="1" min="9" /><br>
      <input type="number" class="input" placeholder="Enter second number" name="secondnumber" value="1" min="1" min="9" /><br><br>
      <button type="submit" class="button">Subtract</button>
    </form>
  </center>
  </div>
  </body>
</html>

Вам не нужно включать теги script или link в пользовательский HTML-код; HtmlWebpackPlugin позаботится об этом, связав URL-адрес файла пакета со сгенерированной страницей.

Объединение CSS сMiniCSSExtractPlugin

css-loader сообщает вебпаку, как работать с модулем CSS. Он интерпретирует @import и URL() как import/require() и разрешает их. css-loader позволяет веб-пакету компилировать все файлы CSS и конвертировать их в формат JavaScript.

Объединение файлов CSS с загрузчиком стилей приводит к тому, что стили HTML-страницы не отвечают до тех пор, пока они не Bundle.js будут полностью загружены. Загрузчик стилей внедряет CSS в DOM, но связанный файл JavaScript должен полностью загрузиться перед внедрением стилей. Чтобы решить эту проблему, мы можем использовать MiniCssExtractPlugin.

Извлекает MiniCssExtractPlugin файлы CSS и объединяет их в один bundle.css файл. Это полезно для уменьшения размера ваших ресурсов CSS и предотвращения ненужных HTTP-запросов для их загрузки.

Мы можем установить css-loader, MiniCssExtractPluginвыполнив в терминале приведенные ниже команды:

npm install css-loader --save-dev
npm install mini-css-extract-plugin --save-dev

Теперь давайте добавим css-загрузчик и MiniCssExtractPlugin в webpack.config.js файл.

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

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

Затем мы добавим к свойству новое правило rules следующим образом:

…
{
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"]
}
…

Когда css-loader компилирует все файлы CSS в JavaScript, MiniCssExtractPlugin.loader загружает CSS в файл пакета CSS.

Далее мы добавим MiniCssExtractPlugin в плагин опцию следующим образом:

plugins: [
    new HtmlWebpackPlugin({
        title: 'our project', // Load a custom template (lodash by default)
        template: 'src/custom.html' }),
    new MiniCssExtractPlugin({
      filename:"bundle.css"})
  ]

Теперь, когда мы настроили css-loaderи MiniCssExtractPlugin, давайте создадим файл CSS и импортируем его в формат index.ts. Внутри srcпапки создайте index.cssфайл и добавьте следующий код CSS:

form {
    background-color:pink;
    margin-top:100px;
    border-radius:40px;
}
.cal{
    width:550px;
    height:300px;
    margin-left:400px;
}

.button{
    border-radius:10px;
    margin-top:20px;
    margin-bottom:20px;
}
.input{
    border-radius:10px;
    margin-top:40px;
}

Внутри index.ts импортируйте стиль CSS следующим образом:

import styles "./main.css"

При запуске npm run build CSS будет объединен и применен к index.html. Когда вы запускаете приложение в режиме разработки и открываете его http://localhost:4000 в браузере, оно должно выглядеть следующим образом:

Предварительный просмотр CSS в комплекте Localhost

Минимизация CSS

Мы можем использовать css-minimizer-webpack-plugin, чтобы уменьшить размер файлов CSS, удалив неиспользуемые правила CSS и оставив только необходимые.

css-minimizer-webpack-plugin анализирует скомпилированный файл CSS и находит все неиспользуемые стили. Затем этот плагин удалит эти неиспользуемые стили из вашего окончательного файла CSS, тем самым уменьшив его размер.

Запустите команду установки ниже, чтобы установить css-minimizer-webpack-plugin:

npm install css-minimizer-webpack-plugin --save-dev

Добавим css-minimizer-webpack-plugin в конфигурацию веб-пакета. Сначала импортируйте плагин следующим образом:

const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

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

optimization: {
    minimizer: [
      new CssMinimizerPlugin()
    ],
  }

Когда мы запустим команду сборки, bundle.css она будет минимизирована, но bundle.js не будет.

Минимизатор по умолчанию bundle.js был заменен установленной нами опцией минимизации. Чтобы решить эту проблему, нам нужно минимизировать JavaScript с помощью TerserWebpackPlugin.

Минимизация JavaScript

В текущей версии вебпака на момент написания, v5.74.0 и новее, вам не нужно устанавливать пакет, TerserWebpackPlugin поскольку он включен в комплект поставки. Сначала нам нужно импортировать TerserWebpackPlugin:

const TerserPlugin = require("terser-webpack-plugin");

Затем добавьте TerserPluginк опции минимизации следующее:

optimization: {
    minimizer: [
      new CssMinimizerPlugin(),
     new TerserPlugin()
    ],
  }

Если вы запустите build сценарий и посмотрите файлы пакета в distпапке, вы увидите, что JavaScript и CSS минимизированы.

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

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

Установите CopyWebpackPlugin с помощью команды ниже:

npm install copy-webpack-plugin --save-dev

Теперь давайте добавим CopyWebpackPluginконфигурацию веб-пакета. Импортируйте плагин следующим образом:

const CopyPlugin = require("copy-webpack-plugin");

Далее мы добавим CopyWebpackPluginв плагин опцию. Свойство from— это папка, из которой мы будем копировать, а toсвойство — это папка в distкаталоге, в которую нужно скопировать все файлы:

...
plugins: [
new HtmlWebpackPlugin({
        title: 'our project', // Load a custom template (lodash by default)
        template: 'src/custom.html' }),
    new MiniCssExtractPlugin({
      filename:"bundle.css"}),
    new CopyPlugin({
      patterns: [
        { from: "src/img", to: "img" }
      ]
    }),
  ]

...

Создайте новую img папку и добавьте в нее изображения. После запуска команды сборки изображения будут скопированы в файл dist/img.

Отладка с использованием sourceMap

Когда мы build создаем пакет путем компиляции файлов TypeScript в файлы JavaScript, нам может потребоваться отладить и протестировать файл пакета с помощью DevTool нашего браузера.

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

Инструмент разработчика для отладки машинописных сценариев

Карты исходного кода отображают исходный исходный файл, что упрощает отладку TypeScript и исправление пакетов и минифицированного кода JavaScript.

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

Чтобы сгенерировать .map файлы для файлов пакета, нам необходимо настроить как веб-пакет, так и TypeScript. В файле конфигурации TypeScript добавьте sourceMap параметр компилятора и установите для него значение true:

{
    "compilerOptions": {
        "noImplicitAny": true,
        "target": "ES5",
        "module": "ES2015",
        "sourceMap": true
    }
}

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

module.exports = {
  devtool: 'source-map',
   ...
}

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

Инструмент сборки исходного кода отладки

Заключение

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

В этом уроке мы пошагово рассмотрели процесс настройки веб-пакета с помощью TypeScript.