Для развития одного из наших потенциальных проектов я заинтересовался современными технологиями трехмерной визуализации. Когда-то давно я много программировал с использованием стэка OpenGL, и хотел быстро освежить в памяти современные концепции и набросать прототип. Моему желанию сходу стали препятствовать несколько обстоятельств.
Во-первых, начиная с macOS Mojave, Apple полностью перешла на свой фреймворк Metal 2, не только убив поддержку NVidia CUDA (до свидания MatLAB, Photoshop, несколько десятков удобный утилит и вся рабочая разработка на CUDA C/C++), но и усложнив работу с OpenGL.
Во-вторых, сейчас вся разработка интерфейсов построена на web-технологиях, и именно эта область развивается быстрее всего. Это значит, что для построения нормального переиспользуемого решения, которое не будет объявлено deprecated уже через полгода после выпуска, также стоит поддаться web тренду. Ведь даже среда разработки, которой я иногда пользуюсь для Haskell — это Visual Studio Code, построенный на основе Electron (сейчас, правда, его почти полностью вытеснил SpaceVim). На нем же работает приложение Ghost (в котором пишется этот текст), а также Slack, Skype и многие другие программы общего пользования.
В качестве примера я решил рассмотреть Three.JS, предоставляющий высокоуровневые интерфейсы для использования WebGL, который, как я надеюсь, следует знакомым мне принципам OpenGL. Так как до этого я веб-разработкой никогда не занимался, меня сильно смутил весь окружающий её тулинг. Этой заметкой я хочу зафиксировать свой путь создания простейшего проекта.
В мире web-разработки безраздельно правит JavaScript, а в мире JavaScript связка node + npm, позволяющая использовать его не только для браузерной разработки, но и писать удобные консольные инструменты. В том числе для сборки классических браузерных проектов. 😊
Но для начала создадим максимально глупый код, состоящий из трех файлов: верстки, стиля и скрипта отрисовки.
В результате мы увидим замечательный белый квадратик (на самом деле кубик) на голубом фоне. Учиться работать с ThreeJS мы будем в следующий раз, а пока попробуем понять, что здесь не так и почему бы не идти просто путем такой разработки в трех файлах.
На самом деле, работать, конечно, можно и так. Но мы сталкиваемся с рядом ограничений, терпеть которые людям, пришедшим из тепличной backend-разработки решительно невозможно, а именно:
- невозможность разбиения кода на модули;
- зависимость от кода на удаленном сервере;
- ограничение vanila-JS, который неравномерно развивается в разных браузерах;
- отсутствие проверок кода и выполнения на нем каких-либо CI процедур;
- необходимость вносить изменения сразу в несколько мест (html, css, js);
- и многое другое...
Будем последовательно бороться с этими недугами.
Создание npm-проекта
В первую очередь создадим проект в нашей директории:
$ npm init -y
Wrote to /prjoect/path/package.json:
{
"name": "example-project",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Теперь её содержимое выглядит так:
$ tree .
.
├── app.js
├── index.html
├── package.json
└── style.css
0 directories, 4 files
Содержимое package.json
еще можно подредактировать, указав автора и прочую мета-информацию, мы же перейдем к следующим шагам.
Подключение webpack
Одна из главных болей — это, конечно, невозможность использовать модули при нашей разработке. Её, и не только, решает превосходная утилита Webpack, также являющаяся почти незаменимой в каждом современном проекте. Для её использования у нас требуется ввести всего лишь команду установки:
$ npm install --save-dev webpack webpack-cli
...
+ webpack-cli@3.3.7
+ webpack@4.39.3
added 453 packages from 237 contributors and audited 5286 packages in 13.776s
found 0 vulnerabilities
Теперь мы можем собирать один JS-"бандл" из нескольких модулей. Воспользуемся этим для того, чтоб подключить ThreeJS:
$ npm install --save three
...
+ three@0.108.0
added 1 package from 1 contributor and audited 5287 packages in 2.73s
found 0 vulnerabilities
Теперь мы можем добавить в наш файл app.js
подключение ThreeJS:
var THREE = require('three');
...
Из index.html
можно убрать строчку подключения библиотеки со стороннего CDN. Но это не единственное изменение, которое нам потребуется сделать в HTML-файле. Дело в том, что результатом "сборки" будет не модификация JS-файла и даже не добавление новых строчек в HTML — это будет создание нового JS, который будет содержать в себе весь используемый код. По умолчанию такой бандл будет лежать в dist/main.js
, а потому на него и сошлемся. Итоговый вид нашего index.html
будет:
Как же теперь собрать наш проект? Для этого в скрипты файла package.json
нужно добавить соответствующую команду:
Теперь проект может быть собран командой:
$ npm run build
> example-project@1.0.0 build /path/to/project
> webpack --mode production
Insufficient number of arguments or no entry found.
Alternatively, run 'webpack(-cli) --help' for usage info.
Hash: c8e9ac5d52d7c3c70555
Version: webpack 4.39.3
Time: 52ms
Built at: 04.09.2019 09:56:46
ERROR in Entry module not found: Error: Can't resolve './src' in '/path/to/project'
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! example-project@1.0.0 build: `webpack --mode production`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the example-project@1.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/dir/.npm/_logs/2019-09-04T06_56_46_316Z-debug.log
Кажется, что-то пошло не так. Действительно, Webpack по умолчанию ищет все JS-файлы в директории src
, а главный файл ожидает увидеть под именем index.js
. Поможем ему:
$ mkdir src
$ mv app.js src/index.js
$ npm run build
> example-project@1.0.0 build /path/to/project
> webpack --mode production
Hash: 9a8895c35652a46d21af
Version: webpack 4.39.3
Time: 706ms
Built at: 04.09.2019 10:05:35
Asset Size Chunks Chunk Names
main.js 587 KiB 0 [emitted] [big] main
Entrypoint main [big] = main.js
[0] ./src/index.js 842 bytes {0} [built]
+ 1 hidden module
...
Скорее всего мы увидим еще несколько предупреждений (они связаны с тем, чот получившийся бандл слишком большой — мы добавили в него всю библиотеку ThreeJS, а не только нужные нам функции), но мы пока их проигнорируем и насладимся результатом, открыв index.html
. Браузер покажет нам ровно тот же белый квадрат на голубом фоне.
Пакуем HTML
Во время наших манипуляций с компиляцией JS-файлов нам пришлось также менять и наш HTML. В будущем таких манипуляций может быть еще много, а потому почему бы не научиться генерировать часть этого HTML автоматически? Webpack поможет нам и в этом. Но для этого нужно научиться его конфигурировать.
Вся конфигурация сборки с помощью Webpack описывается в специальном файле webpack.config.js
. Создадим базовую болванку такого файла:
Запуск npm run build
приведет ровно к тому же результату, поскольку мы, фактически, воспроизвели конфигурацию по умолчанию. При изменении output.filename
нам опять придется менять index.html
. Чтоб этого не делать мы можем воспользоваться волшебным плагином HtmlWebpackPlugin. Для начала поставим его, а также HTML-загрузчик, который нам также потребуется:
$ npm install --save-dev html-webpack-plugin html-loader
...
+ html-loader@0.5.5
+ html-webpack-plugin@3.2.0
added 63 packages from 75 contributors and audited 5407 packages in 4.757s
found 0 vulnerabilities
Как несложно догадаться, теперь нам потребуется промодифицировать конфиг Webpack`а:
Также удалим из index.html
вызов скрипта, теперь Webpack подставит его самостоятельно:
После npm run build
теперь содержимое dist
будет таким:
$ tree dist
dist
├── index.html
└── main.js
0 directories, 2 files
Новый index.html
теперь избавлен от комментариев и содержит вызов скрипта. А потому мы можем увидеть наш знакомый белый квадрат при его открытии (NB: это неправда!). Сам скрипт теперь можно переименовывать как угодно, всё сохранится.
Пакуем CSS
Если открыть-таки index.html
, то можно заметить, что в предыдущем абзаце я соврал: никакого квадрата не будет, по причине того, что наш контейнер был нулевого размера — ведь никакие стили не были импортированы. Конечно, мы можем скопировать их вручную, и все заработает. Но зачем это делать, если Webpack может взять это на себя?
Традиционно начнем с добавления пары модулей в режиме разработки:
$ npm install --save-dev css-loader style-loader
...
+ style-loader@1.0.0
+ css-loader@3.2.0
added 16 packages from 47 contributors and audited 5543 packages in 3.974s
found 0 vulnerabilities
Также промодифицируем файл конфигурации Webpack. А именно, в секцию module.rules
добавим следующее:
Теперь нам нужно указать, откуда брать файл со стилем. Для этого уберем строчку подключения стилей из index.html
и добавим строчку подключения стилей в наш JavaScript:
Теперь наш Webpack с помощью css-loader
подгрузит наши стили прямо в JavaScript, и нам больше не придется ничего копировать. Команды npm install
и npm run build
теперь будут собирать полностью рабочий проект без необходимости что-либо доделывать руками. Это покрывает создание базового проекта web-приложения без каких-либо надстроек или дополнительных фишек типа поддержки новых версий ECMAScript. О них мы поговорим в другой раз.
Итоговый проект будет выглядеть так (автоматически скачиваемые или генерируемые файлы скрыты):