你将要创建 的东西
jsx类似于 XML 和html的混合。您在 react 代码中使用 JSX 轻松地为您的应用程序创建组件。当 React 编译代码时,JSX将s 转换为javascript 。
React 的美妙之处在于您可以创建可重用的代码并轻松地从基于组件的思维方式构建您的应用程序。最后,模拟语义形成的想法和实现它们之间的差距从未如此紧密。
初尝 JSX
下面是一个使用 JSX 渲染 HTML 的示例:
var div = <div classname="foo"></div>; Reactdom.render(div, document.getElementById('example'));
要创建组件,只需使用以大写字母开头的局部变量,例如:
var MyComponent = React.createClass({/*...*/}); var myElement = <mycomponent someproperty="{true}"></mycomponent>; ReactDOM.render(myElement, document.getElementById('example'));
注意:JSX 中有保留字,因为它本质上是 JavaScript,所以不鼓励使用class和之类的关键字for作为属性名称。相反,React 组件需要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
使用Babel REPL测试 JSX。
子组件和命名空间
使用 JSX 和子组件可以轻松创建表单,例如:
var Form = FormComponent; var App = ( <form.row> <form.label htmlfor="password"></form.label> <form.input name="pass" type="password"></form.input> </form.row> );
要完成这项工作,您必须创建子组件作为主组件的属性:
var FormComponent = React.createClass({ ... }); FormComponent.Row = React.createClass({ ... }); FormComponent.Label = React.createClass({ ... }); FormComponent.Input = React.createClass({ ... });
表达式
要使用一些 javaScript 来创建用于属性值的结果,React 只需要您将它包装在{} curl y 大括号中,如下所示:
// You write in JSX: var myUser = <user username="{window.signedIn" window.username :></user>; // 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="">; // Enable a field <input type="text" name="captcha" disabled="">
传播属性
当您想设置多个属性时,请确保您在声明组件时执行此操作,并且不要在之后执行此操作。后来的声明变成了一种危险的反模式,这意味着您可能要等到执行的很久以后才能拥有属性数据。
使用 ES6 的新... 扩展运算符向您的组件添加多个属性。
var props = {}; props.username = username; props.email = email; var userLogin = <userlogin></userlogin>;
您可以多次使用这些道具变量。如果您需要覆盖其中一个属性,可以通过将其附加到...展开运算符之后的组件来完成,例如:
var props = { username: 'jimmyRiddle' }; var userLogin = <userlogin username="{'mickeyFinn'}"></userlogin>; 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 : is a end of></user> </navigation> );
JSX 的常见陷阱
有一些事情会使一些人对 JSX 感到困惑,例如,在向 HTML 规范中不存在的原生 HTML 元素添加属性时。
React 不会在原生 HTML 元素上呈现任何不在规范中的属性,除非您添加data-如下前缀:
<div data-custom-attribute="bar"></div>
此外,由于 esc api ng 和 React 内置的 XSS 保护,动态内容中的 HTML 呈现可能会变得混乱。因此,React 提供了dangerouslySetInnerHTML.
<div dangerouslysetinnerhtml="{{__html:" level="" child=""></div>
聊天应用程序示例
您可能已经看过应用程序Gitter。这是一个主要针对开发人员的实时网络聊天应用程序。许多人使用它在 GitHub 上讨论他们的项目,因为它易于与 GitHub 集成并能够为您的存储库创建通道。
WebRTC通过实现API 在浏览器中创建 p2p 聊天,可以使用 React 轻松创建这种类型的应用程序。为此,我们将为nodePeerJs使用andsocket.io模块。
为了让您清楚地了解应用程序的架构,这里有一个基本的低级 UML 图:
ChatServer 通过 PeerJS 接收信号消息,每个客户端将使用它作为代理来处理 NAT 遍历。
我们将从头开始应用程序,让您很好地了解如何创建 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,一个我们将用于为我们的应用程序提供服务的 Web 框架;peer,我们将用于发送信号的 peerjs 服务器;socket.io它将用于轮询和 webRTC 实现。react-bootstrap并且bootstrap是使用 Twitter 的css框架来设计我们的应用程序的包。
我们还需要一些额外的包,为此我们将使用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来触发事件,并使用peerJSClient 库来包装 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 服务器,使src/我们刚刚获得的文件bower现在可以通过 HTTP 访问。然后socket.io创建一个实例来监听该expressServer对象。这用于轮询和促进 的下一步PeerServer,这实际上将执行 WebRTC 聊天部分。
要配置 a PeerServer,您需要做的就是指定port服务器path将在其上运行,然后开始使用该.on方法配置事件。我们正在使用一个单独的文件Events.js来指定我们应用程序的事件。
peer.on('connection', function (id) { io.emit(events.CONNECT, id); console.log('# Connected', id); });
在这里,我们使用events.CONNECT事件来指定用户何时连接到我们的应用程序。这将被我们的视图组件的状态用来实时更新它们的显示。
为此,我们需要为点对点连接创建服务器以通过代理。
在其中创建一个文件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]; };
这是应用程序的主要内容。在这里,我们配置ChatServer对象及其所有功能。
首先,我们用于socket.io建立通过events.CONNECTas 连接的新用户的信令:
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); });
然后,要连接到 PeerServer,我们使用以下命令:
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); });
components/chat我们在目录中也有我们的 JSX 内部组件。花点时间查看存储库中的所有内容。我现在将专注于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> ); } });
此类使用ChatServer我们之前创建的,将其用作ChatBox组件的代理。
组件和库最终都在页面上呈现index.html并通过 node express 提供服务。
要启动应用程序,请运行npm start并将浏览器指向http://localhost:3001以查看聊天。
在heroku上上线
使用 Heroku 从云端提供服务真的很容易。注册一个免费帐户,然后您可以将heroku工具带安装到您的系统上。阅读有关在Heroku 开发中心Heroku进行设置的更多信息。
现在您已经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 和您的应用程序 URL 中获得一个随机名称——在我们的示例中它是http://sharp-rain-871.herokuapp.com/. Heroku 还为此应用程序创建了一个 git 存储库。
现在就像将代码推送到heroku一样简单:
$ git push heroku master
推送完成后,您将能够使用以下命令启动 Web 服务:
$ heroku ps:scale web=1
现在只需访问提供的 URL,或者作为快捷方式使用以下open命令:
$ heroku open
结论
您已经通过聊天应用程序的详细示例了解了如何创建 JSX 组件并将它们与 React 接口。花一些时间浏览代码,看看 React 和components/chat目录是如何工作的。
结合部署到,您可以开始破解并为云Heroku创建自己的应用程序!React
- 嵌套标签
- 测试 JSX
- 子组件和命名空间
- 表达式
- 传播属性
- JSX 中的注释
- JSX 的常见陷阱
- 在heroku上上线