我們首先考慮一個小需求:頁面上一個數(shù)字各谚,兩個按鈕,分別是「+」和「-」按鈕到千,點擊后數(shù)字分別+1和-1昌渤。
先以「傳統(tǒng)JS」的角度考慮,我們會怎么做憔四?
// html代碼
<div id="root">
<span id="number">0</span>
<button id="add">+</button>
<button id="minus">-</button>
</div>
// 獲取顯示「數(shù)字」的節(jié)點
let number = document.querySelector('#number');
// 獲取「add」按鈕節(jié)點
let add = document.querySelector('#add');
// 獲取「minus」按鈕節(jié)點
let minus = document.querySelector('#minus');
add.addEventListener('click', function() {
number.innerText = parseInt(number.innerText, 10) + 1;
});
minus.addEventListener('click', function() {
number.innerText = parseInt(number.innerText, 10) - 1;
});
我們用一張圖來分下下這個「邏輯」:
如圖膀息,我們是從「頁面」中先獲取元素0望抽,然后在JS里面進行「操作」,最后在返回到頁面中履婉。
React在這里的思想就是能不能減少這種復雜的流程煤篙,從JS返回到頁面((2)處)是不能省略的,所以就試試把(1)處的流程優(yōu)化一下毁腿。
如圖辑奈,我們直接在「JS」部分產生和操作「節(jié)點」,然后傳到「頁面」上顯示已烤。
此時要引入兩個庫鸠窗,「react」和「react-dom」,然后重寫下上面的代碼。
let number = 0;
let add = () => {
number += 1;
render();
}
let minus = () => {
number -= 1;
render();
}
render();
function render() {
let span = React.createElement('span', {}, number);
let button1 = React.createElement('button', {onClick: add}, '+');
let button2 = React.createElement('button', {onClick: minus}, '-');
let div = React.createElement('div', { className: 'parent'}, span, button1, button2);
ReactDOM.render(div, document.querySelector('#root'));
}
如上述代碼胯究,render中有很多「React.createElement」稍计,我們用「h」來表示「React.createElement」。
function render() {
let h = React.createElement;
let span = h('span', {}, number);
let button1 = h('button', {onClick: add}, '+');
let button2 = h('button', {onClick: minus}, '-');
let div = React.createElement('div', { className: 'parent'}, span, button1, button2);
ReactDOM.render(div, document.querySelector('#root'));
}
然后我們又想可以不可以把「div」裕循,「span」臣嚣,「button1」,「button2」直接放到「React.render」里面剥哑?于是乎硅则。
function render() {
let div = React.createElement('div', { className: 'parent'},
React.createElement('span', {}, number),
React.createElement('button', {onClick: add}, '+'),
React.createElement('button', {onClick: minus}, '-')
);
ReactDOM.render(div, document.querySelector('#root'));
}
上面的「div」里面的代碼,看起來是不是很熟悉的「層級感」株婴?是不是跟HTML里面的「標簽」很像怎虫?如果能寫成「標簽」的樣子,但是最后可以轉義為正確的語法就好了困介,于是有了「 JSX」語法大审。
function render() {
ReactDOM.render(
<div className="parent">
<span>{number}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
</div>
, document.querySelector('#root'));
}
是不是感覺熟悉了很多?
這個時候又有一個問題座哩,如果頁面中需要很多「元素」徒扶,那不是要搞成一堆?比如下面這樣
function render() {
ReactDOM.render(
<div className="parent">
<span>{number}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
<span>{number}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
<span>{number}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
<span>{number}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
</div>
, document.querySelector('#root'));
}
有什么方法可以把這些代碼「包裝」一下八回?我們想到了「函數(shù)」酷愧。(做兩個「計數(shù)器」)
let number = 0;
let add = () => {
number += 1;
render();
}
let minus = () => {
number -= 1;
render();
}
let add2 = () => {
number += 2;
render();
}
let minus2 = () => {
number -= 2;
render();
}
function Box1(obj) {
return(
<div>
<span>{number}</span>
<span>{obj.name}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
</div>
);
}
function Box2(obj) {
return(
<div>
<span>{number}</span>
<span>{obj.name}</span>
<button onClick={add2}>+</button>
<button onClick={minus2}>-</button>
</div>
);
}
function App() {
return(
<div className="parent">
<Box1 name='Jason'/>
<Box2 name='Jack'/>
</div>
);
}
render();
function render() {
ReactDOM.render(
<App/> // 等價于 React.createElement(App)
, document.querySelector('#root'));
}
函數(shù)是可以傳「參數(shù)」的,我們在「Box1」和「Box2」函數(shù)組件上增加一個name「屬性」缠诅,然后在
修改成如上代碼后溶浴,我們在「頁面」中點擊「+」號,出現(xiàn)一個bug管引,就是兩個「計數(shù)器」都會變化.
這是為什么呢士败?原來是因為兩個計數(shù)器共用了一個「number」變量,然后我們把兩者翻開。
let number = 0;
let add = () => {
number += 1;
render();
}
let minus = () => {
number -= 1;
render();
}
let number2 = 0;
let add2 = () => {
number2 += 2;
render();
}
let minus2 = () => {
number2 -= 2;
render();
}
function Box1(obj) {
return(
<div>
<span>{number}</span>
<span>{obj.name}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
</div>
);
}
function Box2(obj) {
return(
<div>
<span>{number2}</span>
<span>{obj.name}</span>
<button onClick={add2}>+</button>
<button onClick={minus2}>-</button>
</div>
);
}
function App() {
return(
<div className="parent">
<Box1 name='Jason'/>
<Box2 name='Jack'/>
</div>
);
}
render();
function render() {
ReactDOM.render(
<App/> // 等價于 React.createElement(App)
, document.querySelector('#root'));
}
設置了兩個「number」谅将,兩個「add」漾狼,兩個「minus」。但是如果有10個饥臂,那我們是不是又要分別申請十個逊躁?甚是麻煩。
然后我們又想隅熙,能不能把那些變量放到所需要的自己的「作用域」中稽煤,我們試試把「number」,「add」囚戚,「minus」放到「Box1」中酵熙。
function Box1(obj) {
let number = 0;
let add = () => {
number += 1;
render();
}
let minus = () => {
number -= 1;
render();
}
return(
<div>
<span>{number}</span>
<span>{obj.name}</span>
<button onClick={add}>+</button>
<button onClick={minus}>-</button>
</div>
);
}
此時點擊「+」號,發(fā)現(xiàn)「計數(shù)器」沒有變化驰坊,為啥呢匾二?其實是「add」函數(shù)中的「render()」惹的禍。 當執(zhí)行「add」函數(shù)時拳芙,雖然我們「number += 1」使得「number」變化察藐,但是后一句立即執(zhí)行了「render()」,每一次render都會「初始化」Box1态鳖,就又使得「number」為0.所以造成「沒有反應」的效果转培。所以我們能不能只是render局部呢?我們想到了「class類」浆竭。
class Box1 extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 0
}
}
addOne() {
this.setState({
number: this.state.number + 1
});
}
minusOne() {
this.setState({
number: this.state.number - 1
});
}
render() {
return (
<div>
<span>{this.state.number}</span>
<span>{this.props.name}</span>
<button onClick={this.addOne.bind(this)}>+</button>
<button onClick={this.minusOne.bind(this)}>-</button>
</div>
);
}
}
class Box2 extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 0
}
}
addTwo() {
this.setState({
number: this.state.number + 1
});
}
minusTwo() {
this.setState({
number: this.state.number - 1
});
}
render() {
return (
<div>
<span>{this.state.number}</span>
<span>{this.props.name}</span>
<button onClick={this.addTwo.bind(this)}>+</button>
<button onClick={this.minusTwo.bind(this)}>-</button>
</div>
);
}
}
function App() {
return(
<div className="parent">
<Box1 name='Jason'/>
<Box2 name='Jack'/>
</div>
);
}
render();
function render() {
ReactDOM.render(
<App/> // 等價于 React.createElement(App)
, document.querySelector('#root'));
}
這里有一個細節(jié),就是「onClick」調用函數(shù)的時候惨寿,add函數(shù)里面的「this」指向被改變了邦泄,變成了「undefined」。
onClick.call(undefined, ........);
// onClick強行把「this」改變裂垦,所以在調用的時候顺囊,我們可以
//用「bind」綁定「this」,或者使用「箭頭函數(shù)」
onClick={ this.addOne.bind(this) };
// 或者
onClick = { () => this.addOne() }