Oh!Coder

Coding Life

React-rails Gem 简介

| Comments

今天分享的一个gem跟前端有关。最近Facebook的开源框架React有点火。自然而然的就想到了react如何与rails结合的问题,发现reactjs早就已经有了这样一个gem,索性就写出来初步了解一下。

简介

react-rails让你在Ruby on Rails(3.2以上版本)中使用ReactJSX更容易。react-rails完成如下工作:

  • 针对不同的运行环境提供了对应的react创建方案
  • 在资源管道中转换.jsx文件
  • 在view中渲染组件,并通过view层的helper以及react_ujs进行挂载
  • 通过prerender: true在服务器端渲染组件
  • 使用Rails生成器生成组件

安装

react-rails添加到gemfile中:

1
gem 'react-rails', '~> 1.0'

下一步,运行安装脚本:

1
rails g react:install

上面这行脚本将会完成如下操作:

  • 创建一个components.js清单文件和一个放置组件的目录app/assets/javascripts/components/
  • application.js文件中添加如下代码:
1
2
3
//= require react
//= require react_ujs
//= require components

用法

创建React.js

在每一个运行环境的配置文件中,可以添加相应的配置来选择创建React.js(development或production环境,有或没有附加项)。下面是默认配置:

1
2
3
4
5
6
7
8
9
# config/environments/development.rb
MyApp::Application.configure do
  config.react.variant = :development
end

# config/environments/production.rb
MyApp::Application.configure do
  config.react.variant = :production
end

如果要添加附加项,使用如下配置:

1
2
3
MyApp::Application.configure do
  config.react.addons = true # defaults to false
end

随后需要重新启动Rails的server,//= require react将会通过文件中的配置,实现React.js的创建。

react-rails提供了一些其它可选项,比如React.js创建的版本号。详情可见VERSIONS.md获取更多使用react-sourcegem或属于你自己的React.js版本。

JSX

安装完react-rails之后,重新启动rails server。现在,.js.jsx文件会在资源管道中进行转换。

可以在配置文件添加如下配置使用JSX的--harmony--strip-types可选项:

1
2
3
4
5
config.react.jsx_transform_options = {
  harmony: true,
  strip_types: true, # for removing Flow type annotations
  asset_path: "path/to/JSXTransformer.js", # if your JSXTransformer is somewhere else
}

为了使用CoffeeScript,创建.js.jsx.coffee文件,在代码的反引号内嵌入JSX,比如:

1
2
3
Component = React.createClass
  render: ->
    `<ExampleComponent videos={this.props.videos} />`

渲染以及加载

react-rails包括了一个view的helper(react_component)以及一个稳定的JavaScript驱动(react_ujs),这两个协同工作,把React的组件在页面上渲染。你应该会在react之后,文件清单中需要加入UJS驱动(如果你使用了turbolinks,那么就放到Turbolinks之后)。

view helper在页面上通过使用请求组件的class以及props,加入一个div。比如:

1
2
3
<%= react_component('HelloMessage', name: 'John') %>
<!-- becomes: -->
<div data-react-class="HelloMessage" data-react-props="{&quot;name&quot;:&quot;John&quot;}"></div>

在页面加载时,react_ujs驱动将会搜寻页面并且使用data-react-classdata-react-props加载组件。在页面卸载之前,将会卸载组件(如果你想关闭这种行为,在componentDidMount中移除data-react-class属性)。

如果Turbolinks的event可用,react_ujs将会使用Turbolinks的event,否则将会使用native的event。推荐使用2.4.0以上版本的Turbolinks,因为它暴露出了更好的event。

下面是view中的helper签名:

1
react_component(component_class_name, props={}, html_options={})
  • component_class_name是一个全局可访问组件的class。它的命名可以包括点(比如, "MyApp.Header.MenuItem" )。
  • props要么是一个响应类型为#to_json的对象,要么是已经被字符串化的JSON对象(比如,使用Jbuilder创建)。
  • html_options可能包括如下:
    • tag:在除了div以外的元素中嵌入data-react-class-props使用。
    • prerender: true可以在服务器端渲染组件。
    • **other任何其它通过content_tag传递的参数(比如:class:id:)。

服务器端渲染

为了能够实现服务器端渲染,需要在react_component中传入prerender: true

1
2
3
4
5
<%= react_component('HelloMessage', {name: 'John'}, {prerender: true}) %>
<!-- becomes: -->
<div data-react-class="HelloMessage" data-react-props="{&quot;name&quot;:&quot;John&quot;}">
  <h1>Hello, John!</h1>
</div>

(在页面加载的时候会被UJS进行加载)

为了完成这个工作,有一些其它需求:

  • react-rails必须加载你的代码。传统意义上来说会使用components.js,这个文件是在安装任务的时候进行创建的。这个文件必须包括在你的组建中以及它们的依赖中(比如,Underscore.js)。
  • 你的组件必须在全局范围内可访问。如果你正在使用.js.jsx.coffee文件,那么这个wrapper方法需要考虑在内:
1
2
3
4
# @ is `window`:
@Component = React.createClass
  render: ->
    `<ExampleComponent videos={this.props.videos} />`
  • 你的代码不能引用document。预渲染处理不需要访问document,所以jQuery以及其它一些类库不能够在此环境中工作。

可以在JS虚拟机的pool中进行配置,指定在那里进行加载代码:

1
2
3
4
5
6
7
8
9
10
11
12
# config/environments/application.rb
# These are the defaults if you dont specify any yourself
MyApp::Application.configure do
  # Settings for the pool of renderers:
  config.react.server_renderer_pool_size  ||= 10
  config.react.server_renderer_timeout    ||= 20 # seconds
  config.react.server_renderer = React::ServerRendering::SprocketsRenderer
  config.react.server_renderer_options = {
    files: ["react.js", "components.js"], # files to load for prerendering
    replay_console: true,                 # if true, console.* will be replayed client-side
  }
end

组件生成器

react-rails借着使用Rails的生成器,以此来帮助你通过一个简单的组件脚手架起步。你可以通过运行rails generate react:component ComponentName命令进行快速创建。对于默认的propType,生成器有一个参数列表,列表遵循的集合在React文档的组件重用有详细介绍。

例如:

1
rails generate react:component Post title:string body:string published:bool published_by:instanceOf{Person}

app/assets/javascripts/components/post.js.jsx文件中可生成如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var Post = React.createClass({
  propTypes: {
    title: React.PropTypes.string,
    body: React.PropTypes.string,
    published: React.PropTypes.bool,
    publishedBy: React.PropTypes.instanceOf(Person)
  },

  render: function() {
    return (
      <div>
        <div>Title: {this.props.title}</div>
        <div>Body: {this.props.body}</div>
        <div>Published: {this.props.published}</div>
        <div>Published By: {this.props.published_by}</div>
      </div>
    );
  }
});

生成器可以使用如下参数来创建基本的propType:

  • any
  • array
  • bool
  • element
  • func
  • number
  • object
  • node
  • shape
  • string

如下额外的参数有特殊的行为:

  • instanceOf以{className}格式,有一个可选的类名。
  • oneOf看起来就像是一个enum类型,有一个'name:oneOf{one, two, three}'格式的字符串列表。
  • oneOfType有一个react的可选列表,以及一个'model:oneOfType{string,number,OtherType}'格式的自定义类型。

注意oneOfoneOfType参数必须被单引号包裹起来,以防止在终端输入时扩展成一个参数列表。

Jbuilder和react-rails

如果你使用Jbuilder传递一个JSON字符串给react_component,确保你的JSON是一个字符串化的hash,而不是一个数组。这个特性不是Rails默认的,你应该在root节点进行添加。比如:

1
2
3
4
5
6
7
8
9
10
11
# BAD: returns a stringified array
json.array!(@messages) do |message|
  json.extract! message, :id, :name
  json.url message_url(message, format: :json)
end

# GOOD: returns a stringified hash
json.messages(@messages) do |message|
  json.extract! message, :id, :name
  json.url message_url(message, format: :json)
end

CoffeeScript

通过使用CoffeeScript调用JSX是可行的。我们需要在JSX文件内的反引号之间嵌入JSX,CoffeeScript会忽略这个关键字。这里有一个例子:

1
2
3
Component = React.createClass
  render: ->
    `<ExampleComponent videos={this.props.videos} />`

更多以及更新

写这篇简介的时候,ReactJS项目还处于beta阶段,所以想要了解最新的文档,推荐直接去查看原文档

Comments