React.js讀書與總結:《react-tutorial》
最近接觸的一個專案中使用了React為主要框架,雖然之前也多多少少學過一些React,但是還需要更深入的學習和更熟練的掌握,所以最近我打算好好的讀上幾本React的書,今天帶來第一本書:《react-tutorial》的學習總結。
前言
技術在沒有真正使用之前,沒法評價哪一個好,沒有最好的,只有最合適的。
一 Webpack 配置 React 開發環境
搭建一個現代的前端開發環境配套的工具有很多,比如 Grunt / Gulp / Webpack / Broccoli,
這些都是要解決前端工程化問題。
而ES6 編譯工具 Babel,則內建了對 JSX 語法的支援。
(1)Webpack簡介:
前端資源載入/打包工具,只需簡單的配置就可提供前端工程化需要的各種功能,
如有需要還可被整合到其他如 Grunt / Gulp 的工作流
(2)webpack 的優勢:
- 以 commonJS 的形式來書寫指令碼,對 AMD/CMD 的支援也很全面,方便舊專案進行程式碼遷移。
- 能被模組化的不僅僅是 JS 。
- 開發便捷,能替代部分 grunt/gulp 的工作,比如打包、壓縮混淆、圖片轉base64等。
- 擴充套件性強,外掛機制完善
(3)安裝 Webpack:
npm install -g webpack
使用 webpack.config.js 配置檔案
要編譯 JSX,先安裝對應的 loader: npm install babel-loader –save-dev
(4)webpack配置
module下的loaders 是最關鍵的一塊配置,它告知 webpack 每一種檔案都需要使用什麼載入器來處理。
二 JSX
(1)為什麼要引入 JSX 這種語法?
傳統的 MVC 是將模板放在其他地方,比如 script 標籤或者模板檔案,再在 JS 中通過某種手段引用模板。按這種思路,想想多少次我們面對四處分散的模板片段不知所措?糾結模板引擎,糾結模板存放位置,糾結如何引用模板。
React 認為元件才是王道,而元件是和模板緊密關聯的,元件模板和元件邏輯分離讓問題複雜化了。所以就有了 JSX 這種語法,就是為了把 HTML 模板直接嵌入到 JS 程式碼裡面,這樣就做到了模板和元件關聯,但是 JS 不支援這種包含 HTML 的語法,所以需要通過工具將 JSX 編譯輸出成 JS 程式碼才能使用。
(2)JSX基本語法
1.小寫的字串是 HTML 標籤,大寫開頭的變數是 React 元件。
2.HTML 裡的 class 在 JSX 裡要寫成 className,因為 class 在 JS 裡是保留關鍵字。同理某些屬性比如 for 要寫成 htmlFor。
3.表示式
屬性值使用表示式,只要用 {} 替換 “”。
表示式是非常強大的,我們可以使用它在JSX標籤間或屬性中放置js的各種表示式或函式實現各種功能。
子元件也可以作為表示式使用,如:
// Input (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// Output (JS):
var content = React.createElement(
Container,
null,
window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
4.JSX註釋:
在< >內部可使用js的註釋,如果在一個元件的子元素位置使用註釋要用 {} 包起來。
5.自定義 HTML 屬性:
如果在 JSX 中使用的屬性不存在於 HTML 的規範中,這個屬性會被忽略。如果要使用自定義屬性,可以用 data- 字首。
6.屬性擴散:
使用es6的寫法,可給元件設定多個屬性,如:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
props 物件的屬性會被設定成 Component 的屬性。
另外,jsx中:
style 屬性接受由 CSS 屬性構成的 JS 物件
onChange 事件表現更接近我們的直覺(不需要 onBlur 去觸發)
(3)元件
元件的兩個核心概念:props和state。
1.props
props 即元件的屬性,由外部通過 JSX 屬性傳入設定,一旦初始設定完成,就可以認為 this.props 是不可更改的,所以不要輕易更改設定 this.props 裡面的值(雖然對於一個 JS 物件你可以做任何事)。
2.state
state 即元件的當前狀態,可以把元件簡單看成一個“狀態機”,
根據狀態 state 呈現不同的 UI 展示。一旦狀態(資料)更改,元件就會自動呼叫 render 重新渲染 UI,這個更改的動作會通過 this.setState()來觸發。
劃分狀態的原則:讓元件儘可能地少狀態,便於元件易維護。
當更改某個資料需要更新元件 UI 的就可以認為是 state。
3.無狀態元件
也可以用純粹的函式定義無狀態元件(沒有狀態和生命週期,只簡單的接受 props 渲染生成 DOM 結構)。
無狀態元件非常簡單,開銷低,可能的話儘量去使用無狀態元件。比如使用箭頭函式定義:
const HelloMessage = (props) => <div> Hello {props.name}</div>;
render(<HelloMessage name="John" />, mountNode);
因為無狀態元件只是函式,沒有例項返回,這點在想用 refs 獲取無狀態元件的時候要注意。
4.元件生命週期
1.初始化 this.state 的值,只在元件載入之前呼叫一次。
ES6中可以在建構函式中初始化狀態,如:
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: props.initialCount };
}
render() {
// ...
}
}
2.載入(渲染)元件觸發的生命週期函式:
componentWillMount()
只在載入之前呼叫,在 render 之前呼叫,可以在這個方法裡面呼叫 setState 改變狀態,並且不會導致額外呼叫一次 render
componentDidMount()
只在載入完成之後呼叫,在 render 之後呼叫,從這裡開始可以通過 ReactDOM.findDOMNode(this) 獲取到元件的 DOM 節點。
3.更新(重新渲染)元件觸發的生命週期函式:
這些方法不會在首次 render 元件的週期呼叫。
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
4.解除安裝元件觸發的生命週期函式:
componentWillUnmount
(4)React事件處理
1.繫結事件
例:
...
handleClick(e) {
this.setState({ liked: !this.state.liked });
}
render() {
const text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick.bind(this)}>
You {text} this. Click to toggle.
</p>
);
}
...
React 裡面繫結事件的方式和在 HTML 中繫結事件類似,使用駝峰式命名指定要繫結的事件型別(比如 onClick)屬性為元件定義的一個方法如( {this.handleClick.bind(this)} )。
注意要顯式呼叫 bind(this) 將事件函式上下文繫結要元件例項上,這也是 React 推崇的原則:儘量使用顯式的容易理解的js程式碼。
2.合成事件和原生事件
React 實現了一個“合成事件”層,這個事件模型保證了和 W3C 標準保持一致,“合成事件”提供了額外的好處:事件委託。
“合成事件”會以事件委託的方式繫結到元件最上層,並且在元件解除安裝(unmount)的時候自動銷燬繫結的事件。
原生事件:比如在 componentDidMount 方法裡面通過 addEventListener 繫結的事件就是瀏覽器原生事件。
所有通過 JSX 方式繫結的事件都是繫結到“合成事件”,建議都使用 React 的方式處理事件。
3.給事件處理函式傳遞引數
方法:bind(this, arg1, arg2, …)
例:
render: function() {
return <p onClick={this.handleClick.bind(this, 'extra param')}>;
},
handleClick: function(param, event) {
// handle click
}
(5)DOM 操作
大部分情況下不需要通過查詢 DOM 元素去更新元件的 UI,只要關注設定元件的狀態(setState)。可能在某些情況下確實需要直接操作 DOM。
ReactDOM.render 元件返回的是對元件的引用,也就是元件例項(對於無狀態狀態元件來說返回 null),注意 JSX 返回的不是元件例項,它只是一個 ReactElement 物件。
例:
// A ReactElement
const myComponent = <MyComponent />
// render
const myComponentInstance = ReactDOM.render(myComponent, mountNode);
myComponentInstance.doSomething();
findDOMNode()
當元件載入到頁面上之後(mounted),都可以通過 react-dom 提供的 findDOMNode() 方法拿到元件對應的 DOM 元素。
import { findDOMNode } from 'react-dom';
// Inside Component class
componentDidMound() {
const el = findDOMNode(this);
}
注意:findDOMNode() 不能用在無狀態元件上。
Refs
另一種方式是通過在要引用的 DOM 元素上面設定一個 ref 屬性指定一個名稱,然後通過 this.refs.name 來訪問對應的 DOM 元素。
例:
clearAndFocusInput() {
this.setState({ userInput: '' }, () => {
this.refs.theInput.focus(); //2.
});
}
render() {
return (
<div>
<div onClick={this.clearAndFocusInput.bind(this)}>
Click to Focus and Reset
</div>
<input
ref="theInput" //1.
value={this.state.userInput}
onChange={this.handleChange.bind(this)}
/>
</div>
);
}
如果 ref 是設定在原生 HTML 元素上,它拿到的就是 DOM 元素,如果設定在自定義元件上,它拿到的就是元件例項,這時候就需要通過 findDOMNode 來拿到元件的 DOM 元素。
因為無狀態元件沒有例項,所以ref 不能設定在無狀態元件上,如果想要拿無狀態元件的 DOM 元素的時候,就需要用一個狀態元件封裝一層,然後通過 ref 和 findDOMNode 去獲取。
可以使用 ref 到的元件定義的任何公共方法,比如 this.refs.myTypeahead.reset()
Refs 是訪問到元件內部 DOM 節點唯一可靠的方法
Refs 會自動銷燬對子元件的引用(當子元件刪除時)
refs注意:
不要在 render 或者 render 之前訪問 refs
不要濫用 refs,比如只是用它來按照傳統的方式操作介面 UI:找到 DOM -> 更新 DOM
(6)元件間通訊
未完待續
(7)Mixins
未完待續