react.js做小程序
本文最初发表在Stormpath上 。 感谢您支持使SitePoint成为可能的合作伙伴。
React(有时称为React.js)是一种构建Web UI的绝妙方式。 Stormpath React SDK通过路由和组件扩展了React和React Router,使您能够使用Stormpath解决常见的用户管理任务,例如身份验证和授权 。
最近,React得到了很多关注,并且很容易理解为什么。 React使您可以将复杂的UI变成可以轻松组合在一起的简单且可重用的组件。
这篇文章将向您展示如何使用Stormpath React SDK从零开始构建React应用程序,并添加允许人们注册,登录甚至查看自己的用户个人资料的功能。
让我们开始吧!
React + Express.js应用程序堆栈
由于我们是从头开始构建应用程序,因此我们将使用ES6和JSX编写尽可能少的代码,以及用于用户功能的Stormpath React SDK。
为了让您对我们将要使用的内容有一个很好的了解:
- React –允许我们编写简单而强大的UI。
- ReactRouter –在我们的React应用程序中组织URL导航。
- ES6 – JavaScript的下一版本。 允许我们编写真正JavaScript类。
- JSX –允许我们将HTML放置在JavaScript中而无需连接字符串。
- Stormpath –允许我们存储和验证用户,而无需为其创建自己的后端。
- Stormpath React SDK –只需很少的工作即可将注册表单,登录页面和身份验证集成到我们的React应用程序中。
- Express –允许我们提供HTML和JavaScript文件。
- Express Stormpath –允许我们通过Express来提供Stormpath的API。
- Webpack –允许我们将所有JavaScript文件打包到一个捆绑包中。
- Babel –允许我们将ES6和JSX转换为ES5。
- Bootstrap –因为我们希望事情变得漂亮。
设置我们的React + Express.js项目
首先创建一个新的项目目录和一个package.json
文件。
$ mkdir my-react-app
$ cd my-react-app
$ npm init --yes
现在安装Express,Express的Stormpath模块和Body Parser:
$ npm install --save express express-stormpath body-parser
我们需要一个服务器来承载我们的应用程序,因此创建一个名为server.js
的新文件,并将下面的代码放入其中:
var express = require('express');
var stormpath = require('express-stormpath');
var bodyParser = require('body-parser');
var app = express();
app.use(stormpath.init(app, {
web: {
produces: ['application/json']
}
}));
app.on('stormpath.ready', function () {
app.listen(3000, 'localhost', function (err) {
if (err) {
return console.error(err);
}
console.log('Listening at http://localhost:3000');
});
});
太棒了 现在,我们可以通过使用以下代码创建一个名为stormpath.yml
的新文件,将其连接到Stormpath应用程序。 是的,您必须用自己的值替换其中的那些值。
client:
apiKey:
id: YOUR_API_KEY_ID
secret: YOUR_API_KEY_SECRET
application:
href: https://api.stormpath.com/v1/applications/XXXX <-- YOUR APP HREF
到目前为止,一切都很好。 现在,通过运行$ node server.js
尝试服务器。 如果一切设置正确,则应该看到:
Listening at http://localhost:3000
如果看到该消息,则说明您已成功配置服务器以与Stormpath进行通信,并公开了REST API供我们的React应用程序使用。
配置Webpack
在您太兴奋之前,请终止服务器并安装Webpack,以便我们可以打包所有客户端脚本(我们很快将需要此组织)。
$ npm install --save webpack
$ npm install --save-dev webpack-dev-middleware
通过创建一个名为webpack.config.js
的新文件来配置Webpack,并将以下代码放入其中:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: [
'./src/app'
],
devtool: 'eval-source-map',
output: {
path: __dirname,
filename: 'app.js',
publicPath: '/js/'
},
module: {
loaders: []
}
};
要做的就是查看我们的/src/
目录(我们将很快创建),并将所有脚本及其依赖项打包到该目录下,作为一个模块。 然后,将文件/src/app.js
及其导出用作该模块的导出。 最后,当它生成该模块包时,它将通过Express在/js/app.js
端点下提供该/js/app.js
。
但是为了使Express服务Webpack文件,我们必须打开server.js
并将这些行添加到它的顶部:
var webpack = require('webpack');
var config = require('./webpack.config');
然后紧接在行var app = express();
加:
var compiler = webpack(config);
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: config.output.publicPath
}));
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
如前所述,这将允许Webpack拦截请求并提供打包的/js/app.js
文件。
配置Babel
由于我们将使用ES6和JSX,因此我们需要将这些文件转换为ES5(以与非现代浏览器向后兼容)。 这就是Babel的用处。Babel可以将我们的ES6 / JSX文件作为输入,并将其转换为ES5。
要使用Babel ,请先安装一些依赖项:
$ npm install --save babel-core babel-runtime babel-loader babel-plugin-react-transform \
babel-preset-es2015 babel-preset-react babel-preset-stage-0
现在,我们将指导Babel如何编译我们的文件,因此创建一个名为`.babelrc`的新文件并添加以下代码:
{
"presets": ["stage-0", "es2015", "react"]
}
最后,为了使Babel能够与Webpack一起使用,我们需要编辑`webpack.config.js`并将一个条目添加到`module.loaders`数组中,如下所示:
module: {
loaders: [{
test: /\.js$/,
loaders: ['babel'],
include: path.join(__dirname, 'src')
}]
}
Index.html和Bootstrap
现在,在开始使用React之前,我们将为我们的应用程序准备输入页面。 该页面将告诉浏览器在初始化React和应用程序之前必须加载什么。 因此,创建一个名为build
的新目录,然后在其中放入名为index.html
的文件。 我们的服务器将提供此文件夹中的所有静态文件。
$ mkdir build
$ cd build
$ touch index.html
然后在index.html
,放入以下内容:
<!doctype html>
<!--[if lt IE 7]><html class="no-js lt-ie9 lt-ie8 lt-ie7"><![endif]-->
<!--[if IE 7]><html class="no-js lt-ie9 lt-ie8"><![endif]-->
<!--[if IE 8]><html class="no-js lt-ie9"><![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<base href="/">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="/css/bootstrap.min.css" />
</head>
<body>
<div id="app-container"></div>
<script src="/js/app.js"></script>
</body>
</html>
另外,在build
目录下,创建一个名为css
的新目录,并将Bootstrap下载到该目录。 将文件命名为bootstrap.min.css
。
$ mkdir css
$ cd css
$ curl -O https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css
$ cd ../.. # return to /my-react-app
现在,为了使我们的浏览器能够访问这些文件,我们需要对其进行配置,以便通过Express进行提供。 因此,打开server.js
并在文件顶部添加:
var path = require('path');
然后在app.use(stormpath.init(app, ...));
加:
app.get('/css/bootstrap.min.css', function (req, res) {
res.sendFile(path.join(__dirname, 'build/css/bootstrap.min.css'));
});
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, 'build/index.html'));
});
React如何工作?
现在我们已经完成了应用程序的框架,我们可以专注于构建React应用程序。 但是在编写任何代码之前,让我们看一下React是什么以及它对我们有什么作用。
组件
在React中,一切都建立在组件上。 您可以将组件视为呈现DOM节点的组件。 一个简单的React组件如下所示:
class HelloWorld extends React.Component {
render() {
return <span>Hello World!</span>;
}
}
那很简单。 现在,如果您想将此组件呈现到页面上,那么您要做的就是导入React,然后调用:
ReactDOM.render(
<HelloWorld />,
document.getElementById('hello-world-element')
);
然后React将组件渲染到该元素。
当然,React组件还有更多的东西,例如状态。 下面是一个计数器组件的示例,该组件在添加到DOM时开始计数,而在删除时停止。
class Counter extends React.Component {
state = {
current: 0
}
constructor() {
super(arguments...);
this.intervalId = null;
}
updateCounter() {
this.setState({ counter: this.state.current + 1 });
}
componentWillMount() {
this.setState({ counter: this.props.from || 0 });
this.intervalId = setInterval(this.updateCounter.bind(this), 1000);
}
componentWillUnmount() {
clearInterval(this.intervalId);
}
render() {
return <span>{ this.state.current }</span>;
}
}
注意方法componentWillMount()
和componentWillUnmount()
。 这些是组件生命周期方法,将在组件生命周期的各个点(在这种情况下为安装和卸载)执行。 这些方法通常用于设置和拆卸组件,并且是必须使用的,因为如果在尚未安装组件时尝试设置组件的状态,React将会出错。
另请注意this.props.from
。 成员this.props
是传递给组件的所有属性(输入)的集合。 可以如下设置组件的属性:
<Counter from="50" />
<Counter from={ myVariable } />
JSX变量
可以使用{ nameOfVariable }
轻松将变量插入到您的JSX DOM中,例如,如下所示:
render() {
var myVariable = 123;
return <span>{ myVariable }</span>;
}
JSX和保留JavaScript标识符
由于JSX是JavaScript,因此在使用React时需要了解一些注意事项。 即设置的属性时作出ReactDOM组件,你不能既使用for
或class
,因为这些被认为是预留JavaScript标识符。 为了解决这个问题,React提供了htmlFor
和className
,您应该使用它。
为了说明问题,这将不起作用:
<label for="my-input" class="my-label">My Input</label>
但这将:
<label htmlFor="my-input" className="my-label">My Input</label>
虚拟DOM
在React中,不是直接针对DOM,而是将所有组件都保存在自己的虚拟DOM中。 您可以将虚拟DOM视为JavaScript中的DOM实现(因为实际上是这样)。 然后将此虚拟DOM映射到实际DOM元素。 因此,当您渲染React组件时,React将查看该组件的DOM输出,将其与虚拟DOM中的表示形式进行比较,然后为实际DOM生成补丁。
这意味着您不必再考虑手动操作DOM元素。 您所要做的就是告诉React您希望组件的外观如何,它将负责以必要的方式(最小的工作量)来转换DOM。
安装React依赖
现在,当我们熟悉了React时,我们将通过安装一些React依赖项来开始:
$ npm install --save react react-dom react-router react-stormpath react-document-title history
在开始编码之前,我们需要一个放置React文件的地方,因此创建一个名为src
的新目录,然后将其用作您的工作目录。
$ mkdir src
$ cd src
现在,让我们从应用程序的入口开始。 这将是我们设置React应用程序及其路由的地方。 因此,创建一个名为app.js
的新文件并输入以下代码:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, IndexRoute, Route, browserHistory } from 'react-router';
ReactDOM.render(
<Router history={browserHistory}>
</Router>,
document.getElementById('app-container')
);
因此,现在我们为应用程序奠定了基础。 让我们继续并导入Stormpath SDK和其中需要的一些东西。 在app.js
文件的顶部,添加导入语句:
import ReactStormpath, { Router, HomeRoute, LoginRoute, AuthenticatedRoute } from 'react-stormpath';
如您在app.js
看到的,现在有两个相互冲突的Router
导入。 由于ReactStormpath.Router
从延伸ReactRouter.Router
我们将不会需要再这样了。 因此,继续并从react-router
删除Router
导入。 重要提示: 剩下其他的ReactRouter导入,稍后我们将需要它们。
现在,我们将初始化Stormpath SDK。 在ReactDOM.render()
上方添加以下行。
ReactStormpath.init();
那很简单! 现在,我们准备开始构建页面。
母版页
在创建页面之前,我们必须设置路由器。 路由器决定了我们如何在React应用程序中导航。 我们将从创建共享根路由开始。 这将充当我们的“母版页”。 即,该路由下的所有路由都将共享相同的主组件(标头)。 因此,将下面的代码放在app.js
的<Router>
标记内,使其看起来像这样:
<Router history={browserHistory}>
<Route path='/' component={MasterPage}>
</Route>
</Router>
如您所见,我们引用了MasterPage
。 尚不存在的东西。 因此,让我们继续在新目录中创建该目录,并将其命名为src
文件夹中的pages
。
$ mkdir pages
$ cd pages
现在创建一个名为MasterPage.js
的新文件,并将以下代码添加到其中:
import React from 'react';
import { Link } from 'react-router';
import { LoginLink } from 'react-stormpath';
import DocumentTitle from 'react-document-title';
import Header from './Header';
export default class is extends React.Component {
render() {
return (
<DocumentTitle title='My React App'>
<div className='MasterPage'>
<Header />
{ this.props.children }
</div>
</DocumentTitle>
);
}
}
如您所见,我们还没有Header
组件,让我们在具有以下内容的同一目录中创建一个名为Header.js
的新文件:
import React from 'react';
import { Link } from 'react-router';
import { LoginLink, LogoutLink, Authenticated, NotAuthenticated } from 'react-stormpath';
export default class Header extends React.Component {
render() {
return (
<nav className="navbar navbar-default navbar-static-top">
<div className="container">
<div id="navbar-collapse" className="collapse navbar-collapse">
<ul className="nav navbar-nav">
<li><Link to="/">Home</Link></li>
</ul>
<ul className="nav navbar-nav navbar-right">
</ul>
</div>
</div>
</nav>
);
}
}
索引页
在我们的MasterPage
注意属性this.props.children
。 这将包含我们路由器匹配的子路由的组成部分。 因此,如果我们有一条看起来像的路线:
<Route path='/' component={MasterPage}>
<Route path='/hello' component={HelloPage} />
</Route>
并且我们尝试访问/hello
。 将使用HelloPage
组件填充this.props.children
数组,因此该组件将在我们的母版页中呈现。
现在想象一下您尝试访问/
的场景。 没有任何this.props.children
,这只会呈现您的母版页,但内容为空。 这就是IndexRoute
发挥作用的地方。 使用IndexRoute
您可以指定当您点击母版页路线的路径时所应呈现的组件(在本例中为“ /”)。
但是,我们添加我们之前IndexRoute
我们的路由器,让我们创建我们的一个新的文件pages
指定的目录IndexPage.js
及以下添加到它:
import { Link } from 'react-router';
import React, { PropTypes } from 'react';
import { LoginLink } from 'react-stormpath';
export default class IndexPage extends React.Component {
render() {
return (
<div className="container">
<h2 className="text-center">Welcome!</h2>
<hr />
<div className="jumbotron">
<p>
<strong>To my React application!</strong>
</p>
<p>Ready to begin? Try these Stormpath features that are included in this example:</p>
<ol className="lead">
<li><Link to="/register">Registration</Link></li>
<li><LoginLink /></li>
<li><Link to="/profile">Custom Profile Data</Link></li>
</ol>
</div>
</div>
);
}
}
现在,让我们添加IndexRoute
。 打开app.js
并在标签<Route path='/' component={MasterPage}>
添加您的IndexRoute
,使其类似于以下内容:
<Route path='/' component={MasterPage}>
<IndexRoute component={IndexPage} />
</Route>
登录页面
现在,我们有了一个React应用程序,其中显示了带有默认页面的标题。 但是我们还没有登录页面。 因此,让我们创建一个名为`LoginPage.js`的新文件,并向其中添加一些内容:
import React from 'react';
import DocumentTitle from 'react-document-title';
import { LoginForm } from 'react-stormpath';
export default class LoginPage extends React.Component {
render() {
return (
<DocumentTitle title={`Login`}>
<div className="container">
<div className="row">
<div className="col-xs-12">
<h3>Login</h3>
<hr />
</div>
</div>
<LoginForm />
</div>
</DocumentTitle>
);
}
}
注意LoginForm
组件。 这是我们要添加的全部内容,以便我们拥有可以供人们注册的完整工作表格。
但是在使用它之前,我们需要打开app.js
并在路由器中为该页面添加路由。 因此,在标记<Route path='/' component={MasterPage}>
添加以下内容:
<LoginRoute path='/login' component={LoginPage} />
为了能够访问登录页面,我们需要将其添加到菜单中。 因此,继续打开Header.js
并在元素<ul className="nav navbar-nav navbar-right">
添加以下内容:
<NotAuthenticated>
<li>
<LoginLink />
</li>
</NotAuthenticated>
如您所见,我们正在使用NotAuthenticated
组件。 这样,我们将仅在用户尚未登录时显示LoginLink
。
注册页面
现在,让我们添加一个页面,供人们注册。 我们将其称为RegistrationPage
。 因此,创建一个名为RegistrationPage.js
的新文件,并将以下内容放入其中:
import React from 'react';
import DocumentTitle from 'react-document-title';
import { RegistrationForm } from 'react-stormpath';
export default class RegistrationPage extends React.Component {
render() {
return (
<DocumentTitle title={`Registration`}>
<div className="container">
<div className="row">
<div className="col-xs-12">
<h3>Registration</h3>
<hr />
</div>
</div>
<RegistrationForm />
</div>
</DocumentTitle>
);
}
}
注意,我们使用了RegistrationForm
组件。 您可能已经猜到了,这将呈现一个Stormpath注册表格。 注册后,它将使用户指向登录页面,他们将可以登录。
为了访问此页面。 我们需要添加一条路线。 因此,继续打开app.js
并在<Route path='/' component={MasterPage}>
标记内添加:
<Route path='/register' component={RegistrationPage} />
现在,我们有了一条路线,但是除非我们链接到该页面,否则人们将无法找到该页面,因此请打开Header.js
并在<ul className="nav navbar-nav navbar-right">
的结束标记( </ul>
)之前添加以下内容<ul className="nav navbar-nav navbar-right">
:
<NotAuthenticated>
<li>
<Link to="/register">Create Account</Link>
</li>
</NotAuthenticated>
请注意,使用了NotAuthenticated
组件。 这样,我们只会在用户未登录时显示/register
链接。
个人资料页
用户登录后,我们希望能够向他们显示一些个性化内容(他们的用户数据)。 因此,创建一个名为ProfilePage.js
的新文件,并将以下代码放入其中:
import React from 'react';
import DocumentTitle from 'react-document-title';
import { UserProfileForm } from 'react-stormpath';
export default class ProfilePage extends React.Component {
render() {
return (
<DocumentTitle title={`My Profile`}>
<div className="container">
<div className="row">
<div className="col-xs-12">
<h3>My Profile</h3>
<hr />
</div>
</div>
<div className="row">
<div className="col-xs-12">
<UserProfileForm />
</div>
</div>
</div>
</DocumentTitle>
);
}
}
注意,我们使用了UserProfileForm
。 这是一个简单的帮助程序表单,允许您编辑最基本的用户字段。
但是,为了实际修改用户个人资料,我们需要在服务器中进行一些更改。 因此,打开server.js
并在app.use(stormpath.init(app, ...));
下添加以下路由app.use(stormpath.init(app, ...));
:
app.post('/me', bodyParser.json(), stormpath.loginRequired, function (req, res) {
function writeError(message) {
res.status(400);
res.json({ message: message, status: 400 });
res.end();
}
function saveAccount () {
req.user.givenName = req.body.givenName;
req.user.surname = req.body.surname;
req.user.email = req.body.email;
req.user.save(function (err) {
if (err) {
return writeError(err.userMessage || err.message);
}
res.end();
});
}
if (req.body.password) {
var application = req.app.get('stormpathApplication');
application.authenticateAccount({
username: req.user.username,
password: req.body.existingPassword
}, function (err) {
if (err) {
return writeError('The existing password that you entered was incorrect.');
}
req.user.password = req.body.password;
saveAccount();
});
} else {
saveAccount();
}
});
这将允许表单更改用户的给定名称,姓氏,电子邮件和密码。
如果您还有其他要编辑的字段,则只需自定义UserProfileForm
表单,然后在上面的路由中添加要编辑的字段。
现在,为了使我们能够从菜单访问此页面,请打开Header.js
然后在<li><Link to="/">Home</Link></li>
下方添加:
<Authenticated>
<li>
<Link to="/profile">Profile</Link>
</li>
</Authenticated>
这样,使用Authenticated
组件,当我们有一个用户会话时,我们将呈现一个指向/profile
页面的链接,并允许我们的用户查看其用户个人资料。
为了使我们能够访问该页面,我们必须像其他页面一样将其添加到路由器中。 打开app.js
并在<Route path='/' component={MasterPage}>
标记内添加:
<AuthenticatedRoute path='/profile' component={ProfilePage} />
注意,我们正在使用AuthenticatedRoute
。 这是只有经过身份验证的用户会话才能访问的路由。 如果没有会话,则用户将自动重定向到LoginLink
的路径。
本国路线
现在,当我们设置完大部分路由之后。 让我们看一下称为HomeRoute
的特殊路由。 这条路线本身没有任何作用。 但是充当“标记”,以指示登录和注销时重定向到的位置。
因此,为了指定退出时要在哪里结束,请打开app.js
并更改:
<Route path='/' component={MasterPage}>
...
</Route>
进入:
<HomeRoute path='/' component={MasterPage}>
...
</HomeRoute>
现在注销时,Stormpath SDK将知道它应该重定向到“ /”路径。 现在,要指定注销时重定向到的位置,请更改我们在上一步中创建的AuthenticatedRoute
:
<AuthenticatedRoute path='/profile' component={ProfilePage} />
这样看起来像:
<AuthenticatedRoute>
<HomeRoute path='/profile' component={ProfilePage} />
</AuthenticatedRoute>
注意AuthenticatedRoute
如何包装HomeRoute
。 这用于指示登录后我们要重定向到的经过身份验证的路由。
登出
最后,一旦我们的用户注册并登录。我们希望为他们提供注销选项。 幸运的是,添加它确实很简单。
因此,打开Header.js
并在<ul className="nav navbar-nav navbar-right">
添加以下代码:
<Authenticated>
<li>
<LogoutLink />
</li>
</Authenticated>
注意LogoutLink
组件。 单击此选项后,用户会话将被自动破坏,并且用户将被重定向到未经HomeRoute
验证的HomeRoute
。
组件中的用户状态
class ContextExample extends React.Component {
static contextTypes = {
authenticated: React.PropTypes.bool,
user: React.PropTypes.object
};
render() {
if (!this.context.authenticated) {
return (
<div>
You need to <LoginLink />.
</div>
);
}
return (
<div>
Welcome {this.context.user.username}!
</div>
);
}
}
导入元件
为了能够引用我们的页面,我们需要导入它们。 为了使导入变得容易,我们将它们全部放入一个index.js
文件中,因此我们只需要导入一次即可。 因此,让我们在pages
目录中创建一个名为index.js
的新文件,并从中导出所有页面,如下所示:
export MasterPage from './MasterPage'
export IndexPage from './IndexPage'
export LoginPage from './LoginPage'
export RegistrationPage from './RegistrationPage'
export ProfilePage from './ProfilePage'
这样,我们只需要做一次导入就可以访问我们的所有页面。
因此,让我们这样做。 打开app.js
文件,并在文件顶部添加以下导入语句:
import { MasterPage, IndexPage, LoginPage, RegistrationPage, ProfilePage } from './pages';
运行项目
现在,我们有了一个应用程序,用户可以在其中注册,登录和显示其用户数据。 因此,让我们尝试一下!
和以前一样,通过运行以下命令来启动我们的服务器:
$ node server.js
如果一切运行顺利,您应该可以看到以下消息:
Listening at http://localhost:3000
因此,在浏览器中打开http:// localhost:3000并尝试一下!
摘要
如您在本文中所见,React是一个非常强大的工具,当与ES6,JSX和Stormpath结合使用时,构建应用程序突然变得很有趣。
如果您不确定任何部分,请随时检查示例项目并将其用作参考实现。 我也很喜欢有关React设计的文章 –它详细说明了React为什么很棒。
并且,如果您对Stormpath React SDK有疑问,请务必查看其API文档 。
翻译自: https://www.sitepoint.com/tutorial-build-a-react-js-application-with-user-login-and-authentication/
react.js做小程序