Реагировать с нуля, часть 1: Webpack
Многие другие руководства по началу работы с React обычно упускают из виду все сопутствующие вещи, такие как WebPack и код на стороне сервера. Они, как правило, заставляют вас клонировать существующее репо, использовать кодовое слово или использовать упрощенную цепочку инструментов, чтобы показать основы. Здесь еще бред…
В этой серии статей я хочу показать, как начать с нуля. Начиная с отсутствия файлов и заканчивая работающим проектом.
В этой статье мы рассмотрим основы подготовки к разработке на React.
Предпосылки
Для этого путешествия вам понадобятся:
И это все.
Шаг 1. Настройка
Давайте создадим где-нибудь нашу папку проекта:
$ mkdir react-from-scratch $ cd react-from-scratch/
Теперь давайте запустим NPM и Git.
$ git init Initialized empty Git repository in blah/react-from-scratch/.git/ $ npm init This utility will walk you through blah blah blah About to write to blah/react-from-scratch/package.json { "name": "react-from-scratch", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "MIT" } Is this ok? (yes) $
Далее установим Экспресс для базового хостинга.
$ npm install --save express npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN [email protected] No description npm WARN [email protected] No repository field. + [email protected] added 42 packages in 8.109s $
И создайте server/index.js
файл
const express = require('express'); const app = express(); app.use(express.static('www')); app.listen(3000, () => { console.log('Example app listening on port 3000!') });
И client/index.html
файл
<!DOCTYPE html> <html lang="en"> <head> <title>React from scratch</title> </head> <body> Hello World! </body> </html>
И беги!
$ node server Example app listening on port 3000!
Теперь у нас есть очень простой веб-сервер со статическим файловым хостингом. Пока оставим это так.
Примечание. Я пропускаю такие вещи, как создание папок, добавление материалов в .gitignore
, фиксацию, настройку редактора и т. д.
Шаг 2: веб-пакет
Затем мы установим веб-пакет, который будет нашим сборщиком. Он возьмет все наши ресурсы и скрипты и должным образом скоммит их.
$ npm install webpack --save-dev > [email protected] postinstall blah > node lib/post_install.js npm WARN [email protected] No description npm WARN [email protected] No repository field. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: [email protected] (node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) + [email protected] added 362 packages in 47.499s $
Примечание: этот раздел более или менее соответствует руководству по WebPack с меньшим изложением. Если вам нужны подробности, загляните сюда…
Для webpack необходим файл конфигурации. Для начала создадим базовый webpack.config.js
файл:
const path = require('path'); module.exports = { entry: './client/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'www') } };
И базовый client/index.js
файл
function component() { var element = document.createElement('div'); element.innerHTML = "Hello world!"; return element; } document.body.appendChild(component());
Теперь, если вы запустите webpack из командной строки, он построит вывод:
$ npx webpack Hash: fa1454c9f41b0eb6748e Version: webpack 3.5.4 Time: 57ms Asset Size Chunks Chunk Names bundle.js 2.65 kB 0 [emitted] main [0] ./client/index.js 175 bytes {0} [built] $
Примечание. Вы могли заметить, что я звоню npx
выше. Это было включено в npm 5.2.0 и позволяет легко запускать сценарии пакетов npm. Если у вас его нет, вы можете обновить npm, запустив npm install -g npm
(может потребоваться sudo
в Linux).
Теперь давайте изменим наш html так, чтобы он ссылался на связанный файл:
<!DOCTYPE html> <html lang="en"> <head> <title>React from scratch</title> </head> <body> <script src="bundle.js"></script> </body> </html>
И беги!
$ node server Example app listening on port 3000!
Теперь, когда у нас есть рабочая настройка веб-пакета, давайте добавим несколько модулей, чтобы сделать вещи более полезными.
Модуль HTML
Во-первых, давайте заменим этот HTML-файл, который мы создали сами, на файл, созданный с помощью webpack. Установите это:
$ npm install --save-dev html-webpack-plugin ... stuff ... + [email protected] added 153 packages in 27.109s
И отредактируйте файл webpack.config.js
, чтобы использовать новый плагин:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './client/index.js', plugins: [ new HtmlWebpackPlugin({ title: 'React from scratch' }) ], output: { filename: 'bundle.js', path: path.resolve(__dirname, 'www') } };
Теперь, когда мы построим, он сгенерирует файл index.html
для нас:
$ ./node_modules/.bin/webpack Hash: d451e987f4ef8beb712e Version: webpack 3.5.4 Time: 418ms Asset Size Chunks Chunk Names bundle.js 2.65 kB 0 [emitted] main index.html 189 bytes [emitted] [0] ./client/index.js 175 bytes {0} [built] Child html-webpack-plugin for "index.html": 1 asset [2] (webpack)/buildin/global.js 509 bytes {0} [built] [3] (webpack)/buildin/module.js 517 bytes {0} [built] + 2 hidden modules
Чистый модуль
Также добавим чистый модуль. Это обеспечит удаление старых файлов из нашего www
каталога.
$ npm install clean-webpack-plugin --save-dev npm WARN optional ... npm WARN notsup ... + clean-webpack-plugin@0.1.16 added 122 packages in 19.149s
А затем наш конфиг веб-пакета:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const dist = path.resolve(__dirname, 'www'); module.exports = { entry: './client/index.js', plugins: [ new CleanWebpackPlugin([dist]), new HtmlWebpackPlugin({ title: 'React from scratch' }) ], output: { filename: 'bundle.js', path: path.resolve(__dirname, dist) } };
Я также добавил константу dist
для удобства.
Исходные карты
Чтобы сделать разработку практичной, давайте включим исходные карты в webpack.config.js
:
module.exports = { entry: './client/index.js', devtool: 'inline-source-map', plugins: [ ... };
Есть еще несколько вариантов на выбор, но давайте пока воспользуемся inline-source-map
. Этот параметр обычно меняется в производственной среде, но пока мы оставим его таким.
Сервер
Чтобы упростить процесс разработки, было бы полезно, если бы пакет перестраивался каждый раз при изменении файла. Для этого есть три варианта: использовать webpack-dev-server
, который берет на себя хостинг и имеет все встроенные тонкости (например, перезагрузку в реальном времени). Есть режим просмотра (запуск webpack --watch
), в котором webpack работает в фоновом режиме и наблюдает за файлами. Еще есть webpack-dev-middleware
, который делает то же самое, но будет работать как часть нашего сервера Express.
В этом руководстве мы будем использовать webpack-dev-middleware
.
Почему бы не использовать webpack-dev-server
? Это входит в основу данного руководства. Я жалуюсь на то, что во многих новых технологиях есть инструменты, которые делают все за вас. Вроде webpack-dev-server
. Хотя изначально они упрощают задачу, они не помогут вам в тот день, когда вам нужно перейти в производство. Я знаю проекты, где это было такой проблемой, что они в конечном итоге использовали webpack-dev-server
в производстве. Так что давайте лучше придумаем практическое решение, которое нам не нужно будет существенно менять, когда мы перейдем к производству.
$ npm install --save-dev webpack-dev-middleware
Согласно документации меняем server/index.js
:
const express = require('express'); const webpack = require('webpack'); const webpackconfig = require('../webpack.config'); const webpackMiddleware = require("webpack-dev-middleware"); const app = express(); app.use(express.static('www')); const wpmw = webpackMiddleware(webpack(webpackconfig),{}); app.use(wpmw); app.listen(3000, () => { console.log('Example app listening on port 3000!') });
А теперь, если мы запустим:
$ node server clean-webpack-plugin: ... has been removed. Example app listening on port 3000! Hash: d451e987f4ef8beb712e Version: webpack 3.5.4 Time: 423ms Asset Size Chunks Chunk Names bundle.js 6.47 kB 0 [emitted] main index.html 189 bytes [emitted] [0] ./client/index.js 175 bytes {0} [built] Child html-webpack-plugin for "index.html": Asset Size Chunks Chunk Names index.html 544 kB 0 [0] ./node_modules/html-webpack-plugin/lib/loader.js!./node_modules/html-webpack-plugin/default_index.ejs 538 bytes {0} [built] [1] ./node_modules/lodash/lodash.js 540 kB {0} [built] [2] (webpack)/buildin/global.js 509 bytes {0} [built] [3] (webpack)/buildin/module.js 517 bytes {0} [built] webpack: Compiled successfully.
Он компилирует веб-пакет при запуске и хранится в памяти. Теперь измените файл client/index.js
и посмотрите, что произойдет:
webpack: Compiling... Hash: 163359edcf1649bf3d75 Version: webpack 3.5.4 Time: 51ms Asset Size Chunks Chunk Names bundle.js 6.49 kB 0 [emitted] main index.html 189 bytes [emitted] [0] ./client/index.js 182 bytes {0} [built] Child html-webpack-plugin for "index.html": Asset Size Chunks Chunk Names index.html 544 kB 1 [0] ./node_modules/html-webpack-plugin/lib/loader.js!./node_modules/html-webpack-plugin/default_index.ejs 538 bytes {1} [1] ./node_modules/lodash/lodash.js 540 kB {1} [2] (webpack)/buildin/global.js 509 bytes {1} [3] (webpack)/buildin/module.js 517 bytes {1} webpack: Compiled successfully.
Теперь, если вы перезагрузите страницу, она обновится!
Шаг 3: стили и файлы
Итак, теперь у нас функционирует веб-пакет, объединяющий наш JavaScript (еще не React, но мы доберемся до него), давайте перейдем к изображениям и стилям.
Картинки
В качестве примера давайте загрузим это изображение: https://facebook.github.io/react/img/logo.svg, которое выглядит так:
И положи в client/content
.
Чтобы иметь возможность импортировать изображение, нам понадобится file-loader
:
$ npm install --save-dev file-loader + [email protected] added 116 packages in 14.652s
И нам нужно обновить нашу конфигурацию, чтобы использовать загрузчик файлов:
module.exports = { entry: './client/index.js', devtool: 'inline-source-map', plugins: [ ... ], module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: ['file-loader'] } ] }, output: ... };
Теперь давайте импортируем его из нашего JavaScript:
import reactlogo from './content/react.svg'; function component() { var element = document.createElement('div'); element.innerHTML = "Hello world!"; var logo = new Image(); logo.src = reactlogo; element.appendChild(logo); return element; }
И сборка webpack:
$ ./node_modules/.bin/webpack clean-webpack-plugin: blah has been removed. Hash: b981df6dcd6b8f00c593 Version: webpack 3.5.4 Time: 482ms Asset Size Chunks Chunk Names 3d593052bb2819bdf7709d9f7e512c8f.svg 1.84 kB [emitted] bundle.js 7.71 kB 0 [emitted] main index.html 189 bytes [emitted] [0] ./client/index.js 307 bytes {0} [built] [1] ./client/content/react.svg 82 bytes {0} [built] Child html-webpack-plugin for "index.html": 1 asset [2] (webpack)/buildin/global.js 509 bytes {0} [built] [3] (webpack)/buildin/module.js 517 bytes {0} [built] + 2 hidden modules
Как вы можете видеть, теперь он также создал svg. Если мы запустим, то увидим логотип!
Поскольку это ужасно, давайте перейдем к стилям.
Стили
Для стилей CSS можно использовать style-loader
и css-loader
, однако мы будем использовать Sass вместо обычных файлов CSS.
Для начала давайте создадим файл sass, чтобы все выглядело немного лучше. Создать client/styles/main.scss
:
body { background: #222; color: white; font-size: 1.1rem; font-family: Arial, Helvetica, sans-serif; div { margin: 20px; font-size: 2em; text-align: center; & > img { width: 300px; height: 300px; display: block; margin: 20px auto; } } }
Установите sass-loader
. Нам также понадобятся node-sass
, а также style-loader
и css-loader
. Это преобразует импорт из SCSS в CSS, из CSS в javascript и из javascript в узлы стилей.
$ npm install sass-loader node-sass \ css-loader style-loader --save-dev + [email protected] + [email protected] + [email protected] + [email protected] added 114 packages and updated 4 packages in 29.43s
Нам нужно зарегистрировать их в нашей конфигурации webpack.
... module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: ['file-loader'] }, { test: /\.(scss|sass)$/, use: ['style-loader', 'css-loader', 'sass-loader'] } ] }, ...
Теперь мы можем импортировать его из нашего client/index.js
файла:
import reactlogo from './content/react.svg'; import './styles/main.scss';
А теперь, если мы запустим:
Шаг 4: другие вещи
Нет, поскольку у нас есть базовая настройка веб-пакета, мы можем посмотреть на некоторые другие вещи.
Замена горячего модуля
Горячая замена / перезагрузка / обновление / замена модулей (только HMR) позволяет перезагружать модули (изображения, стили и т. Д.) Во время работы приложения (в вашем браузере). Это достигается путем обнаружения изменений в файлах (во время работы приложения), перекомпиляции веб-пакета и синхронизации изменений в вашем браузере. Если вы используете сервер webpack dev, это почти бесплатно, но если вы используете webpack-dev-middleware, нам нужно внести пару небольших изменений. Я написал другую статью, в которой идет речь. Сейчас хорошее время посмотреть на него, если вам нужна эта функция:
Опн
Иногда бывает полезно открывать браузер при каждом запуске проекта. Мы можем получить это, используя opn. Добавим:
$ npm install --save-dev opn
И обновите server/index.js
:
const express = require('express'); ... const opn = require('opn'); const development = process.env.NODE_ENV != 'production'; const port = 3000; const app = express(); ... app.listen(port, () => { console.log(`Example app listening on port ${port}!`); if (development) { opn(`http://127.0.0.1:${port}/`); } });
Теперь браузер будет открываться каждый раз при запуске сервера! Намного удобнее.
Некоторые случайные настройки
Мы можем добавить несколько скриптов в наш package.json
, чтобы упростить задачу:
{ "name": "react-from-scratch", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\"", "build": "webpack", "start": "node server" }, "author": "Cillié Malan", ... }
так что теперь мы можем:
$ npm run build > [email protected] build blah > webpack clean-webpack-plugin: blah blah Hash: blah blah yada yada ... ...
or:
$ npm start > [email protected] start blah > node server clean-webpack-plugin: blah blah Example app listening on port 3000! webpack: wait until bundle finished: / webpack built 65f222c3fcfabd3d95d2 in 1266ms blah blah ...
Эти вещи делают вещи только немного лучше.
Что дальше
Далее мы рассмотрим добавление React в имеющуюся у нас настройку webpack dev.
Чтобы увидеть весь код, который мы сделали в этой статье, вы можете проверить конкретную ветку в репозитории github:
Https://github.com/cilliemalan/react-from-scratch/tree/step-1