Простой статический сайт на Webpack 4

  • Tutorial


После прочтения ряда статей (например, этой) решил перейти на современный подход с использованием Node.js при написании простых сайтов с подхода «динозавров». Ниже представлен разбор примера сборки простого статического сайта с помощью Webpack 4. Статья написана, так как инструкции с решением моей задачи не нашел: пришлось собирать всё по кусочкам.


Постановка задачи


Сайт представляет собой простой набор HTML-страниц со своим CSS стилями и файлом JavaScript. Необходимо написать проект, который бы собирал наш сайт из исходников:


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

В собранном сайте не должны использоваться React, Vue.js.


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


Для примера будет сверстано несколько страничек на базе Bootstrap 4. Но это только для примера.


Предполагается, что Node.js установлен (в Windows просто скачивается установщик и устанавливается в стиле «далее, далее»), и вы умеете работать с командной строкой.


Update. Нужно получить набор готовых HTML страниц, которые можно залить на хостинг без дополнительных настроек (например, на GitHub Pages) или открыть локально на компьютере.


Структура проекта


Общая структура проекта представлена ниже:


.
├── dist                 - папка, куда будет собираться сайт
├─┬ src                  - папка с исходниками сайта
│ ├── favicon            - папка с файлами иконок для сайта
│ ├── fonts              - папка со шрифтами
│ ├─┬ html               - папка заготовок HTML страниц
│ │ ├── includes         - папка с встраиваемыми шаблонами (header, footer)
│ │ └── views            - папка с самими HTML страницами
│ ├── img                - папка с общими изображениями (логотип, иконки и др.)
│ ├── js                 - папка с JavaScript файлами
│ ├── scss               - папка с SСSS файлами
│ └── uploads            - папка с файлами статей (картинки, архивы и др.)
├── package.json         - файл настроек Node.js
└── webpack.config.js    - файл настроек Webpack

Та же структура, но с показом файлов, которые присутствуют в примере:
.
├── dist
├─┬ src
│ ├─┬ favicon
│ │ └── favicon.ico
│ ├─┬ fonts
│ │ └── Roboto-Regular.ttf
│ ├─┬ html
│ │ ├─┬ includes
│ │ │ ├── footer.html
│ │ │ └── header.html
│ │ └─┬ views
│ │   ├── index.html
│ │   └── second.html
│ ├─┬ img
│ │ └── logo.svg
│ ├─┬ js
│ │ └── index.js
│ ├─┬ scss
│ │ └── style.scss
│ └─┬ uploads
│   └── test.jpg
├── package.json
└── webpack.config.js

Под favicon выделена целая папка, так как в современном web обычным одним ico файлом не обойтись. Но для примера используется только этот один файл.


Спорным решением может показаться разделение картинок на две папки: img и uploads. Но здесь использовал идеологию расположения файлов из Wordpress. На мой взгляд, кидать все изображения в одну папку — не очень хорошая идея.


Для работы с проектом использую Visual Studio Code, которым очень доволен. Особенно мне нравится, что командная строка встроена в программу и вызывается через Ctrl + `.



Сделаем болванку Node.js проекта. Для этого создадим папку нашего проекта с вышеописанной структурой и перейдем в неё в командной строке, где вызовем команду для создания файла package.json.


npm init

На все вопросы можно просто отвечать, нажимая Enter, если заполнять подробную информацию не хочется.


Установим три общих пакета, которые нам потребуются в любом случае: webpack, webpack-cli (работу с командной строкой в webpack вынесли в отдельный пакет) и webpack-dev-server (для запуска локального сервера, чтобы в браузере сразу отображались сохраненные изменения проекта).


npm install webpack webpack-cli webpack-dev-server --save-dev

Файл package.json сейчас выглядит примерно так:
{
  "name": "static-site-webpack-habrahabr",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.1.1",
    "webpack-cli": "^2.0.11",
    "webpack-dev-server": "^3.1.1"
  }
}

Также создастся файл package-lock.json, который вообще не трогаем. Но в git репозиторий добавлять этот файл нужно, в отличии от папки node_modules, которую нужно прописать в файле .gitignore, если пользуетесь git.


Собираем JavaScript


Так как Webpack создан в первую очередь для сборки js файлов, то эта часть будем самой простой. Чтобы можно было писать javascript в современном виде ES2015, который не поддерживается браузерами, поставим пакеты babel-core, babel-loader, babel-preset-env.


npm install babel-core babel-loader babel-preset-env --save-dev

После создаем файл настроек webpack.config.js с таким содержимым:


const path = require('path');

module.exports = {
  entry: [
    './src/js/index.js',
  ],
  output: {
    filename: './js/bundle.js'
  },
  devtool: "source-map",
  module: {
    rules: [{
        test: /\.js$/,
        include: path.resolve(__dirname, 'src/js'),
        use: {
          loader: 'babel-loader',
          options: {
            presets: 'env'
          }
        }
      },
    ]
  },
  plugins: [
  ]
};

В разделе entry (точки входа) указываем, какой js файл будем собирать, в разделе output указываем путь в папке dist, куда будем помещаться собранный файл. Обратите внимание, что в webpack 4 в пути output саму папку dist указывать не нужно! И да, как же мне не нравится, что в одном файле webpack в одних случаях нужно писать относительный путь, в других случаях относительный путь в специальной папке, в третьих случаях нужен уже абсолютный путь (например, его получаем этой командой path.resolve(__dirname, 'src/js')).


Также указано значение параметра devtool, равное: source-map, что позволит создавать карты исходников для js и css файлов.


Для обработки конкретных файлов (по расширению, по месторасположению) в webpack создаются правила в разделе rules. Сейчас у нас там стоит правило, что все js файлы пропускаем через транслятор Babel, который преобразует наш новомодный ES2015 в стандартный javascript вариант, понятный браузерам.


В нашем тестовом примере мы верстаем наши странице на Boostrap 4. Поэтому нам нужно будет установить три пакета: bootstrap, jquery, popper.js. Второй и третий пакет мы устанавливаем по требованию Bootstrap.


npm install bootstrap jquery popper.js --save

Обратите внимание на то, что эти три пакета нам нужны именно для самого сайта, а не для его сборки. Поэтому эти пакеты мы устанавливаем с флагом --save, а не --save-dev.


Теперь можно приступить к написанию нашего index.js файла:


import jQuery from 'jquery';
import popper from 'popper.js';
import bootstrap from 'bootstrap';

jQuery(function() {
    jQuery('body').css('color', 'blue');
});

В качестве примера пользовательского кода js просто перекрасили цвет текста на синий.


Теперь можно перейти к сборке js файла. Для этого в файле package.json в разделе scripts пропишем следующие npm скрипты:


  "scripts": {
    "dev": "webpack --mode development",
    "build": "webpack --mode production",
    "watch": "webpack --mode development --watch",
    "start": "webpack-dev-server --mode development --open"
  },

Теперь при запуске в командной строке строчки npm run dev произойдет сборка проекта (css и html файлы потом также будут собираться этой командой), и в папке /dist/js появятся файлы bundle.js и bundle.js.map.


При запуске команды npm run build также произойдет сборка проекта, но уже итоговая (с оптимизацией, максимальной минимизацией файла), которую можно выкладывать на хостинг.


При запуске npm run watch запускается режим автоматического просмотра изменений файлов проекта с автоматическим допостроением измененных файлов. Да, чтобы в командной строке отключить этот режим (например, чтобы можно было написать другие команды) можно нажать Ctrl + C (как минимум в PowerShell).


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


Режим построения проекта создает или переписывает файлы в папке dist. Но во время разработки проекта при разных сборках файлы могут переименовываться, удаляться. И Webpack не будет следить, чтобы уже ненужные файлы, оставшиеся после предыдущих сборок, удалялись из папки dist. Поэтому добавим еще один пакет clean-webpack-plugin, который будет очищать папку dist перед каждой сборкой проекта.


Update 2018.04.11. Пришлось отказаться от clean-webpack-plugin. Почему? Когда запускаешь сервер через команду npm run start (webpack-dev-server --mode development --open), то webpack компилирует файлы автоматом, не сохраняя их в папку dist. И это нормально. Но при этом папка dist очищается из-за наличия clean-webpack-plugin. В результате в режиме работы локального сервера папка dist пустует, что негативно сказывается на работе с git (только в случае, если вы в git репозиторий сохраняется сборку проекта, как и я): после каждого запуска сервера появляется куча изменений из-за удаленных файлов. Было бы хорошо, чтобы очистка папки dist происходила только при полноценной сборке, например, npm run build-and-beautify (об этой команде ниже). Плагин clean-webpack-pluginнастроить нужным способом не смог. Поэтому использую другой плагин del-cli, который не связан с webpack и работает отдельно.


npm install del-cli --save-dev

Внесем изменения в файл package.json.


{
...
  "scripts": {
...
    "clear": "del-cli dist"
  },
...
}

Сборка CSS файла


CSS файл будем собирать из SCSS файлов, под которые у нас зарезервирована папка src/scss. В ней создадим файл style.scss, например, со следующим содержимым:


$font-stack: -apple-system, BlinkMacSystemFont,Roboto,'Open Sans','Helvetica Neue',sans-serif;

@import "~bootstrap/scss/bootstrap";

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

body {
  font-family: $font-stack;
  #logo {
    width: 10rem;
  }
  .container {
    img {
      width: 20rem;
    }
  }
}

Обратите внимание на то, что стили Bootstrap подключаем не через его CSS файл, а через SСSS (@import "node_modules/bootstrap/scss/bootstrap" @import "~bootstrap/scss/bootstrap";), который позволит в случае надобности переписать те или иные свойства библиотеки, использовать его миксины и др. Но что печалит. Если при сборке js файла при подключении js файла Bootstrap библиотеки Webpack знает, где находятся нужные файлы, то при подключении стилей нужно указывать путь к папке в node_modules.


Для обработки css файлов нам будут нужны следующие модули: node-sass, sass-loader, css-loader и extract-text-webpack-plugin (говорят, что в следующей версии Webpack в последнем плагине надобность отпадет).


Важно! На момент написания статьи плагин extract-text-webpack-plugin в стабильной версии не умеет работать с Webpack 4. Поэтому нужно устанавливать его beta версию через @next:


npm install node-sass sass-loader css-loader extract-text-webpack-plugin@next --save-dev

Надеюсь, что вскоре можно будет устанавливать все плагины по нормальному:


npm install node-sass sass-loader css-loader extract-text-webpack-plugin --save-dev

В webpack.config.js добавим следующие изменения:


...
const ExtractTextPlugin = require("extract-text-webpack-plugin");
...

module.exports = {
  entry: [
    ...
    './src/scss/style.scss'
  ],
  ...
  module: {
    rules: [{
      ...
      {
        test: /\.(sass|scss)$/,
        include: path.resolve(__dirname, 'src/scss'),
        use: ExtractTextPlugin.extract({
          use: [{
              loader: "css-loader",
              options: {
                sourceMap: true,
                minimize: true,
                url: false
              }
            },
            {
              loader: "sass-loader",
              options: {
                sourceMap: true
              }
            }
          ]
        })
      },
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: './css/style.bundle.css',
      allChunks: true,
    }),
    ...
  ]
};

Обратите внимание на то, что в точках входа entryмы добавили новый входной файл style.scss, но выходной файл указали не в output, а в вызове плагина ExtractTextPlugin в разделе plugins. Включаем поддержку карт источников sourceMap для пакетов sass-loader и css-loader.


Также можно заметить, что тут нет пакета style-loader, который чаще всего упоминается при работе с css в Webpack. Данный пакет встраивает css код в файл HTML, что может быть удобно для одностраничных приложений, но никак не для многостраничного.


И самый спорный момент. Для пакета css-loader мы добавили параметр url, равный false. Зачем? По умолчанию url=true, и если Webpack при сборке css находит ссылки на внешние файлы: фоновые изображения, шрифты (например, в нашем случае есть ссылка на файл шрифта url(../fonts/Roboto-Regular.ttf)), то он эти файлы попросит как-то обработать. Для этого используют чаще всего пакеты file-loader (копирует файлы в папку сборки) или url-loader (маленькие файлы пытается встроить в HTML код). При этом прописанные относительные пути к файлам в собранном css могут быть изменены.


Но с какой проблемой столкнулся на практике. Есть у меня папка src/scss с SСSS кодом. Есть папка src/img с картинками, на которые ссылаются в SСSS коде. Всё хорошо. Но, например, мне потребовалось подключить на сайт стороннюю библиотеку (например, lightgallery). SCSS код у неё располагается в папке node_modules/lightgallery/src/sass, который ссылается на картинки из папки node_modules/lightgallery/src/img через относительные пути. И если добавить стили библиотеки в наш style.scss, то file-loader будет искать картинки библиотеки lightgallery в моей папке src/img, а не там, где они находятся. И побороть я это не смог.


Update. С последней проблемой можно справиться, как подсказал Odrin, с помощью пакета resolve-url-loader и file-loader.


Пример решения
...

module.exports = {
  ...
  module: {
    rules: [
    ...
     {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {name: 'img/[name].[ext]'}  
          }
        ]
      },
      {
        test: /\.(sass|scss)$/,
        include: path.resolve(__dirname, 'src/scss'),
        use: ExtractTextPlugin.extract({
          use: [{
              loader: "css-loader",
              options: {
                sourceMap: true,
                minimize: true//,
                //url: false
              }
            },
            {
              loader: "resolve-url-loader"
            },
            {
              loader: "sass-loader",
              options: {
                sourceMap: true
              }
            }
          ]
        })
      }
      ...
    ]
  },
...
};

То есть пакет resolve-url-loader вместо относительных путей ставит пути, которые webpack поймет. А уже file-loader будет копировать нужные файлы. Проблема в свойстве name в file-loader. Если его указать как name: '[path]/[name].[ext]', то в моей примере в папке dist появится папка dist\node_modules\lightgallery\src\img, в которой уже находятся изображения. Нет, в css будут прописаные верные пути до этой папки, но это будет не красиво. Поэтому лучше название файла указывать без пути (например name: 'img/[name].[ext]'). Правда, тогда все картинки пойдут в одну папку — не всегда это будет полезно.


Поэтому установкой url=false говорим, что все ссылки на файлы в SCSS коде не трогаем, пути не меняем, никакие файлы не копируем и не встраиваем: с ними разберемся потом отдельно. Возможно, это решение плохое, и вы предложите более правильный подход.


Сборка HTML страниц


Перейдем к самому веселому: к сборке HTML страниц, где у меня возникли самые большие трудности.


Для сборки HTML страниц будем использовать плагин html-webpack-plugin, который поддерживает различные виды шаблонизаторов. Также нам потребуются пакет raw-loader.


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

В качестве шаблонизатора HTML будем использовать шаблонизатор по умолчанию lodash. Вот так будет выглядеть типичная HTML страница до сборки:


<% var data = {
  title: "Заголовок | Проект",
  author: "Harrix"
}; %>
<%= _.template(require('./../includes/header.html'))(data) %>

<p>text</p>

<%= _.template(require('./../includes/footer.html'))(data) %>

Вначале в переменной data прописываем все наши переменные страницы, которые хотим использовать на этой странице. Потом встраиваем шаблоны шапки и футера через _.template(require()).


Важное уточнение. В статьях про сборку HTML страниц через html-webpack-plugin обычно подключают встраиваемые шаблоны просто через команду:


require('html-loader!./../includes/header.html')

Но при этом в этих встраиваемых шаблонах синтаксис lodash работать не будет (я так и не понял, почему так происходит). И данные из переменной data туда не передадутся. Поэтому принудительно говорим webpack, что мы встраиваем именно шаблон, который надо обработать как lodash шаблон.


Теперь мы можем использовать полноценные lodash синтаксис в встраиваемых шаблонах. В коде файла header.html ниже через <%=title%> печатаем заголовок статьи.


<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="shortcut icon" href="favicon/favicon.ico">
    <link rel="stylesheet" href="css/style.bundle.css">

    <title><%=title%></title>
  </head>
  <body>
    <header><img src="img/logo.svg" id="logo"></header>

В пакете html-webpack-plugin есть возможность генерировать несколько HTML страниц:


 plugins: [
    new HtmlWebpackPlugin(), // Generates default index.html
    new HtmlWebpackPlugin({  // Also generate a test.html
      filename: 'test.html',
      template: 'src/assets/test.html'
    })
  ]

Но прописывать для каждой страницы создание своего экземпляра плагина точно не есть хорошо. Поэтому автоматизируем этот процесс, найдя все HTML файлы в папке src/html/views и создадим для них свои версии new HtmlWebpackPlugin().


Для этого в файле webpack.config.js внесем следующие изменения:


...
const HtmlWebpackPlugin = require('html-webpack-plugin');
const fs = require('fs')

function generateHtmlPlugins(templateDir) {
  const templateFiles = fs.readdirSync(path.resolve(__dirname, templateDir));
  return templateFiles.map(item => {
    const parts = item.split('.');
    const name = parts[0];
    const extension = parts[1];
    return new HtmlWebpackPlugin({
      filename: `${name}.html`,
      template: path.resolve(__dirname, `${templateDir}/${name}.${extension}`),
      inject: false,
    })
  })
}

const htmlPlugins = generateHtmlPlugins('./src/html/views')

module.exports = {
  module: {
      ...
      {
        test: /\.html$/,
        include: path.resolve(__dirname, 'src/html/includes'),
        use: ['raw-loader']
      },
    ]
  },
  plugins: [
    ...
  ].concat(htmlPlugins)
};

Функция generateHtmlPlugins будет осуществлять поиск всех HTML страниц. Обратите внимание, что в коде функции есть настройка inject: false, которая говорит Webpack, что не нужно встраивать ссылки на js и css файл в HTML код самостоятельно: мы сделаем всё сами вручную в шаблонах header.html и footer.html.


Также нужно отметить, что встраиваемые шаблоны обрабатываются плагином raw-loader (содержимое файла просто загрузить как текст), а не html-loader, как чаще всего предлагают. И также, как в случае с CSS, не использую пакеты file-loader или url-loader.


И остается последний необязательный момент для работы с HTML. JavaScript файл и CSS файл у нас будут минимифицроваться. А вот HTML файлы хочу, наоборот, сделать красивыми и не минифицировать. Поэтому после сборки всех HTML файлов хочется пройтись по ним каким-то beautify плагином. И тут меня ждала подстава: не нашел способа как это сделать в Webpack. Проблема в том, что обработать файлы нужно после того, как будут вставлены встраиваемые шаблоны.


Нашел пакет html-cli, который может это сделать независимо от Webpack. Но у него 38 установок в месяц. То есть это означает два варианта: либо никому не нужно приводить к красивому внешнему виду HTML файлы, либо есть другое популярное решение, о котором я не знаю. А ради только одной этой функции Gulp прикручивать не хочется.


Устанавливаем этот плагин:


npm install html-cli --save-dev

И в файле package.json прописываем еще два скрипта, которые после работы Webpack будут приводить к красивому внешнему виду HTML файлы с установкой табуляции в два пробела.


  "scripts": {
    "build-and-beautify": "del-cli dist && webpack --mode production && html dist/*.html --indent-size 2",
    "beautify": "html dist/*.html --indent-size 2"
  },

Update 2018.04.11 Обратите внимание на то, что в команду build-and-beautify я добавил еще del-cli dist, который очищает папку dist перед сборкой.


Поэтому для итоговой сборки рекомендую использовать не команду *npm run build, а команду npm run build-and-beautify.


Копирование оставшихся файлов


Мы сгенерировали js, css файлы, HTML страницы. Остались файлы изображений, шрифтов и др., которые мы не трогали и сознательно не копировали через file-loader или url-loader. Поэтому скопируем все оставшиеся папки через плагин copy-webpack-plugin:


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

В файле webpack.config.js внесем изменения:


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

module.exports = {
  ...
  plugins: [
  ...
    new CopyWebpackPlugin([{
        from: './src/fonts',
        to: './fonts'
      },
      {
        from: './src/favicon',
        to: './favicon'
      },
      {
        from: './src/img',
        to: './img'
      },
      {
        from: './src/uploads',
        to: './uploads'
      }
    ]),
  ]...
};

Всё. Теперь командой npm run build-and-beautify собираем проект и в папке dist появится собранный статический сайт.



Итоговые файлы


Файл package.json:
{
  "name": "static-site-webpack-habrahabr",
  "version": "1.0.0",
  "description": "HTML template",
  "main": "src/index.js",
  "scripts": {
    "dev": "webpack --mode development",
    "build": "webpack --mode production",
    "build-and-beautify": "del-cli dist && webpack --mode production && html dist/*.html --indent-size 2",
    "watch": "webpack --mode development --watch",
    "start": "webpack-dev-server --mode development --open",
    "beautify": "html dist/*.html --indent-size 2",
    "clear": "del-cli dist"
  },
  "dependencies": {
    "bootstrap": "^4.1.0",
    "jquery": "^3.3.1",
    "popper.js": "^1.14.3"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.3",
    "babel-preset-env": "^1.6.1",
    "copy-webpack-plugin": "^4.5.0",
    "css-loader": "^0.28.11",
    "del-cli": "^1.1.0",
    "extract-text-webpack-plugin": "^4.0.0-beta.0",
    "html-cli": "^1.0.0",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.8.3",
    "raw-loader": "^0.5.1",
    "sass-loader": "^6.0.6",
    "webpack": "^4.5.0",
    "webpack-cli": "^2.0.14",
    "webpack-dev-server": "^3.1.3"
  }
}

Файл webpack.config.js:
const path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const fs = require('fs')

function generateHtmlPlugins(templateDir) {
  const templateFiles = fs.readdirSync(path.resolve(__dirname, templateDir));
  return templateFiles.map(item => {
    const parts = item.split('.');
    const name = parts[0];
    const extension = parts[1];
    return new HtmlWebpackPlugin({
      filename: `${name}.html`,
      template: path.resolve(__dirname, `${templateDir}/${name}.${extension}`),
      inject: false,
    })
  })
}

const htmlPlugins = generateHtmlPlugins('./src/html/views');

module.exports = {
  entry: [
    './src/js/index.js',
    './src/scss/style.scss'
  ],
  output: {
    filename: './js/bundle.js'
  },
  devtool: "source-map",
  module: {
    rules: [{
        test: /\.js$/,
        include: path.resolve(__dirname, 'src/js'),
        use: {
          loader: 'babel-loader',
          options: {
            presets: 'env'
          }
        }
      },
      {
        test: /\.(sass|scss)$/,
        include: path.resolve(__dirname, 'src/scss'),
        use: ExtractTextPlugin.extract({
          use: [{
              loader: "css-loader",
              options: {
                sourceMap: true,
                minimize: true,
                url: false
              }
            },
            {
              loader: "sass-loader",
              options: {
                sourceMap: true
              }
            }
          ]
        })
      },
      {
        test: /\.html$/,
        include: path.resolve(__dirname, 'src/html/includes'),
        use: ['raw-loader']
      },
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: './css/style.bundle.css',
      allChunks: true,
    }),
    new CopyWebpackPlugin([{
        from: './src/fonts',
        to: './fonts'
      },
      {
        from: './src/favicon',
        to: './favicon'
      },
      {
        from: './src/img',
        to: './img'
      },
      {
        from: './src/uploads',
        to: './uploads'
      }
    ]),
  ].concat(htmlPlugins)
};

Файл шаблона index.html:
<% var data = {
  title: "Заголовок | Проект",
  author: "Harrix"
}; %>
<%= _.template(require('./../includes/header.html'))(data) %>

<div class="container">
  <p>Первая страница.</p>
  <p><img src="uploads/test.jpg"></p>
</div>

<%= _.template(require('./../includes/footer.html'))(data) %>

Файл шаблона шапки header.html:
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="shortcut icon" href="favicon/favicon.ico">
    <link rel="stylesheet" href="css/style.bundle.css">

    <title><%=title%></title>
  </head>
  <body>
    <header><img src="img/logo.svg" id="logo"></header>

Файл шаблона footer.html:
<footer><%=author%></footer>

<script src="js/bundle.js"></script>
</body>
</html>

Сгенерированный index.html:
<!doctype html>
<html lang="ru">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <link rel="shortcut icon" href="favicon/favicon.ico">
  <link rel="stylesheet" href="css/style.bundle.css">

  <title>Заголовок | Проект</title>
</head>

<body>
  <header><img src="img/logo.svg" id="logo"></header>

  <div class="container">
    <p>Первая страница.</p>
    <p><img src="uploads/test.jpg"></p>
  </div>

  <footer>Harrix</footer>

  <script src="js/bundle.js"></script>
</body>

</html>

Исходники


Ссылка на репозиторий с рассмотренным проектом.

Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 83
  • 0
    Спасибо, как раз на днях занимался подобным вопросом. Ранее читал, что сборка webpack-ом стилей, html и т.п. медленнее чем галпом. Вы не пробовали сравнить быстродействие? У меня есть отдельно настроенная сборка галп+вебпак с шаблонизатором jade. Так там при большом количестве jade миксинов пересборка длится ощутимое время. Если же webpack 4 покажет лучшее быстродействие — стоит на него в таком случае переезжать.
    • +2

      gulp быстрее, однако webpack это несколько иной инструмент. Webpack собирает бандлы с учётом зависимостей и умеет tree-snaking. То есть если типичный сценарий с gulp собираем все стили в одном файлы, скрипты в другом. С вебпаком, если у вас есть некий компонент, которому требуют скрипты и стили, а также имеет зависимости, то вы все стили и зависимости подключаете непосредственно в скрипте этого компонента. Поэтому если вдруг вам компонент больше не нужен будет, и вы перестанете его использовать, он у вас перестанет попадать в бандл.

    • +2

      А использовались ли какие-нибудь фичи нового webpack? А то по названию как будто упор на 4 версию идёт. А с виду тот же третий.

      • +1

        из новых фич: сборка с использованием флага --mode

        • 0
          Как уже отметили, режимы --mode development и --mode production. Также отсутствие необходимости указывать паку dist в путях в файле webpack.config.js. А также отсутствие некоторых плагинов (это скорее антифича). Например, jshint пока прикрутить нельзя, так как jshint-loader не работает с новым Webpack.
          • 0
            eplaksin, Harrix, спасибо.
            Тут недавно статья была про новый вебпак, и как там все здорово будет. А по сути изменения получаются скорее косметические. Я то думал что будет какой-то режим сборки из коробки, без возни с конфигами, но его нет. И в результате, по моим личным ощущениям, получается, что использовать webpack что 3-ий, что 4-ый, для сбора статического сайта это все таки небольшой оверхед.
            • 0
              Режим для сборки из коробки там есть. Но как показывает практика, всегда будут появляться свои хотелки, которые потребуют написание своих конфигов.

              Разумеется, для полноценного статического сайта куда лучше подойдет специализированный инструмент, как тот же Jekyll. Но и там те же css и js надо будет кем-то собирать. И тогда появляется связка Jekyll + webpack или Jekyll + gulp. Вот и решил для простых вариантов обойтись одним инструментом.
        • +2
          `new CleanWebpackPlugin(['dist', 'build']),`
          зачем build здесь, ведь эта папка не фигурирует нигде. Потом, столько мусора, в bundle.js есть пути до scss. Зачем…
          • 0
            Ошибку с CleanWebpackPlugin поправил. Спасибо!

            А вот про мусор не понял. В bundle.js нет упоминаний про scss. А вы как собирали проект? Через npm run dev или npm run build? В первом случае bundle.js будет содержать много мусорного, так как сборка происходит в режиме разработки.
        • +1
          Но что печалит. Если при сборке js файла при подключении js файла Bootstrap библиотеки Webpack знает, где находятся нужные файлы, то при подключении стилей нужно указывать путь к папке в node_modules.

          Можно писать так:


          @import "~bootstrap/scss/bootstrap";

          по крайней мере node_modules не надо писать. Путь внутри пакета конечно придётся писать, т. к. в package.json пакета нет ключа "main" для стилей.

          • 0
            Спасибо! Исправил и в репозитории, и в статье.
          • 0
            После прочтения ряда статей (например, этой) решил перейти на современный подход с использованием Node.js при написании простых сайтов с подхода «динозавров».

            Это сарказм?? Перед вами бизнес (вы сами) поставил простейшую задачу:
            Сайт представляет собой простой набор HTML-страниц со своим CSS стилями и файлом JavaScript

            Ваш репозиторий с установленными зависимостями весит 177 MB..
            Война и Мир в HTML будет весить не более 20 Мб. 2300 зависимостей!
            Вот дерево зависимостей которые вы для этого подтянули, оно не умещается в комментарий:
            gist.github.com/DexterHD/9ed32eedf9412e7c1fdfaf6a1c458fae
            И это все чтобы создать набор из HTML страниц с несколькими CSS и JS??? Серьезно Современный подход??
            • +5
              Скомпилированные js, css весят 155 KB и 118 KB соответственно.
              Некомпилированные js, scss, шаблоны — несколько килобайт.

              Все остальное — использованные библиотеки, плюс «компиляторы» и тп. Считать их размер в размер репозитория некорректно.

              К примеру, в .Net вы тоже считаете вес всего фреймворка, .Net компиляторов — это же тоже зависимости? Затем зависимости следующего уровня — API операционной системы, в общем операционную систему, компилятор в машинные команды, ещё кучи библиотек.
              • 0
                В реальности собирается чуть посложнее сайт, но да. Современный подход, к сожалению или к счастью, выглядит так. От количества файлов в node_modules у самого глаза на лоб лезут. А пока собирал проект по кусочкам — обматерил всех и вся. Но, представьте, что папки node_modules нет. И она заменена на обычное приложение, которое весит все те же 177 Мб и выполняет тоже самое. В этом случае такой жесткой критики не будет.

                А по правде говоря меня очень даже устраивала определенное время Koala для решения задачи сборки CSS из SASS и минификации js файлов. Но потом появились хотелки, которые она уже не могла решать (та же шаблонизация HTML). Вот и появился повод разобраться в «современном подходе». Но я уверен, что в ближайшие годы многое изменится.
                • 0
                  Современный подход, к сожалению или к счастью, выглядит так
                  Вы ошибаетесь. Подход сам по себе не может быть «современным» или «олдовым». Любой подход определяется выбором правильного инструмента для реализации. Вы выбрали современный инструмент, но это вовсе не значит, что этот инструмент обеспечил правильный подход и правильное/оптимальное решение вашей задачи.
                  • 0
                    Подход сам по себе не может быть «современным» или «олдовым».

                    Вы выбрали современный инструмент


                    Вы сами себе противоречите. Есть современные и есть олдовые подходы. А то, что современный подход является оптимальным решением — я этого нигде не говорил. Например, серьезный статический сайт, на подобии Jekyll не написать (хотя для таких задач я предпочту динамический сайт, но это тема другого холивара). А сам webpack мне очень многим не нравится. И я уверен, что на смену ему придет что-то другое.
                    • –1
                      Вы сами себе противоречите… А то, что современный подход является оптимальным решением — я этого нигде не говорил.
                      Вроде нет противоречия. Поясню. Я исхожу от задачи, озвученной вами в статье. Задача решена, но плохо, хоть и современными методами.

                      Если бы ваша постановка задачи выглядела как-то так:
                      Задача (цель): Изучить современные технологии (webpack)
                      Способ решения: Собрать простой статический сайт с помощью webpack 4.
                      то можно было бы сказать, что задача решена хорошо.
                      • +2
                        Почему решение задачи плохое? И какое решение, на ваш взгляд, хорошее?
                        • –1
                          Почему решение задачи плохое?
                          Как вы сами признали, оно не является оптимальным. Почти по всем параметрам — время на разработку, пара тысяч зависимостей (в курсе истории о том, что было, когда одна из зависимостей сломалась и пропала из репо?), дальнейшее сопровождение и т.д. и т.п. Иными словами — вы простейшую, можно сказать — тестовую задачу решали, создавая себе максимальные сложности.

                          Однако опять отмечу, что если вы все-таки решали другую задачу (самообучение), то задача решена хорошо.
                          К сожалению, сейчас повсеместная беда с постановкой задачи, как таковой. Люди путают цели со средствами их достижения.

                          И какое решение, на ваш взгляд, хорошее?

                          Понимаете, как штука… Думаю, что это риторический вопрос с вашей стороны, потому что во первых, в комментах уже писали как минимум о нескольких, во вторых, стараюсь не участвовать в религиозных спорах.

                          Ну как пример, вы пишите о подключаемых хедере и футере, что код шапки прописывать вручную в тысячах файлов — это ад…
                          Могу сказать, что технология SSI известна больше 20 лет, но наверняка нарвусь на обвинение в «подходе динозавра».
                          • 0
                            Мда… Ожидал нормальная аргументацию.
                            Как вы сами признали, оно не является оптимальным.

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

                            время на разработку

                            По вашему любое решение с высоким порогом вхождения — это плохое решение?

                            пара тысяч зависимостей (в курсе истории о том, что было, когда одна из зависимостей сломалась и пропала из репо?), дальнейшее сопровождение

                            Есть такой файл. Называется package-lock.json, который хранит подробную информацию о всех пакетах с конкретными номерами версий. Так что я всегда, пока существует npm, смогу получить свой набор пакетов конкретных версий. Но даже если что-то пропадет, то мне достаточно заархивировать папочку node_modules и вообще беспокоиться не о чем.
                            А если произойдет какой-то капец в пакетах с многомиллионными загрузками, то проблема будет решена очень быстро.

                            К сожалению, сейчас повсеместная беда с постановкой задачи, как таковой. Люди путают цели со средствами их достижения.

                            И вот опять. «Цель — конечный результат, на который преднамеренно направлен процесс». В постановке не описаны конкретные инструменты, но они там могли быть на законных основаниях.

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

                            Не надо сливаться. Мне нужен инструмент, который собирал бы HTML страницы, CSS собирал из SASS, Javascript собирал в один файл (желательно с поддержкой модулей), все это минифицировал, находил ошибки, шаблонизаторы HTML поддерживали условия (например, в шапке в меню можно было бы к пунктам меню ставить параметр active в зависимости от имени html страницы), желательно имелся быстрый способ обновления всех библиотек до последних версий и сборка на лету в статический(!) вариант, который мог бы работать локально. Предложите вариант.

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

                            Могу сказать, что технология SSI известна больше 20 лет

                            Server Side Includes — зачем вы предлагаете инструмент динамического сайта для статического сайта? Вы это серьезно? Нужна будет динамика — я возьму php или другой язык, куда лучше подходящий, чем SSI. Прочитайте название статьи. Требуется инструмент создания статического(!) сайта!
                            • –1
                              Server Side Includes — зачем вы предлагаете инструмент динамического сайта для статического сайта? Вы это серьезно? Нужна будет динамика
                              Дальнейшие обсуждения не имеют смысла. Динамического? Нет, серьезно? Т.е. вы на самом деле считаете. что статику нельзя подключать через SSI? Знаете, прежде чем следовать моде, неплохо бы с основами разобраться.
                              • 0
                                неплохо бы с основами разобраться.


                                Это какие-то старые основы. Пока я нахожу статьи 2011 или 2012 года, где рассказывается как SSI использовать на Apache. И статей как-то маловато. Тогда подскажите, пожалуйста, мне недалекому, который основ не знает, как с помощью SSI сгенерировать статические html файлы.
                                • –1
                                  подскажите, пожалуйста, мне недалекому, который основ не знает, как с помощью SSI сгенерировать статические html файлы
                                  SSI не генерирует статические html файлы, а позволяет делать вставки в статические html файлы. Например, создаете статические header.html и footer.html. Потом можете создавать сколько угодно статических html файлов примерно следующего содержания:
                                  <!--#include file="header.html" -->
                                  <p>Моя статическая страница</p>
                                  <!--#include file="footer.html" -->
                                  

                                  • 0
                                    SSI не генерирует статические html файлы


                                    Если он не генерирует статические html страницы, то зачем мне он нужен? Правильно ли я понимаю, что есть сервер, который динамически подгружает include в html страницы по запросу пользователя? Так я про это и говорил, когда сказал «зачем вы предлагаете инструмент динамического сайта», а вы мне обвинили, что я основы не знаю. Сайт получается динамический!!! Ему нужен сервер для генерирования HTML страниц! Или я что-то не понимаю, и всё-таки можно его попросить сгенерировать как-то конкретные статические конечные html файлы.

                                    Мне нужен инструмент статического(!) сайта! Чтобы я на выходе получил набор html файлов, которые я могу закинуть на тот же GitHub Pages или вообще открыть локально на компьютере. SSI для этого не подходит! Статический сайт! Не динамический!

                                    В динамическом сайте HTML страницы генерируются по запросу пользователя на лету из исходников. И не важно являются ли исходники статическими файлами или нет!

                                    В статическом сайте HTML страницы генерируются заранее, и на хостинге хранятся уже сгенерированные HTML страницы!
                                    • 0
                                      Чтобы я на выходе получил набор html файлов, которые я могу… открыть локально на компьютере
                                      Если без сервера, то (навскидку) я бы написал однострочник на bash минут за несколько. Это вообще не проблема — сгенерировать такие файлы.
                                      • 0
                                        То есть вы назвали решение плохим, не вникнув даже в постановку задачи, и перепутали динамический сайт со статическим. Понятно.

                                        я бы написал однострочник на bash минут за несколько


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

                                        И да, даже если вы напишите скрипт на bash, то что изменится? Да, написать программу, которая заменяет содержимое некоторых строчек на содержимое других файлов во всех файлах папки — это легко везде. Только не понимаю, почему это делает мой вариант плохим.

                                        Плюс к этому потом я предложу усложнить задачу: в меню шапки пункты меню, которые соответствуют названию файлов, должны иметь класс acitive. Или еще что-то. В моем решении в lodash шаблоне можно использовать любой js код.

                                        Но даже, если вы покажите вариант (чего наверно не будет), когда bash осуществляет сборку html файлов, то это ничего не поменяет. Так как остаются задачи работы с SASS и js файлами. Сборка, минификация и др. Это как вы предлагаете сделать? Использовать другие инструменты? И завести целый зоопарк технологий? Или что-то другое?
                                        • 0
                                          Плюс к этому потом я предложу усложнить задачу
                                          :) Ну а потом, после потом… Нет уж, давайте я не буду тратить свое и ваше время, а просто соглашусь, что был неправ, и ваше решение совсем неплохое. если учитывать все нюансы.
                                        • 0

                                          Вот есть вариант, если что: https://github.com/kidwm/node-ssi


                                          В принципе, идея SSI норм, но видимо есть какие-то подводные камни, раз эта идея не стала популярной.


                                          Возможно виной тому отсутствие эскейпа html и уязвимость к XSS. Может быть, просто не хватало возможностей (не увидел поддержки циклов).


                                          Еще так и не нашел единого описания стандарта. Сложилось впечатление, что каждый веб-сервер (Apache, Jigsaw и т.п.)
                                          поддерживает свой несовместимый набор команд

                                  • –3
                                    Купите лучше какую-нибудь книгу, лучше фундаментальную, касающуюся веб разработки в целом, истории вэба, истории развития браузеров и HTML.
                                    • 0
                                      Зачем?
                                      • 0
                                        Потому что изучение фундаментальных трудов это отличная инвестиция в будущее, если конечно вы хотите стать профессионалом в области разработки ПО. Конечно можно изучать все исключительно по статьям и Ютубам, но вряд ли это поможет сделать карьеру или найти работу на которой можно будет решать сложные и интересные задачи.
                                        • +1
                                          Литература, представляющая сугубо исторический интерес, поможет сделать карьеру с ещё меньшей вероятностью.
                                          Хотя, конечно, чтиво занимательное.
                                          • 0
                                            Труды Танненбаума, «Архитектура компьютеров», «Операционные Системы», «Компьютерные Сети», это сугубо исторический интерес? Или может быть «Код» Петцольда представляет сугубо исторический интерес? А может быть Деннис Ритчи «Язык Си» ни кому не нужная историческая книжка? «Чистый код», «Программист прагматик», «Совершенный код», «Архитектура корпоративных приложений», или вот… Кнут «Искусство программирования», которая была написана пол века назад. Это все сугубо исторические книги? Я на 100% уверен что люди которые не читали хотя бы половину из перечисленного, понятия не имеют как писать программы, более того, в голове у них как правило каша и я регулярно с этим сталкиваюсь когда приходится нанимать людей на работу.

                                            Любая фундаментальная литература не имеет срока давности и дает глубокие знания и понимание всего того что программист использует.
                                            А читать книги по языкам программирования или не дай бог по Библиотекам, типа «Изучаем jQuery» или программируем на «React» — пустая трата времени.
                                            • +1
                                              Какое отношение, например, «Совершенный код» относится к «книге, лучше фундаментальной, касающуюся веб разработки в целом, истории вэба, истории развития браузеров и HTML». Или тот же Кнут? А вам сказали именно насчет книг истории развития IT, про которые вы упомянули. А вы в ответ привели список отличных книг, но они про другое.

                                              А историю веба и так далее, как считаю, знаю достаточно. И читать еще какие-то дополнительные книги не хочется.
                                              • –2
                                                Это какие-то старые основы. Пока я нахожу статьи 2011 или 2012 года, где рассказывается как SSI использовать на Apache. И статей как-то маловато. Тогда подскажите, пожалуйста, мне недалекому, который основ не знает, как с помощью SSI сгенерировать статические html файлы.

                                                Достаточно хорошо чтобы писать такое? Ну ок. У вас кстати в постановке задачи первым стоит слово «Сайт», изучите чем «сайт» отличается от набора html страниц.
                                                • 0
                                                  Достаточно хорошо чтобы писать такое?

                                                  Да, я так считаю. Вы считаете, что каждый человек должен знать никем не используемую технологию? Таких технологий вагон и маленькая тележка.

                                                  У вас кстати в постановке задачи первым стоит слово «Сайт», изучите чем «сайт» отличается от набора html страниц.

                                                  Из-за того, что в статье опущено описания заливки файлов на хостинг никак не меняет сути.
                                                  А про свои косяки с книгами вы решили умолчать… Ладно.
                                                  • 0
                                                    Да, я так считаю. Вы считаете, что каждый человек должен знать никем не используемую технологию? Таких технологий вагон и маленькая тележка.

                                                    Я считаю что web-разработчик должен хорошо знать фундамент на котором стоит Web. А web стоит на целой пачке технологий некоторый из которых наложены одна на другую или являются развитием более старых вещей. И чтобы оптимально и правильно решать задачи нужно хотя бы иметь общее представление о всех них. Книги отсылающие к истории веба как нельзя лучше подходят для того чтобы вообще понять почему современный вэб такой какой он есть и где какие технологии стоит использовать, а где они избыточны или не нужны.
                                                    • 0
                                                      Существует огромная масса технологий, которые умерли или устарели. Вы знаете особенности VHTML, конструкции языка COBOL? Про файловый сервер NetWare что-нибудь слышали? Знать их все невозможно и не нужно. Разумеется, определенная база нужна и необходима. Без этого никуда. Но SSI к этому не относится. Эта технология устарела и никто её не пользуется. Она не решает даже часть задачи, которую я озвучил в статье. Но даже, если бы она мне подходила, то никогда бы не остановился на неё выбор. Технология устарела, справочного материала мало, непонятна её поддержка в будущем в серверах и так далее. Если бы у меня стоял выбор какую технологию выбрать, то остановился на старом добром PHP, который пока что не устарел.
                                                      • –1
                                                        Ох, участвовать в дискуссии, заранее зная, чем она закончится..., но было любопытно скорее с точки зрения психологии.
                                                        Вот я пишу:
                                                        стараюсь не участвовать в религиозных спорах… Могу сказать, что технология SSI известна больше 20 лет, но наверняка нарвусь на обвинение в «подходе динозавра».
                                                        Ну и вы:
                                                        Но SSI к этому не относится. Эта технология устарела и никто её не пользуется.

                                                        (С грустью) Опять угадал.

                                                        Но все-же хочу дать вам несколько советов
                                                        Про файловый сервер NetWare что-нибудь слышали?
                                                        как человек, который писал NLM для Novell Netware (намек на возраст).

                                                        Но SSI к этому не относится. Эта технология устарела и никто её не пользуется.
                                                        Если вы не уверены в своем утверждении, не выдавайте свои домыслы за непреложную истину. Человек, узнавший об SSI пару дней назад, мог бы добавить «как мне кажется».

                                                        Если бы у меня стоял выбор какую технологию выбрать, то остановился на старом добром PHP, который пока что не устарел.
                                                        Никогда не говорите так на собеседовании. SSI и PHP — это даже разные предметные области.

                                                        Откланиваюсь, читайте книги. а не википедию, всяческих вам удач в дальнейшем.
                                                        • 0
                                                          Ну и вы:
                                                          Но SSI к этому не относится. Эта технология устарела и никто её не пользуется.


                                                          А это предложение было не для вас, а для DexterHD и его пассажей.

                                                          как человек, который писал NLM для Novell Netware (намек на возраст).


                                                          Опять таки это ответ DexterHD, с которым я почти одного возраста, а не вам.

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


                                                          Объясняю. Если популярные поисковики при запросе о технологии выдают статьи 2010-2012 годов, не находится нормальная документация, а stackoverflow также не многословен, то технологию можно считать устаревшей. Это не означает, что она плохая. Но её не используют. И это в отрыве от того: подходит или нет для моей задачи.

                                                          Никогда не говорите так на собеседовании. SSI и PHP — это даже разные предметные области.


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

                                                          Откланиваюсь, читайте книги. а не википедию, всяческих вам удач в дальнейшем.

                                                          Вначале сказать, что решение неправильное, плохое, потом прикрыться словами, что в дискуссии не участвуете. Потом все-таки вступить в дискуссию. Потом сказать человеку, что он не прав в терминологии. После ответа вместо контр-доводов сказать «Откланиваюсь, читайте книги. а не википедию». Всё понятно.
                                                          • 0
                                                            Если популярные поисковики при запросе о технологии выдают статьи 2010-2012 годов, не находится нормальная документация, а stackoverflow также не многословен, то технологию можно считать устаревшей
                                                            Забыли добавить — «как мне кажется». Ибо я могу предложить другую трактовку. Технология настолько проста и хорошо документирована что у Apache, что у nginx — что там и обсуждать то нечего.
                                                            Вначале сказать, что решение неправильное, плохое, потом прикрыться словами, что в дискуссии не участвуете. Потом все-таки вступить в дискуссию. Потом сказать человеку, что он не прав в терминологии. После ответа вместо контр-доводов сказать «Откланиваюсь, читайте книги. а не википедию». Всё понятно.
                                                            Забыли еще несколько пунктов. Честно пытаться разобраться в вашей терминологической путанице и пытаться понять, какую же задачу вы решали, а после того, как это все-таки мне удалось, често признаться, что был неправ и ваше решение неплохое, с учетом всех нюансов.

                                                            Поэтому еще совет. Читайте собеседника внимательно.
                                                            • –1
                                                              Опять таки это ответ DexterHD, с которым я почти одного возраста, а не вам.

                                                              А возраст о чем то говорит кроме возраста? На мой взгляд ни о чем. Если это намек на опыт, то считайте сами, «Hello World» я написал в 2000г. А в 2004 получил первые деньги за разработку сайта под заказ (Как сейчас помню, это был «варезный» портал интегрированный с phpBB 2.0 с единой аутентификацией/авторизацией через форум и плотной интеграцией с ним). Куда уж мне до «статических HTML сайтов» в 2018 году.
                                                              • 0
                                                                Пожалуйста, очень прошу внимательно читать сообщения. Вот очень прошу. Кому я написал про возраст? Вам? Нет. Я отвечал redfs. На что отвечал? На его комментарий «как человек, который писал NLM для Novell Netware (намек на возраст).» Где redfs в свою очередь зачем-то ответил на мой ответ вам, где я привел список технологий, с которыми вы скорее всего не сталкивались. И этот список (в первую очередь выбор NLM) выбрал на основании уже возраста: технологии стали непопулярными раньше, чем вы могли ими начать заниматься.
                                                                Внимательно смотрите кому и на что я отвечаю. И не надо сводить дискуссию к «у кого длиннее писька».
                                                                • 0
                                                                  технологии стали непопулярными раньше, чем вы могли ими начать заниматься.

                                                                  Непопулярные и устаревшие — разные вещи. Абсолютно. Выбирать только лишь на основе «потому что популярно», не профессионально.

                                                                  где я привел список технологий, с которыми вы скорее всего не сталкивались.

                                                                  Почему вы так решили? Исходя из возраста? Что за бред. Еще раз, возраст не значит ни чего.
                                                                  И не надо сводить дискуссию к «у кого длиннее писька».

                                                                  При чем тут это? Еще раз не нужно судить по возрасту об опыте.

                                                                  Отмотайте вообще в начало. Самая первая моя претензия была в 3000 зависимостях для решения простейшей задачи.

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

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

                                                                  Просто это бывает так: Вы сделали статический сайт и все хорошо, он проработал 3 года, через 3 года к вам обратились для того чтобы вставить пару HTML блоков. Вы пулите репку, делаете npm i, а за 3 года половина зависимостей тех версий что есть в lock файле уже безбожно устарели и выпилились.

                                                                  Как итог собрать ничего не получается, и вам приходится тратить несколько дней чтобы добавить вставку HTML блока. Кто это все будет оплачивать? Заказчик? Задачу которая делается за 5 мин он будет ждать 2 дня?

                                                                  Используя тот же SSI вы можете просто залить на сервер новый кусок HTML и вставить одну строку в готовый файл, и оно будет работать годами. Зачем усложнять простую задачу? Вот главная претензия.

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

                                                                  Устаревшей является технология которая давно выпилена из индустрии и осталась только в книжках и статьях. SSI поддерживается всеми современными web серверами, многие сайты до сих пор используют ее а это значит что технология жива.
                                                                  • 0
                                                                    Почему вы так решили? Исходя из возраста? Что за бред. Еще раз, возраст не значит ни чего.


                                                                    Рука-лицо. Да причем тут возраст? Я хоть какой-то дурной намек сделал относительно возраста чтоль? Я привел просто список технологий, с которыми вы не работали скорее всего. А для redfs в аналогичной ситуации я привел бы другой список, так как он старше. Всё. Никакого другого смысла это не имело.

                                                                    Еще раз не нужно судить по возрасту об опыте.

                                                                    Где я судил об опыте по возрасту? Где? Покажите мне это место в моих сообщениях? Зачем вы придумываете то, чего не было?

                                                                    Непопулярные и устаревшие — разные вещи. Абсолютно.


                                                                    Это понятия сильно коррелирующие. А для технологий «много лет тому назад» корреляция становится еще больше.

                                                                    Выбирать только лишь на основе «потому что популярно», не профессионально.


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

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


                                                                    Так я же на это отвечал. Есть package-loack.json. Есть бэкапы node_modules. То есть, если нужно, чтобы всё просто работало, то никаких проблем не возникнет.

                                                                    Используя тот же SSI вы можете просто залить на сервер новый кусок HTML и вставить одну строку в готовый файл, и оно будет работать годами. Зачем усложнять простую задачу? Вот главная претензия.


                                                                    Да не подходит для поставленной задачи SSI! Сколько об этом можно говорить? Даже redfs это признал. Как мне поможет SSI на Github Pages например? Для того, чтобы больше не было разногласий я добавил уточнение (на мой взгляд оно лишнее) в постановку задачи. А про сборку SASS и js вообще молчу.

                                                                    нужно выбирать технологии под задачу.


                                                                    Абсолютно верно! Только вы зачем-то предлагаете технологию ради технологию, которая не решает задачу!

                                                                    Как итог собрать ничего не получается, и вам приходится тратить несколько дней чтобы добавить вставку HTML блока. Кто это все будет оплачивать?


                                                                    Нельзя принцип зависимости проекта от других проектов применять во всех случаях. Нельзя! Иначе его можно везде применить. Например, сделали человеку сайт на Wordpress, а через три года Wordpress сильно обновился и функции перестали работать. Написали программу под Windows 10, а через несколько лет в Windows поменялась политика работы с правами доступа и приложение перестало работать. Написали статический сайт на jekyll, а через несколько лет он может перестать работать из-за обновлений. И так далее.
                                                  • 0
                                                    Ну или можно сменить постановку задачи и статью на: «Генерируем набор HTML страниц с помощью WebPack 4», тогда все вопросы и споры отпадут сами собой.
                                                    Вот кстати неплохая цитатка про SSI, источник указывать не буду, вы и сами знаете:

                                                    Static websites may still use server side includes (SSI) as an editing convenience, such as sharing a common menu bar across many pages. As the site's behaviour to the reader is still static, this is not considered a dynamic site.
                                                    • 0
                                                      Хм… Тут не смогу не согласиться и наоборот согласится. Например, в том же источнике ниже написано «Server-side dynamic pages are generated „on the fly“ by computer code that produces the HTML (CSS are responsible for appearance and thus, are static files).» Что как бы входит в противоречие с цитатой, которую вы привели. Но это уже чисто терминалогический спор: статический сайт определяется со стороны клиента или сервера.
                                                      • 0
                                                        Там 2 раздела. Первый называется Static website, второй Dynamic website. Я привел текст из первого раздела, вы приводите со второго. Нет ни каких противоречий. Просто прочитайте эти разделы и все станет на свои места.

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

                                                        Вы путаетесь в терминологии в данном конкретном случае, а терминология крайне важная вещь в нашей отрасли.
                                                        К слову я тоже не семи пядей во лбу, поэтому частенько поднимаю теорию и вам того же советую.
                                                        • 0
                                                          Но это уже чисто терминалогический спор: статический сайт определяется со стороны клиента или сервера
                                                          Коллега, поясните, пожалуйста, вашу мысль — «статический сайт со стороны клиента»?
                                                          Вам действительно надо привести в порядок терминологию, хотя бы для того, чтобы ваши читатели вас правильно понимали.
                                                          • 0
                                                            Не люблю я эти терминалогические споры. Когда частично отличающиеся определения приводят к таким ситуациям. Хорошо, давайте разбираться. Отвечу тебе и DexterHD вместе.

                                                            В статье Website википедии в разделе про динамический сайт приводится одно из главных его свойств: «Server-side dynamic pages are generated „on the fly“ by computer code that produces the HTML». То есть HTML страницы генерируются по запросу пользователя из каких-либо исходников. В разделе про статический сайт данный вид сайта определяется как «A static website is one that has web pages stored on the server in the format that is sent to a client web browser.» То есть, что хранится на сервере, то и отдается пользователю. На данный момент всё согласуется с тем, как я использовал понятия динамического и статического сайт.

                                                            Однако, вы нашли следующий момент «Static websites may still use server side includes (SSI) as an editing convenience, such as sharing a common menu bar across many pages. As the site's behaviour to the reader is still static, this is not considered a dynamic site.» По данному моменту получается, что, если для ПОЛЬЗОВАТЕЛЯ, сайт всегда выдает одно и то же, то такой сайт тоже называется статическим. Отсюда получается, что, если, например, у нас будет php скрипт, который собирает html страницу из нескольких исходников (аналогично SSI) и ничего более не делает, то для ПОЛЬЗОВАТЕЛЯ данный сайт будет статическим.

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

                                                            Переходим на основную страницу, посвященной статическим сайтам Static web page. И тут статический сайт определяется как «A static web page (sometimes called a flat page/stationary page) is a web page that is delivered to the user exactly as stored, in contrast to dynamic web pages which are generated by a web application.» То есть здесь опять же главным отличием от динамических сайтов определяется отдача файлов «как они есть» в отличии от генерирования их из исходников.

                                                            Однако ниже написано «However, loose interpretations of the term could include web pages stored in a database, and could even include pages formatted using a template and served through an application server, as long as the page served is unchanging and presented essentially as stored.» То есть можно рассматривать сайт статическим, если для ПОЛЬЗОВАТЕЛЯ неизменен. Обратите внимание на словосочетание «loose interpretations» — свободное толкование в переводе.

                                                            То есть есть толкование понятия статического сайта, которое НЕ ЯВЛЯЕТСЯ основным, где под статический сайт попадают динамически генерируемые страницы, но которые для пользователя неизменны.

                                                            Итого. С точки основного определения статического сайта, данный вид сайтов определяется через процесс происходящий с момент прихода запроса на сервер и отдачи ответа пользователю: если HTML файлы генерируются из исходников, то сайт динамический, если HTML страницы отдаются как хранились, то сайт статический. Для вашего понимания я это назвал статический сайт с точки зрения сервера. Есть НЕОСНОВНОЕ, свободное толкование, где статический сайт определяется с точки зрения пользователя: если пользователь получает неизменные HTML страницы при одном и том же адресе HTML страницы, то сайт можно назвать статическим. Если же страницы могут менять своё содержимое, то сайт можно назвать динамическим. Это статический сайт с точки зрения пользователя.

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

                                                            Скорее всего вы оба этот материал не знали. Ничего страшного. Но в будущем, пожалуйста, тщательней изучайте матчасть.
                                                            • 0
                                                              Вы спутали два определения, web page и website. Вы ссылаетесь на статью про веб-страницу и называете веб-страницу сайтом. Это все равно что я системный блок или монитор буду называть компьютером, ну знаете раньше так делали. Веб-страница это веб-страница, сайт это сайт! И да веб-страница использующая SSI является HTML страницей она ни во что не преобразовывается и не меняется во времени. Ее части вставляются сервером `как есть`. И да, на последок, вы прочитайте весь абзац про Static Web Site, там вполне четкие критерии описано, когда и как сайт считается статическим, вплоть до того с помощью чего статические сайт создаются, а то что вы тут скомпилировали исключительно ваша интерпретация. В общем успехов вам… :)
                                                              • 0
                                                                Вы спутали два определения, web page и website

                                                                Мда… Смотрим. Раздел «Static website» на странице «Website». Читаем далее «Main article: Static web page». То есть это статья, описывающая детально тему «Static website». Переходим на «Static web page». Смотрим как называется русская статья, соответствующая этой: «Статический сайт». Дальше будете спорить?
                                                                И описание неосновного толкования по смыслу в статьях «Static website» и «Static web page» очень похоже.

                                                                Ее части вставляются сервером `как есть`

                                                                Рука-лицо. Вам не кажется, что процесс вставки «как есть» — уже автоматом означает, что HTML страница, которая хранится на сервере, и тот вариант, который передается пользователю — разные? И то, что отправленный вариант был сгенерирован сервером? Нет, не кажется?
                                                                • 0
                                                                  А если я покажу, например, эту ссылку https://www.techopedia.com/definition/5399/static-web-page, то наверно скажите, что её составляли идиоты.
                        • –6
                          Боже, в какое дно превратили Интернет любители NodeJS. Это не сайты, а уродцы какие-то. Тоже самое можно сделать в десять раз проще если выкинуть весь этот ужас.
                          • +1
                            Это не сайты, а уродцы какие-то.

                            Уродство с точки зрения сборки или с точки зрения получившегося результата? Если второе, то что там уродского? Обычныt Bootstrap страницы.

                            Тоже самое можно сделать в десять раз проще если выкинуть весь этот ужас.

                            Специально в статье приведена постановка задачи. Скажите как её можно решить в десять раз проще. Честно, буду очень признателен.
                            • –4
                              С точки зрения процесса. Нафиг NodeJS нужен для сайтов я так и не понял.
                              Типичный мой проект:
                              1. Сгенерированные статические роутеры через vibed c шаблонизатором pug/diet
                              2. Динамические части на vuejs подгружаемые через http-vue
                              3. css по вкусу, которую можно прям в компонентах vue разместить.

                              В итоге проект весит сотни килобайт (исходники). Переносится с компа на комп копи-пастом. Никаких мучительных конфигураций веб-пака и развертывания NodeJS.
                              • 0
                                Смутно представляю эти технологии, так что задам несколько вопросов.
                                Правильно ли я понимаю, что это получается скорее web приложение, чем обычные web страницы, где html код можно посмотреть просто в html файлах?
                                Как я понимаю, вначале были прописаны эти исходники, настроены и всё такое. А чем тогда от предложенного способа отличается? Мне нужно будет в другом проекте написать только npm i и вся настроенная система появится опять.
                                Где-нибудь можно посмотреть на пример такого проекта? Возможно от webpack перейду к vue.js.
                                • –1
                                  Нет получаются очень удобные html-страницы. Которые генерятся на сервере т.е. их видит поисковик. Всякие модные интерактивные компоненты можно оформлять именно как веб-компоненты и подгружать в нужные места.
                                  Тоесть проблем с индексацией не будет. Страницы все открываются пулей.
                                  pug шаблон позволяет описать каркас сайта один раз и потом просто цеплять к нему нужные блоки.
                                  Пример кину в личку.
                                • 0

                                  vibed — это вы имеете в виду vibe-d, веб-сервер на D?

                                  • 0
                                    Да, именно
                                    • +4

                                      Так это же веб-сервер, его запускать где-то надо.


                                      Статья про принципиально другой подход. Статический сайт собирается из исходников скриптом и получается несколько html-файлов. Эти файлы загружаются на любое файлохранилище (surge, netlify или Github pages). Затем прикручиваете кастомный домен, и сайт готов. Поддерживать запущенный сервер не надо вообще.

                                • –3
                                  Открыть любой текстовый редактор и решить. В 10 раз проще и главное быстрее. Постановка задачи на самом деле, это первое предложение, а второе предложение:
                                  Необходимо написать проект, который бы собирал наш сайт из исходников:

                                  Это создание надуманной проблемы которую потом всю остальную часть статьи приходится решать.
                                  • +4
                                    Открыть любой текстовый редактор и решить.


                                    Ввиду того, что раньше так и делал, то могу оценить насколько эти два варианта. Во-первых, без SCSS работу с CSS я сейчас вообще не представляю. Повышает удобство работы во много раз. Можно найти программы, которые позволяют работать с SASS без node.js (например, Koala), но внутри них также стоят настроенные бандлеры. Во-вторых, работа с js с модулями и компоновкой в один файл также сильно повышает удобство работы (но не так сильно). В-третьих, шаблонизаторы с возможностью написания условий, циклов — так это вообще красота. А вручную прописывать один и тот же код шапки, например, в сотнях html файлах — это ад.
                              • 0
                                А такой инструмент parceljs.org рассматривали?
                                Для подобных задач вроде как подходит.
                                • 0
                                  Опа… Надо будет на него глянуть. В декабре было 8725 звезд на гихабе, а сейчас 20338! Правда число установок в node.js в месяц у webpack пока еще сильно больше: 13 188 708 против 35 097. Но обязательно посмотрю на этого зверя.
                                  • +1
                                    Webpack сильно врос в экосистему — те же create-react-app/create-react-native-app.
                                    Webpack появился и обошел browserify как ответ на необходимость экспериментировать и настраивать.
                                    А parceljs появился когда стэк устаканился и с конфигами наигрались.
                                    Кстати, у того же browserify все ещё 3,5 млн загрузок. Раз вас это радует)
                                • 0
                                  И если добавить стили библиотеки в наш style.scss, то file-loader будет искать картинки библиотеки lightgallery в моей папке src/img, а не там, где они находятся. И побороть я это не смог.

                                  Если я правильно понял суть проблемы, то она решается с помощью resolve-url-loader
                                  • 0
                                    Спасибо! Да, данный пакет решает проблему с путями в разных sass файлах. Добавил в статью об этом пакете пример решения.
                                  • +1

                                    Как раз в процессе переноса сборки с gulp'а на webppack — так что Ваша статья в руку, есть то, с чем я еще не разобрался.


                                    И если добавить стили библиотеки в наш style.scss, то file-loader будет искать картинки библиотеки lightgallery в моей папке src/img, а не там, где они находятся. И побороть я это не смог.

                                    Опция exclude не поможет, примерно как здесь указано — https://stackoverflow.com/questions/44211666/how-to-exclude-bootstrap-files-when-css-modules-is-enabled ?


                                    Обратите внимание на то, что в точках входа entryмы добавили новый входной файл style.scss

                                    Кстати, если в output указать не конкретное имя, а чанки, напр.,


                                    filename: "js/[name].js",

                                    то в папке dist/js появится style.js… пустой) (ну, не совсем пустой — там будет вебпаковаская обертка, но по сути, ничего не делающая, поскольку стили извлечены extract плагином)


                                    Поэтому для итоговой сборки рекомендую использовать не команду *npm run build, а команду npm run build-and-beautify.

                                    В npm можно указывать команды с префиксами "pre" и "post", которые запустяться, соотвественно, до и после основной команды, т.е. вот это:


                                    "scripts": {
                                        "build-and-beautify": "webpack --mode production && html dist/*.html --indent-size 2",
                                        "beautify": "html dist/*.html --indent-size 2"
                                      },

                                    можно записать так:


                                    "scripts": {
                                        "build": "webpack --mode production && html dist/*.html --indent-size 2",
                                        "postbuild": "html dist/*.html --indent-size 2"
                                      },

                                    команда postbuild запуститься автоматически после выполнения build

                                    • +1

                                      Еще очень рекомендую эту книгу о вебпак — SurviveJS — Webpack
                                      И курс Ильи Кантора (хоть и 2015 г., но все равно полезный в плане понимация настройки Webpack) — Скринкаст по Webpack

                                      • +1

                                        поправка к "build-and-beautify":


                                        "scripts": {
                                            "beautify": "html dist/*.html --indent-size 2",
                                            "build": "webpack --mode production",
                                            "postbuild": "npm run beautify"
                                          },
                                        • 0
                                          exclude к сожалению не поможет, но поможет пакет resolve-url-loader, как отметили выше.

                                          Про скрипты с приставкой post не знал. Спасибо!
                                          • 0

                                            Про файлы-пустышки, кода имя указываешь через filename: "js/[name].js". Сам столкнулся с этим.
                                            Можно решить через пакет webpack-extraneous-file-cleanup-plugin. Но он пока с webpack 4 не работает. Но есть pull request, который эту проблему решает.
                                            Или можно, указать массив точек входа:


                                            entry: {
                                                'app': [
                                                  './src/js/index.js',
                                                  './src/scss/style.scss'
                                                ]
                                              },

                                            С несколькими файлами:


                                            entry: {
                                                'app': [
                                                  './src/js/index.js',
                                                  './src/scss/style.scss'
                                                ],
                                                '../katex/katex': [
                                                  './src/js/katex.js',
                                                  './src/scss/katex.scss'
                                                ]
                                              },

                                            Вышеприведенный код создаст app.js, app.css, katex.css, katex.js. И пустышек не будет.

                                          • 0
                                            * промах
                                            • –2
                                              Мда, простые статичные сайты уже не те.

                                              В чем преимущества такого подхода вместо того чтобы просто выложить файлы на сервер?
                                              • +1
                                                Прежде чем выложить файлы, их нужно как-то подготовить. Например, формируется документация в виде HTML страниц. Страниц таких больше сотни. И допустим, я писал их вручную. И потом мне в шапке каждой страницы нужно что-то поменять. Как?
                                                • –1
                                                  Ну, например автозаменой по регулярке.
                                                  В том самом текстовом редакторе, в которым вы их писали вручную.
                                                  • 0
                                                    Например, в шапке в пунктах меню некоторые пункты, которые соответствуют файлам html выделены классом active. И при этом регулярка сильно усложняется.
                                                    Я молчу про сборку sass и js файлов.
                                              • +1
                                                Интересно, можно ли использовать JSX в отрыве от реакта. Как шаблонизатор. В итоге, сочетая с описанной в статье конфигурацией, в итоге можно посадить дизайнера-верстальщика делать макет, а потом скопировать верстку в реальный проект (естесственно с некоторыми правками — другим разбиением на компоненты, с заточками под условное отображение и т.п.).
                                                • 0
                                                  JSX is an XML-like syntax extension to ECMAScript without any defined semantics.
                                                  This specification does not attempt to comply with any XML or HTML specification. JSX is designed as an ECMAScript feature and the similarity to XML is only for familiarity.
                                                  • +1
                                                    Запросто. Написать свою функцию, которая как-то воспринимает jsx и сказать babel её использовать.

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

                                                  Самое читаемое