Я опубликовал обновленное руководство на 2020 год, в котором описаны улучшения и обновления экосистемы React. Начать работу сейчас намного проще, чем в 2017 году, когда было опубликовано это руководство. Направляйтесь туда, чтобы получить самую свежую информацию!

tl;dr

По-прежнему довольно сложно создать основы с помощью React с нуля; следуйте этому руководству или вместо этого клонируйте это репо!

Фон

Я уже пару месяцев хотел научиться пользоваться React. На то есть множество причин, но для меня некоторые из них наиболее очевидны:

  • Мы используем его в Stripe для некоторых наших объектов, и мне нравится свободно владеть нашим технологическим стеком, хотя я работаю над маркетингом.
  • Такие технологии, как Webpack 2 и babel, действительно интересны.
  • Вокруг этого много шума, и я хочу знать, почему!

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

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

Примечание: Почему не create-react-app? Ну, это определенно ближе всего к тому, что я хотел. Однако он не позволяет производить дополнительную настройку конфигурации webpack или какой-либо поддержки SASS без предварительного извлечения, поэтому мы идем дальше :(

Итак, после прочтения многих, многих руководств (и многократных ударов головой о стол) я внес все необходимые изменения и, наконец, создал основу для своего простого приложения.

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

«Стек»

Этот учебник поможет вам:

  • создать многостраничное приложение React, которое будет бесплатно размещено на Heroku.
  • Мы будем использовать React Router для обслуживания разных вещей по разным URL.
  • Также будем использовать Webpack (и настроим поддержку babel и SASS).
  • Мы тонко обернем встроенный webpack-dev-сервер Webpack в Express для фактического обслуживания наших страниц.

Примечания

После прочтения многих, многих руководств этот «стек», кажется, включает в себя наиболее популярные инструменты для хорошей отправной точки для погружения в React.

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

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

Предпосылки

Это руководство предполагает знакомство с общими концепциями веб-разработки и JavaScript. С тактической точки зрения предполагается, что вы установили:

$ node -v
v7.5.0
$ npm -v
v4.1.2
$ git --version
git version 2.11.1

Настройка файла

Хорошо, приступим. Я собираюсь создать двухстраничный веб-сайт с «домашней» страницей и страницей «свяжитесь со мной».

$ mkdir -p about-me && cd about-me
$ npm init

Вы можете либо заполнить подсказки, когда npm init, либо просто нажать Enter до конца. Теперь у вас в папке должен быть файл с именем package.json.

about-me/
└── package.json

Пока мы это делаем, давайте также создадим репозиторий git. Мы также позаботимся о том, чтобы не помещать в репозиторий тысячи файлов, которые вскоре будут заполнены путем установки пакетов узлов:

$ git init
$ touch .gitignore && echo 'node_modules' > .gitignore

Теперь давайте создадим необходимую нам структуру каталогов:

$ touch Procfile && touch server.js && touch webpack.config.js && touch webpack.config.dev.js && mkdir -p src/components/views && mkdir -p src/stylesheets && touch index.html && touch src/index.jsx && touch src/routes.jsx && touch src/components/app.jsx && touch src/components/views/home.jsx && touch src/components/views/contact.jsx && touch src/stylesheets/base.scss && touch src/stylesheets/home.scss && touch src/stylesheets/contact.scss

Теперь наш каталог должен выглядеть так:

about-me/
├── src/
|   ├── components/
|   |   ├── app.jsx
|   |   └── views/
|   |       ├── home.jsx
|   |       └── contact.jsx
|   ├── index.jsx
|   ├── routes.jsx
|   └── stylesheets/
|       ├── base.scss
|       ├── home.scss
|       └── contact.scss
├── .gitignore
├── index.html
├── package.json
├── Procfile
├── server.js
├── webpack.config.js
└── webpack.dev.config.js

Откройте папку в любом редакторе - я использую SublimeText. (С SublimeText я рекомендую установить плагины для выделения формата JSX и SCSS.) Вот как сейчас выглядит структура папок и файлов:

Пришло время установить некоторые зависимости для ядра React, а также обслуживание и маршрутизацию:

$ npm i --save react react-dom react-router
$ npm i --save express body-parser

Затем давайте установим webpack с несколькими зависимостями для переноса scss и jsx в css и js:

$ npm i --save webpack webpack-dev-server webpack-dev-middleware webpack-hot-middleware
$ npm i --save [email protected]
$ npm i --save babel-core babel-loader babel-preset-es2015 babel-preset-react node-sass sass-loader css-loader style-loader

(Примечание: важно установить нестандартную версию extract-text-plugin, поскольку она несовместима с последней версией Webpack.)

База и маршрутизация

Давайте настроим нашу страницу index.html для доступа к скомпилированным js и css:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/public/app.css" />
  </head>
  <body>
    <div id="app"></div>
    <script type="text/javascript" src="/public/bundle.js" charset="utf-8"></script>
  </body>
</html>

Маршрутизация

Мы используем модуль под названием react-router, чтобы React отображал разные представления для разных URL-адресов.

Мы будем использовать index.jsx, чтобы настроить наш маршрутизатор и получить нужные файлы SCSS…

import React from 'react';
import ReactDom from 'react-dom';
import { Router, browserHistory } from 'react-router';
import routes from './routes';
require('./stylesheets/base.scss');
require('./stylesheets/home.scss');
require('./stylesheets/contact.scss');
ReactDom.render(
  <Router history={browserHistory} routes={routes} />,
  document.querySelector('#app')
);

… И routes.jsx, чтобы определить их:

import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/app';
import Home from './components/views/home';
import Contact from './components/views/contact';
export default (
  <Route path='/' component={App}>
    <IndexRoute component={Home} />
    <Route path='contact' component={Contact} />
    <Route path='*' component={Home} />
  </Route>
);

Как видите, путь / будет направлен на app.jsx, и мы настроим два представления для / (home.jsx) и / contact (contact.jsx). Мы также указываем, что любой неуказанный URL-адрес должен обслуживать нашу домашнюю страницу.

Представления и контроллеры

Теперь мы можем настроить некоторые из простых контроллеров для разных страниц, которые мы хотим обслуживать:

Вот как выглядит app.jsx:

import React, { Component } from "react";
export default class App extends Component {
  render() {
    return (
      <div>
        {this.props.children}
      </div>
    );
  }
}

Он ничего не делает, кроме как настраивает шаблон для отображения представлений, которые мы настроим следующим, начиная с home.jsx:

import React, { Component } from "react";
import { browserHistory } from 'react-router';
export default class Home extends Component {
  componentDidMount() {
    browserHistory.push('/');
  }
  render() {
    return (
      <div id="home">
        This is the home page.
      </div>
    );
  }
}

Поскольку это представление для всех, я предпочитаю изменить URL-адрес на /, если кто-то попадет в это представление. (например, если я действительно запрошу / shop, мы откроем главный экран и изменим URL-адрес на /.)

Вот очень похожий contact.jsx:

import React, { Component } from "react";
export default class Contact extends Component {
  render() {
    return (
      <div id="contact">
        This is the contact me page.
      </div>
    );
  }
}

Стиль

Давайте настроим несколько фиктивных файлов SASS, чтобы увидеть, как все проверяется при компиляции. Начиная с base.scss

$var: blue;
body {
  background: $var;
}

… Затем contact.scss

#contact {
  color: #fff
}

… И, наконец, home.scss:

#home {
  color: red;
}

Эти файлы мы в конечном итоге будем использовать для хранения стилей CSS для конкретной страницы.

Экспресс и Webpack

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

Прежде всего, давайте внесем небольшое изменение в package.json, чтобы сообщить ему, что он использует нашу версию Node в качестве движка и изменит точку входа с index.js на server.js:

...
"main": "server.js",
"engines": {
  "node": ">=7.5.0"
},
...

Чтобы запустить приложение, нам нужно настроить два файла конфигурации для webpack, которые соответствуют средам prod и dev.

webpack.config.js:

const ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require('webpack');
module.exports = {
  context: __dirname,
  entry: "./src/index.jsx",
  output: {
    path: __dirname + '/public',
    filename: "bundle.js",
    publicPath: '/public/'
  },
  module: {
    loaders: [
      {
        test: /\.js|.jsx?$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader',
        query: {
          presets: ['react', 'es2015']
        }
      },
      {
        test: /\.scss$/,
        loader: ExtractTextPlugin.extract('css-loader!sass-loader')
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
  plugins: [
    new ExtractTextPlugin({ filename: 'app.css', allChunks: true })
  ],
  devServer: {
    historyApiFallback: true,
    contentBase: './'
  }
};

webpack.config.dev.js

var path = require('path');
var webpack = require('webpack');
module.exports = {
  context: __dirname,
  entry: "./src/index.jsx",
  output: {
    path: path.resolve(__dirname, 'public/'),
    filename: "bundle.js",
    publicPath: '/public/'
  },
  module: {
    loaders: [
      {
        test: /\.js|.jsx?$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader',
        query: {
          presets: ['react', 'es2015']
        }
      },
      {
        test: /\.scss$/,
        loader: 'style-loader!css-loader!sass-loader?sourceMap'
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    historyApiFallback: true,
    contentBase: './'
  }
};

И наконец, наш файл server.js выглядит так:

var path = require('path');
var bodyParser = require('body-parser');
var express = require('express');
var webpack = require('webpack');
var config = require('./webpack.config.dev.js');
var app = express();
var compiler = webpack(config);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(require('webpack-dev-middleware')(compiler, {
  noInfo: true,
  publicPath: config.output.publicPath
}));
app.use(require('webpack-hot-middleware')(compiler));
app.use('/public', express.static('public'));
app.get('*', function(req, res) {
  res.sendFile(path.resolve(__dirname, 'index.html'));
});
app.listen(process.env.PORT || 5000, function(err) {
  if (err) {
    console.log(err);
    return;
  }
console.log('Listening at http://localhost:5000');
});

Запуск нашего сайта

Давайте обновим наш файл package.json, добавив несколько скриптов, чтобы упростить запуск наших серверов:

...
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "dev": "webpack-dev-server --hot --inline",
  "start": "node server.js"
},
...

Теперь, когда все настроено, вы можете запускать:

npm run dev

(Это подберет параметры нашего webpack.config.dev.js.)

Если вы посетите http: // localhost: 8080 /, вы должны увидеть:

Если вы хотите запустить сайт в соответствии с нашей производственной установкой, вы можете запустить:

npm start

Если вы посетите http: // localhost: 5000 / contact, вы должны увидеть:

Развертывание на Heroku

Совершить

Если пока все выглядит хорошо, давайте перейдем к git:

$ git add .
$ git commit -am "initial scaffolding for about-me app"

Настройка Heroku

Я предполагаю, что вы создали учетную запись и следовали их руководству, чтобы настроить интерфейс командной строки Heroku.

Первое развертывание

Внутри файла Procfile добавьте следующее:

web: node server

Затем вы можете выполнить шаги на странице документации Heroku, чтобы развернуть приложение:

$ heroku create
$ git push heroku master
$ heroku ps:scale web=1
$ heroku open

Вот и все! Наше простое приложение теперь работает на Heroku!

Мысли на прощание

Этот урок оставляет вас только в самом начале. Теперь вы можете начать создавать это простое приложение по своему усмотрению, настроив все вещи и начав экспериментировать с различными частями этого конкретного «стека» React.

Хотя мне будет сложно поддерживать это руководство в актуальном состоянии, я настоятельно призываю вас отправить запрос на перенос в репозиторий, если вы заметите что-то неладное!

Спасибо за прочтение :)