Rails5.1增加了Webpacker:
Webpacker essentially is the decisions made by the Rails team and bundled up into a gem.
Webpacker帮我们做好了几乎所以配置工作,我们只需要聚焦在写Js上。
React :
React is a JavaScript view library designed to quickly create dynamic user interfaces.
Chapter13 使用Webpacker and React 做一个动态的用户界面。
- Configuring Webpacker and Installing React。
- Replace our existing payment-type drop-down with a React-rendered version.
初次安装步骤: (5.1教程安装步骤不能使用React)
brew install yarn
gem install "webpacker'
bundle install
❌rails webpacker:install❌,不要用,看git上的说明:
我在 从新安装。
# Available Rails 5.1+rails new myapp --webpack=react
# Gemfilegem 'webpacker', '~> 3.4'
bundle
bundle exec rails webpacker:install
rails webpacker:install:react
之后重新启动服务器,在orders/new.html.erb加a helper method 代码:
<%= javascript_pack_tag("hello_react") %>
成功在browser上显示!证明Webpack的internals成功在app上运行了.
---- -------------------------------
创建了配置文件在config/webpack 中,并安装了js 库。 js库列表在package.json中(类似于gemfile)。
类似于bundle install,yarn install下载所有必要的js库。不过因为约定,webpacker:install已经替我们运行了yarn install。
Webpacker可以安装配置主流的js框架(Vue,React,Angulaer)
Updating Our Development Environment for Webpack
app/javascript/packs , 在这个目录中有一个hello_react.jsx, 里面注释让程序员进行测试,看是否能够正常使用。帮助方法:javascript_pack_tag。
//Run this example by adding <%= javascript_pack_tag 'hello_react' %> to the head of your layout file, like app/views/layouts/application.html.erb. All it does is render <div>Hello React</div> at the bottom of the page.
因为需求和效能,Rails 不简单的把全部js加入 。
Webpacker允许我们独立的管理一堆packs。所以只把需要的功能加入。
Learning Just Enough React先学习需要的React知识点
React是什么?
is a view library for JavaScript. Like the erb files we've been using. React dynamically renders HTML. Unlike ERB, React does this in the browser,
and it is optimized (优化的)to do it fast.
不光是 一个api 函数库,也是一个迷你框架,它的扩展功能让工作更容易。
React是组件,一个组件是一个视图,基于背后的状态。当状态变化,视图就渲染。视图的渲染是基于状态内部的组件。
对我们来说,我们根据当前选择框,让我们的视图渲染不同的input tags。
我们可以用js api完成这个功能。但代码过于冗长,难以跟随和维护。于是出现了JSX 。
hello_react.jsx中的 jsx 是什么,有什么用?
这个格式内部混合了js code 和 类html标记 在一个文件中,更方便的使用React components组件。
React 提供了一个编译器,把JSX编译成JS, Webpack可以使用这个编译器。
Creating a React-Powered Drop-Down
- 创建一个新pack
- 创建PayTypeSelector component, 用来取代传统的selector: dropdown
- 通过使用javascript_pack_tag和React相关的标记语言,把这个组件带入核对视图。
在javascript/packs中新建文件pay_type.jsx。这个文件的代码用来引导React 组件放在合适的位置。
最直接的方法:定位一个element in the DOM, 使用React.render方法,把组件渲染进这个元素。
详解:
#类似Ruby中的require ,使用React 库
#ReactDom对象,有用来渲染我们使用的React组件的render function
import PayTypeSelector from 'PayTypeSelector'
#自建的选择器,是我们之后用的组件。
document.addEventListener('turbolinks:load', function() { var element = document.getElementById("pay-type-component"); ReactDOM.render(, element);
});
# 使用标准函数addEventListener确保我们将执行的代码在全部DOM加载之后运行,
# turbolinks:load 确保React每次页面被渲染后建立,如果用DOMContentLoaded,在导航到其他页面后,在使用后退按钮返回这个页面,这个页面的React不会正常工作。# var element 是定位的元素,用js方法得到id。
# ReactDOM.render 看起来很怪异,它的作用是用React组件PayTypeSelector取代element.
2 Creating the PayTypeSelector Component
建立app/javascript/PayTypeSelector/index.jsx
This file will contain a React component that renders the exact same HTML for the pay type drop-down as our current Rails view.
在我们import PayTypeSelector时,Webpack将加载这个文件index.jsx
A React 组件是一个类,扩展了React.Component, 并有一个render方法用来返回组件的视图。最新版的js 支持创建类和方法。
这个文件是标准的xml,和html有区别,如className htmlFor,在html是class和for,这里需要看React文档。
#注意大小写一个字都不能❌,否则报告❌,而且找不到。
//name的值作为select,和Rails form helper一样。这样控制器就可以找到这个value
这里是export default <class>
3 Bring the PayTypeSelector Component into the Rails View
...
<select onChange={this.onPayTypeSelected} name="order[pay_type]">
在chrome的检查器console中,当state变化时,通过调用组件的render方法,view被渲染。
替换console.log代码为:this.setState({ selectedPayType: event.target.value});
Dynamically Replacing Components Based on User Actions
第一步:
在index.jsx中 ,对select元素加上属性onChange= {} ,⚠️使用了{},不用"",这是JSX的特点。
html和react的一些区别:
DOM属性用驼峰写法tabIndex
这个方法的定义:
onPayTypeSelected(event) { this.setState({ selectedPayType: event.target.value });}
但是程序不会有效,提示this 关键字没有被定义,why?
Because, this 和Ruby中的self相似 ,常常代表了当前类的实例对象,但有例外event handler.
解释:
js的类和方法其实都是函数。当我们使用一个类的对象的方法时,这个方法就是一个函数,它的值会传给这个对象,但是当这个方法被一个事件处理器event handler调用时,this的意义就变化了。此时this所代表的对象不会被js发现。this在js中是一个复杂的原则concept.
为了确保this关键字被发现并因此设置为这个对象,我们要调用bind方法捆绑。
this.onPayTypeSelected = this.onPayTypeSelected.bind(this);
js 类有建筑器(也就是构造函数) :Js classes have constructors, just like Ruby classes, and that is the rigth location to execute this code(指上面那行代码).
React 组件构造函数 接收一个参数props,并把这个参数传给它的超类superclass.同时,我们也需要初始化initialize我们的状态state. 看全部点击->()
constructor(props) { super(props); this.onPayTypeSelected = this.onPayTypeSelected.bind(this); this.state = { selectedPayType: null }; }
在render代码块中,我们可以使用this.state.selectedPayType属性得到value.
现在,声明一个客制组件,基于?通过this.state.selectedPayType得到的value,来渲染不同的组件内容。
let PayTypeCustomComponent = “等待条件赋值,可以先给一个可空值” ;
return这个组件<PayTypeCustomComponent />.的位置是在下拉列表下面:用户点击不同的选项,出现不同的内容。
不同的内容放到不同的同目录夹下,用import关键字来引进。如:
import NoPayType from './NoPayType';
#⚠️ preceded by a dot and a slash (./),告诉Webpack区同文件夹找。
最后在render块内, 对组件进行赋值:
let PayTypeCustomComponent = NoPayType; if (this.state.selectedPayType == "Credit card") { PayTypeCustomComponent = CreditCardPayType; } else if (this.state.selectedPayType == "Check") { PayTypeCustomComponent = CheckPayType; } else if (this.state.selectedPayType == "Purchase order") { PayTypeCustomComponent = PurchaseOrderPayType; }
最后点击:看全部点击->()
下一步增加4个文件,内含组件的4种代码。
其中
import React from 'react'
class PurchaseOrderPayType extends React.Component { render() { return (pasting); }}export default PurchaseOrderPayType
⚠️: 我们已经选择field'name values 来匹配Rails的约定。当React组件使用一个名字name如:order["credit_card_number"],我们就能够在Ruby中通过使用params[:order][:credit_card_number]存取那个field's value,
Wrapping Up Webpack and React.
幽灵Webpacker一切都用约定,无需自己配置了。
使用CapyBara 和 ChromeDriver 进行系统测试。
CapyBara:(git)
https://github.com/teamcapybara/capybara#the-dsl
ChromeDriver
https://sites.google.com/a/chromium.org/chromedriver/