React.js生态系统概览 [译]
Posted 开发者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React.js生态系统概览 [译]相关的知识,希望对你有一定的参考价值。
开发者( KaiFaX )
面向开发者、程序员的专业平台!
Koa
进入第一个中间件时(上游),遇到yield后,先进入第二个中间件(下游)将响应体设置为Hello
,然后再从yield恢复执行,继续执行上游中间件代码,为响应体追加了World
,然后打印了时间日志,在这里上下游共享了相同的上下文(this)。这要比Express更强大,如果用Express来写的话可能会是这样:
var start;
// Downstream middleware
app.use(function(req, res, next) {
start = new Date;
next();
// Already returned, cannot continue here
});
// Response
app.use(function (req, res, next){
res.send('Hello World')
next();
});
// Upstream middleware
app.use(function(req, res, next) {
// res already sent, cannot modify
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
next();
});
app.listen(3000);
也许你已经发现问题了,使用全局的start
变量在出现并发请求时会产生污染。
用koa时,可以很容易地用Generator做异步操作:
app.use(function *(){
try {
const part1 = yield fs.readFile(this.request.query.file1, 'utf8');
const part2 = yield fs.readFile(this.request.query.file2, 'utf8');
this.body = part1 + part2;
} catch (err) {
this.status = 404
this.body = err;
}
});
app.listen(3000);
可以设想下这个例子在Express中用promise和回调会是什么样子。
上面讨论的Node.js这些跟React有关系吗?当然咯,Node是React的首选后端服务器。因为Node也用javascript,这样可以前后端共享代码,用来构建同构的React Web应用,稍后会介绍到。
Flux库
React很擅长创建视图组件,但我们还需要一种方式去管理数据和整个应用的状态。Flux应用架构被普遍认为是React最好的补充。要是你对Flux还很陌生,建议看看这篇快速导览。
Flux
目前还没有出现大多数人都认同的Flux实现,Facebook官方的Flux实现很多人都觉得很冗余。我们主要关心能不能少写点模板代码,配置方便,并且给多层组件提供些好用的功能,支持服务端渲染等等这些。可以看看这里给这些实现库列出的一些指标。我关注了Alt, Reflux, Flummox, Fluxxor, 和Marty.js。
我选择库的方式并不能说客观,但很有用。Fluxxor是我发现的第一个库,但现在看起来它有点旧了。Marty.js很有意思,有很多功能,但还是需要写很多模板代码,有些功能看起来比较多余。Reflux看起来蛮有吸引力,但感觉对初学者来说有点难,还缺少合适的文档。Flummox和Alt很相似,但Alt不需要写很多模板代码,开发也非常活跃,文档更新块,而且有个Slack社区。所以我选了Alt。
Alt Flux
Alt Flux
Alt的Flux实现简单而强大。Facebook的Flux文档描述了很多关于dispatcher的东西,但在Alt中这些都可以忽略,因为dispatcher被隐式关联到了action,通常不需要写多余代码。只需要关心store,action,component。这三层对应MVC模型:store为模型层,action为控6制层,component为视图层。差异是Flux模型是单向数据流,意味着控制层不能直接修改视图层,而是触发模型层后,视图层被动修改。这对有些Angular开发者来说已经是最佳实践了。
component初始化action
store监听action然后更新数据
component被绑定到store,当数据更新时就重新渲染
Alt Flux
Action
使用Alt库时,action通常有两种写法:自动与手动。自动action用generateActions
函数创建,它们直接发给dispatcher,手动方法则写成action类的方法,它们可以附带一个payload发给dispatcher。常用例子是自动action通知store有关app的一些事件。其余由手动action负责,它是处理服务端交互的首选方式。
REST API调用这种就属于action,完整流程如下:
Component触发action
action创建者发起一个异步服务器请求,把结果作为一个payload发给dispatcher
store监听action,对应的action处理器接收结果,然后store更新它的相应状态
对于AJAX请求,我们可以用axios库,无缝地处理JSON数据和头数据。可以用ES7的async/await
关键字,免去promise与回调。如果POST
响应状态不是2XX,抛出错误,然后发出返回的数据或接收到的错误。
看个简单的Alt登录示例,在这里注销action不需要做任何事情,只需要通知store,所以可以自动生成它。登录action是手动的,会把登录数据作为一个参数发给action的创建者。从服务端获取响应后,发出数据,有错误的话就发出错误。
class LoginActions {
constructor() {
// Automatic action
this.generateActions('logout');
}
// Manual action
async login(data) {
try {
const response = await axios.post('/auth/login', data);
this.dispatch({ok: true, user: response.data});
} catch (err) {
console.error(err);
this.dispatch({ok: false, error: err.data});
}
}
}
module.exports = (alt.createActions(LoginActions));
Store
Flux模型的store有两个任务:作为action的处理器,保存状态。继续看看登录例子是怎么做的吧。
创建一个LoginStore
,有两个状态属性:user
用于当前登录的用户,error
用于当前登录相关的错误。为了减少模板代码数量,Alt可以用一个bindActions
函数绑定所有action到store类。
class LoginStore {
constructor() {
this.bindActions(LoginActions);
this.user = null;
this.error = null;
}
...
处理器定义起来很方便,只需要在对应action名字前加on
。因此login
的action由onLogin
处理。注意action名的首字母是陀峰命名法的大写。在LoginStore
中,有以下两个处理器,被对应的action调用:
...
onLogin(data) {
if (data.ok) {
this.user = data.user;
this.error = null;
router.transitionTo('home');
} else {
this.user = null;
this.error = data.error
}
}
onLogout() {
this.user = null;
this.error = null;
}
}
Component
通常将store绑定到component的方式是用mixin。但mixin快过时了,需要找个其他方式,有个新方法是使用嵌套的component。将我们的component包装到另一个component里面,它会托管store的监听然后重新调用渲染。component会在props
中接收到store的状态。这个方法对于smart and dumb component的代码组织很有用,是以后的趋势。对于Alt来说,包装component是由AltContainer实现的:
export default class Login extends React.Component {
render() {
return (
<AltContainer stores={{LoginStore: LoginStore}}>
<LoginPage/>
</AltContainer>
)}
}
LoginPage
component也用了我们之前介绍过的changeHandler
装饰器。LoginStore
的数据用来显示登陆失败后的错误,重新渲染则由AltContainer
负责。点击登录按钮后执行login
action,完整的Alt工作流:
@changeHandler
export default class LoginPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loginForm: {}
};
}
login() {
LoginActions.login(this.state.loginForm)
}
render() {
return (
<Alert>{{this.props.LoginStore.error}}</Alert>
<Input
label='Username'
type='text'
value={this.state.login.username}
onChange={this.changeHandler.bind(this, 'loginForm', 'username')} />
<Input
label='Password'
type='password'
value={this.state.login.password}
onChange={this.changeHandler.bind(this, 'loginForm', 'password')} />
<Button onClick={this.login.bind(this)}>Login</Button>
)}
}
同构渲染
同构Web应用是近些年来的热点话题,它能解决传统单页面应用最大的问题:浏览器用JavaScript动态创建html,如果浏览器禁用了JavaScript,内容就无法显示了,搜索引擎索引不到Web页面,内容不会出现在搜索结果中。实际上有方法解决这个问题,但做的并不够好。同构的方式是通过服务端渲染内容来解决问题。Node.js是服务端的JavaScript,React当然也能运行在服务端。
一些使用单例方式的Flux库,很难做服务端渲染,单例的Flux 在遇到并发请求时,store数据就会变得混乱。一些Flux库用实例来解决这个问题,但需要在代码间传递实例。Alt实际也提供了Flux实例,但它用单例解决服务端渲染问题,它会在每次请求后清空store,以便并发请求时每次store都是初始状态。
React.renderToString
函数是服务端渲染的核心,整个React应用运行在服务端。服务端生成HTML后响应给浏览器。浏览器JavaScript运行时,再渲染剩余部分。可以用Iso库实现这点,它同时也被集成到了Alt中。
首先,我们在服务端用alt.bootstrap
初始化Flux,这会初始化Flux store数据。然后区分哪个URL对应的渲染哪个component,哪个由客户端Router
渲染。由于使用alt
的单例版,在每次渲染完后,需要使用alt.flush()
初始化store以便为下次请求做好准备。使用iso
插件,可以把Flux的状态数据序列化到HTML字符串中,以便客户端知道去哪找到这份状态数据:
// We use react-router to run the URL that is provided in routes.jsx
var getHandler = function(routes, url) {
var deferred = Promise.defer();
Router.run(routes, url, function (Handler) {
deferred.resolve(Handler);
});
return deferred.promise;
};
app.use(function *(next) {
yield next;
// We seed our stores with data
alt.bootstrap(JSON.stringify(this.locals.data || {}));
var iso = new Iso();
const handler = yield getHandler(reactRoutes, this.request.url);
const node = React.renderToString(React.createElement(handler));
iso.add(node, alt.flush());
this.render('layout', {html: iso.render()});
});
在客户端,拿到了服务端的状态数据,用来初始化alt
的状态。然后可以运行Router
和React.render
去更新服务端生成的HTML:
Iso.bootstrap(function (state, _, container) {
// Bootstrap the state from the server
alt.bootstrap(state)
Router.run(routes, Router.HistoryLocation, function (Handler, req) {
let node = React.createElement(Handler)
React.render(node, container)
})
})
一些有用的库
例如CSS布局容器,样式表单,按钮,验证,日期选择器等等。
React-Bootstrap
Twitter的Bootstrap框架应用已经非常普遍,对那些不想花时间写CSS的开发者很有用。特别是在原型开发阶段,Bootstrap不可或缺。要在React中使用Bootatrap,可以试试React-Bootstrap,它使用原生React component重新实现了Bootstrap的jQuery插件。代码简洁易懂:
<Navbar brand='React-Bootstrap'>
<Nav>
<NavItem eventKey={1} href='#'>Link</NavItem>
<NavItem eventKey={2} href='#'>Link</NavItem>
<DropdownButton eventKey={3} title='Dropdown'>
<MenuItem eventKey='1'>Action</MenuItem>
<MenuItem eventKey='2'>Another action</MenuItem>
<MenuItem eventKey='3'>Something else here</MenuItem>
<MenuItem divider />
<MenuItem eventKey='4'>Separated link</MenuItem>
</DropdownButton>
</Nav>
</Navbar>
个人感觉这才是简单易懂的HTML吧。
也可以在Webpack中使用Bootstrap,看你喜欢哪个CSS预处理器。支持自定义引入的Bootstrap组件,可以在CSS代码中使用LESS或SASS的全局变量。
React Router
React Router几乎已经成了React的路由标准了。它支持嵌套路由,重定向,同构渲染。基于JSX的例子:
<Router history={new BrowserHistory}>
<Route path="/" component={App}>
<Route path="about" name="about" component={About}/>
<Route path="users" name="users" component={Users} indexComponent={RecentUsers}>
<Route path="/user/:userId" name="user" component={User}/>
</Route>
<Route path="*" component={NoMatch}/>
</Route>
</Router>
React Router可以用Link
component做应用导航,只需要指定路由名称:
<nav>
<Link to="about">About</Link>
<Link to="users">Users</Link>
</nav>
还有集成了React-Bootstrap的路由库,不想手写Bootstrap的active
类,可以用react-router-bootstrap:
<Nav>
<NavItemLink to="about">About</NavItemLink>
<NavItemLink to="users">Users</NavItemLink>
</Nav>
Formsy-React
表单开发通常都比较麻烦,用formsy-react来简化一下吧,它可以用来管理验证和数据模型。Formsy-React不包含实际的表单输入,而是推荐用户自己写(这是正确的)。如果只用通用表单,可以用formsy-react-components。它包括了Bootstrap类:
日期与选择器
日期与选择器组件算是UI库的锦上添花了,遗憾的是这两个组件在Bootstrap 3上被移除了,因为它们对于一个通用CSS框架来说比较特殊了。不过我发现了两个不错的代替:react-pikaday和react-select。我尝试过10多个库,这两个总体来说很不错。非常易用:
import Pikaday from 'react-pikaday';
import Select from 'react-select';
export default class CalendarAndTypeahead extends React.Component {
constructor(props){
super(props);
this.options = [
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' }
];
}
dateChange(date) {
this.setState({date: date});
},
selectChange(selected) {
this.setState({selected: selected});
},
render() {
return (
<Pikaday
value={this.state.date}
onChange={this.dateChange} />
<Select
name="form-field-name"
value={this.state.selected}
options={this.options}
onChange={selectChange} />
)}
}
总结 - React.js
这篇文章介绍了目前Web开发的一些技术与框架。有些是React相关的,因为React的开放性,它们也能被用在其他环境。有时候技术进步总会被对新事物的恐惧所阻碍,我希望这篇文章可以打消你对尝试React, Flux和新的ECMAScript的疑虑。
来源:http://www.inkpaper.io/blog/post/2015/10/18/navigating-the-react-ecosystem.html
1. 回复“m”可以查看历史记录;
2. 回复“h”或者“帮助”,查看帮助;
开发者已开通多个微信群交流学习,请加若飞微信:13511421494 进群
以上是关于React.js生态系统概览 [译]的主要内容,如果未能解决你的问题,请参考以下文章
从传统的 Laravel/Rails 应用迁移到 React.js 生态系统。需要在前端进行数据管理