Пакетные менеджеры и системы сборки модулей

Разработка динамических веб приложений

Гладкий Максим Валерьевич / github:MaksHladki

Содержание лекции

Node.js за 5 минут

Node.js

Программная платформа, основанная на движке V8 (транслирует JS в машинный код), превращающая JavaScript из узкоспециализированного языка в язык общего назначения. Node.js добавляет возможность JavaScript взаимодействовать с устройствами ввода-вывода через свой API (на C++), подключать другие библиотеки, написанные на разных языках, обеспечивая вызовы к ним из JS-кода

Цель проекта

Предоставить естественную неблокирующую, событийно-ориентированную инфраструктуру для написания программ с высокой конкурентностью (с) Ryan Dahl

Особенности

  • Использует движок Google V8 (Chromium: Google Chrome, Chrome OS...)
  • Превращает V8 в мощную машину для серверных приложений
  • Сохраняет философию JavaScript
  • Event loop - неблокирующий ввод/вывод

Для чего подходит Node

Много I/O

  • RIA (rich internet application)
  • API
  • Proxy

Realtime

  • Чаты
  • Онлайн игры
  • Трансляции
  • Модель Publish/Subscribe

Вспомним Event loop

Пример кода

						
// Загружаем модуль http
var http = require('http');

// Создаем web-сервер с обработчиком запросов
var server = http.createServer(function (req, res) {
    console.log('Начало обработки запроса');
    // Передаем код ответа и http-заголовки
    res.writeHead(200, {
        'Content-Type': 'text/plain; charset=UTF-8'
    });
    res.end('Hello world!');
});

// Запускаем web-сервер
server.listen(2002, "127.0.0.1", function () {
    console.log('Сервер запущен http://127.0.0.1:2002/');
});
						
					

C чего начать

https://nodejs.org/

  • LTS - расширенная поддержка
  • Current - новый функционал

Hello world

						
>node -v //9.5.0
>node --version //9.5.0
						
					
						
>node
>console.log('Hello world');
//Hello world
						
					
						
//создаем файл index.js
console.log('Hello World');

>node index.js
//Hello World
						
					

Менеджер пакетов Node (NPM)

Node Package Manager

  • Создан в 2009 года open source сообществом
  • Цель - помочь JS-разработчикам "шарить" модули с кодом
  • Private/Public модули
  • Command-line клиент
  • Используется даже теми разработчиками, кто не работает с Node.js
  • Входит в состав пакета Node.js

Минусы NPM

  • Установка небольшого пакета может привести к скачиванию еще 5-10 пакетов, на которые ссылается данный пакет
  • Удаление автором пакета может привести к "бездействию" программистов, зависимые пакеты нельзя будет скачать/обновить (до 2016г.)

Пакеты Node.js

  • Глобальные - {prefix}/lib/node_modules/ или %AppData%\npm\node_modules
  • Локальные - /node_modules (в той же директории, где запущен npm install)

По умолчанию npm будет устанавливать все пакеты в локальном каталоге, в котором вы сейчас работаете

Команды

install установка пакета
search поиск пакета
view просмотр информации о пакете
uninstall удаление пакета
link устанавливает связь между пакетами
unlink удаляет связь между пакетами
init инициализация нового пакета
publish публикация пакета в репозитории
unpublish удаление пакета из репозитория
owner изменение прав доступа к пакету в репозитории
adduser добавление нового пользователя
list список установленных пакетов
update обновление пакета либо самого npm
config установка параметров конфигурации

Команда install

						
>npm install 'package name'
>npm install -g 'package name'	
>npm install --global 'package name'
						
					
						
>npm install -g gulp
						
					
						
>npm install gulp@3.9.1
						
					

Еще пример

						
> npm install uglify-js --global
└─┬ uglify-js@2.6.2
  ├── async@0.2.10
  ├── source-map@0.5.3
  ├── uglify-to-browserify@1.0.2
  └─┬ yargs@3.10.0
    ├── camelcase@1.2.1
    ├─┬ cliui@2.1.0
    │ ├─┬ center-align@0.1.3
    │ │ ├─┬ align-text@0.1.4
    │ │ │ ├─┬ kind-of@3.0.2
    │ │ │ │ └── is-buffer@1.1.2
    │ │ │ ├── longest@1.0.1
    │ │ │ └── repeat-string@1.5.4
    │ │ └── lazy-cache@1.0.3
    │ ├── right-align@0.1.3
    │ └── wordwrap@0.0.2
    ├─┬ decamelize@1.1.2
    │ └── escape-string-regexp@1.0.5
    └── window-size@0.1.0
						
					

Команда search

						
>npm search 'package name'
//не удобно		

//лучше использовать ui интерфейс
https://www.npmjs.com/search?q=gulp
						
					

Команда view

						
>npm view 'package name'
						
					
						
>npm view gulp

 { name: 'gulp',
  description: 'The streaming build system',
  dist-tags: { latest: '3.9.1' },
  versions:
   [ '0.0.1',
     '0.0.2',
     ......
   ]
  author: 'Fractal (http://wearefractal.com/)',
  repository: { 
      type: 'git',
      url: 'git+https://github.com/gulpjs/gulp.git' },
  users:
   { jden: true,
     stevelacy: true,
     .....
   }
 }
						
					

Команда update

						
>npm update [-g] 'package name' //latest version
						
					
						
>npm update -g gulp
>npm update -g npm						
						
					

Команда config

						
>npm config set 'key' 'value' [--global] 
>npm config get 'key' 
>npm config delete 'key' 
>npm config list 
>npm config edit npm c [set|get|delete|list] 
>npm get 'key' 
>npm set 'key' 'value' [--global]
						
					
						
>npm set init.author.name "maks"
>npm set init.author.email "test@gmail.com"
>npm set init.author.url "https://google.com"
						
					

Команда uninstall

						
>npm uninstall 'package name' [@ver] [--save| [--save-dev] [--global]
//--save пакет будет удален из dependencies
//--save-dev пакет будет удален из devDependencies
						
					
						
>npm uninstall sax --save
>npm uninstall gulp --global
>npm uninstall node-tap --save-dev
>npm uninstall dtrace-provider --save-optional
						
					

Команда list

						
>npm list [--global] [--depth]
						
					
						
>npm list -g --depth=0
//без лишних зависимостей
├── npm@3.7.5
└── uglify-js@2.6.2
						
					

package.json

Файл содержит в себе информацию о приложении: название, версию, зависимости и тому подобное. Любая директория, в которой есть этот файл, интерпретируется как Node.js-пакет, даже если вы не собираетесь публиковать его

Основные поля

name имя пакета
description описание пакета
version версия пакета
keywords набор ключевых слов
author информация об авторе пакета
main главный файл пакета
license тип лицензии
dependencies все пакеты, от которых зависит текущий пакет (production)
devDependencies все пакеты, от которых зависит текущий пакет (development)
repository тип репозитория и ссылка на него
config установка параметров конфигурации

Пример

						
{
  "name": "ddwa",
  "version": "1.0.0",
  "description": "",
  "main": "index.html",
  "scripts": {
    "build": "gulp build"
  },
  "author": "Maksim Hladki",
  "license": "MIT",
  "devDependencies": {
    "del": "^2.2.2",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^3.1.1",
    "gulp-clean-css": "^2.3.2",
    "gulp-concat": "^2.6.1",
    "gulp-connect": "^5.0.0",
    .........
  }
}
						
					

Спецификация версий пакетов

Semantic Versioning
						
MAJOR.MINOR.PATCH
						
					
						
{ "dependencies" :
  { 
    "foo" : "1.0.0 - 2.9999.9999",
    "bar" : ">=1.0.2 <2.1.2",
    "baz" : ">1.0.2 <=2.3.4",
    "boo" : "2.0.1",
    "qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0",
    "asd" : "http://asdf.com/asdf.tar.gz",
    "til" : "~1.2",
    "elf" : "~1.2.3",// >= 1.2.3 < 1.3.0
    "tra" : "^1.2.1",// >= 1.2.1 < 2.0.0
    "two" : "2.x",
    "thr" : "3.3.x",
    "lat" : "latest",
    "dyl" : "file:../dyl"
  }
}
						
					

package-lock.json (npm 5+)

					
{
  "requires": true,
  "lockfileVersion": 1,
  "dependencies": {
    "ansi-gray": {
      "version": "0.1.1",
      "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
      "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
      "dev": true,
      "requires": {
        "ansi-wrap": "0.1.0"
      }
    },
    "ansi-regex": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
      "dev": true
	},
............................
					
				

NPM и VS Code

  • npm - Florian Knop
  • npm - egamma
  • npm Intellisense - Christian Kohler

Менеджер пакетов Bower

Bower

Менеджер пакетов, ориентированный на применении в разработке клиентской части веб-приложений с использованием JS библиотек и фреймворков
						
>npm install -g bower
						
					
						
//установка пакета angular
>bower install angular

//установка пакета из репозитория
>bower install git://github.com/angular/angular.js/
						
					

Пример

						
>bower install jquery
>bower cloning git://github.com/components/jquery.git
>bower cached git://github.com/components/jquery.git
>bower fetching jquery
>bower checking out jquery#2.0.0
>bower copying /Users/danheberden/.bower/cache/jquery/29cb...
>bower installing jquery#2.0.0
						
					

Плюсы использования

  • Делает установку зависимостей более легкой
  • Легкая настройка с использованием bower.json
  • Все пакеты загружаются в единую директорию /bower_components (если зависимость уже загружена, заново она не загружается)
  • Механизм разрешения конфликтных ситуаций

Bower vs npm

Главное отличие — подход к установке зависимостей пакетов. Npm устанавливает зависимости для каждого пакета отдельно, в папку этого пакета, потом так же ставит зависимости зависимостей и так далее. В frontend-разработке это недопустимо: нельзя подключить на страницу две версии библиотеки
В Бовере каждый пакет устанавливается один раз, и в случае конфликта зависимостей Бовер просто не станет устанавливать пакет, несовместимый с уже установленными

Данное ограничение npm было устранено в 2016 году

Основные команды

search поиск пакета по имени
install установка пакета
list список установленных пакетов
update обновление выбранного пакета
uninstall удаление пакета
init инициализация конфигурационного файла bower.json

Команда search

						
>bower search jquery
Search results:
    jQuery https://github.com/jquery/jquery.git
    jquery https://github.com/jquery/jquery-dist.git
    jquery.x https://github.com/jljLabs/jquery.x.git
    jt_jquery https://github.com/vicanso/jt_jquery.git
    jquery.Q https://github.com/jsbuzz/jQuery_Q.git
    jquery-m https://github.com/meetup/jquery.git
    jquery.j2d https://github.com/fsggs/jquery.j2d.git
    jquery.hx https://github.com/millennialmedia/jquery.hx.git
    jquery-ts https://github.com/andraaspar/jquery-ts.git
    jquery-tm https://github.com/trymore/jquery-tm.git
    jquery.gk https://github.com/ezoapp/jquery.gk.git
    jquery-jec https://github.com/RaYell/jquery-jec.git
	........
						
					

Команда install

						
>bower install jquery#2.0.0
//result
bower jquery#2.0.0  not-cached https://github.com/jquery/jquery-dist.git#2.0.0
bower jquery#2.0.0  resolve https://github.com/jquery/jquery-dist.git#2.0.0
bower jquery#2.0.0  download https://github.com/jquery/jquery-dist/2.0.0.tar.gz
bower jquery#2.0.0  extract archive.tar.gz
bower jquery#2.0.0  resolved https://github.com/jquery/jquery-dist.git#2.0.0
jquery#2.0.0 bower_components\jquery
						
					

Команда list

						
>bower list
//result
bower check-new  Checking for new versions of the project dependencies...
HP C:\Users\HP
└── jquery#3.1.1 extraneous
						
					

Команда uninstall

						
>bower uninstall jquery
						
					

Команда init

						
>bower init
//bower.json
{
  name: 'Maks Hladki',
  authors: [
    'Maks Hladki <MaksHladki@gmail.com>'
  ],
  description: 'test project',
  main: 'index.html',
  keywords: [
    'js',
    'frontend'
  ],
  license: 'MIT',
  homepage: 'https://github.com/MaksHladki/DDWA'.
  dependencies: {
        "jquery": "~3.1.1",
  },
  devDependencies: {}
}
						
					

Bower и VS Code

  • Bower - Don Jayamanne

Стоит ли использовать?

psst! While Bower is maintained, we recommend using Yarn and Webpack for front-end projects read how to migrate!

Менеджер пакетов yarn

Yarn

Менеджер пакетов, совместно созданный Facebook, Google, Exponent и Tilde. Является современной альтернативой пакетного менеджера npm, отличается высокой скоростью, надежностью и безопасностью

Преимущества над npm

  • Распараллеливание операций для быстрой установки пакетов
  • Устойчивость к сбоям во время установки. В случае ошибки yarn отправит повторный запрос
  • Проверка целостности пакетов
  • Одинаковая установка и работа на разных системах
  • Поддержка эмодзи (и с котиками тоже)
  • Пакеты скачиваются с npm-репозитория (npm.org)
  • Кеширование всех скачанных пакетов локально, что в будущем дает их установку без подключения к сети

Сравнение скорости установки

Пакет npm 4 npm 5 Yarn
express 8 сек. 6 сек. 5.5 сек.
gulp 11 сек. 9 сек. 8 сек.

Популярные команды

init инициализация нового пакета (создает файл package.json)
install устанавливает все зависимости из файла package.json
add добавляет пакет и устанавливает зависимости
upgrade обновляет пакеты до последней/определенной версии
remove удяляет пакет и зависимости из package.json и yarn.lock
licenses выводит список лицензий всех установленных в проекте пакетов
why проверяет граф зависимостей и выясняет, почему указанный пакет установлен в проекте
generate-lock-entry генерирует файл yarn.lock на основе зависимостей из package.json (похоже на npm shrinkwrap)
config устанавливает конфигурационные значения в виде пар ключ-значение
list выводит список зависимостей
pack создает gzip-архив зависимостей
publish позволяет опубликовать пакет в npmjs.com
global позволяет выполнять команды add, bin, list и remove в глобальной области

Команда init

						
yarn init [--yes]						
						
					
					
>yarn init

yarn init v1.3.2
question name (DDWA): DDWA
question version (1.0.0): 0.0.7
question description: Development of dynamic web applications
question entry point (index.js): index.js
question repository url: https://github.com/MaksHladki/DDWA
question author: Maksim Hladki
question license (MIT): MIT
question private: without private key
success Saved package.json
Done in 19.30s.
					
				

Команда install

						
yarn install [--check-files] [--ignore-scripts] [--force]
						
					
					
>yarn install

yarn install v1.3.2
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
info Lockfile not saved, no dependencies.
Done in 0.34s.

					
				

Команда add

					
yarn add [package-name] [--dev/-D]
yarn add [package]@[version]
					
				
					
>yarn add gulp --dev

yarn add v1.3.2
[1/4] Resolving packages...
warning gulp > gulp-util@3.0.8: gulp-util is deprecated
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 200 new dependencies.
├─ ansi-regex@2.1.1
├─ ansi-wrap@0.1.0
├─ archy@1.0.0
....
├─ vinyl-fs@0.3.14
├─ vinyl@0.4.6
├─ which@1.3.0
└─ xtend@4.0.1
Done in 16.12s.
					
				

Команда upgrade

					
yarn upgrade [package]
yarn upgrade [package]@[version]
					
				
					
>yarn upgrade gulp@3.9.0

yarn upgrade v1.3.2
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Rebuilding all packages...
success Saved lockfile.
success Saved 2 new dependencies.
├─ gulp@3.9.0
└─ interpret@0.6.6
Done in 4.18s.
					
				

Команда remove

					
yarn remove [package] --[flag]
					
				
					
>yarn remove gulp

yarn remove v1.3.2
[1/2] Removing module gulp...
[2/2] Regenerating lockfile and installing missing dependencies...
success Uninstalled packages.
Done in 1.78s.
					
				

Команда global

					
yarn global add/bin/list/remove/upgrade [--prefix]
					
				
					
>yarn global add gulp

yarn global v1.3.2
[1/4] Resolving packages...
warning gulp > gulp-util@3.0.8: gulp-util is deprecated
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Installed "gulp@3.9.1" with binaries:
      - gulp
Done in 9.05s.
					
				

Режим offline

					
>yarn add jquery
					
				
					
>yarn add jquery

yarn add v1.3.2
[1/4] Resolving packages...
error An unexpected error occurred: 
"https://registry.yarnpkg.com/jquery:
getaddrinfo ENOENT registry.yarnpkg.com:443".	
					
				
					
>yarn add jquery --offline

yarn add v1.3.2
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved 1 new dependency.
└─ jquery@3.3.1
Done in 0.58s.				
					
				

Файл yarn.lock

Позволяет получить согласованную установку зависимостей на разных машинах путем фиксации конкретной версии каждого пакета
					
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


jquery@^3.3.1:
  version "3.3.1"
  resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
					
				

Варианты установки

  • Использование установщика под конкретную ОС
  • npm

Yarn и VS Code

  • yarn - Gamunu Balagalla

Cистема сборки и выполнения задач

Процесс front-end разработки

Develop Build
Watch Testing
Live Reload Compile
ES6/TypeSctipt/Coffee Concat
Sass/Less Rename
JsHint Minify
Image optimization
Deployment

Популярные системы сборки

  • Grunt
  • Gulp
  • Webpack
  • Browserify

Gulp vs Grunt

Сходства

  • Минификация кода
  • Анализ качества кода
  • Оптимизация изображений
  • Добавление вендорных (браузерных) префиксов
  • Тестирование
  • Многое другое

Различия

Gulp Grunt
ориентирован на поток ориентирован на файл
запускает задачи параллельно запускает задачи последовательно
синтаксис более краток и лаконичен основан на конфигурации
проектировался для больших проектов проектировался для средних и небольших проектов
скорость выполнения выше

Gulp

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

						
├── app/
      ├── css/
      ├── fonts/
      ├── images/ 
      ├── index.html
      ├── js/ 
      └── scss/
├── dist/
├── gulpfile.js
├── node_modules/
└── package.json
						
					

Еще пример

						
├── .tmp
│   ├── app.css
│   ├── app.js
│   └── index.html
├── bower_components
│   └── angular
├── dist
│   ├── app.min.css
│   ├── app.min.js
│   └── index.html
├── src
    ├── app.scss
    ├── app.ts
    ├── components
    └── index.html
├── gulpfile.js
├── node_modules/
└── package.json
						
					

Особенности

  • Легок в использовании
  • Эффективен
  • Высокая надежность
  • Прост в изучении

Легок в использовании

Предпочитая код конфигурации, Gulp оставляет простые задачи простыми, а из сложных делает управляемые
						
const gulp = require('gulp'),
      coffee = require('gulp-coffee'),
      concat = require('gulp-concat'),
      cssmin = require('gulp-cssmin');

gulp.task('coffee', () => {
    //TODO
});
						
					

Эффективен

Используя преимущества потока данных, процесс выполнения задач получается более быстрым, промежуточные файлы не создаются и на диск не записываются (Vinul FS)

Пример

						
gulp.task('html', () => {
    return gulp.src(srcPath.html)// Считываем файл с диска
        .pipe(useref()) //Обрабатываем в оперативной памяти
        .pipe(gulpif('*.js', uglify())) //Обрабатываем в ОП
        .pipe(gulpif('*.css',
            pipe(
                autoprefixer({
                    browsers: ['last 2 versions']
                }),
                cssmin()
            )
        ))// Обрабатываем в оперативной памяти
        .pipe(gulp.dest(distPath.html))// Записываем файл на диск
        .pipe(connect.reload());
});
						
					

Высокая надежность

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

Общее количество плагинов на декабрь 2016: 1100+

Прост в изучении

Можно начать работать с Gulp с минимальными знаниями API. Нужно всего лишь понимать принцип работы основных четырех функций: task, watch, src и dest

Установка и запуск Gulp

  1. Создать package.json
  2. Установить Gulp глобально
  3. Установить Gulp локально как зависимость
  4. Создать gulpfile.js
  5. Выполнить команду gulp
						
>npm install --global gulp

>npm install --save-dev gulp
						
					

Пример gulpfile.js

						
var gulp  = require('gulp');

gulp.task('default', function() {
    console.log('Gulp is running!');
});
						
					
						
>gulp

[16:20:01] Using gulpfile G:\test\gulpfile.js
[16:20:01] Starting 'default'...
Gulp is running!
[16:20:01] Finished 'default' after 200 µs
						
					

Еще пример

						
>gulp default
//если имя задачи не установлено, вызывается задача с именем default
[16:21:17] Using gulpfile G:\test\gulpfile.js
[16:21:17] Starting 'default'...
Gulp is running!
[16:21:17] Finished 'default' after 181 µs
						
					

Основные методы Gulp

Метод task

						
gulp.task(name[, deps], fn)
//name - required
//deps - массив зависимых зависимостей
//fn - callback вызова
						
					
						
gulp.task('hello', function() {
   console.log('Hello world!'); 
}); 

gulp.task('css', ['greet'], function () {
   //TODO
}); 

gulp.task('build', ['css', 'js', 'imgs']); 

gulp.task('default', function () { 
   //TODO default task
});
						
					

Метод watch

						
gulp.watch(glob[, opts, fn])
//globs - строка или массив
//options - доп. параметры или callback
						
					
						
gulp.watch('js/**/*.js', function(event) {
  console.log('File ' + event.path + ' type ' + event.type);
});
						
					
						
gulp.task('watch', function(){
    gulp.watch('app/scss/**/*.scss', ['sass']); 
	gulp.watch('app/css/**/*.css', ['css']); 
	gulp.watch('app/js/**/*.js', ['js']); 
    // Other watchers
});
						
					

Метод src

						
gulp.src(globs[, options])
//globs - строка или массив
//options - доп. параметры
//возвращает входной поток
						
					
						
gulp.src('./*.jade')
gulp.src('*.+(js|css)')
gulp.src('*.{jpg,jpeg,png,gif}')
gulp.src(['js/**/*.js', '!js/**/*.min.js'])
						
					

Метод dest

						
gulp.dest(path, [,options])
//принимает путь к файлу и возвращает исходящий поток
						
					
						
gulp.task('css', () => {
    return gulp.src(srcPath.css)
        .on('data', (file) => {
            console.log({
                path:file.path,
                basename: file.basename
            });
        })
        .pipe(sourcemaps.init())
        .pipe(autoprefixer(autoprefixerSettings))
        .pipe(cleanCSS(cleanCSSSettings))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest(distPath.css));
});
						
					

Метод pipe

						
.pipe(destination)
//возвращает поток
//destination - тоже поток
						
					
						
gulp.src('./client/templates/*.jade')
    .pipe(jade())
    .pipe(gulp.dest('./app'))
    .pipe(minify())
    .pipe(gulp.dest('./dist'));
						
					

browser-sync

Пример 1

						
>npm install browser-sync gulp --save-dev
						
					
						
const gulp = require('gulp'),
      browserSync = require('browser-sync').create();
gulp.task('html', () => {
    return gulp.src('src/**/*.html').pipe(gulp.dest('dist/'));
});
gulp.task('css', () => {
    return gulp.src("src/css/*.css")
        .pipe(gulp.dest("dist/css"))
        .pipe(browserSync.stream());//*
});
gulp.task('serve', ['html' ,'css'], () => {
    browserSync.init({ server: "./dist" });
});
gulp.task('watch', () => {
  gulp.watch('src/css/*.css', ['css']);
  gulp.watch('src/**/*.html', ['html'])
      .on('change', browserSync.reload);//*
});
gulp.task('default', ['serve', 'watch']);
						
					

Пример 2

						
gulp.task('serve', () => {
    browserSync.init({
        server: distPath.dist,
        port: 4000
    });

    browserSync.watch(distPath.dist).on('change', browserSync.reload);
});
						
					

Метод reload

						
browserSync.reload();

browserSync.reload("styles.css");

browserSync.reload(["styles.css", "ie.css"]);

browserSync.reload("*.css");
						
					

Метод stream

						
//возвращает трансформированный поток данных, 
//автоматически вставляемых в браузер

gulp.task('sass', function () {
    return gulp.src('scss/styles.scss')
        .pipe(sass({includePaths: ['scss']}))
        .pipe(gulp.dest('css'))
        .pipe(browserSync.stream());
        //автоматически вставляет трансформированный поток в браузер
});
						
					
						
.pipe(browserSync.stream({once: true}));
// перегружает только один раз для потока данных 
//(исходных файлов может быть несколько)
						
					
						
.pipe(browserSync.stream({match: "**/*.css"}));
//фильтр обновления только определенных файлов
						
					

Метод exit

						
var browserSync = require("browser-sync").create();

// запускаем сервер
browserSync.init({server: "./app"});

// останавливаем сервер через 5 секунд
setTimeout(function () {
    browserSync.exit();
}, 5000);
						
					

Метод init

						
browserSync.init({
    server: "./dist",
    port: 8000,
    index: 'index.html',
    //proxy: "localhost:8888",
    browser: ["firefox"]
});
						
					

gulp-connect

Пример

						
>npm install --save-dev gulp-connect
						
					
						
const gulp = require('gulp'),
    connect = require('gulp-connect');

gulp.task('html', () => {
    return gulp.src('src/**/*.html')
        .pipe(gulp.dest('dist/'))
        .pipe(connect.reload()); //*
})

gulp.task('serve', ['html'], () => {
    connect.server({
        root: 'dist', livereload: true //*
    });
});

gulp.task('watch', () => {
    gulp.watch('src/**/*.html', ['html']);
});

gulp.task('default', ['serve', 'watch']);
						
					

Запуск и остановка

						
gulp.task('something', function() {
  connect.server({
    port: 8888
  });
  
  //BODY

  connect.serverClose();
});
						
					

Параметры инициализации

						
gulp.task('serve', ['html'], () => {
    connect.server({
        name: 'Test application',
        root: ['dist', 'tmp'],
        port: 8000,
        livereload: true,
        index: 'index.html'
    });
});
						
					

gulp-autoprefixer

Пример 1

						
>npm install --save-dev gulp-autoprefixer
						
					
						
const gulp = require('gulp'),
      autoprefixer = require('gulp-autoprefixer');
 
gulp.task('css', () =>
    gulp.src('src/app.css')
        .pipe(autoprefixer({
            browsers: ['last 2 versions']
        }))
        .pipe(gulp.dest('dist/css/'))
);
						
					
						
a {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex
}
						
					

Пример 2

						
const gulp = require('gulp'),
      sourcemaps = require('gulp-sourcemaps'),
      autoprefixer = require('gulp-autoprefixer'),
      concat = require('gulp-concat');
 
gulp.task('css', () =>
    gulp.src('src/**/*.css')
        .pipe(sourcemaps.init())
        .pipe(autoprefixer())
        .pipe(concat('all.css'))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest('dist/css/'))
);
						
					

Параметры инициализации

						
autoprefixer(
	{
        browsers: ['last 2 versions', 'ie 9', '> 1%'],
        cascade: true, //визуальный каскад, если css не сжат
        supports: false,//добавлять префикс к @supports параметрам,
        stats: { //собственная статистика
                 "ie": {
                         "6": 0.01,
                         "7": 0.4,
                         "8": 1.5
                       },
                 "chrome": {
                      …
                           },
                      …
                } 
    }
)					
						
					

Browsers list

  • last 2 versions
  • last 2 Chrome versions
  • > 5%
  • > 5% in US
  • ie 6-8
  • ie 10
  • Firefox >= 20
  • iOS 7
  • not ie <=8

Браузеры

  • Chrome
  • Firefox (ff)
  • Explorer (ie)
  • Edge
  • iOS
  • Opera
  • Safari (ios_saf)
  • ExplorerMobile
  • Android
  • BlackBerry (bb)
  • ChromeAndroid
  • FirefoxAndroid
  • OperaMobile
  • OperaMini
  • Samsung

gulp-clean-css

Пример 1

						
>npm install gulp-clean-css --save-dev
						
					
						
const gulp = require('gulp'),
      cleanCSS = require('gulp-clean-css');

gulp.task('css', () => {
  return gulp.src('app/styles/*.css')
    .pipe(cleanCSS({compatibility: 'ie8'}))//ie8, * или '' для ie9+
    .pipe(gulp.dest('dist/'));
});
						
					

Пример 2

						
const gulp = require('gulp'),
      cleanCSS = require('gulp-clean-css');

gulp.task('css', function() {
    return gulp.src('app/styles/*.css')
        .pipe(cleanCSS({debug: true}, (details) => {
            console.log(details.name + ': ' + details.stats.originalSize);
            console.log(details.name + ': ' + details.stats.minifiedSize);
        }))
        .pipe(gulp.dest('dist/css/'));
});
						
					

Пример 3

						
const gulp = require('gulp'),
      cleanCSS = require('gulp-clean-css'),
      sourcemaps = require('gulp-sourcemaps');

gulp.task('css', function() {
    return gulp.src('app/styles/*.css')
        .pipe(sourcemaps.init())
        .pipe(cleanCSS())
        .pipe(sourcemaps.write())
        .pipe(gulp.dest('dist/css/'));
    });
});
						
					

gulp-sass

Пример 1

						
>npm install gulp-sass --save-dev
						
					
						
const gulp = require('gulp'),
      sass = require('gulp-sass');
 
gulp.task('sass', () => {
  return gulp.src('./sass/**/*.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('./css'));
});
 
gulp.task('watch', () => {
  gulp.watch('./sass/**/*.scss', ['sass']);
});
						
					

Пример 2

						
const gulp = require('gulp'),
      sass = require('gulp-sass');
 
gulp.task('sass', () => {
  return gulp.src('./sass/**/*.scss')
    .pipe(sass.sync().on('error', sass.logError))
     //выполняется синхронно
    .pipe(gulp.dest('./css'));
});
 
gulp.task('watch', () => {
  gulp.watch('./sass/**/*.scss', ['sass']);
});
						
					

Пример 3

						
const gulp = require('gulp'),
      sass = require('gulp-sass'),
      sourcemaps = require('gulp-sourcemaps');
 
gulp.task('sass', function () {
 return gulp.src('./sass/**/*.scss')
  .pipe(sourcemaps.init())
  .pipe(sass().on('error', sass.logError))
  .pipe(sourcemaps.write())
  .pipe(gulp.dest('./css'));
});
						
					

gulp-less

Пример 1

						
>npm install gulp-less --save-dev
						
					
						
const gulp = require('gulp'),
      less = require('gulp-less');

gulp.task('less', () => {
    return gulp.src('./less/**/*.less')
               .pipe(less())
               .pipe(gulp.dest('./public/css'));
});
						
					

Пример 2

						
const gulp = require('gulp'),
      less = require('gulp-less'),
      LessAutoprefix = require('less-plugin-autoprefix');

const autoprefix = new LessAutoprefix({ browsers: ['last 2 versions'] });

gulp.task('less', () => {
    return gulp.src('./less/**/*.less')
               .pipe(less({
                   plugins: [autoprefix]
               }))
               .pipe(gulp.dest('./public/css'));
});
						
					

Пример 3

						
const gulp = require('gulp'),
      less = require('gulp-less'),
      sourcemaps = require('gulp-sourcemaps');

gulp.task('less', () => {
return gulp.src('./less/**/*.less')
  .pipe(sourcemaps.init())
  .pipe(less())
  .pipe(sourcemaps.write())
  .pipe(gulp.dest('./public/css'));
});
						
					

gulp-concat

Пример 1

						
>npm install --save-dev gulp-concat
						
					
						
const gulp = require('gulp'),
      concat = require('gulp-concat');
 
gulp.task('scripts', () => {
  return gulp.src('src/js/*.js')
    .pipe(concat('bundle.js'))
    .pipe(gulp.dest('dist/js/'));
});
						
					

Пример 2

						
const gulp = require('gulp'),
      concat = require('gulp-concat'),
	  sourcemaps = require('gulp-sourcemaps');
 
gulp.task('css', () => {
  return gulp.src('src/**/*.css')
    .pipe(sourcemaps.init())
    .pipe(concat('bundle.css'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist/'));
});
						
					

gulp-imagemin

Пример 1

						
>npm install --save-dev gulp-imagemin
						
					
						
const gulp = require('gulp'),
      imagemin = require('gulp-imagemin');
 
gulp.task('img', () =>
    gulp.src('src/img/*')
        .pipe(imagemin())
        .pipe(gulp.dest('dist/img/'))
);
						
					

Пример 2

						
const gulp = require('gulp'),
      imagemin = require('gulp-imagemin');
 
gulp.task('img', () =>
    gulp.src('src/img/*')
        .pipe(imagemin(
           [ imagemin.gifsicle(), 
		     imagemin.jpegtran(), 
		     imagemin.optipng(), 
		     imagemin.svgo()
		   ],
           { verbose: true})
        )
        .pipe(gulp.dest('dist/img/'))
);

[10:43:01] gulp-imagemin: ✔ vs-code-bower.png (saved 2.97 kB - 11.1%)
[10:43:01] gulp-imagemin: ✔ window.png (saved 11.6 kB - 13.3%)
						
					

Используемые пакеты по умолчанию

  • gifsicle — оптимизация GIF
  • jpegtran — оптимизация JPEG
  • optipng — оптимизация PNG
  • svgo — оптимизация SVG

Дополнительные пакеты

  • imagemin-pngquant
  • imagemin-giflossy
  • imagemin-jpeg-recompress
  • imagemin-gm
  • imagemin-mozjpeg
  • imagemin-webp
  • imagemin-pngout
  • imagemin-zopfli

Конфигурация плагинов

						
…
.pipe(imagemin([
    imagemin.gifsicle({interlaced: true}),
    imagemin.jpegtran({progressive: true}),
    imagemin.optipng({optimizationLevel: 5}),
    imagemin.svgo({
        plugins: [
            {removeViewBox: true},
            {cleanupIDs: false}
        ]
    })
]))
…
						
					

gulp-rename

Пример 1

						
>npm install gulp-rename --save-dev
						
					
						
const gulp = require('gulp'),
      rename = require("gulp-rename");

gulp.task('rename', () => {
    return gulp.src('./src/css/main.css')
                .pipe(rename('css/bundle.css'))
                .pipe(gulp.dest('./dist')); 
});
						
					

Пример 2

						
const gulp = require('gulp'),
      rename = require("gulp-rename");

gulp.task('rename', () => {
    return gulp.src('./src/css/main.css')
               .pipe(rename(function (path) {
                     path.dirname += '/base';
                     path.basename += '-bundle';
                     path.extname = '.css'
                }))
               .pipe(gulp.dest('./dist')); 
               //dist/css/base/main-bundle.css
});
						
					

Пример 3

						
const gulp = require('gulp'),
      rename = require("gulp-rename");

gulp.task('rename', () => {
    return gulp.src("./src/css/main.css")
               .pipe(rename(
                   {
                      dirname: "main/",
                      basename: "main",
                      prefix: "style-",
                      suffix: ".min",
                      extname: ".css"
                   }
               ))
               .pipe(gulp.dest("./dist"));
               //dist/css/main/style-main.min.css
});
						
					

gulp-babel

Пример 1

https://babeljs.io/

						
>npm install --save-dev gulp-babel
>npm install --save-dev babel-preset-es2015 //*
						
					
						
const gulp = require('gulp'),
      babel = require('gulp-babel');

gulp.task('default', () =>
    gulp.src('src/app.js')
        .pipe(babel({
            presets: ['es2015']
        }))
        .pipe(gulp.dest('dist'))
);
						
					

Пример 2

						
const gulp = require('gulp'),
      sourcemaps = require('gulp-sourcemaps'),
      babel = require('gulp-babel'),
      concat = require('gulp-concat');

gulp.task('default', () =>
    gulp.src('src/**/*.js')
        .pipe(sourcemaps.init())
        .pipe(babel({
            presets: ['es2015']
        }))
        .pipe(concat('all.js'))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest('dist'))
);
						
					

Пример 3

						
>npm install --save-dev babel-plugin-transform-runtime
						
					
						
const gulp = require('gulp'),
      babel = require('gulp-babel');

gulp.task('default', () =>
    gulp.src('src/app.js')
        .pipe(babel({
            plugins: ['transform-runtime']
        }))
        .pipe(gulp.dest('dist'))
);
						
					

gulp-uglify (ES5)

Пример 1

						
>npm install --save-dev gulp-uglify
						
					
						
const gulp = require('gulp'),
      uglify = require('gulp-uglify');

gulp.task('compress', () => {
  return gulp.src('lib/*.js')
             .pipe(uglify())
             .pipe(gulp.dest('dist/'))
});
						
					

Пример 2

						
const gulp = require('gulp'),
      uglify = require('gulp-uglify');

gulp.task('compress', () => {
  return gulp.src('lib/*.js')
             .pipe(uglify({
                mangle: false,
                output: {
                   beautify: true,
                   comments: true
                },
                compress: {
                   drop_debugger : false,
                   warnings : false,
                   loops : true
                }
             }))
             .pipe(gulp.dest('dist/'))
});
						
					

Параметры output

							
indent_start : 0,    //start indentation on every line
indent_level : 4,    //indentation level
quote_keys   : false,//quote all keys in object literals?
space_colon  : true, //add a space after colon signs?
ascii_only   : false,//output ASCII-safe?
inline_script: false,//escape "</script"?
width        : 80,   //informative maximum line width
max_line_len : 32000,//maximum line length
ie_proof     : true, //output IE-safe code?
beautify     : false,//beautify output?
source_map   : null, //output a source map
bracketize   : false,//use brackets every time?
comments     : false,//output comments?
semicolons   : true, //use semicolons to separate statements?
						
					

Параметры compressor

						
sequences    : true, //join consecutive statemets with “comma operator”
properties   : true, //optimize property access: a["foo"] → a.foo
dead_code    : true, //discard unreachable code
drop_debugger: true, //discard “debugger” statements
unsafe       : false,//some unsafe optimizations (see below)
conditionals : true, //optimize if-s and conditional expressions
comparisons  : true, //optimize comparisons
evaluate     : true, //evaluate constant expressions
booleans     : true, //optimize boolean expressions
loops        : true, //optimize loops
unused       : true, //drop unused variables/functions
hoist_funs   : true, //hoist function declarations
hoist_vars   : false,//hoist variable declarations
if_return    : true, //optimize if-s followed by return/continue
join_vars    : true, //join var declarations
cascade      : true, //try to cascade `right` into `left` in sequences
side_effects : true, //drop side-effect-free statements
warnings     : true, //warn about potentially dangerous optimizations
global_defs  : {}    //global definitions
						
					

del

Пример 1

						
>npm install --save del
						
					
						
const gulp = require('gulp'),
      del = require('del');

gulp.task('clean', () => {
    return del([distPath.dist]);
});
						
					

Пример 2

						
const gulp = require('gulp'),
      del = require('del');

gulp.task('clean', () => {
    return del(['dist/css/', 'dist/js/**/*.min.js']).then(paths => {
        console.log('Deleted files and folders:\n', paths.join('\n'));
    });
});
						
					

gulp-if

Принцип работы

Пример 1

						
>npm install gulp-if --save-dev
						
					
						
const gulp = require('gulp'),
      gulpif = require('gulp-if'),
      uglify = require('gulp-uglify');
 
var condition = true; 
//условие, может быть функция, паттерн и т.д

gulp.task('task', function() {
  gulp.src('./src/*.js')
    .pipe(gulpif(condition, uglify()))
    .pipe(gulp.dest('./dist/'));
});
						
					

Пример 2

						
const gulp = require('gulp'),
      gulpIgnore = require('gulp-ignore'),
      gulpif = require('gulp-if'),
      autoprefixer = require('gulp-autoprefixer'),
      cleanCSS = require('gulp-clean-css'),
      uglify = require('gulp-uglify');
const autoprefixerSettings = {};
const cleanCSSSettings = {};

gulp.task('html', () => {
    return gulp.src('src/**/*.*')
        .pipe(gulpif('*.js', uglify()))
        .pipe(gulpif('*.css',
            pipe(
                autoprefixer(autoprefixerSettings),
                cleanCSS(cleanCSSSettings)
            )
        ))
        .pipe(gulp.dest('dist/'));
});
						
					

gulp-useref

Пример 1

						
>npm install --save-dev gulp-useref
						
					
						
const gulp = require('gulp'),
      useref = require('gulp-useref');
 
gulp.task('html', function () {
    return gulp.src('src/**/*.html')
        .pipe(useref())
        .pipe(gulp.dest('dist'));
});
						
					

Пример 2

						
const gulp = require('gulp'),
      useref = require('gulp-useref'),
      gulpif = require('gulp-if'),
      uglify = require('gulp-uglify'),
      cleanCSS = require('gulp-clean-css');
 
gulp.task('html', function () {
    return gulp.src('src/*.html')
        .pipe(useref())
        .pipe(gulpif('*.js', uglify()))
        .pipe(gulpif('*.css', cleanCSS()))
        .pipe(gulp.dest('dist'));
});
						
					

Изменения в html

						

...список js или css файлов

						
					
						
<!-- build:css css/bundle.min.css -->
    <link rel="stylesheet" href="css/reveal.css">
    <link rel="stylesheet" href="css/reveal.font.css">
    <link rel="stylesheet" href="css/reveal.theme.css">
    <link rel="stylesheet" href="css/font-awesome.css">
<!-- endbuild -->
						
					

gulp-sequence

Пример 1

						
>npm install --save-dev gulp-sequence
						
					
						
const gulp = require('gulp'),
      gulpSequence = require('gulp-sequence');
 
gulp.task('a', () => { /* callback code */ });
gulp.task('b', () => { /* callback code */ });
gulp.task('c', () => { /* callback code */ });
gulp.task('d', () => { /* callback code */ });
gulp.task('e', () => { /* callback code */ });
 
gulp.task('sequence-1', gulpSequence(['a', 'b'], 'c', ['d', 'e']));
 
gulp.task('sequence-2', (cb) => {
  gulpSequence('a', ['b', 'c', 'd'], 'e', cb);
});
 
gulp.task('sequence-3', (cb) => {
  gulpSequence(['a', 'b', 'e'], 'd', 'c')(cb);
});
						
					

Пример 2

						
gulp.task('build', gulpSequence('clean', [
    'html',
    'img',
    'js',
    'css'
]));
						
					
						
gulp.task('default', gulpSequence('build', ['watch', 'connect']));
						
					

gulp-watch

Пример 1

						
>npm install --save-dev gulp-watch
						
					
						
const gulp = require('gulp'),
      watch = require('gulp-watch');

gulp.task('stream', () => {
    return watch('css/**/*.css', { ignoreInitial: false })//*
	//ignoreInitial event add/add folder будут вызваны
        .pipe(gulp.dest('build'));
});

gulp.task('callback', () => {
    return watch('css/**/*.css', () => {//*
        gulp.src('css/**/*.css')
            .pipe(gulp.dest('build'));
    });
});
						
					

Пример 2

						
gulp.task('watch', () => {
    watch(srcPath.css, () => gulp.start('css'));
    watch(srcPath.html, () => gulp.start('html'));
    watch(srcPath.js, () => gulp.start('js'));
    watch(srcPath.img, () => gulp.start('img'));
    watch(srcPath.font, () => gulp.start('font'));
    watch(srcPath.task, () => gulp.start('task'));
});							
						
					

gulp-jshint

Пример 1

						
>npm install jshint gulp-jshint --save-dev
						
					
							
const gulp = require('gulp'),
      jshint = require('gulp-jshint');
 
gulp.task('js:hint', function () {
   return gulp.src('./src/js/**/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter());
});
src\js\classList.min.js: line 2, col 95, Missing "use strict" statement.
src\js\classList.min.js: line 2, col 1499, Unnecessary semicolon.
src\js\classList.min.js: line 2, col 389, 'DOMException' is not defined.
src\js\classList.min.js: line 2, col 1492, 'self' is not defined.
							
						

Пример 2

							
const gulp = require('gulp'),
      jshint = require('gulp-jshint');

gulp.task('js:lint', () => {
    return gulp.src('./src/**/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('jslint_xml'));
});
[22:00:39] Using gulpfile C:\Projects\DDWA\gulpfile.js
[22:00:39] Starting 'js:lint'...

<?xml version="1.0" encoding="utf-8"?>
<checkstyle version="4.3">
        <file name="src\js\classList.min.js">
                <error line="2" 
                          column="95" 
                          severity="error" 
                          message="Missing &quot;use strict&quot; statement." 
                          source="jshint.E007" />
							
						

Пример 3

							
const gulp = require('gulp'),
      jshint = require('gulp-jshint');

gulp.task('js:hint', () => {
    return gulp.src('./src/**/*.js')
        .pipe(jshint())
        .pipe(jshint.reporter('checkstyle'))
        .pipe(jshint.reporter('fail'));//*
});
							
						

Пример 4

						
const gulp = require('gulp'),
      jshint = require('gulp-jshint'),
	  jshintReporter = require('gulp-jshint-html-reporter');

gulp.task('js:hint', () => {
    return gulp.src('./src/**/*.js')
        .pipe(jshint('.jshintrc'))//*
        .pipe(jshint.reporter(jshintReporter, {//*
            filename: './src/code-analysis/js/jshint-output.html',
            createMissingFolders : true
        }));
});						
						
					

Файл .jshintrc

Описание параметров

						
{
    "bitwise": true,
    "camelcase": true,
    "curly": true,
    "eqeqeq": true,
    "es3": false,
    "forin": true,
    "freeze": true,
    "immed": true,
    "indent": 4,
    "latedef": "nofunc",
    "newcap": true,
    "noarg": true,
    "noempty": true,
    "nonbsp": true,
    "nonew": true,
    "plusplus": false,
    "quotmark": "single",
    "undef": true,
    "unused": false,
    "strict": false,
    "maxparams": 10,
    "maxdepth": 5,
    "maxstatements": 40,
    "maxcomplexity": 8,
    "maxlen": 120,

    "asi": false,
    "boss": false,
    "debug": false,
    "eqnull": true,
    "esnext": false,
    "evil": false,
    "expr": false,
    "funcscope": false,
    "globalstrict": false,
    "iterator": false,
    "lastsemic": false,
    "laxbreak": false,
    "laxcomma": false,
    "loopfunc": true,
    "maxerr": false,
    "moz": false,
    "multistr": false,
    "notypeof": false,
    "proto": false,
    "scripturl": false,
    "shadow": false,
    "sub": true,
    "supernew": false,
    "validthis": false,
    "noyield": false,

    "browser": true,
    "node": true,

    "globals": {
        "angular": false,
        "$": false
    }
}
						
					

gulp-csslint

Пример 1

						
>npm install --save-dev gulp-csslint
						
					
							
const gulp = require('gulp'),
      csslint = require('gulp-csslint');
	  			
gulp.task('css:lint', () => {
    return gulp.src('./src/**/*.css')
        .pipe(csslint())
        .pipe(csslint.formatter());
});

[22:12:31] Using gulpfile C:\Projects\DDWA\gulpfile.js
[22:12:33] Starting 'css:lint'...
csslint: There are 15 problems in C:\Projects\DDWA\src\css\font-awesome.css.
font-awesome.css
1: warning at line 7, col 1
Rule doesn't have all its properties in alphabetical order.
							
						

Пример 2

								
const gulp = require('gulp'),
      csslint = require('gulp-csslint');

gulp.task('css:lint', () => {
    return gulp.src('./src/**/*.css')
        .pipe(csslint())
        .pipe(csslint.formatter('junit-xml'));
});								
							
						

Пример 3

							
const gulp = require('gulp'),
      csslint = require('gulp-csslint');

gulp.task('css:lint', () => {
    return gulp.src('./src/**/*.css')
        .pipe(csslint())
        .pipe(csslint.formatter());
        .pipe(csslint.formatter('fail'));
});
							
						

Большой пример

					
//modules

const gulp = require('gulp'),
    gulpif = require('gulp-if'),
    gulpSequence = require('gulp-sequence'),
    autoprefixer = require('gulp-autoprefixer'),
    browserSync = require('browser-sync'),
    concat = require('gulp-concat'),
    cleanCSS = require('gulp-clean-css'),
    del = require('del'),
    imagemin = require('gulp-imagemin'),
    lazypipe = require('lazypipe'),
    newer = require('gulp-newer'),
    rename = require('gulp-rename'),
    pipe = require('multipipe'),
    sourcemaps = require('gulp-sourcemaps'),
    uglify = require('gulp-uglify'),
    useref = require('gulp-useref'),
    watch = require('gulp-watch');

//variables

const srcPath = {
    'src': './src',
    'html': ['./src/**/*.html'],
    'img': './src/**/*.+(jpg|png|svg|ico)',
    'css': ['./src/!(css|js)*/**/*.css'],
    'js': './src/!(js)*/**/*.js',
    'font': './src/font/**/*.*',
    'task': './src/task/**/*.pdf'
};

const distPath = {
    'dist': './dist/',
    'html': './dist/',
    'img': './dist/',
    'css': './dist/css/',
    'js': './dist/',
    'font': './dist/font/',
    'task': './dist/task/'
};

const pluginSettings = {
    autoprefixer: {
        browsers: ['last 2 versions', 'ie 11']
    },
    cleanCSS: {
        compatibility: '*'
    },
    imagemin: {
        optimizationLevel: 2
    }
};

//tasks

gulp.task('clean', () => {
    return del([distPath.dist]);
});

gulp.task('html', () => {
    return gulp.src(srcPath.html)
        .pipe(newer(distPath.html))
        .pipe(useref({}, lazypipe().pipe(sourcemaps.init)))
        .pipe(gulpif('*.js', pipe(
            uglify()
        )))
        .pipe(gulpif('*.css',
            pipe(
                autoprefixer(pluginSettings.autoprefixer),
                cleanCSS(pluginSettings.cleanCSS)
            )
        ))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest(distPath.html));
});

gulp.task('css', () => {
    return gulp.src(srcPath.css)
        .pipe(sourcemaps.init())
        .pipe(autoprefixer(pluginSettings.autoprefixer))
        .pipe(cleanCSS(pluginSettings.cleanCSS))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest(distPath.css));
});

gulp.task('js', () => {
    return gulp.src(srcPath.js)
        .pipe(gulp.dest(distPath.js));
});

gulp.task('img', () => {
    return gulp.src(srcPath.img)
        .pipe(newer(distPath.img))
        .pipe(imagemin(pluginSettings.imagemin))
        .pipe(gulp.dest(distPath.img));
});

gulp.task('font', () => {
    return gulp.src(srcPath.font)
        .pipe(gulp.dest(distPath.font));
});

gulp.task('task', () => {
    return gulp.src(srcPath.task)
        .pipe(gulp.dest(distPath.task));
});

gulp.task('serve', () => {
    browserSync.init({
        server: distPath.dist,
        port: 4000
    });

    browserSync.watch(distPath.dist).on('change', browserSync.reload);
});

gulp.task('build', gulpSequence('clean', [
    'html',
    'img',
    'js',
    'css',
    'font',
    'task'
]));

gulp.task('watch', () => {
    watch(srcPath.css, () => gulp.start('css'));
    watch(srcPath.html, () => gulp.start('html'));
    watch(srcPath.js, () => gulp.start('js'));
    watch(srcPath.img, () => gulp.start('img'));
    watch(srcPath.font, () => gulp.start('font'));
    watch(srcPath.task, () => gulp.start('task'));
});

gulp.task('default', gulpSequence('build', ['watch', 'serve']));
					
				

Webpack

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

Webpack vs Gulp

Плюсы

  • Всеобщая система сборки
  • Может быть интегрирован с Grunt или Gulp
  • Предоставляет динамическую подгрузку
  • Использует Chunk для общих фрагментов кода
  • "Горячая" замена модулей на странице

Минусы

  • Тяжел в освоении
  • Ориентирован только на работу с JS-файлами (до версии 1.0)
  • Плохая документация (до версии 3.0)

Среда окружения webpack

  • AMD & CommomJS
  • Entry points
  • Webpack dev server
  • Plugins
  • Loaders
  • Hash
  • Chunk

Загрузчики и плагины

Загрузчики

  • Преобразуют данные из одного формата в другой
  • Могут добавлять в упаковку другие модули
  • Как правило, делают одну трансформацию
  • Работают только с одним модулем

Плагины

  • Имеют доступ ко всем модулям (до и после трансформации)
  • Могут добавлять в упаковку свои модули (например, "runtime")
  • Имеют доступ ко всем ресурсам, создаваемым после упаковки
  • Применяются для изменения конфигурации сборки, оптимизации, горячего обновления ресурсов

Начало работы

					
>npm install webpack -g

>npm install webpack --save-dev
					
				

Простой пример

Список файлов

						
├── index.html
├── index.js
├── util.js
├── package.json
└── webpack.config.js
						
					

webpack.config.js

						
module.exports ={
    entry: "./index.js",
    output: {
        path: __dirname + "/dist",
        filename: "bundle.js"
    },
    optimization: {
        minimize: false
    }
}
						
					

Другие файлы

util.js

						
function logger(msg) {
    console.log('util.js: ' + msg);
}

exports.logger = logger;
						
					

index.js

						
const util = require('./util.js');
util.logger('test message!');	
						
					

index.html

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

Выполнение

							
>webpack
Hash: d6d2f71e8c9a06fc28da
Version: webpack 4.0.1
Time: 837ms
Built at: 3/2/2018 10:45:39 AM
    Asset       Size  Chunks             Chunk Names
bundle.js  639 bytes       0  [emitted]  main
Entrypoint main = bundle.js
   [0] ./util.js 88 bytes {0} [built]
   [1] ./index.js 65 bytes {0} [built]
							
					
							
//index.html console
util.js: test message!
							
					

bundle.js

						
  (function(modules) { // webpackBootstrap
 	// The module cache
 	var installedModules = {};

 	// The require function
 	function __webpack_require__(moduleId) {

 		// Check if module is in cache
 		if(installedModules[moduleId]) {
 			return installedModules[moduleId].exports;
 		}
 		// Create a new module (and put it into the cache)
 		var module = installedModules[moduleId] = {
 			i: moduleId,
 			l: false,
 			exports: {}
 		};

 		// Execute the module function
 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

 		// Flag the module as loaded
 		module.l = true;

 		// Return the exports of the module
 		return module.exports;
 	}


 	// expose the modules object (__webpack_modules__)
 	__webpack_require__.m = modules;

 	// expose the module cache
 	__webpack_require__.c = installedModules;

 	// define getter function for harmony exports
 	__webpack_require__.d = function(exports, name, getter) {
 		if(!__webpack_require__.o(exports, name)) {
 			Object.defineProperty(exports, name, {
 				configurable: false,
 				enumerable: true,
 				get: getter
 			});
 		}
 	};

 	// define __esModule on exports
 	__webpack_require__.r = function(exports) {
 		Object.defineProperty(exports, '__esModule', { value: true });
 	};

 	// getDefaultExport function for compatibility with non-harmony modules
 	__webpack_require__.n = function(module) {
 		var getter = module && module.__esModule ?
 			function getDefault() { return module['default']; } :
 			function getModuleExports() { return module; };
 		__webpack_require__.d(getter, 'a', getter);
 		return getter;
 	};

 	// Object.prototype.hasOwnProperty.call
 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

 	// __webpack_public_path__
 	__webpack_require__.p = "";


 	// Load entry module and return exports
 	return __webpack_require__(__webpack_require__.s = 1);
 })
/************************************************************************/
 ([
/* 0 */
/***/ (function(module, exports) {

function logger(msg) {
    console.log('util.js: ' + msg);
}

exports.logger = logger;

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

const util = __webpack_require__(0);
util.logger('test message!');

/***/ })
 ]);
						
					

Массив модулей

						
([
/* 0 */
/***/ (function(module, exports) {
function logger(msg) {
    console.log('util.js: ' + msg);
}
exports.logger = logger;

/***/ }),

/* 1 */
/***/ (function(module, exports, __webpack_require__) {
const util = __webpack_require__(0);
util.logger('test message!');
/***/ })

 ]);
						
					

Внешний доступ к модулям

webpack.config.js

						
module.exports ={
    entry: "./index.js",
    output: {
        path: __dirname + "/dist",
        filename: "bundle.js",
        library: 'webpack' //*
    },
    optimization: {
        minimize: false
    }
}
						
					

JS файлы

util.js

						
function logger(msg) {
    console.log('util.js: ' + msg);
}

exports.logger = logger;
						
					

index.js

						
const util = require('./util.js');
util.logger('test message!');

exports.logger = util.logger; //*	
						
					

index.html

						
<!DOCTYPE>
<html>

<body>
    <script src="dist/bundle.js"></script>
    <script>
        webpack.logger('index.html');//*
    </script>
</body>

</html>
						
					

Выполнение

						
>webpack
Hash: bb56f76f21d37cb07dec
Version: webpack 4.0.1
Time: 207ms
Built at: 3/2/2018 1:59:39 PM
    Asset      Size  Chunks             Chunk Names
bundle.js  2.89 KiB       0  [emitted]  main
Entrypoint main = bundle.js
   [0] ./util.js 90 bytes {0} [built]
   [1] ./index.js 102 bytes {0} [built]
						
					
						
util.js: test message!
util.js: index.html
						
					

Изменения в bundle.js

						
var webpack =  //*** добавилась внешняя переменная
 (function(modules) {
 	// The module cache
 	var installedModules = {};

 	// The require function
 	function __webpack_require__(moduleId) {

 		// Check if module is in cache
 		if(installedModules[moduleId])
 			return installedModules[moduleId].exports;
//...................................................

						
					

Несколько точек входа

					
module.exports ={
    context: __dirname,
    entry: {
        index: './index',
        about: './about'
    },
    output: {
        publicPath: '/dist',
        filename: "[name].js",
        library: '[name]'
    },
    optimization: {
        minimize: false
    }
}
					
				

Пересборка приложения при изменениях

Свойство watch

Позволяет автоматически пересобирать приложение и запускать задачи при обнаружении изменений
						
module.exports ={
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        publicPath: '/dist',
        filename: "[name].js"
    },
    watch: true,
    watchOptions :{//не обязательные свойства
        aggregateTimeout: 300,//мс
        ignored: './node_modules/',
        poll: 1000//мс
    },
    optimization: {
        minimize: false
    }
}
						
					

watch options

  • aggregateTimeout (мс) - время задержки старта процесса пересобирания после обнаружения первого изменения
  • ignored - игнорируемые файлы, можно использовать паттерн ('/files/*.js')
  • poll (мс) - проверяет изменения через заданный промежуток времени

Отладка скриптов

Проблема отладки

До включения devtool

После

devtool

Краткое описание

  • eval - каждый модуль выполняется с использованием eval и ссылки на карту кода
  • source-map - генерируются отладочные карты кода (файл *.js.map)
  • hidden-source-map - то же, что и source-map, но не добавляется ссылка в JS-файл
  • inline-source-map - карта добавляется как sourceMappingURL в JS-файлы/li>
  • eval-source-map - каждый модуль выполняется с использованием eval, карта добавляется как sourceMappingURL в eval
  • cheap-source-map - то же, что и source-map, но номера строк некорректные (номера строк модифицированных файлов вместо оригинальных). Карты кода, подгружаемые загрузчиком (для внешних модулей), не используются
  • cheap-module-source-map - то же, что и source-map, но при сопоставлении данных корректные номера имеют только строки. Загружает карты кода для внешних модулей

Пример

						
module.exports ={
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        publicPath: '/dist',
        filename: "bundle.js"
    },
    watch: true,
    devtool: 'source-map',
    optimization: {
        minimize: false,
    }
}
						
					

Babel.js

Установка

						
>npm install babel-loader babel-core babel-preset-es2015 
         babel-plugin-transform-runtime webpack --save-dev
// или
>npm install webpack --save-dev
>npm install babel-loader --save-dev
>npm install babel-core --save-dev
>npm install babel-plugin-transform-runtime --save-dev
>npm install babel-preset-es2015 --save-dev
						
					

Список файлов

						
├── index.html
├── index.js
├── util.js
├── package.json
└── webpack.config.js
						
					

webpack.config.js

						
module.exports = {
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        filename: 'bundle.js',
		path: __dirname + '/dist',
		library: 'webpack'
    },
    devtool: 'source-map',
    module: {
        rules: [{
            test: /\.js$/,
            exclude: /(node_modules)/,
            loader: 'babel-loader',
            query: {
                presets: ['es2015']
            }
        }]
    }
}
						
					

JS файлы

util.js

						
export default function logger(msg) {
    console.log('util.js: ' + msg);
}
						
					

index.js

						
import {logger} from './util';
logger('test message!');

export {logger};
						
					

index.html

index.html

						
<!DOCTYPE>
<html>

<body>
    <script src="dist/bundle.js"></script>
    <script>
        webpack.logger('index.html');
    </script>
</body>

</html>						
						
					

Выполнение

							
Hash: 353c157e6aec3bb683bb
Version: webpack 4.0.1
Time: 1339ms
Built at: 3/3/2018 12:33:20 PM
        Asset       Size  Chunks             Chunk Names
bundle.js  864 bytes       0  [emitted]  index
bundle.js.map   3.46 KiB       0  [emitted]  index
Entrypoint index = bundle.js bundle.js.map
   [0] ./util.js 168 bytes {0} [built]
   [1] ./index.js 208 bytes {0} [built]
							
					
							
util.js: test message!
util.js: index.html
							
					

bundle.js (фрагмент)

						
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";

Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.default = logger;
function logger(msg) {
    console.log('util.js: ' + msg);
}

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.logger = undefined;

var _util = __webpack_require__(0);

(0, _util.logger)('test message!');

exports.logger = _util.logger;

/***/ })
/******/ ]);
						
					

UglifyJsPlugin

					
npm i uglifyjs-webpack-plugin --save-dev
					
				
					
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        publicPath: '/dist',
        filename: "bundle.js"
    },
    watch: true,
    plugins: [
        new UglifyJsPlugin({
            cache: true,
            exclude: /\/node_modules/,
            uglifyOptions: {
                ecma: 8,
                warnings: false,
                parse: {},
                compress: {
                    warnings: false,
                    drop_debugger: true,
                    unsafe: false,
                    drop_console: true
                },
                output: {
                    comments: false,
                    beautify: false
                }
            }
        })
    ]
}
					
				

DefinePlugin

							
const webpack = require('webpack');
module.exports = {
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        publicPath: '/dist',
        filename: "bundle.js"
    },
    watch: true,
    plugins: [
        new webpack.DefinePlugin({
            DEBUG: JSON.stringify(true)
        })
    ]
}			
							
					

index.js

							
const util = require('./util.js');
util.logger('test message!');

console.log(DEBUG ? "true" : "false");//*
							
						

CopyWebpackPlugin

					
npm i copy-webpack-plugin --save-dev			
					
				
						
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        publicPath: '/dist',
        filename: "bundle.js"
    },
    watch: true,
    plugins: [
        new CopyWebpackPlugin([
            { from: 'index.html', to: 'html/' },//dist/html
            { from: '**/*.txt', to: 'txt/'}//dist/txt
        ])
    ]
}		
					
				

CleanWebpackPlugin

						
>npm i clean-webpack-plugin --save-dev
						
					
						
const CleanWebpackPlugin = require('clean-webpack-plugin');

const pathsToClean = [
    'dist',
    'build'
];

module.exports = {
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },
    plugins: [
        new CleanWebpackPlugin(pathsToClean)
    ]
}
						
					

HtmlWebpackPlugin

						
npm install --save-dev html-webpack-plugin
						
					
						
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: 'test app'
        }),
    ]
}
						
					

Результат

						
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>test app</title>
  </head>
  <body>
  <script type="text/javascript" src="bundle.js"></script></body>
</html>
						
					

Использование шаблона

						
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const templatePath = path.join(__dirname, 'templates', 'index.html');

module.exports = {
    context: __dirname,
    entry: {
        index: './index'
    },
    output: {
        filename: 'bundle.js',
        path: __dirname + '/dist'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: templatePath
        }),
    ]
}
						
					

Минификация

							
new HtmlWebpackPlugin({
    minify:{
        removeAttributeQuotes: true,
        collapseWhitespace: true,
        html5: true,
        minifyCSS: false,
        removeComments: true,
        removeEmptyAttributes: true
    }
}),
							
						

Несколько файлов

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

Работа со стилями

style.css

						
body{
    background: red;
}
						
					

index.js

						
import style from './style.css';

import {logger} from './util';
logger('test message!');
						
					
						
ERROR in ./style.css
Module parse failed: Unexpected token (1:4)
You may need an appropriate loader to handle this file type.
| body{
|     background: red;
| }
 @ ./index.js 3:13-35
						
					

Установка зависимостей

						
npm install style-loader css-loader postcss-loader 
          postcss-smart-import --save-dev

или
npm install style-loader --save-dev
npm install css-loader --save-dev
npm install postcss-loader  --save-dev
npm install postcss-smart-import --save-dev
						
					

postcss.config.js

						
module.exports = {
  plugins: [
    require('postcss-smart-import')({ /* ...options */ }),
    require('autoprefixer')({ 
        cascade: false,
        browsers: ['last 2 versions', '> 1%']
     })
  ]
}
						
					

Изменения в webpack.config.js

						
//........................................................
module: {
    rules: [{
            test: /\.js$/,
            exclude: /(node_modules)/,
            loader: 'babel-loader',
            query: {
                presets: ['es2015']
            }
        },
        {
            test: /\.css$/,
            loaders: [
                'style-loader',
                'css-loader?importLoaders=1&sourceMap',
                'postcss-loader'
            ]
        }
    ]
},
//........................................................
						
					

Выполнение

						
Hash: c7d1247fe4e9733684b3
Version: webpack 4.0.1
Time: 2242ms
Built at: 3/3/2018 12:47:03 PM
        Asset      Size  Chunks             Chunk Names
bundle.js  19.1 KiB       0  [emitted]  index
bundle.js.map    24 KiB       0  [emitted]  index
Entrypoint index = bundle.js bundle.js.map
   [0] ./util.js 168 bytes {0} [built]
   [4] ./node_modules/css-loader?importLoaders=1&sourceMap!./node_modules/postcss-loader/lib!./style.css 396 bytes {0} [built]
   [5] ./style.css 1.25 KiB {0} [built]
   [6] ./index.js 264 bytes {0} [built]
    + 3 hidden modules
						
					

После выполнения....

						
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__(3)(true);
// imports

// module
exports.push([module.i, "body{\r\n    background: red;\r\n}", "", {"version":3,"sources":["C:/Users/HP/Desktop/webpack/style.css"],"names":[],"mappings":"AAAA;IACI,gBAAgB;CACnB","file":"style.css","sourcesContent":["body{\r\n    background: red;\r\n}"],"sourceRoot":""}]);

// exports
/***/ }),
						
					

Работа с изображениями

style.css

						
body{
    background: red;
    background-image: url('background.png');
}
						
					

index.js

						
import style from './style.css';
import {logger} from './util';
logger('test message!');
						
					
						
//при условии, что style-loader и css-loader установлены
ERROR in ./background.png
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type.
(Source code omitted for this binary file)
 @ ./node_modules/css-loader?importLoaders=1&sourceMap!./node_modules/postcss-loader/lib!./style.css 7:96-123
 @ ./style.css
 @ ./index.js
						
					

Установка зависимостей

						
>npm install file-loader image-webpack-loader --save-dev

//или
>npm install file-loader --save-dev
>npm install image-webpack-loader --save-dev
						
					

Изменения в webpack.config.js

						
//........................................       
module: {
    rules: [
    {
        test: /\.css$/,
        loaders: [
            'style-loader',
            'css-loader?importLoaders=1&sourceMap',
            'postcss-loader'
        ]
    },
    {
        test: /\.(gif|png|jpe?g|svg)$/i,
        use: [
           'file-loader',
            //'file-loader?name=[name].[ext]',
            {
                loader: 'image-webpack-loader',
                options: {
                    bypassOnDebug: true,
                    mozjpeg: {
                        progressive: true,
                        quality: 65
                    },
                    optipng: {
                        enabled: false,
                        optimizationLevel: 5
                    }
                },
            },
        ],
    }
    ]
},
//........................................
						
					

После выполнения

						
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__.p + "69070e565786768e1dcf6b933da514b2.png";

/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

var escape = __webpack_require__(5);
exports = module.exports = __webpack_require__(4)(true);
// imports


// module
exports.push([module.i, "body{\r\n    background: red;\r\n    background-image: url(" + escape(__webpack_require__(3)) + ");\r\n}", "", {"version":3,"sources":["C:/Users/HP/Desktop/webpack/style.css"],"names":[],"mappings":"AAAA;IACI,gBAAgB;IAChB,gDAAwC;CAC3C","file":"style.css","sourcesContent":["body{\r\n    background: red;\r\n    background-image: url('background.png');\r\n}"],"sourceRoot":""}]);

// exports

/***/ }),
						
					

Webpack Dev Server

						
	>npm install webpack-cli -g
	>npm install webpack-dev-server -g
	>npm install webpack-dev-server --save-dev
						
					
						
	const path = require('path');
	module.exports = {
		context: __dirname,
		entry: { index: './index' },
		output: {
			filename: 'bundle.js',
			path: __dirname + '/dist'
		},
		devServer: {
			contentBase:  path.resolve(__dirname, 'dist'),
			compress: false,
			port: 9000,
			historyApiFallback: true,
			hot: false,
			inline: true,
			stats: 'errors-only',
			host: process.env.HOST,
		}
	}
						
					

Выполнение

							
	//режим errors-only
	>webpack-dev-server
	i 「wds」: Project is running at http://localhost:9000/
	i 「wds」: webpack output is served from /
	i 「wds」: Content not from webpack is served from C:\Users\HP\Desktop\webpack\dist
	i 「wds」: 404s will fallback to /index.html
	‼ 「wdm」:
	i 「wdm」: Compiled with warnings.
							
						

NPM Scripts

						
	"scripts": {
	  "start": "webpack-dev-server"
	}
	
	>npm run start
						
					
						
	{
	  "name": "webpack-test",
	  "version": "1.0.0",
	  "main": "index.js",
	  "scripts": {
		"start": "webpack-dev-server"
	  },
	  "license": "ISC",
	  "devDependencies": {
		"webpack": "^4.0.1",
		"webpack-cli": "^2.0.10",
		"webpack-dev-server": "^3.1.0"
	  }
	}
	
						
					

Hot Module Replacement

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

Схема работы

  1. Приложение запрашивает изменения у HMR runtime
  2. HRM асинхронно загружает изменения
  3. Приложение запрашивает HRM применить изменения
  4. HRM синхронно применяет изменения

DEVELOPMENT ONLY

WITH webpack-dev-server

HotModuleReplacementPlugin

							
	>npm install webpack-cli -g		
	>npm install webpack-dev-server --save-dev			
	>webpack-dev-server
							
						
							
	const webpack = require('webpack');
	module.exports = {
		context: __dirname,
		entry: {
			index: './index'
		},
		output: {
			filename: 'bundle.js',
			path: __dirname + '/dist'
		},
		watch: true,
		devServer: {
			contentBase: '/dist',
			hot: true
		},
		plugins: [
			new webpack.HotModuleReplacementPlugin()
		]
	}
							
						

Большой пример

Redux TodoMVC

package.json

						
{
  "name": "todo-mvc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "cross-env DEBUG=false webpack --config webpack.prod.js",
    "start": "cross-env DEBUG=true webpack-dev-server --open --config webpack.dev.js",
    "lint": "eslint . --ext .js --ext .jsx"
  },
  "author": "Maksim Hladki",
  "license": "ISC",
  "devDependencies": {
    "autobind-decorator": "^2.1.0",
    "babel-core": "^6.26.0",
    "babel-eslint": "^8.2.1",
    "babel-loader": "^7.1.2",
    "babel-plugin-array-includes": "^2.0.3",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-plugin-transform-object-assign": "^6.22.0",
    "babel-plugin-transform-object-rest-spread": "^6.26.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "classnames": "^2.2.5",
    "clean-webpack-plugin": "^0.1.17",
    "cross-env": "^5.1.3",
    "css-loader": "^0.28.7",
    "eslint": "^4.15.0",
    "eslint-loader": "^1.9.0",
    "eslint-plugin-react": "^7.5.1",
    "html-webpack-plugin": "^3.0.4",
    "style-loader": "^0.19.0",
    "uuid": "^3.1.0",
    "webpack": "^4.0.1",
    "webpack-cli": "^2.0.10",
    "webpack-dev-server": "^3.1.0",
    "webpack-merge": "^4.1.1"
  },
  "dependencies": {
    "keycode-js": "^1.0.0",
    "normalize.css": "^7.0.0",
    "prop-types": "^15.6.0",
    "react": "^16.1.1",
    "react-dom": "^16.1.1",
    "react-redux": "^5.0.6",
    "redux": "^3.7.2",
    "redux-actions": "^2.2.1",
    "redux-define": "^1.1.1",
    "redux-localstorage": "^0.4.1",
    "redux-logger": "^3.0.6",
    "todomvc-app-css": "^2.1.0"
  }
}		
						
					

webpack.common.js

						
const path = require('path'),
    webpack = require('webpack'),
    CleanWebpackPlugin = require('clean-webpack-plugin'),
    HtmlWebpackPlugin = require('html-webpack-plugin');

const baseDir = 'src';
const PATH = {
    app: path.join(__dirname, baseDir),
    template: path.join(__dirname, baseDir, 'templates', 'index.html'),
    build: path.join(__dirname, 'build')
};

const DEBUG = JSON.parse(process.env.DEBUG || 'false');
const devFlagPlugin = new webpack.DefinePlugin({
    DEBUG: JSON.stringify(DEBUG)
});

const config = {
    entry: {
        app: PATH.app
    },
    output: {
        path: PATH.build,
        filename: '[name].bundle.js'
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [
            {
                loader: 'style-loader',
            }, 
            {
                loader: 'css-loader',
                options: {
                    minimize: !DEBUG,
                    sourceMap: DEBUG
                }
            }]
        },
        {
            test: /\.jsx?$/,
            include: PATH.app,
            use: [{
                loader: 'babel-loader',
                options: {
                    cacheDirectory: true
                }
            }]
        },
        {
            test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
            use: [{
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    mimetype: 'application/font-woff',
                    name: '[name].[ext]',
                    outputPath: PATH.font,
                }
            }]
        },
        {
            test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
            use: [{
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]',
                    outputPath: PATH.font
                }
            }]
        }
        ]
    },
    plugins: [
        new CleanWebpackPlugin([PATH.build]),
        new HtmlWebpackPlugin({
            template: PATH.template,
            minify: DEBUG ? false : {
                removeAttributeQuotes: true,
                collapseWhitespace: true,
                html5: true,
                minifyCSS: false,
                removeComments: true,
                removeEmptyAttributes: true,
              }
        }),
        devFlagPlugin
    ]
};

module.exports = {
    config,
    path: PATH
};
						
					

webpack.dev.js

						
const webpack = require('webpack'),
    merge = require('webpack-merge'),
    common = require('./webpack.common.js');

const PATH = common.path,
    config = common.config;

module.exports = merge(config, {
    module: {
        rules: [
            {
                enforce: 'pre',
                test: /\.js$/,
                include: PATH.app,
                loader: 'eslint-loader',
                options: {}
            },
        ],
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: PATH.build,
        compress: false,
        port: 9000,
        historyApiFallback: true,
        hot: true,
        inline: true,
        stats: 'normal',
        host: process.env.HOST,
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
});
						
					

webpack.prod.js

						
const webpack = require('webpack'),
    merge = require('webpack-merge'),
    UglifyJSPlugin = require('uglifyjs-webpack-plugin'),
    common = require('./webpack.common.js');

const config = common.config;

module.exports = merge(config, {
    plugins: [
        new UglifyJSPlugin(),
        new webpack.DefinePlugin({
            'process.env': {
                'NODE_ENV': JSON.stringify('production')
            }
        })
    ]
});
						
					

Gulp и Webpack

Пример 1

						
const gulp = require('gulp'),
      webpack = require('webpack-stream');

gulp.task('default', function() {
  return gulp.src('src/entry.js')
    .pipe(webpack( require('./webpack.config.js') ))
    .pipe(gulp.dest('dist/'));
});
						
					

Пример 2

						
const gulp = require('gulp'),
      webpack = require('webpack-stream');

gulp.task('default', function() {
  return gulp.src('src/entry.js')
    .pipe(webpack({
      entry: {
        app: 'src/app.js',
        test: 'test/test.js',
      },
      output: {
        filename: '[name].js',
      },
    }))
    .pipe(gulp.dest('dist/'));
});
						
					

Спасибо за внимание