4. Работа с новым обвесом проекта


Как открыть папку проекта в VS Code через консоль?

Для этого существует команда в терминале, которую нужно запустить из папки проекта:

code .

Если команда не найдена, нужно сначала включить её через VS Code. Для этого заходим в редактор кода, жмём сочетание клавиш Shift+Cmd+P. Откроется наверху меню команд (оно отличается от обычного окошка поиска), начинайте вбивать “shell”. Выберите “Install ‘code’ command in PATH”. Если вылетает ошибка, тогда переустановим команду: сначала выполним Uninstall (вторая строчка сверху на картинке), затем Install.

Ещё раз о Webpack

Главные идеи Webpack из их документации:

https://webpack.js.org/concepts/

Webpack — сборщик модулей, который позволяет собрать JavaScript-модули в единый JS-файл. Также работает с TypeScript, CoffeeScript, его можно использовать совместно с Node.js. Webpack реализован как программа, которая при запуске обрабатывает модули, строит между ними граф зависимостей, а потом на основе графа генерирует единый файл.

Зачем нужен webpack?

  • для быстрого объединения множества JavaScript-файлов в один большой (точно так же с css);
  • более легкого подключения кода JS к HTML-странице;
  • транспиляции, или перевода кода в SASS на «чистый» CSS;
  • подготовки файлов к размещению на сервере для их оптимизации;
  • адаптации кода к старым версиям браузера;
  • тестирования написанного кода с помощью локального сервера, где запускается собранный проект;
  • разнообразных преобразований кода и выполнения дополнительных действий.

Понятия Webpack

  • Entry. Точка входа — файл, с которого сборщик начинает объединять модули. Обычно это основной JS-файл проекта, и от него зависят все остальные.
  • Output. Точка выхода — файл, в который запишется собранный проект. При настройке Webpack в качестве точки выхода указывают путь к конечному файлу.
  • Bundle. Бандл — собранный из модулей большой файл, результат работы сборщика.
  • Loaders. Они же лоадеры, или загрузчики — дополнительные сущности, которые расширяют возможности Webpack по преобразованию. По умолчанию сборщик распознает только файлы JS и JSON. С помощью лоадеров можно «научить» его понимать и другие файлы, от таблиц стилей CSS до изображений. Загрузчики скачиваются отдельно.
  • Plugins. Плагины — второй вид дополнительных сущностей. Это надстройки для решения дополнительных задач, например автоматического создания HTML-файла, к которому подключается бандл. Они тоже скачиваются отдельно.
  • Режимы. Режимы работы — наборы настроек для разных задач.
    • Режим production — для финальной сборки перед загрузкой на сервер. Предусматривает максимальную оптимизацию и сжатие кода.
    • Режим development — для тестовых сборок, которые нужны при разработке. Упор делается не на оптимизацию, а на читаемость. Можно скачать дополнительный модуль webpack-dev-server и запустить локальный сервер для тестирования проекта. С ним проект будет пересобираться при каждом изменении кода, и эти изменения будут применяться в реальном времени.
    • Режим none не предусматривает оптимизации и отключает ее настройки. По умолчанию Webpack работает в режиме none.

Установим html и css лоадеры

Для загрузки HTML используем HtmlWebpackPlugin (есть ещё html-loader, но я его не использую). Мы его установили на предыдущем занятии.

https://webpack.js.org/plugins/html-webpack-plugin/

Подробная документация на GitHub:

https://github.com/jantimon/html-webpack-plugin#options

Для сборки CSS кода используем css-loader и MiniCssExtractPlugin:

https://webpack.js.org/loaders/css-loader/
https://webpack.js.org/plugins/mini-css-extract-plugin/

Обновите webpack.config.js

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

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dev_build'),
    filename: 'index.js'
  },

  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  },

  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
    new MiniCssExtractPlugin()
  ]
}

Устанавливаем лоадеры либо по отдельности

yarn add -D css-loader

Либо все вместе

yarn add -D css-loader mini-css-extract-plugin

Кеширование и минификация

Для просрачивания кэша есть стандартная настройка

https://webpack.js.org/guides/caching/

Для минификации используем CssMinimizerWebpackPlugin

https://webpack.js.org/plugins/css-minimizer-webpack-plugin/

Для переиспользования HTML кода используем html-webpack-partials-plugin и создаём «лего» из HTML файлов (такие файлы называются partials)

https://github.com/colbyfayock/html-webpack-partials-plugin

Установка SASS и документация

Для него есть специальный лоадер sass-loader, но также нужен пакет sass

https://www.npmjs.com/package/sass
https://webpack.js.org/loaders/sass-loader/

Поддержка браузеров при сборке CSS (post-css)

https://www.npmjs.com/package/postcss
https://webpack.js.org/loaders/postcss-loader/
https://github.com/postcss/autoprefixer

Используем пресет

https://github.com/csstools/postcss-preset-env

Как это работает

https://preset-env.cssdb.org/

Загрузка файлов (например шрифты)

https://v4.webpack.js.org/loaders/file-loader/

Загружаем JS

https://babeljs.io/
https://babeljs.io/docs/babel-core
https://babeljs.io/docs/babel-plugin-transform-class-properties
https://babeljs.io/docs/babel-preset-env
https://babeljs.io/docs/babel-preset-react
https://webpack.js.org/loaders/babel-loader/

Финальная конфигурация

Копируем код в файл package.json и запускаем в терминале команду yarn

{
  "name": "static-site-08",
  "version": "0.1.0",
  "author": "Anna Komkova",
  "license": "false",
  "scripts": {
    "start": "webpack serve --open --config webpack.dev.js",
    "watch": "webpack --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },
  "devDependencies": {
    "@babel/core": "^7.17.5",
    "@babel/plugin-proposal-class-properties": "^7.16.7",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-react": "^7.16.7",
    "autoprefixer": "^10.4.7",
    "babel-loader": "^8.2.3",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^5.2.4",
    "css-minimizer-webpack-plugin": "^2.0.0",
    "extract-loader": "^5.1.0",
    "file-loader": "^6.2.0",
    "html-loader": "^2.1.2",
    "html-webpack-partials-plugin": "^0.8.0",
    "html-webpack-plugin": "^5.3.1",
    "mini-css-extract-plugin": "^2.6.1",
    "postcss": "^8.4.14",
    "postcss-loader": "^7.0.0",
    "postcss-preset-env": "^7.7.0",
    "sass": "^1.32.12",
    "sass-loader": "^11.0.1",
    "webpack": "^5.33.2",
    "webpack-cli": "^4.6.0",
    "webpack-dev-server": "^4.11.0",
    "webpack-merge": "^5.7.3"
  }
}

Удаляем webpack.config.js и вместо него создаём три файла с кодом

webpack.common.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackPartialsPlugin = require('html-webpack-partials-plugin')

const webpack = require('webpack')
const path = require('path')

module.exports = {
  entry: {
    index: './src/index.js'
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'docs')
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: ['@babel/plugin-proposal-class-properties']
          }
        }
      },
      {
        test: /\.js?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true
          }
        }
      },
      {
        test: /\.scss$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
      },
      {
        test: /\.css$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [['postcss-preset-env']]
              }
            }
          }
        ]
      },
      {
        test: /\.html$/i,
        loader: 'html-loader'
      },
      {
        resourceQuery: /raw/,
        type: 'asset/source'
      },
      {
        test: /\.png/,
        type: 'asset/resource',
        generator: {
          filename: 'images/[hash][ext][query]'
        }
      },
      {
        test: /\.svg/,
        type: 'asset/resource',
        generator: {
          filename: 'images/[hash][ext][query]'
        }
      },
      {
        test: /\.(ttf|otf)$/i,
        loader: 'file-loader',
        options: {
          name: 'fonts/[name].[ext]'
        }
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css'
    }),

    // Index
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: './index.html'
    }),

    // Article
    // new HtmlWebpackPlugin({
    //   template: './src/articles/superorganisms/S_Popup.html',
    //   filename: './superorganisms/S_Popup.html'
    // }),

    // Partials
    new HtmlWebpackPartialsPlugin([
      {
        path: path.join(__dirname, './src/partials/analytics.html'),
        location: 'analytics',
        template_filename: '*',
        priority: 'replace'
      }
    ])
  ],
  optimization: {
    minimizer: [new CssMinimizerPlugin()]
  }
}

webpack.dev.js

const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
const path = require('path')

module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dev_build'
  },
  watch: true,
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dev_build')
  }
})

webpack.prod.js

const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')

module.exports = merge(common, {
  mode: 'production'
})

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

+корневая папка docs

Файл index.css

@import 'stylesheets/reset.scss';
@import 'stylesheets/fonts.scss';
@import 'stylesheets/vars.scss';
@import 'stylesheets/index.scss';

reset.scss

https://meyerweb.com/eric/tools/css/reset/

vars.scss

:root {
  --white: #ffffff;
  --black: #000000;
  --dark: #737373;
  --middle: #979797;
  --light: #dedede;
  --pink: #ff0aa9;
}

fonts.scss

// @font-face {
//   font-family: 'Inter';
//   src: url('fonts/inter-regular.ttf');
//   font-weight: 400;
//   font-style: normal;
// }

basics.scss

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 14px;
  font-weight: 400;
}

После настройки запустите

yarn watch

Все изменения в коде будут автоматически собираться. Чтобы завершить этот процесс нажмите Ctrl + C.