寫在前面
以前寫Vue寫慣了浊闪,心血來潮,寫起了React概荷。并根據(jù)Vue官網(wǎng)文檔的語法順序秕岛,寫了對(duì)應(yīng)的React的語法,并附一個(gè)教程demo误证。
項(xiàng)目使用框架版本主要有 react(15.4.1)
+ react-dom(15.4.1)
+ webpack(1.13.3) + axios(0.15.3)
+ node(6.2.2)
, 詳情具體可見下文的【環(huán)境配置】
目前該項(xiàng)目有兩個(gè)分支, half-es6
+ master
half-es6和master實(shí)現(xiàn)的功能一樣, 實(shí)現(xiàn)了CURD + Axios + Others
half-es6的寫法并沒有完全使用es6的class的概念, master是完善了它
環(huán)境配置
寫react就需要先配置webpack還有jsx
首先继薛,新建一個(gè)項(xiàng)目,npm init
然后在package中加入下面這些依賴
"dependencies": {
"react": "^15.4.1",
"react-dom": "^15.4.1",
},
"devDependencies": {
"axios": "^0.15.3",
"babel-core": "^6.18.2",
"babel-loader": "^6.2.8",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-react-hmre": "^1.1.1",
"bootstrap": "^4.0.0-alpha.2",
"css-loader": "^0.26.1",
"file-loader": "^0.9.0",
"html-webpack-plugin": "^2.24.1",
"node-sass": "^3.13.0",
"open-browser-webpack-plugin": "0.0.3",
"sass-loader": "^4.0.2",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack": "^1.13.3",
"webpack-dev-server": "^1.16.2"
}
有兩個(gè)比較重要的指令
"scripts": {
"dev": "webpack-dev-server --progress --profile --hot",
"build": "webpack --progress --profile --colors"
},
webpack.config
在webpack的配置中雷厂,我想要的目錄結(jié)構(gòu)是橫向目錄(自創(chuàng)詞 ???惋增,即所有index頁面要用到的東西,包括sass和js都寫在index目錄下,底下會(huì)有目錄示意圖)改鲫,目的是達(dá)到诈皿,我在src下編輯我想要的文件林束,打包后生成到public下去。
寫在配置最前面的是路徑的配置
var ROOT_PATH = path.resolve(__dirname);
var SRC_PATH = path.resolve(ROOT_PATH, 'src');
var PUBLIC_PATH = path.resolve(ROOT_PATH, 'Public');
配合著入口文件和輸出文件的配置
entry: {
index: path.resolve(SRC_PATH, 'index/index.js'),
},
output: {
path: PUBLIC_PATH,
filename: '[name].bundle.js',
},
主要的插件就是這個(gè)html生成的插件和自動(dòng)打開瀏覽器的插件稽亏,還有babel的配置壶冒,不管三七二十一都把他們的等級(jí)開到最大
plugins: [
new HtmlwebpackPlugin({
title: 'My first react-webpack'
}),
new OpenBrowserPlugin({
url: 'http://localhost:8200'
})
],
babel: { //配置babel
"presets": ["es2015",'stage-0', 'react'],
},
npm run dev,會(huì)自動(dòng)打開localhost:8200,就可以在瀏覽器上看到初始化的頁面
jsx
當(dāng)你開始要寫js的時(shí)候發(fā)現(xiàn)截歉,怎么這么多警告胖腾,
不用擔(dān)心 google 一下都能解決。
在這里下載react 和 react-native:
![](https://user-gold-cdn.xitu.io/2016/12/22/17408742b73f4370d47b1e9b268d72d4.png)
并勾選對(duì)應(yīng)項(xiàng)瘪松,保存:
![](https://user-gold-cdn.xitu.io/2016/12/22/6c8f261b0c3f0d362832dd01372154e1.png)
警告會(huì)少很多咸作,但是還是有一些警告,怎么辦呢
點(diǎn)擊這個(gè)小燈泡宵睦,然后選擇configure
![](https://user-gold-cdn.xitu.io/2016/12/22/02d1790c386c5054d3dd1c73669d4f2b.png)
把這兩項(xiàng)勾選掉记罚,保存,就一片清凈了壳嚎。
![](https://user-gold-cdn.xitu.io/2016/12/22/54f170f1d5461463351c33dbbc723df9.png)
項(xiàng)目描述
Public是打包后生成的目錄桐智,src是寫目錄
src采用橫向目錄結(jié)構(gòu)(自創(chuàng)詞 ???),即所有index頁面要用到的東西烟馅,包括sass和js都寫在index目錄下说庭。
![](https://user-gold-cdn.xitu.io/2016/12/22/913b348addff9c7c7ab203d96ccfb722.png)
指令運(yùn)行項(xiàng)目
npm i
npm run build 生成打包后的文件
npm run dev
數(shù)據(jù)綁定
1 文本插值
<span>{text}</span>
2 html 插值
<div dangerouslySetInnerHTML={{__html: "<p>balabalabalabala.......</p>"}} />
3 屬性賦值
<span id = {this.props.idName}></span>
<span className = "nav-box"></span>
4 帶js表達(dá)式的插值 xxx = {三元表達(dá)式}
<span className={this.props.idx == this.props.choice? "tab on" : "tab"} ></span>
5 事件綁定
事件綁定和屬性綁定一樣
// 如果沒有使用class繼承的寫法的話
getInitialState() {
return {
tabTxt: ['CURD', 'Axios', 'Others'],
choice: 0,
}
},
switchChoice(idx){
this.setState({
choice: idx
})
},
renderTabInit(text, idx) {
return (<Tab key={idx} idx={idx}
choose={this.switchChoice} // 綁定了switchChoice方法
choice={this.state.choice} // 數(shù)據(jù)data的綁定,this.state可以獲取到整個(gè)state
>{text}</Tab>)
},
有可能會(huì)遇到一些BOOM爆炸的bug郑趁,請(qǐng)看react父子組件間的事件綁定
css和style的綁定
1 className
className={this.props.idx == this.props.choice? "tab on" : "tab"}
2 style
第一個(gè)括號(hào)是插值刊驴,第二個(gè)括號(hào)表示style對(duì)象
style={{color: '#FEC264', fontSize: '40px'}}
列表渲染 & 條件渲染
在getInitalState中定義了一個(gè)數(shù)組tabTxt
getInitialState() {
return {
tabTxt: ['CURD', 'Axios', 'Others'],
choice: 0,
}
},
循環(huán)渲染這個(gè)子組件,每個(gè)子組件有自己的唯一的key穿撮,作用和track-by(或v-bind:key)的作用類似
renderTabInit(text, idx) {
return (<Tab key={idx} idx={idx}
choose={this.switchChoice}
choice={this.state.choice}
>{text}</Tab>)
},
列表渲染的v-for 在react中使用map
v-if 的條件渲染可用三元缺脉,如復(fù)雜判斷則需要在return前寫邏輯代碼
render() {
let currentPage = null;
if(this.state.choice == 0) {
currentPage = <PageA />
} else if (this.state.choice == 1){
currentPage = <PageB />
} else {
currentPage = <PageC />
}
return (
<div id="content">
<div id="navBox">
{this.state.tabTxt.map(this.renderTabInit)}
</div>
<div id="pageBox">
{currentPage}
</div>
</div>
)
}
表單控件
表單組件有幾個(gè)受用戶影響的屬性:
value,用于input悦穿、textarea組件
checked, 用于類型為 checkbox 或者 radio 的 input 組件
selected业踢,用于option組件
每個(gè)表單控件都有一個(gè)onChange事件用來監(jiān)聽組件的變化:
當(dāng) input 或 textarea 的value 發(fā)生變化時(shí)
input 的 checked 狀態(tài)改變時(shí)
option 的 selected 狀態(tài)改變時(shí)
受限組件:
//es5
render: function() {
return <input type="text" value="Hello!" />;
}
// 在渲染出來的元素里輸入任何值都不起作用栗柒,因?yàn)?React 已經(jīng)賦值為 Hello!
如果要讓用戶修改的值有用,則需要:
getInitialState() {
return {value: 'Hello!'};
},
handleChange(event) {
this.setState({value: event.target.value});
},
render() {
let value = this.state.value;
return <input type="text" value={value} onChange={this.handleChange} />;
}
不受限組件:
//es5
render: function() {
return (
<div>
<input type="radio" name="opt" defaultChecked /> Option 1
<input type="radio" name="opt" /> Option 2
<select defaultValue="C">
<option value="A">Apple</option>
<option value="B">Banana</option>
<option value="C">Cranberry</option>
</select>
</div>
);
}
// 用戶輸入將立即反應(yīng)到元素上知举。
// 和受限元素一樣瞬沦,使用 onChange 事件可以監(jiān)聽值的變化。
// default 有一個(gè)初始值雇锡,但這個(gè)值用戶可以改變并會(huì)反應(yīng)到界面上逛钻。
父子組件通信
父子組件通信
// 父組件,相當(dāng)于最大的組件
// 子組件是一個(gè)tab锰提,和三個(gè)page曙痘,切換tab 就能切換 page
const Content = React.createClass({
getInitialState() {
return {
tabTxt: ['CURD', 'Axios', 'Others'],
choice: 0, // 當(dāng)前選中的tab下標(biāo)
}
},
switchChoice(idx){
this.setState({ // 修改state
choice: idx
})
},
renderTabInit(text, idx) {
return (<Tab key={idx} idx={idx}
choice={this.state.choice} // key\idx\choice 分別都是作為props傳入tab子組件的參數(shù)名
choose={this.switchChoice} // choose 作為props作為傳入tab子組件的方法名
>{text}</Tab>)
},
render() {
let currentPage = null;
if(this.state.choice == 0) { // 條件判斷
currentPage = <PageA />
} else if (this.state.choice == 1){
currentPage = <PageB />
} else {
currentPage = <PageC />
}
return (
<div id="content">
<div id="navBox">
{this.state.tabTxt.map(this.renderTabInit)} //循環(huán)輸出
</div>
<div id="pageBox">
{currentPage}
</div>
</div>
)
}
});
在使用事件綁定choose={this.switchChoice} 的時(shí)候芳悲,因?yàn)闆]有采用class的學(xué)法所以不用bind
class的寫法的時(shí)候需要bind: choose={this.switchChoice.bind(this)}
不用class的寫法的時(shí)候不綁定不會(huì)導(dǎo)致子組件的this指向錯(cuò)誤,如果綁定了還會(huì)報(bào)錯(cuò)(如綁定this會(huì)有警告)
使用了class的寫法的時(shí)候則需要手動(dòng)bind, 這個(gè)在文章最后會(huì)詳細(xì)解說
// tab 子組件
const Tab = React.createClass({
chooseTab() {
this.props.choose(this.props.idx); //一定要將父組件的方法在子組件中做一個(gè)中轉(zhuǎn)
},
render(){
return (
<span className={this.props.idx == this.props.choice? "tab on" : "tab"}
style={{color: '#FEC264', fontSize: '40px'}}
data-idx={this.props.idx}
onClick={this.chooseTab} // 調(diào)用子組件的方法
>{this.props.children}</span>
)
}
});
獲取dom元素
當(dāng)你的組件還沒有掛載在容器上边坤,可以用this.refs訪問
已經(jīng)掛載完畢名扛,通過react-dom提供findDOMNode方法拿到組件對(duì)應(yīng)的dom
另外:
如果ref是設(shè)置在原生HTML元素上,它拿到的就是DOM元素;
如果設(shè)置在自定義組件上茧痒,它拿到的就是組件實(shí)例肮韧,這時(shí)候就需要通過 findDOMNode來拿到組件的DOM元素。
//es5
var MyComponent = React.createClass({
handleClick: function() {
this.refs.myTextInput.getDOMNode().focus(); // 通過this.refs.xxxxx拿到元素
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" /> // 給輸入框命名ref
<input
type="button"
value="Focus the text input"
onClick={this.handleClick}
/>
</div>
);
}
});
幾個(gè)常用api
componentDidMount
componentWillReceiveProps(nextProps)
花一分鐘,改成正統(tǒng)的class寫法
第一步旺订,把所有createClass 換成 class xxx extends Component
我們用一半的es6的姿勢(shì)寫出來的代碼如下:
// half-es6
import React from 'react';
const List = React.createClass({ // 用createdClass創(chuàng)建一個(gè)組件
getInitialState() { // 初始化數(shù)據(jù)state
return { // 在函數(shù)的return里定義state
status: false,
}
}, // 這里一定寫逗號(hào)
saveLiValue() { // 組件內(nèi)要調(diào)用的function
this.setState({
status: false
})
},
....
})
我們用完整的es6的姿勢(shì)寫出來的代碼如下:
// master
// 利用class姿勢(shì)的es6
import React, {Component} from 'react';
class List extends Component{
constructor(props){
super(props);
this.state = {
status: false,
}
} // 沒有逗號(hào)
saveLiValue() {
this.setState({
status: false
})
}
....
}
第二步弄企,在父組件中,給所有需要傳遞給子組件的方法加bind(this)
這句話有點(diǎn)繞口区拳,但一定要理解桩蓉。
1、第一層意思是在父組件上加bind(this)
2劳闹、加的目的是防止子組件在調(diào)用方法的時(shí)候this指向錯(cuò)誤
例如下面這個(gè)初始化列表的函數(shù)
// half-es6
// 如果在這種寫法下bind(this)院究,編譯后的頁面會(huì)報(bào)警告
// 大概是說react已經(jīng)提供了豐富的方法可以避免指向錯(cuò)誤,不需要手動(dòng)bind
initListLi(val, idx) {
return (
<List {...val} key={idx} index={idx}
handleTxtChange={this.handleTxtChange}
handleCheckChange={this.handleCheckChange}
deleteItem={this.deleteItem}
/>
)
},
render() {
return (
<article className="page">
<h3 className="h3">List總條數(shù): {this.state.list.length}</h3>
<h3 className="h3">目前完成條數(shù): {this.state.didCount}</h3>
<ul className="ul">
{
this.state.list.map(this.initListLi)
}
</ul>
<Add addLiItem={this.addLiItem}/>
</article>
)
}
但是使用了class的寫法之后本涕,就可能會(huì)出現(xiàn)警告說 props 是null
這個(gè)時(shí)候就需要手動(dòng)bind(this)
// master
// es6的class寫法下的函數(shù)的事件綁定业汰,
// 如果子組件會(huì)需要調(diào)用函數(shù),則在父組件中手動(dòng)向子組件中bind(this)
initListLi(val, idx) {
return (
<List {...val} key={idx} index={idx}
// 以下三個(gè)方法都是在向List組件中綁定this
handleTxtChange={this.handleTxtChange.bind(this)}
handleCheckChange={this.handleCheckChange.bind(this)}
deleteItem={this.deleteItem.bind(this)}
/>
)
}
render() {
return (
<article className="page">
<h3 className="h3">List總條數(shù): {this.state.list.length}</h3>
<h3 className="h3">目前完成條數(shù): {this.state.didCount}</h3>
<ul className="ul">
{
this.state.list.map(this.initListLi.bind(this)) //子組件中會(huì)需要調(diào)用函數(shù)
}
</ul>
<Add addLiItem={this.addLiItem.bind(this)}/>
</article>
)
}
寫在后面
我是嘉寶Appian菩颖,一個(gè)賣萌出家的算法妹紙样漆。