Russian (Pусский) translation by Anton L (you can also view the original English article)



JSX похож на смесь XML и HTML. JSX используется в React коде, чтобы без лишних усилий создавать компоненты вашего приложения. JSX компилируется React в JavaScript код.
Вся прелесть React'а заключается в том, что вы можете создать код, который легко повторно использовать и структурировать ваше приложение основываясь на идеи компонентов. Наконец, создание структуры полагающейся на идеи семантики и реализация самого приложения, идут рука об руку.
Первый взгляд на JSX
Пример рендеринга JSX в HTML:
var div = <div className="foo" />; ReactDOM.render(div, document.getElementById('example'));
Для создания компонента, используйте локальные переменные, начинающиеся с большой буквы, то есть:
var MyComponent = React.createClass({/*...*/}); var myElement = <MyComponent someProperty={true} />; ReactDOM.render(myElement, document.getElementById('example'));
Обратите внимание: в JSX есть зарезервированные слова, так как в свой основе это тот же JavaScript—тем самым не стоит использовать слова class
и for
, в качестве имён атрибутов. Вместо этого свойства React компонентов должны иметь название className
и htmlFor
, к примеру.
Вложенные теги
Создание дочерних элементов в JSX, выглядит следующим образом:
var User, Profile; // You write in JSX: var app = <User className="vip-user"><Profile>click </Profile></User>; // What will get outputted in JS: var app = React.createElement( User, {className:"vip-user"}, React.createElement(Profile, null, "click") );
Тестирование JSX
Чтобы проверить, как работает JSX, воспользуйтесь Babel REPL.
Суб-компоненты и пространство имён
Создать форму с JSX и суб-компонентами невероятно просто, к примеру:
var Form = FormComponent; var App = ( <Form> <Form.Row> <Form.Label htmlFor='login' /> <Form.Input name='login' type='text' /> </Form.Row> <Form.Row> <Form.Label htmlFor='password' /> <Form.Input name='pass' type='password' /> </Form.Row> </Form> );
Чтобы это работало, следует сделать суб-компонент и назначить его, как атрибут основного компонента:
var FormComponent = React.createClass({ ... }); FormComponent.Row = React.createClass({ ... }); FormComponent.Label = React.createClass({ ... }); FormComponent.Input = React.createClass({ ... });
Выражения
Чтобы добавить результат выполнения JavaScript кода в качестве значения атрибута React, вам следует обернуть выражение в {}
фигурные скобки:
// You write in JSX: var myUser = <User username={window.signedIn ? window.username : ''} />; // Will become this in JS: var myUser = React.createElement( User, {username: window.signedIn ? window.username : ''} );
Также вы можете передать булево значение в качестве атрибута формы, disabled
, checked
и так далее. Эти значения также могут быть написаны обычным HTML.
// Make field required <input type="text" name="username" required />; <input type="text" name="username" required={true} />; // Check by default <input type="checkbox" name="rememberMe" value="true" checked={true} />; // Enable a field <input type="text" name="captcha" disabled={false} />
Spread атрибуты
Когда вы хотите назначить несколько атрибутов, убедитесь, что вы делаете это во время объявления компонента, а не после. Позднее объявления - опасный анти-паттерн, это значит, есть вероятность, что на момент исполнения данные свойства могут быть недоступны.
Добавить несколько свойств компоненту, можно следующим образом, используйте ES6 новый ...
spread оператор.
var props = {}; props.username = username; props.email = email; var userLogin = <userLogin {...props} />; ``` You can use these prop variables multiple times. If you need to override one of the properties, it can be done by appending it to the component after the `...` spread operator, for example: ```js var props = { username: 'jimmyRiddle' }; var userLogin = <userLogin {...props} username={'mickeyFinn'} />; console.log(component.props.username); // 'mickeyFinn'
Комментарии в JSX
В JSX можно использовать как //
так и /* ... */
- комментарий для нескольких строк. К примеру:
var components = ( <Navigation> {/* child comment, put {} around */} <User /* multi line comment here */ name={window.isLoggedIn ? window.name : ''} // Here is a end of line comment /> </Navigation> );
Подводные камни JSX
В JSX есть несколько вещей, которые могут некоторых застать врасплох, к примеру добавление атрибутов нативным HTML элементам, которые не существуют в HTML спецификации.
React не будет рендерить любые атрибуты HTML элементов, которых нет в спецификации, пока вы не добавите префикс data-
:
<div data-custom-attribute="bar" />
Также, рендеринг HTML внутри динамичного контента, может сбивать с толку, в связи с экранированием и XSS защитой, встроенной в React. По этому React предоставляет нам dangerouslySetInnerHTML
.
<div dangerouslySetInnerHTML={{__html: 'Top level » Child'}} />
Пример чат приложения
Возможно вам доводилось встречаться с приложением Gitter. Это приложение - веб-чат в реальном времени, в основном предназначенный для разработчиков. Многие используют это приложения, чтобы обсудить проекты на GitHub, связано это с простой интеграцией с GitHub и возможностью создания канала для вашего репозитория.
Приложение такого типа можно запросто сделать с React, используя WebRTC
API для создания p2p чата в браузере. Мы будем использовать Node модули PeerJs
и socket.io
.
Для того чтобы вы поняли идею архитектуры приложения, вот простая низкоуровневая UML диаграмма:



ChatServer получает сообщения от PeerJS, и каждый клиент будет использовать прокси, чтобы позаботиться о NAT traversal.
Мы начнём делать это приложения с нуля, чтобы вы могли полностью почувствовать, как создавать React приложение. Сперва создайте новую директорию для вашего приложения, затем внутри этой директории создайте файл package.json
.
{ "name": "react-webrtc-chat", "version": "0.0.0", "description": "React WebRTC chat with Socket.io, BootStrap and PeerJS", "main": "app.js", "scripts": { "start-app": "node app.js", "start": "npm run start-app" }, "keywords": [ "webrtc", "react" ], "license": "MIT", "dependencies": { "express": "~4.13.3", "peer": "~0.2.8", "react": "~0.12.2", "react-dom": "^0.14.1", "socket.io": "~1.0.6" }, "devDependencies": { "babel-preset-react": "^6.0.14", "babelify": "^7.1.0", "browserify": "^12.0.1" } }
Файл package.json
используется npm
, чтобы сконфигурировать ваше приложение. Мы указываем в нём зависимости: express
; веб-фреймворк, который мы будем использовать, чтобы отправлять файлы нашего приложения; peer, сервер peerjs
будет использовать для отправления сигналов; socket.io
для пулинга и реализации webRTC. react-bootstrap
и bootstrap
библиотеки для использования CSS фреймворка от Twitter, тем самым мы сможем добавить стили нашему приложению.
Нам также понадобятся дополнительные библиотеки и для этого мы используем bower
.
Создадим bower.json
и добавим в него следующее:
{ "name": "react-webrtc-chat", "main": "app.js", "version": "0.0.0", "ignore": [ "**/.*", "node_modules", "bower_components", "public/lib", "test", "tests" ], "dependencies": { "react": "~0.12.2", "jquery": "~2.1.4", "eventEmitter": "~4.2.7", "peerjs": "~0.3.14", "bootstrap": "~3.3.5" } }
В этом конфигурационном файлы, мы укажем react
, jQuery
, bootstrap
, eventEmitter
для запуска событий и peerJS
клиентская библиотека, который послужит обёрткой WebRTC.
Ну и наконец, следует обозначить куда всё это должно быть установлено, в нашем .bowerrc
файле:
{ "directory": "src/lib" }
На этом моменте, просто расслабьтесь и установите зависимости, с помощью команды:
$ bower install && npm install
Наконец когда закончится выполнение команды, вы увидите новые директории node_modules
и src/lib
. Тут находятся наши модули, готовые к использованию.
Теперь создайте app.js
в основной директории рядом с package.json
. Это будет главный файл нашего приложения.
//Configure our Services var express = require('express'), PeerServer = require('peer').PeerServer, events = require('./src/Events.js'), app = express(), port = process.env.PORT || 3001; //Tell express to use the 'src' directory app.use(express.static(__dirname + '/src')); //Configure the http server and PeerJS Server var expressServer = app.listen(port); var io = require('socket.io').listen(expressServer); var peer = new PeerServer({ port: 9000, path: '/chat' }); //Print some console output console.log('#### -- Server Running -- ####'); console.log('Listening on port', port); peer.on('connection', function (id) { io.emit(events.CONNECT, id); console.log('# Connected', id); }); peer.on('disconnect', function (id) { io.emit(events.DISCONNECT, id); console.log('# Disconnected', id); });
Это создаст наш Express сервер, делая файлы, которые мы установили с помощью bower
, в папке src/
доступными через HTTP. Далее мы создали экземпляр socket.io
, чтобы слушать объект expressServer
. Это будет использоваться для пулинга и послужит следующим шагом для создания PeerServer
, который будет отвечать за WebRTC чат.
Чтобы сконфигурировать PeerServer
, всё что вам нужно сделать, так это указать port
и path
сервер будет работать согласно этому, после чего начните конфигурацию событий при помощи метода .on
. Мы используем отдельный файл под названием Events.js
, чтобы указать события нашего приложения.
peer.on('connection', function (id) { io.emit(events.CONNECT, id); console.log('# Connected', id); });
Мы используем событие events.CONNECT
, чтобы определить когда пользователь присоединился к нашему приложению. Это понадобится состоянию нашего компонента представления (view), для обновления изображения на экране в реальном времени.
Чтобы это сделать, мы должны создать сервер для peer-to-peer соединения через прокси.
Создайте файл src/Server.js
и добавьте туда следующее:
/* global EventEmitter, events, io, Peer */ 'use strict'; function ChatServer() { EventEmitter.call(this); this._peers = {}; } ChatServer.prototype = Object.create(EventEmitter.prototype); ChatServer.prototype.onMessage = function (cb) { this.addListener(events.MSG, cb); }; ChatServer.prototype.getUsername = function () { return this._username; }; ChatServer.prototype.setUsername = function (username) { this._username = username; }; ChatServer.prototype.onUserConnected = function (cb) { this.addListener(events.CONNECT, cb); }; ChatServer.prototype.onUserDisconnected = function (cb) { this.addListener(events.DISCONNECT, cb); }; ChatServer.prototype.send = function (user, message) { this._peers[user].send(message); }; ChatServer.prototype.broadcast = function (msg) { for (var peer in this._peers) { this.send(peer, msg); } }; ChatServer.prototype.connect = function (username) { var self = this; this.setUsername(username); this.socket = io(); this.socket.on('connect', function () { self.socket.on(events.CONNECT, function (userId) { if (userId === self.getUsername()) { return; } self._connectTo(userId); self.emit(events.CONNECT, userId); console.log('User connected', userId); }); self.socket.on(events.DISCONNECT, function (userId) { if (userId === self.getUsername()) { return; } self._disconnectFrom(userId); self.emit(events.DISCONNECT, userId); console.log('User disconnected', userId); }); }); console.log('Connecting with username', username); this.peer = new Peer(username, { host: location.hostname, port: 9000, path: '/chat' }); this.peer.on('open', function (userId) { self.setUsername(userId); }); this.peer.on('connection', function (conn) { self._registerPeer(conn.peer, conn); self.emit(events.CONNECT, conn.peer); }); }; ChatServer.prototype._connectTo = function (username) { var conn = this.peer.connect(username); conn.on('open', function () { this._registerPeer(username, conn); }.bind(this)); }; ChatServer.prototype._registerPeer = function (username, conn) { console.log('Registering', username); this._peers[username] = conn; conn.on('data', function (msg) { console.log('Message received', msg); this.emit(events.MSG, { content: msg, author: username }); }.bind(this)); }; ChatServer.prototype._disconnectFrom = function (username) { delete this._peers[username]; };
Это главные внутренности приложения. Здесь мы конфигурируем объект ChatSever
всеми необходимыми функциями.
Сначала мы используем socket.io
для установки отправления сигналов, когда новый пользователь присоединился, через events.CONNECT
:
ChatServer.prototype.connect = function (username) { var self = this; this.setUsername(username); this.socket = io(); this.socket.on('connect', function () { self.socket.on(events.CONNECT, function (userId) { if (userId === self.getUsername()) { return; } self._connectTo(userId); self.emit(events.CONNECT, userId); console.log('User connected', userId); });
Затем, чтобы присоединиться к PeerSever, мы используем следующее:
this.peer = new Peer(username, { host: location.hostname, port: 9000, path: '/chat' });
Затем мы будет слушать события с помощью метода on
:
this.peer.on('open', function (userId) { self.setUsername(userId); }); this.peer.on('connection', function (conn) { self._registerPeer(conn.peer, conn); self.emit(events.CONNECT, conn.peer); });
Также внутри наших компонентов будет JSX, в components/chat
директории. Советую потратить время и взглянуть на них в репозитории проекта. С этого момента я сфокусируюсь на компоненте ChatBox
:
/** @jsx React.DOM */ 'use strict'; var ChatBox = React.createClass({ getInitialState: function () { return { users: [] }; }, componentDidMount: function () { this.chatProxy = this.props.chatProxy; this.chatProxy.connect(this.props.username); this.chatProxy.onMessage(this.addMessage.bind(this)); this.chatProxy.onUserConnected(this.userConnected.bind(this)); this.chatProxy.onUserDisconnected(this.userDisconnected.bind(this)); }, userConnected: function (user) { var users = this.state.users; users.push(user); this.setState({ users: users }); }, userDisconnected: function (user) { var users = this.state.users; users.splice(users.indexOf(user), 1); this.setState({ users: users }); }, messageHandler: function (message) { message = this.refs.messageInput.getDOMNode().value; this.addMessage({ content: message, author : this.chatProxy.getUsername() }); this.chatProxy.broadcast(message); }, addMessage: function (message) { if (message) { message.date = new Date(); this.refs.messagesList.addMessage(message); } }, render: function () { return ( <div className="chat-box" ref="root"> <div className="chat-header ui-widget-header">React p2p Web RTC Chat</div> <div className="chat-content-wrapper row"> <UsersList users={this.state.users} username={this.props.username} ref="usersList"></UsersList> <MessagesList ref="messagesList"></MessagesList> </div> <MessageInput ref="messageInput" messageHandler={this.messageHandler}> </MessageInput> </div> ); } });
Этот класс использует ChatSever
, который мы создали ранее, используя его как прокси для нашего ChatBox
компонента.
Компонент и библиотеки наконец отрендерились на странице index.html
и были отправлены благодаря node express.
Чтобы запустить приложение введите команду npm start
и откройте браузер по адресу http://localhost:3001
, чтобы взглянуть на чат.
Деплой на Heroku
Разместить наше приложение в облаке довольно легко с помощью Heroku. Зарегистрируйте бесплатный аккаунт, затем установите heroku
toolbelt на ваш компьютер. Прочитать больше о настройке Heroku
можно на Heroku Dev Center.
Теперь когда Heroku
доступен, залогинтесь и создайте новый проект, сделать это можно следующим образом:
$ git clone git@github.com:tomtom87/react-p2p-chat.git $ cd react-p2p-chat $ heroku create Creating sharp-rain-871... done, stack is cedar-14 http://sharp-rain-871.herokuapp.com/ | https://git.heroku.com/sharp-rain-871.git Git remote heroku added
Вы получите случайное имя вашего приложения на Heroku, в моём случае ссылка, по которой можно открыть приложение выглядит так - http://sharp-rain-871.herokuapp.com/
. Heroku также создаёт git репозиторий приложения.
Теперь вам остаётся лишь отправить код на heroku:
$ git push heroku master
После того как процесс отправки закончится, вы можете запустить веб-сервис следующим образом:
$ heroku ps:scale web=1
Осталось лишь открыть соответствующую ссылку, или можете воспользоваться командой open
:
$ heroku open
Заключение
Вы научились, как создать JSX компоненты и как взаимодействовать с ними с помощью React, на наглядном примере приложения - чата. Потратьте время и изучите код, чтобы понять как React и директория components/chat
устроена и работают.
Благодаря деплою на Heroku
, вы можете начать создавать и работать над собственными React
приложения в облаке!