Rails5.1增長了Webpacker:javascript
Webpacker essentially is the decisions made by the Rails team and bundled up into a gem. html
Webpacker幫咱們作好了幾乎因此配置工做,咱們只須要聚焦在寫Js上。java
React :react
React is a JavaScript view library designed to quickly create dynamic user interfaces.webpack
Chapter13 使用Webpacker and React 作一個動態的用戶界面。git
初次安裝步驟: (5.1教程安裝步驟不能使用React)程序員
brew install yarngithub
gem install "webpacker'web
bundle installchrome
❌rails webpacker:install❌,不要用,看git上的說明:
我在https://github.com/rails/webpacker 重新安裝。
# Available Rails 5.1+ rails new myapp --webpack=react
# Gemfile gem '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
在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(<PayTypeSelector />, element);
});
# 使用標準函數addEventListener確保咱們將執行的代碼在所有DOM加載以後運行,
# turbolinks:load 確保React每次頁面被渲染後創建,若是用DOMContentLoaded,在導航到其餘頁面後,在使用後退按鈕返回這個頁面,這個頁面的React不會正常工做。
# var element 是定位的元素,用js方法獲得id。
# ReactDOM.render 看起來很怪異,它的做用是用React組件PayTypeSelector取代element. <PayTypeSelector />
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的一些區別:
https://reactjs.org/docs/dom-elements.html 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 ( <div> <div className="field"> <label htmlFor="order_po_number">PO #</label> <input type="password" name="order[po_number]" id="order_po_number" /> </div> </div> ); } } 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/