Предыстория
Еще какой-то год назад я работал в совершенно другой сфере и только в теории задумывался о смене профессии, но примерно в декабре 2018 нашел свое и начал действовать.
Примерно через полгода тотального обучения я устраиваюсь работать frontend-программистом. За плечами обучение фундаментальным вещам(мне хочется так думать), js, взаимодействие с DOM, react+redux. HTML и CSS самый минимум+ общее понимание о bootstrap и сборке, работа с git, командной строкой.
Помимо теории сделано пара учебных проектов, в том числе чат на react+redux, а так же пара попыток реализации каких-то своих задумок.
В общем, такой себе стандартный современный джентельменский набор для начинающего front'a.
Первые полторы недели настраиваю виртуальную машину, там куча всего и все мне незнакомо и непонятно.
По ходу дела знакомлюсь с новыми инструментами и технологиями: с базами данных(и ставлю себе очередную закладку в список «выучить»), putty, wincsp и пр.
Успешно прохожу эту полосу препятствий и перехожу к фронту.
Предисловие
Уже написав свой релоадер и эту статью, я нашел аналоги в том числе на Хабре. Однако все-таки решил опубликовать свой велосипед.
Начало
У нас довольно большой проект, доставшийся в наследство, написанный на angularJS, со всеми его прелестями. Мне после React'а он показался динозавром, но ничего, покупаю курсы по angularjs, быстро въезжаю и начинаю приносить пользу.
Положительное впечатление- проект написан хорошо, людьми с явно прямыми руками. Переменные с отличным понятным именованием, строение везде одинаковое и в целом вся логика весьма доступно и просто выражена.
Но и минусов хватает.
Первая проблема: проект собирается каким-то древним минимизатором и использовать современный синтаксис js нельзя. Никаких () => {}, const res = [...data, subRes], async/await…
Вторая проблема: нет ни webpack, ни даже хотя бы gulp, а соответственно нет и привычного мне webpack-dev-server c его прекрасным hot reload.
Написал. Сохранил. F5. Неудобно. Боль? Не прям боль, но очень неудобно.
Третья проблема: сборка проекта .bat файлом, в котором часть проекта просто копируется, часть библиотек собираются без минимизации, часть минимизируются в один файл, остальные файлы проекта-в другой. Список библиотек в третьем файле. Список файлов для сборки в четвертом. И так далее.
Четвертая проблема: все библиотеки аккуратно лежат в папочке libs и подключаются скриптом в index.html. Все-все, кроме express и proxy для него(они не участвуют в сборке, а только для разработки).
И далеко не везде есть версии или указание на конкретную библиотеку.
На обучении я жил в прекрасном мире функционального программирования, полном es6+, webpack-dev-server, tdd, eslint, автоматической сборкой и так далее.
А тут во взрослом мире все совсем по-другому…
Завязка
Но работать мне нравится, препятствия рассматриваю как возможности саморазвития, компания хорошая, обстановка отличная, глаза горят!
В рабочее время выполняю рабочие задачи, в свободное пытаюсь что-то улучшить.
Середина июня, начинаю с попытки прикрутить webpack, но первый подход ожидает полный провал. Неделю мучаюсь, сильно от этого устаю, временно откладываю.
Решаю начать с малого — подключаю новый синтаксис через babel. Дописываю в наш волшебный build.bat первоначальную обработку babel'ем, но что-то ломает идиллию и наш старый минификатор спотыкается. Ищу проблему.
Спотыкается на одной из библиотек из аккуратной папочки libs. Смотрю файлы библиотек: они уже минифицированы и в старом синтаксисе.
Говорю babel — «ты сюда не ходи… код башка попадет, совсем плохо будет». Проверяю: все работает! Ура! Теперь мне доступны все те приятные новые стильные модные молодежные штуки! Первая победа. Приятно. Думаю по такому случаю переименовать скрипт в e.bat(e-Evgen), но не решаюсь.
Новый синтаксис так знаком и приятен, но мысли о кривой сборке не покидают меня.
Конец июня-начало июля. Делаю второй подход, более основательный, но снова упираюсь в ошибки между webpack и angularjs. Снова неделя изысканий.
Как-то раз провожу несколько дней и частично ночей за поиском решения, натыкаюсь на крайне интересные выступления с конференции HolyJS, где ребята уже довольно глубоко копают в webpack. Продвигаюсь в его понимании, но материал пока не понимаю до конца.
Интерес укрепляется.
Коллега говорит — забей, проект сдавать через пару месяцев, уже не нужно этим заниматься.
Не забиваю, но откладываю — много работы, она выжимает меня всего, на внеклассные занятия времени пока что не остается.
Середина июля, мне в руки попадает похожий на наш проект с настроенной сборкой. Иду с ним в третий подход и практически настраиваю у нас webpack, но в конце ловлю новые ошибки, на решение которых времени уже не хватает, работа накатывает с новой интенсивностью + морально меня это опустошает, вновь откладываю это дело.
Основная часть
Середина августа. В итоге приятель рассказывает про изучение node.js и его желание написать собственный hot-reloader. Мысли о нашей сборке вспыхивают у меня с новой силой.
Задача: reload текущую страницу при обновлении файлов в проекте.
Особенности: все библиотеки подключаются в index.html, нельзя require, не говоря уже об import. Сборка перед reload пока не нужна, только reload. В сервере для разработки, который проксирует запросы на наш бэк, пакеты использовать я могу, а так же могу require!
Все это происходит в пятницу и я решаю, что в упрощенном варианте для нашего проекта мне вполне по силам реализовать технологию, которая избавит меня и моих коллег от F5.
Мыслительный процесс идет и в голове формируется видение решения.
Простейший сервер(как у нас), в нем я обойду всю папку и подпапки и сформирую дерево с датами изменений каждого файла.
Далее через каждые n миллисекунд буду обходить еще и еще и сравнивать значения времени изменений. Изменилось — reload. Приятель подсказывает — «не изобретай велосипед, есть watch в node.js». Отлично, буду использовать его. В server.js настрою watch за папкой проекта и по изменению чего-то внутри буду вызывать location.reload().
Первая итерация:
var express = require('express');
var app = express();
var server = require('http').Server(app);
const port = 9080;
server.listen(port);
app.use(express.static(__dirname + '/src'));
location.reload().
Первая проблема — location- это не переменная node.js(в этот момент я обретаю понимание, почему мои попытки обращения к process.env на фронте тоже были безуспешны))).
Вторая проблема — как дать front'у понять, что нужно делать reload?
Выход — websocket! Идея заманчива + я с ними работал «на пол-шишки», когда писал чат, общее представление имею. Заодно делаю счетчик перезагрузок за сессию, добавляю переменную и обработку отдающему ее запросу.
Пробую:
var express = require('express'); // Подключаем express
var app = express();
var server = require('http').Server(app); // Подключаем http через app
var io = require('socket.io')(server); // Подключаем socket.io и указываем на сервер
var fs = require('fs');
const port = 9080;
server.listen(port);
app.use(express.static(__dirname + '/src'));
let count = 0;
app.get('/data', (req, res) => {
res.data = count;
res.send(`${count}`);
})
const dir = './src';
fs.watch(dir, () => {
io.emit('change', {data: count});
count += 1;
})
На фронте делаю простейший App на angularjs
angular.module('App', [])
.controller('myAppCtrl',['$scope', '$timeout','$http',
($scope, $timeout, $http) => {
$scope.title = 'Страничка для тестирования простейшего хот релоада без пересборки';
$scope.count = 0;
$scope.todo = [
'прикрутить рекурсивность папок,поискать стандартные методы',
'проверить на отслеживание node.js watch файлы других типов',
'периодически проходить отслеживаемую папку и смотреть,не появились ли в ней или вложенные файлы и папки',
'прикрутить линтер, codeclimate и travis к этому проекту'
]
$scope.marks = [
'watcher не смотрит рекурсивно на каталоги внутри'
]
// var socket = io();
// socket.on('change', (data) => {
// console.log(data.data);
// $scope.count = data.data;
// console.log('scope.count: ', $scope.count);
// $scope.$digest();//
// location.reload();//agfr
// })
$http({method: 'GET',url: 'data'})
.then(response => {
$scope.count = response.data;//
});
}])
И отдельный модуль, который ее reload. Отдельный, чтобы в проект лишнего не попадало.
var socket = io();
socket.on('change', () => {
location.reload();
})
Работает! Файлы кроме js тоже отслеживает(мало ли!): проверял .json, .css.
Проверяю вложенные папки — не работает.
Думаю, ладно, сейчас запилю рекурсивно. На всякий случай гуглю и — вуаля- есть готовое
решение.
Добавляю этот пакет.
var express = require('express'); // Подключаем express
var app = express();
var server = require('http').Server(app); // Подключаем http через app
var io = require('socket.io')(server); // Подключаем socket.io и указываем на сервер
var fs = require('fs');
var watch = require('node-watch');
const port = 9080;
server.listen(port);
app.use(express.static(__dirname + '/src'));
let count = 0;
let changed = [];
app.get('/data', (req, res) => {
res.data = count;
res.send({count, changed});
})
const translate = {
remove: "удален",
update: "изменен"
}
watch('./', { recursive: true }, function(evt, name) {
io.emit('change', {data: count});
count += 1;
changed = [{name, evt}, ...changed];
});
Ура, работает!
Вспоминаю про eslint, codeclimate и travis.
Устанавливаю первый, добавляю остальное.
Подчищаю код, все var на const и так далее.
Linter ругается, что angular is not defined, но у меня особенности подключения библиотек в проекте диктуют такое поведение, отключаю. Заодно немного прикручиваю переменные из командной строки, запускаю, все работает!
В пн пришел на работу и прикрутил все это хозяйство на рабочий проект. Пришлось немного изменить, заодно внести правки, чтобы можно было менять некоторые параметры запуска из командной строки и исключения, чтобы не вотчил все подряд.
В итоге получилось вот так:
const express = require('express'),
http = require('http'),
watch = require('node-watch'),
proxy = require('http-proxy-middleware'),
app = express(),
server = http.createServer(app),
io = require('socket.io').listen(server),
exeptions = ['git', 'js_babeled', 'node_modules', 'build', 'hotreload'], // исключения,которые вотчить не надо, файлы и папки
backPortObj = { /* перечень машин,куда смотреть за back*/ },
address = process.argv[2] || /* адрес машины с back*/,
localHostPort = process.argv[3] || 9080,
backMachinePort = backPortObj[address] || /* порт на back машине*/,
isHotReload = process.argv[4] || "y", // "n" || "y"
target = `http://192.168.${address}:${backMachinePort}`,
str = `Connected to machine: ${target}, hot reload: ${isHotReload === 'y' ? 'enabled' : 'disabled'}.`,
link = `http://localhost:${localHostPort}/`;
server.listen(localHostPort);
app
.use('/bg-portal', proxy({
target,
changeOrigin: true,
ws: true
}))
.use(express.static('.'));
if (isHotReload === 'y') {
watch('./', { recursive: true }, (evt, name) => {
let include = false;
exeptions.forEach(item => {
if (`${name}`.includes(item)) include = true;
})
if (!include) {
console.log(name);
io.emit('change', { evt, name, exeptions });
};
});
};
console.log(str);
console.log(link);
var socket = io.connect();
socket.on('change', ({ evt, name, exeptions }) => {
location.reload();
});
запускающий скрипт в package.json просто вызывает server.js из-под node и запускается это вот так:
npm start 1.100 8080
Написал. Сохранил.
В заключении хочу поблагодарить Ваню, моего друга, местами вдохновителя и главного пинателя, а так же Сашу — человека, которого я считаю своим наставником!
Вместо послесловия
А через 2 недели, в последний день своего испытательного срока, я таки прикрутил webpack и webpack-dev-server на наш проект, отправив тем самым свой hot reloader пылиться на полку истории.
Однако эти 2 недели мы использовали его каждый день и он исправно делал свое дело!
Комментарии (31)
Dimtry44
04.09.2019 22:35Иногда имеет смысл всякие волшебные функции не специфичные для приложения выносить в app-specific custom browser extension. Тогда можно гораздо больше контроля иметь и более вкусные функции реализовывать, при этом не смешивая кодовую базу приложения и вспомогательного кода.
euhoo Автор
04.09.2019 22:38Про такую штуку не слышал. Можешь поподробнее?
Dimtry44
04.09.2019 23:03Просто создаёте browser extension с логикой специфичной для вашего приложения. Т.е. это расширение которое знает про ваши environments, функции и т.п. Можно добавлять и контролировать практически всё что угодно.
У нас есть расширение с функционалом общим для компании и отдельные практически для каждого приложения, очень помогает, а сделать легко.
alexesDev
04.09.2019 22:39+2Hot relead подтягивает только изменения и дает инструменты, чтобы это подхватит. У вас просто reload… Уверены, что https://www.browsersync.io/ не подошел бы?
euhoo Автор
04.09.2019 23:13Бегло погуглил: да, вероятно, подошел бы!
Но в целом у меня получилась весьма удобная штука, настроенная конкретно под наш проект.
Кстати, хочу отметить, что сейчас, когда уже настроил и webpack-dev-server, следующим шагом я донастроил сборку так, чтобы осталась возможность использовать мой «велосипед».
В итоге могу выбирать что использовать: w-d-s или свой велоспед. И я большую часть времени использую велосипед. По 4 причинам:
1.Обновление у меня быстрее, чем пересборка w-d-s.
2.В ангуляре есть include в html файл из другого html файла. w-d-s при изменении такого «внутреннего» html файла не релоадит страницу, а мой велосипед-релоадит.
3.Когда где-то отвалилось, в несобранной версии проще найти что и где.
Правда, уверен, что 2 и 3 пункты можно докрутить, но пока не крутил.)
4.Свое. Работает. Приятно)))funca
04.09.2019 23:52Согласен с предыдущим оратором, у вас получился auto reload. Hot reload это про замену кода "на горячую", т.е. без остановки и перезагрузки приложения — ацкая магия, не уверен, что для angularjs получится это сделать. За исключением этого терминологического казуса, классный опыт и классная статья. Дерзайте, надеюсь на продолжение вашей истории.
euhoo Автор
05.09.2019 09:48Спасибо за слова поддержки. В терминологии пока — да — путаюсь! Следующим шагом есть мысли прикрутить как раз эту ацкую магию. Для начала хотя бы css. В начале статьи я давал ссылку, у ребят получилось, поизучаю их опыт. Надеюсь получится и будет, о чем написать!
Jabher
05.09.2019 01:14Первая проблема: проект собирается каким-то древним минимизатором и использовать современный синтаксис js нельзя. Никаких () => {}, const res = [...data, subRes], async/await…
рискну предположить, что речь об Uglify — из старых популярных минификаторов есть только он и гугловый GCC, а GCC уже поддерживает ES6 довольно давно.
Я раза четыре в свое время уже спотыкался об это, и каждый раз только через пару недель вспоминал, что у него есть форк Terser (https://github.com/terser-js/terser). API вроде на 100% совместимое — надо просто подменить функцию, которая вызывается.
Если не угадал — ну… бывает)
codecity
05.09.2019 05:08Мне после React'а он показался динозавром
Динозавром или монстром, позвольте уточнить?euhoo Автор
05.09.2019 09:39Динозавром. Я имел в виду, что в React я увидел технологии, которые в angularjs были в самом зачатке или даже которых еще не было, т.е. под «динозавром» надо понимать «древним вымирающим прародителем».
Сейчас, когда уже более-менее вник в angularjs, он не кажется мне монстром, даже есть некоторые превосходства(в частности проще обмен данными между компонентами и глобальное хранилище сильно для меня проще, чем redux)Defersa
05.09.2019 10:19Я вот первым попробовал Vuejs, после него пошел посмотреть React и ужаснулся. С ангуляром все еще хуже?
euhoo Автор
05.09.2019 12:15Не могу ответить, пока нет опыта с Vue.
А чем ужаснулся в React? Он, вроде, прикольный и не сложный(разве что redux).
Вообще, чем больше вникаю в Ангуляр 1.x, тем больше понимаю, что он просто другой и тоже вполне себе имеет право на жизнь, хоть и старый.Defersa
05.09.2019 12:18+1(Сейчас ИМХО начинается) Ну по сравнению с Vue, React очень сложен — куча правил, меньшая интуитивность происходящего. И там и там я написал по 1.5 строчке кода, но впечатления у меня остались именно такие. Я вот пишу на чистом JS, и выбирая между ними, 100% отдам предпочтение Vue.
Zeitung
05.09.2019 18:16У меня абсолютно обратные впечатления: переход от AngularJS/Angular2+ к React. Сразу скажу что у нас «кровавый энтерпрайз». То что в Angular«ах делается в 2 строчки в React требует написания собственных wrapper»ов и велосипедов (либо скачивание велосипеда с npm). В Redux так вообще въехать не могу: в пределах модуля Context проще и «out of the box» (ну конечно если из него не делать помойку и применять «правила здравого смысла»), а шерить общее состояния конечно нужно, но reactive approach из Observable«s выпиливает всю „магию“ напрочь и цепочки событий становятся просто супер очевидными.
Написать код, который будет рендерить всю страницу на каждый чих можно и в React, при должном упорстве…
Холивар я тут не начинаю, опыта с React ещё не много, наверное не проникся ещё… Хотя, конечно, контроллировать большую часть логики управляющей решением о рендеринге однозначно удобно.
А ещё мне тяжело доверится инструменту от „тех кто написал самый глючный сайт в интернете“.euhoo Автор
05.09.2019 19:45может, дело в направлении перехода?)))
Я вот когда снова вернусь на react, буду пытаться как-то реализовать там модель emit-on, с которой познакомился в ангуляре
Druu
06.09.2019 08:23Написать код, который будет рендерить всю страницу на каждый чих можно и в React, при должном упорстве…
Так реакт по дефолту и рендерит всю страницу на каждый чих :)
Упорства требует как раз написать на реакте код так, чтобы он не рендерил. При этом стандартные оптимизации требуют перекраивать бизнес-логику и очень хрупки.
ehots
05.09.2019 10:36+1Теперь мне доступны все те приятные новые стильные модные молодежные штуки!
Хорошо, если есть понимание зачем они вообще нужны, а не просто «сильно, модно, молодежно».
А так прикольный велик, как минимум хорошо что не задушили инициативу, на легаси такое бывает частенько…euhoo Автор
05.09.2019 12:16тут 2 момента, по моему скромному мнению:
1. Чтобы не отстать, приходится все эти стильные молодежные штуки изучать и пробовать
2. Часто действительно удобно)
thevladmartin
05.09.2019 12:16euhoo, а меня, как человека, пытающегося «вернуться» в айти, больше заинтересовало как вы нашли работу. Я сижу, учу Питон, С++ (раньше кодил на нем, уже давно не использовал), знаю Делфи на уровне хорошего джуниора (тапками прошу не кидаться), работал в сетях и сисадминстве (понятно, что не в компаниях-гигантах), полтора года в ИнфоБезе, самообразовываюсь, но уже полтора года работаю не в своей сфере, вернуться не выходит — не берут никуда, даже до интервью не доходит.
P.S. События происходят в Польше, я иностранец. А говорили, что им тут айтишников не хватает, обманывали, выходит.Defersa
05.09.2019 12:21К сожалению (или к счастью) работодателей очень пугает отсутствие знаний/практики в нужных им сферах, и это можно понят: проще взять человека, который 100% подходит, чем брать наугад и ждать его отдачи: от 0 до 150%.
thevladmartin
05.09.2019 12:24И я это могу понять, но хотя бы в сети вернуться, да хоть бы на самую начальную позицию. :\
И языки вроде знаю, и инструментами пользоваться умею, но…
Ладненько, пардон, просто крик души уже.
euhoo Автор
05.09.2019 12:25Не знаю, как в Польше, но в РФ(а конкретно я в МО) особых трудностей не возникло.
Точнее будет сказать, я не считаю это трудностями.
Я действительно много учился, разбирался. Если что-то не получалось — пытался все равно найти решение. Раскладывал сложную задачу на маленькие решаемые подзадачи.
Во время обучения была одна задача, из-за которой мне пришлось перепройти предыдущие 2 темы, что заняло 2 недели.
Мое резюме на hh просматривали примерно по 10-15 раз в день в дни, когда я утром его обновлял и меньше.когда не обновлял.
Особо никуда не звали(редко), в основном я сам искал компании, которые нравились и сам туда пробивался. Про каждую что-то старался узнать, писал в каждую свое сопроводительное письмо, где описывал свои навыки в соответствии с их требованиями.
В общем сам в основном действовал. Но тут мои 8 лет в продажниках помогли))
Ну и не пугался требований, слал даже туда, где требования были завышены.
По факту именно в такую компанию(где требования в описании к вакансии завышены) я и устроился. На месте оказалось, что в требованиях к вакансии было 80% вообще не того, чем я занимаюсь)))
Druu
05.09.2019 13:02А в чем проблема была взять готовый бойлерплейт для angularjs+webpack и просто перекинуть туда папку src?
VivAmigo
05.09.2019 14:32Ну а я пока менеджером работаю, решил что ещё не готов для большого Java и С# рынка (отдела которого в нашем маленьком поселении нет). Хочу их прокопать под основание. В программирование ушёл с 5 класса (да здравствует один год на Basic и 4 года на pascal в школе, потом в колледже полгода Pascal, год C++, 2 года Java SE, полгода PHP). И у меня появился вопрос: Вы полностью обучились программированию и верстке за 1 год? Или всё же знаете только основы своей специализации? И как вы поняли, что пора выходить на рынок со своей «продукцией»?
shaman4d
«Еще какой-то год назад я работал в совершенно другой сфере и только в теории задумывался о смене профессии» — а чем вы занимались?
euhoo Автор
Продажами занимался последние лет 8.