【本文源址:http://blog.csdn.net/q1056843325/article/details/54729657 轉載請加入該地址】javascript
明天就是除夕了
預祝你們新春快樂 [ ]~( ̄▽ ̄)~*
每天飯局搞得我是身心疲憊= =
因此更新比較慢
今天想跟你們分享的就是這個大名鼎鼎的React框架php
React是近兩年很是流行的框架
流行到什麼程度呢?
我看了一下Github上的數據css
React達到了5w8+的star
在JavaScript中star排名第4
受歡迎程度可見一斑html
感興趣的同窗。給你們設置一個傳送門:Github-JavaScript-most stars前端
React並不難。仍是挺easy上手的
起源於Facebook內部項目(一個廣告系統)java
傳統頁面從server獲取數據。顯示到瀏覽器上,用戶輸入數據傳入server
但隨着數據量增大,愈來愈難以維護了
Facebook以爲MVC不能知足他們的擴展需求了(巨大的代碼庫和龐大的組織)
每當需要加入一項新功能或特性時,系統複雜度就呈幾何增加
導致代碼脆弱不堪、不可預測,結果致使他們的MVC正走向崩潰
當系統中有很是多的模型和相應視圖時,其複雜度就會迅速擴大。很是難以理解和調試node
總之就是Facebook對市場上所有JS-MVC框架都不愜意。以爲都不適合大規模應用
就本身寫了一套,用來架設Instagram站點
寫完後用着用着,發現哎呦這貨還真是不錯。而後就開源了
隨着這幾年的沉澱。React已經變得愈來愈強大了
值得咱們去了解~react
科普一下MVC
MVC是一種軟件架構模式(後端影響到了前端)
MVC就分爲M、V、C三部分webpack
簡單的理解一下
咱們就是user。在頁面中點擊了一個按鈕觸發了事件
控制器Controller調整數據,模型Model中數據改變
數據改變又會致使視圖View更新
UI的改變反饋呈現給咱們usergit
這裏我要特別說明一下,儘管這裏介紹了MVC
但是不能說React就是MVC框架
可以說React是用於構建組件化UI的庫。是一個前端界面開發工具
它可以做爲MVC中的View視圖部分
React它具備如下特色
我選擇使用webpack搭建好開發環境。固然其它工具也可以
這是個人webpack.config.js配置文件
module.exports = {
entry: {
index: './src/js/entry.js'
},
output: {
path: './static/dist/',
publicPath: 'http://localhost:8080/static/dist/',
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
},
{
test: /.less$/,
loader: 'style!css!less'
}
]
}
}
這裏的關鍵就是除了要安裝babel-loader
和babel-core
還要安裝babel-preset-es2015
和 babel-preset-react
用於解析ES6語法和React的JSX語法
還有react
和react-dom
也是必需要下載的依賴
所有依賴模塊在這裏
"devDependencies": {
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.22.0",
"css-loader": "^0.26.1",
"less": "^2.7.2",
"less-loader": "^2.2.3",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"style-loader": "^0.13.1",
"webpack": "^1.14.0",
"webpack-dev-server": "^1.16.2"
}
簡單說一下React的JSX語法是個神馬東西
可能一下子你們會看大不明覺厲的代碼
比方
return (
<div>hehe<div>
)
這就是JSX代碼,它是React提供的語法糖
是React的重要組成部分,使用類似XML標記的方式來聲明界面及關係
語法糖的意思我寫ES6的時候也說了
就是計算機語言中加入的語法。對語言的功能沒影響
方便咱們開發者使用的,可以加強可讀性
假設使用JS代碼也可以,只是官方推薦使用JSX
這樣結構層次關係都很是清晰
webpack會幫咱們把他們轉換成瀏覽器認識的js代碼(loader的做用)
(假設好奇轉換成了什麼,可以去webpack輸出文件查看,或者找jsx轉js的工具)
JSX語法結構說的通俗一點就是HTML、JS混寫
可能你們會有疑惑,說好的結構、樣式、行爲相分離的前端思想呢?!
React當中一個基本的設計理念是編寫簡單且easy理解的代碼
但咱們爲了實現組件化確實不方便鬆耦合
你們也不要過度較真
這種語法結構是如何解析的呢?事實上並不奇妙
JSX的語法規則:
不理解不要慌。看了如下就懂了
提早滲透一下
最終寫到語法正題了
在此以前咱們必需要引用的兩個對象
一個React核心對象和一個React-Dom對象
(這裏就先不使用ES6的語法了)
var React = require('react');
var ReactDom = require('react-dom');
ReactDom.render()是react最最基本的方法
因此我放到最開始來說
它經過ReactDom將咱們的組件渲染到頁面
我在頁面中加入一個節點
<div id="root"></div>
現在頁面中什麼也沒有
只是當即就有了
ReactDom.render(
<h1>Demo</h1>,
document.getElementById('demo')
);
第一個參數是要插入的組件(只是這裏咱們先插入一個DOM節點)
第二個參數就是要渲染的DOM節點
(React不建議直接加入到body標籤document.body,不相信的話可以試一下會警告)
頁面中出現了「Demo」
實際上react將咱們的節點插入到了div節點的內部
React的一大特色就是組件化
React組件Component有如下特色
React.createClass()就是用於將代碼封裝成組件的方法
它會生成一個React組件
var App = React.createClass({
render: function(){
return (
<p>This is a component...</p>
)
}
});
這種方法參數是一個對象
對象中有一個render返回一個組件
render是輸出組件必需要寫的(關於它如下還會再說)
先記住兩點
因此。各位,如下的寫法都是不正確的
//錯誤的寫法:變量名首字母沒大寫
var app = React.createClass({
render: function(){
return <p>This is a component...</p>
}
})
//錯誤的寫法:存在多個頂級標籤
var App = React.createClass({
render: function(){
return (
<p>This is a component...</p>
<p>This is also a component...</p>
)
}
});
組件中html語法兩邊加括號的目的
是爲了
防止JavaScript本身主動分號機制產生問題
生成的組件要想渲染到頁面
就使用咱們剛剛提到的的ReactDom.render( )
ReactDom.render(
<App></App>,
document.getElementById('root')
);
組件要寫成標籤的形式
這裏咱們就要寫<App></App>
或者單標籤形式<App/>
也可以
爲了加以區分。我把html標籤的屬性叫組件特性
var App = React.createClass({
render: function(){
return <p name="demo">This is a component...</p>;
}
});
ReactDom.render(
<App></App>,
document.getElementById('root')
);
還要注意兩個特例
因爲他們是JavaScript的保留字
假設想要爲組件加入內聯樣式,可以這樣寫
var App = React.createClass({
render: function(){
var styles = {
color: '#fff',
backgroundColor: '#000'
}
return <p className="demo" style={styles}>This is a component...</p>; // <--
}
});
ReactDom.render(
<App></App>,
document.getElementById('root')
);
聲明瞭一個styles對象
但是將它加入到屬性時。要使用 { }
因爲JSX語法中。html中使用js就必須使用大括號
關於這一點如下就再也不贅述了
組件的屬性相同可以像html同樣加入<App name="payen"></App>
並且這個組件屬性內部可以經過this.props對象獲取
var App = React.createClass({
render: function(){
return <p>name:{this.props.name} age:{this.props.age}</p>;
}
});
ReactDom.render(
<App name="payen" age="20"></App>,
document.getElementById('root')
);
瞭解了這個,咱們可以作一個小練習
現在有一組數據,利用它組成一個有序列表組件
var data = ['Mr.A','Mr.B','Mr.C'];
可以將這個數組成爲組件屬性
而後利用this.props.data獲取數據
最後使用ES5數組的map方法就大功告成了
var List = React.createClass({
render: function(){
return (
<ol>
{
this.props.data.map(function(item, index){
return <li key={1000 + index}>{item}</li>;
})
}
</ol>
)
}
});
ReactDom.render(
<List data={data}></List>,
document.getElementById('root')
);
還要注意<li key={1000 + index}>{item}</li>
key值假設不寫的話,儘管可以正常渲染
但會警告咱們數組或迭代器的每一項都應該有一個獨一無二的key值
這裏我就使用了1000加上索引的形式加入了key值
一般組件的屬性與this.props對象中的屬性是一一相應的
但有一個例外,它是this.props.children
它表示咱們組件的所有子節點
什麼意思呢?接着咱們上面的樣例
咱們在List組件中加入一些子節點
改動ReactDom.render( )方法的參數
ReactDom.render(
<List data={data}>
<span>Mr.D</span>
<span>Mr.E</span>
</List>,
document.getElementById('root')
);
咱們發現頁面中並無什麼變化。但瀏覽器也沒有報錯
這時咱們需要使用this.props.children
var data = ['Mr.A','Mr.B','Mr.C'];
var List = React.createClass({
render: function(){
console.log(this.props.children);
return (
<ol>
{
this.props.data.map(function(item, index){
return <li key={1000 + index}>{item}</li>;
})
}
{
this.props.children
}
</ol>
)
}
});
ReactDom.render(
<List data={data}>
<span>Mr.D</span>
<span>Mr.E</span>
</List>,
document.getElementById('root')
);
如此頁面中就顯示出了子節點
這個this.props.children很是奇怪,它有三種類型值
(可以在控制檯上輸出驗證)
因此咱們處理它要特別當心
好在咱們可以使用React給咱們提供的方法
利用React.Children.map( )咱們就可以放心遍歷處理子節點
var data = ['Mr.A','Mr.B','Mr.C'];
var List = React.createClass({
render: function(){
return (
<ol>
{
this.props.data.map(function(item, index){
return <li key={1000 + index}>{item}</li>;
})
}
{
React.Children.map(this.props.children,function(child){
return <li>{child}</li>
})
}
</ol>
)
}
});
ReactDom.render(
<List data={data}>
<span>Mr.D</span>
<span>Mr.E</span>
</List>,
document.getElementById('root')
);
組件的屬性可以接受不論什麼值。數字、字符串、函數、對象什麼都可以
但有時候,咱們拿到一個組件,想要驗證參數是否符合咱們的要求(這事實上很是重要。不要藐視)
這時就需要使用組件的propTypes( )方法和React.PropTypes配合驗證了
var data = ['Mr.A','Mr.B','Mr.C'];
var App = React.createClass({
propTypes: {
data: React.PropTypes.array
},
render: function(){
return (
<div>{this.props.data}</div>
)
}
});
ReactDom.render(
<App data={data}></App>,
document.getElementById('root')
);
這裏我指望的data屬性值爲array數組類型
沒有不論什麼問題,因爲咱們傳入的就是數組
但是假設改爲指望字符串類型data: React.PropTypes.string
瀏覽器就會發出警告
具體見React中文官網:Prop 驗證
還記得React單向數據流的特色麼
也就是說咱們應該把數據傳遞給父節點
父節點經過this.prop將數據傳遞給子節點
子節點再經過本身的this.prop處理收到的數據
var data = ['Mr.A','Mr.B','Mr.C'];
var List = React.createClass({
render: function(){
return (
<ol>
{
this.props.data.map(function(item, index){
return <li key={1000 + index}>{item}</li>;
})
}
</ol>
)
}
});
var App = React.createClass({
render: function(){
return (
<div>
<List data={this.props.data}></List>
</div>
)
}
});
ReactDom.render(
<App data={data}></App>,
document.getElementById('root')
);
所呈現的DOM結構
生命週期不難理解
組件的一輩子無非就是產生、更新、銷燬
在組件的每一個生命週期內,都會按順序觸發一些組件方法
比方咱們剛剛的render方法就會在產生和更新的階段都會觸發
具體觸發的回調函數API以及做用給你們整理在如下
(關於它們在整個生命週期的觸發次數你們應該都能想明確就不寫了)
(不常用的我在後面的標註了*號)
return {name: 'payen'}
至關於初始化了組件屬性this.props.name = 'payen'
return {show: false}
至關於初始化了組件狀態this.state.show = false
this.setState({show: false})
附上一張我盜的圖,幫助你們理解(手動滑稽)
關於這些API更具體的信息
建議你們可以去React中文官網查看:Component Specs and Lifecycle
上面提到了this.state,和咱們以前介紹的this.props同樣重要
只是this.props一般不會變。但this.state會變
就如其字面意思,表示組件的狀態
這個屬性是僅僅讀的
因此設置狀態咱們需要使用this.setState( )
可以使用this.setState( )的方法:
componentWillMount、componentDidMount、componentWillReceiveProps
在此以前咱們需要了解的就是React的事件系統
JavaScript原始行間綁定事件都是廣泛小寫<button onclick="clickHandle()"></button>
但咱們在React中要使用駝峯寫法<button onClick="clickHandle()"></button>
React的事件處理器會傳入虛擬事件對象的實例(一個對瀏覽器本地事件的跨瀏覽器封裝)
它有和瀏覽器本地事件相同的屬性和方法。包括 stopPropagation() 和 preventDefault(),
但是沒有瀏覽器兼容問題
具體支持事件見中文官網:事件系統-支持的事件
現在咱們要來實現這樣一個簡單的功能
點擊按鈕,出現彈框
單擊彈框。彈框消失
先來實現結構與樣式
var App = React.createClass({
render: function(){
return (
<div>
<button>點擊</button>
<PopUp></PopUp>
</div>
)
}
});
var PopUp = React.createClass({
render: function(){
var styles = {
position: 'absolute',
left: '40px',
top: '40px',
width: '100px',
height: '100px',
backgroundColor: '#f40'
}
return (
<div className="popup" style={styles}></div>
)
}
})
ReactDom.render(
<App/>,
document.getElementById('root')
);
首先咱們先來實現第一個功能:點擊按鈕出現彈框
問題是改如何實現
咱們的React是單向數據流
父級向子級傳遞數據
最好的辦法就是在父級設置組件狀態this.state
將狀態經過組件屬性this.props傳遞給子級
這樣點擊事件要作的就是改變父級狀態
子級狀態也會隨之改變
var App = React.createClass({
getInitialState: function(){
return {
open: false
}
},
buttonHandler: function(){
this.setState({
open: true
});
},
render: function(){
return (
<div>
<button onClick={this.buttonHandler}>點擊</button>
<PopUp open={this.state.open}></PopUp>
</div>
)
}
});
var PopUp = React.createClass({
render: function(){
var styles = {
position: 'absolute',
left: '40px',
top: '40px',
width: '100px',
height: '100px',
backgroundColor: '#f40'
}
if(this.props.open){
styles.display = 'block';
}else{
styles.display = 'none';
}
return (
<div className="popup" style={styles}></div>
)
}
})
ReactDom.render(
<App/>,
document.getElementById('root')
);
第一個功能實現了,再來看第二個
點擊彈窗讓其消失
相同子級的顯示與否掌控在父級手裏
要向讓子級消失,就必需要改變父級的組件狀態this.state
因此咱們必需要把事件函數綁定在父級
再利用組件屬性this.props將其傳遞給子級
完整代碼例如如下
var App = React.createClass({
getInitialState: function(){
return {
open: false
}
},
buttonHandler: function(){
this.setState({
open: true
});
},
popupHandler: function(){
this.setState({
open: false
});
},
render: function(){
return (
<div>
<button onClick={this.buttonHandler}>點擊</button>
<PopUp open={this.state.open} handler={this.popupHandler}></PopUp>
</div>
)
}
});
var PopUp = React.createClass({
render: function(){
var styles = {
position: 'absolute',
left: '40px',
top: '40px',
width: '100px',
height: '100px',
backgroundColor: '#f40'
}
if(this.props.open){
styles.display = 'block';
}else{
styles.display = 'none';
}
return (
<div className="popup" style={styles} onClick={this.props.handler}></div>
)
}
})
ReactDom.render(
<App/>,
document.getElementById('root')
);
用一句話來總結一下,那就是數據都交給父級來管理
咱們已經知道了
建立的組件都是虛擬DOM節點
僅僅有當它渲染到了頁面。纔會成爲真正的DOM節點
但是有些時候,咱們需要獲取到真正的DOM節點
這時需要先設置標籤ref屬性,再利用組件的this.refs對象獲取
仍是經過一個小樣例來解釋
現在要實現這樣一個功能
在輸入欄中輸入字符並在外部實時輸出
咱們要獲取的真實DOM節點就是input中的輸入字符串
步驟也很是easy,完整代碼例如如下
var Input = React.createClass({
getInitialState: function(){
return {
val: ''
}
},
changeHandler: function(){
this.setState({
val: this.refs.node.value
});
},
render: function(){
return (
<div>
<input type="text" ref="node" onChange={this.changeHandler}/>
<p>{this.state.val}</p>
</div>
)
}
});
ReactDom.render(
<Input/>,
document.getElementById('root')
);
我爲input標籤設置了ref值爲node
可以把它理解爲爲這個節點起了個小名「node」
那麼this.refs.node
就可以引用這個真實的節點<input/>
經過綁定一個change事件
咱們的輸入每次改變都會改變組件的狀態state
state改變。value就會渲染到頁面
獲取真實DOM節點另外一個不常用的方法
比方在咱們的樣例中可以把input標籤改爲這樣
<input type="text" ref={function(dom){this._node = dom}.bind(this)} onChange={this.changeHandler}/>
向ref屬性中加入一個匿名函數
這個函數的參數就是真實DOM節點
咱們可以把它保存下來,比方作爲組件的_node屬性
不要忘了改變this的指向
事件觸發函數就可以經過this._node獲取真正的DOM節點
changeHandler: function(){
this.setState({
val: this._node.value
});
}
還要注意的一點是
這個真·DOM節點的獲取
必需要等到虛擬DOM插入文檔之後。才幹使用屬性this.refs.[ref-name]
不然會報錯的