React 初探
[1.React 簡(jiǎn)單介紹](https://github.com/laispace/react-explore/blob/master/1. React 簡(jiǎn)單介紹.md)
[2.React 簡(jiǎn)單示例](https://github.com/laispace/react-explore/blob/master/2. React 簡(jiǎn)單示例.md)
[3.使用 webpack + gulp 縱享絲滑構(gòu)建](https://github.com/laispace/react-explore/blob/master/3. 使用 webpack + gulp 縱享絲滑構(gòu)建.md)
[4.零碎總結(jié)](https://github.com/laispace/react-explore/blob/master/4. 零碎總結(jié).md)
React 簡(jiǎn)單介紹
先說 React 與 React Native
他們是真的親戚,可不像 Java 和 Javascript 一樣。
其實(shí)第一次看到 React 的語(yǔ)法我是拒絕的,因?yàn)檫@么丑的寫法,你不能讓我寫我就寫。
但當(dāng)我發(fā)現(xiàn) React Native 橫空出世后,它學(xué)習(xí)一次到處運(yùn)行的理念非常誘人。React Native 可以寫出原生體驗(yàn)的 iOS/Android 應(yīng)用?那不就多了一門裝逼技能?所以我們調(diào)研小組試了一下,感覺 "Duang" 一下,很爽很舒服。寫 React Native 需要兩門基礎(chǔ)技能:React 語(yǔ)法 和 iOS 基礎(chǔ)知識(shí)。
很爽很舒服,索性就研究一下,算是入門。
了解之后發(fā)現(xiàn),React 真是有另一番天地,值得學(xué)習(xí)。
接下來總結(jié)以下我對(duì) React 的理解,分享給大家。
至于 React Native,有機(jī)會(huì)再好好探究下。
這部分廢話太多,喜歡實(shí)戰(zhàn)的可以直接看代碼部分。
React 是 Facebook 出品的一套顛覆式的前端開發(fā)類庫(kù)。
為什么說它是顛覆式的呢?
內(nèi)存維護(hù)虛擬 DOM
對(duì)于傳統(tǒng)的 DOM 維護(hù),我們的步驟可能是:
1. 初始化 DOM 結(jié)構(gòu)
2. 從服務(wù)器獲取新數(shù)據(jù)
3. 使用新數(shù)據(jù)更新局部 DOM
4. 綁定各種事件
首先,我們操作 DOM 是最昂貴的開銷,對(duì)于 需要反復(fù)更新 DOM 的網(wǎng)頁(yè),無疑是噩夢(mèng)。其次,對(duì) DOM 局部的更新以及事件綁定加大了維護(hù)的難度。
而 React 引入了一個(gè)全新的概念:虛擬 DOM。
虛擬 DOM 是躺在內(nèi)存里的一種特殊的結(jié)構(gòu),我們可以理解為這是真實(shí) DOM 在內(nèi)存里的映射。
除了結(jié)構(gòu)上的映射外,這個(gè)虛擬的 DOM 還包括了渲染
真實(shí)所需要的數(shù)據(jù)以及事件綁定。
全量更新真實(shí) DOM
虛擬 DOM 在創(chuàng)建時(shí),首先是使用 JSX 的語(yǔ)法生成一個(gè)真實(shí) DOM 樹的映射,其次是從服務(wù)器端拉取遠(yuǎn)程數(shù)據(jù),接著注入到這個(gè)虛擬 DOM 樹中,同時(shí)綁定事件。
好了,有了虛擬 DOM、數(shù)據(jù)、事件,萬(wàn)事俱備。
接下來,調(diào)用 render() 方法一次性渲染出真實(shí)的 DOM,然后全量插入到網(wǎng)頁(yè)中。
虛擬 DOM 靜靜地躺在內(nèi)存里,等待數(shù)據(jù)更新。
新數(shù)據(jù)來臨,調(diào)用 setState() 方法更新數(shù)據(jù)到虛擬 DOM 中,然后自動(dòng)調(diào)用 render() 再一次性渲染出真實(shí)的 DOM ,然后全量更新到網(wǎng)頁(yè)中。
一個(gè)虛擬 DOM,對(duì)應(yīng)一個(gè)真實(shí) DOM
一份數(shù)據(jù)更新,重新生成虛擬 DOM ,全量更新真實(shí) DOM
就這么簡(jiǎn)單。
除了帶來性能上的提升之外,很顯然這種寫法簡(jiǎn)化了我們維護(hù) DOM 的成本 -- 我們只需要維護(hù)一份數(shù)據(jù)。
只是 View,可配合其他類庫(kù)使用
可以看到,React 里最重要的概念有虛擬 DOM、單向數(shù)據(jù)注入(虛擬 DOM 到真實(shí) DOM)。
這里并沒有引入太多其他的東西,所以我對(duì) React 的理解是一個(gè)類庫(kù),而非框架。
如果要使用 MVC、MVVM 等技術(shù)的吧,完全可以把 React 當(dāng)做其中的 V,即 View, 配合其他類庫(kù)使用。
組件化
我雖然是個(gè)前端菜鳥,但日觀天象也是能嗅到下一代 Web 將是組件化、組件復(fù)用共享的時(shí)代。
React 編寫起來,就是編寫一個(gè)個(gè)的組件。
我對(duì)一個(gè) React 組件的理解是:
- 模板 HTML (JSX 語(yǔ)法格式)
- 樣式 CSS (還是獨(dú)立的樣式文件)
- 交互 JS (與HTML一起,揉和到 JSX 語(yǔ)法中)
以上三者可以打包復(fù)用,甚至是無縫接入,我腳得它就可能是未來了。
HTML 與 JS 使用 JSX 語(yǔ)法糅合到一起的方式是見仁見智,恐怕會(huì)引起戰(zhàn)爭(zhēng)。
我剛接觸到 JSX 的時(shí)候,一開口也是『我*,好丑』。
但慢慢地卻發(fā)現(xiàn),這種方式一開始寫起來別扭,但用得卻很爽。
接下來,我通過編寫一個(gè)簡(jiǎn)單的應(yīng)用來入門 React。
看完如果大呼不過癮,建議直飛 React 官方看文檔,那才是寶藏!
React 簡(jiǎn)單示例
示例代碼放置在 demo/目錄下,每個(gè)文件夾為一個(gè)獨(dú)立的示例。
先看下這個(gè) demo 最終的樣子吧:
每個(gè)示例的入口文件 index.html 結(jié)構(gòu)大體相同:
<!-- React 真實(shí) DOM 將會(huì)插入到這里 -->
<div id="demo"></div>
<!-- 引入 React -->
<script src="../../bower_components/react/react.js"></script>
<!-- 引入 JSX 語(yǔ)法格式轉(zhuǎn)換器 -->
<script src="../../bower_components/react/JSXTransformer.js"></script>
<!-- 注意:script 需要注明 type 為 text/jsx 以指定這是一個(gè) JSX 語(yǔ)法格式 -->
<script type="text/jsx" src="demo.js"></script>
</body>
渲染一個(gè)虛擬 DOM 為真實(shí) DOM
使用 render() 方法生成真實(shí) DOM 并插入到網(wǎng)頁(yè)中。
// 使用 React.createClass 創(chuàng)建一個(gè)組件
var DemoComponent = React.createClass({
// 使用 render 方法自動(dòng)渲染 DOM
render: function () {
return (
<div className="component-hello">
<h1 className="hello-title">Hello React</h1>
<p className="hello-desc">React 初探</p>
<div className="hello-movies">
<p2>我喜歡的電影</p2>
<ul>
<li className="movie-item">
<span className="movie-name">速度與激情7</span>
-
<span className="movie-date">2015</span>
</li>
</ul>
</div>
</div>
)
}
});
// 將組件插入到網(wǎng)頁(yè)中指定的位置
React.render(<DemoComponent />, document.getElementById('demo'));
設(shè)置初始數(shù)據(jù)
第一次渲染真實(shí) DOM 時(shí)將使用 getInitialState() 返回的數(shù)據(jù)。
// 使用 React.createClass 創(chuàng)建一個(gè)組件
var DemoComponent = React.createClass({
// getInitialState 中返回的值將會(huì)作為數(shù)據(jù)的默認(rèn)值
getInitialState: function () {
return {
title: '我喜歡的電影',
movies: [
{
id: 7,
name: '速度與激情7',
date: 2015
},
{
id: 6,
name: '速度與激情6',
date: 2013
}
]
}
},
// 使用 render 方法自動(dòng)渲染 DOM
render: function () {
// this.state 用于存儲(chǔ)數(shù)據(jù)
var title = this.state.title;
var movies = this.state.movies.map(function (movie) {
return (
<li className="movie-item" key={movie.id}>
<span className="movie-name">{movie.name}</span>
-
<span className="movie-date">{movie.date}</span>
</li>
)
});
return (
<div className="component-hello">
<h1 className="hello-title">Hello React</h1>
<p className="hello-desc">React 初探</p>
<div className="hello-movies">
<p2>{title}</p2>
<ul>{movies}</ul>
</div>
</div>
)
}
});
// 將組件插入到網(wǎng)頁(yè)中指定的位置
React.render(<DemoComponent />, document.getElementById('demo'));
動(dòng)態(tài)更新數(shù)據(jù)
第二次更新渲染真實(shí) DOM 時(shí)將使用 setState() 設(shè)置的數(shù)據(jù)。
// 使用 componentDidMount 在組件初始化后執(zhí)行一些操作
componentDidMount: function () {
// 拉取遠(yuǎn)程數(shù)據(jù)
// 開啟假數(shù)據(jù)服務(wù)器:
// cd fake-server && npm install && node index.js
this.fetchData();
},
// 使用自定義的 fetchData 方法從遠(yuǎn)程服務(wù)器獲取數(shù)據(jù)
fetchData: function () {
var self = this;
// 發(fā)起 ajax 獲取到數(shù)據(jù)后調(diào)用 setState 方法更新組件的數(shù)據(jù)
var url = '../../fake-data/movies.json';
$.getJSON(url, function (movies) {
// 本地模擬返回太快了,模擬一下網(wǎng)絡(luò)延遲
setTimeout(function() {
self.setState({
movies: movies
});
}, 2000);
});
},
綁定事件
綁定事件時(shí),我們可以使用 ref="name" 屬性對(duì)一個(gè) DOM 節(jié)點(diǎn)進(jìn)行標(biāo)記,同時(shí)可以通過 React.findDOMNode(this.refs.name) 獲取到這個(gè)節(jié)點(diǎn)的原生 DOM。
// 使用 render 方法自動(dòng)渲染 DOM
render: function () {
var self = this;
// this.state 用于存儲(chǔ)數(shù)據(jù)
var title = this.state.title;
var movies = this.state.movies.map(function (movie) {
return (
<li className="movie-item" key={movie.id}>
<span className="movie-name">{movie.name}</span>
-
<span className="movie-date">{movie.date}</span>
<a href="#" onClick={self.onRemove.bind(null, movie)}>刪除</a>
</li>
)
}.bind(this));// 注意這里 bind(this) 修正了上下文
return (
<div className="component-hello">
<h1 className="hello-title">Hello React</h1>
<p className="hello-desc">React 初探</p>
<div className="hello-movies">
<p2>{title}</p2>
<form onSubmit={this.onAdd}>
{/* 注意這里指定 ref 屬性,然后我們就可以使用 this.refs.xxx 訪問到 */}
<input type="text" ref="name" placehlder="輸入你喜歡的電影"/>
<input type="text" ref="date" placeholder="上映時(shí)間"/>
<input type="submit" value="提交"/>
</form>
<ul>{movies}</ul>
{this.state.loading ? <div>大家好我是菊花, 我現(xiàn)在在轉(zhuǎn)</div> : null}
</div>
</div>
)
}
onRemove: function (movie) {
var id = movie.id;
console.log(movie)
// 刪除這個(gè) item
var movies = this.state.movies;
var len = movies.length;
var index = -1;
for(var i = 0; i < len; i++) {
var _movie = movies[i];
if (_movie.id === id) {
index = i;
break;
}
}
if (index > 0) {
movies.splice(index, 1);
this.setState({
movies: movies
});
}
},
onAdd: function (e) {
e.preventDefault();
var refs = this.refs;
var refName = React.findDOMNode(refs.name);
var refDate = React.findDOMNode(refs.date);
if (refName.value === '') {
alert('請(qǐng)輸入電影名');
return;
} else if (refDate === '') {
alert('請(qǐng)輸入上映時(shí)間');
return;
}
var movie = {
// 使用 findDOMNode 獲取到原生的 DOM 對(duì)象
name: refName.value,
date: refDate.value,
id: Date.now() // 粗暴地以時(shí)間數(shù)字作為隨機(jī) id
};
var movies = this.state.movies;
movies.push(movie);
this.setState(movies);
refName.value = '';
refDate.value = '';
},
多組件與組件嵌套
一個(gè)組件就包含了 JSX 模板、數(shù)據(jù)維護(hù)、事件綁定的話,代碼量已經(jīng)夠多了,這時(shí)候可以采用 AMD/CMD 的方式,將組件進(jìn)行更細(xì)粒度的劃分,可以以文件即組件的方式來編寫,這里就不上 demo 了。
組件間通信
在 React 中,數(shù)據(jù)流是單向的,且組件之間可以嵌套,我們可以通過對(duì)最頂層組件傳遞屬性方式,向下層組件傳送數(shù)據(jù)。
嵌套組件間,使用 this.props 屬性向下傳遞數(shù)據(jù)
獨(dú)立組件之間,自行維護(hù)數(shù)據(jù)則需要自行維護(hù)一個(gè)全局?jǐn)?shù)據(jù)存儲(chǔ),或者使用發(fā)布訂閱地方式通知數(shù)據(jù)的更新。
全局?jǐn)?shù)據(jù)存儲(chǔ)怎么做呢?可以理解為不同的組件獲取的數(shù)據(jù)源一致,在組件的外部維護(hù)這個(gè)數(shù)據(jù)集合,或者干脆直接從服務(wù)器端獲取。
有人會(huì)說了,這樣很不方便。
但我覺得,既然是一個(gè)組件,那就配套有獲取組件所需數(shù)據(jù)的方式,獨(dú)立組件間有很強(qiáng)的數(shù)據(jù)依賴時(shí),要么使用上述方式,要么可以簡(jiǎn)單粗暴,將獨(dú)立組件用一個(gè)頂層組件包裹起來,轉(zhuǎn)化為嵌套組件的關(guān)系,即可數(shù)據(jù)互通。
// 將子組件抽離出來
var LiWrapper = React.createClass({
render: function () {
// 使用 this.props 獲得傳入組件的數(shù)據(jù)
var movie = this.props.movie;
return (
<li>{/* ... */}</li>
)
}
});
// 使用 React.createClass 創(chuàng)建一個(gè)組件
var DemoComponent = React.createClass({
// 使用 getInitialState 的返回值作為數(shù)據(jù)的默認(rèn)值
getInitialState: function () {
// ...
},
// 使用 render 方法自動(dòng)渲染 DOM
render: function () {
// this.state 用于存儲(chǔ)數(shù)據(jù)
var movies = this.state.movies.map(function (movie) {
return (
<LiWrapper movie={movie}/>
)
}.bind(this));// 注意這里 bind(this) 修正了上下文
return (
<div className="component-hello">
{/* ... */}
<div className="hello-movies">
<ul>{movies}</ul>
</div>
</div>
)
}
});
// 將組件插入到網(wǎng)頁(yè)中指定的位置
// 在使用組件時(shí)傳入 movies 數(shù)據(jù)
var movies = [// ...];
React.render(<DemoComponent movies={movies}/>, document.getElementById('demo'));
打造絲滑的構(gòu)建 使用 ES6 + gulp + webpack
ES6 和 gulp 的話就不多介紹啦。
webpack 是一款新生的前端構(gòu)建工具,兼容 AMD/CMD 等寫法,支持 Browser 和 Node 端共享代碼,在瀏覽器端可以像寫 Node 一樣方便的進(jìn)行模塊化的劃分。
在這里主要用 webpack 的兩個(gè)插件:
使用 jsx-loader 這個(gè)插件支持 jsx 語(yǔ)法解析
使用 esx-loader 這個(gè)插件支持 es6 語(yǔ)法解析
來看下簡(jiǎn)單目錄結(jié)構(gòu):
- js/main.js 為入口文件,引入了兩個(gè)組件。
var React = require('react');
var MovieListComponent = require('./components/movie-list');
var HelloMessageComponent = require('./components/hello');
var movies = [
{
id: 5,
name: '速度與激情5',
date: 2011
},
{
id: 4,
name: '速度與激情4',
date: 2009
}
];
var wording = '保羅';
var MainComponent = React.createClass({
render: function () {
return (
<div className="component-hello">
<HelloMessageComponent name={wording}/>
<MovieListComponent movies={movies} />
</div>
)
}
});
React.render(<MainComponent />, document.getElementById('demo'));
- js/components/movie-list.js 組件為 JSX 語(yǔ)法編寫
var React = require('react');
// 引入子組件
var MovieComponent = require('./movie');
// 使用 React.createClass 創(chuàng)建一個(gè)組件
var MovieListComponent = React.createClass({
// 使用 getInitialState 的返回值作為數(shù)據(jù)的默認(rèn)值
getInitialState: function () {
return {
loading: true,
title: '我喜歡的電影',
// 注意這里將 外部傳入的數(shù)據(jù)賦值給了 this.state
movies: []
}
},
// 使用 render 方法自動(dòng)渲染 DOM
render: function () {
// this.state 用于存儲(chǔ)數(shù)據(jù)
var title = this.state.title;
// this.props 用于從組件外部傳入數(shù)據(jù)
var movies = this.props.movies;
movies = movies.map(function (movie) {
return (
<MovieComponent movie={movie}/>
)
}.bind(this));// 注意這里 bind(this) 修正了上下文
return (
<ul>{movies}</ul>
)
}
});
module.exports = MovieListComponent;
- js/components/hello.js 組件為 ES6 + JSX 語(yǔ)法編寫
var React = require('react');
class HelloComponent extends React.Component {
constructor(props) {
super(props);
this.state = {wording: '你好呀, '};
}
render() {
return <div>{this.state.wording} {this.props.name}</div>;
}
}
module.exports = HelloComponent;
- webpack.config.js 指定 jsx-loader 和 es6-loader
module.exports = {
entry: ['./js/main.js'],
output: {
path: __dirname,
filename: 'js/bundle.js'
},
module: {
loaders: [
{ test: /\.js$/, loader: 'es6-loader' },
{ test: /\.js$/, loader: 'jsx-loader' }
]
}
};
- gulpfile.js 在這里配置 webpack 任務(wù),啟動(dòng)文件監(jiān)聽
var gulp = require('gulp');
var livereload = require('gulp-livereload');
var webpack = require("gulp-webpack");
var webpackConfig = require('./webpack.config');
gulp.task("webpack", function() {
return gulp.src('./js/main.js')
.pipe(webpack(webpackConfig))
.pipe(gulp.dest('.'));
});
gulp.task('watch', function() {
livereload.listen();
gulp.watch(['js/**/*.js', '!js/bundle.js'], ['webpack']);
});
gulp.task('default', [
'webpack',
'watch'
]);
- index.html 示例頁(yè)面,引入 webpack 打包后的 js/bundle.js
<!-- React 真實(shí) DOM 將會(huì)插入到這里 -->
<div id="demo"></div>
<script src="./js/bundle.js"></script>
在 js/main.js 中引入兩個(gè)不同的組件,然后在 webpack.config.js 中指定編譯 JSX 和 ES6 的 loader 工具,使用 gulp 監(jiān)聽 js/ 中文件變化,自動(dòng)編譯出的 js/bundle.js 將被 index.html 引用。
嗯,再在 webpack 中加入各種你喜歡的 loader,在 gulp 中加上各種 css、js、img 的處理任務(wù),編寫代碼,自動(dòng)重新編譯,縱享絲滑。
零碎總結(jié)
文章到這里應(yīng)該就算結(jié)束了,接下來是一些在學(xué)習(xí)過程中記下來的幾個(gè)小點(diǎn),也分享給大家。
簡(jiǎn)單理解 JSX 語(yǔ)法
JSX 把 JS 和 HTML 糅合起來了,這么理解是不是感覺比較簡(jiǎn)單:
遇到 {} 包裹的是 JS,遇到 <> 包裹的是 HTML
render() 中 返回的的 JSX 模板需要一個(gè)根元素包裹起來
比如:
// 錯(cuò)誤的寫法
var MyComponent = React.createClass({
render: function () {
return (
<h1>速度與激情7</h1>
<p>致敬保羅</p>
)
}
});
應(yīng)該寫成:
// 正確的寫法
var MyComponent = React.createClass({
render: function () {
return (
<div>
<h1>速度與激情7</h1>
<p>致敬保羅</p>
</div>
)
}
});
幾個(gè)重要方法
- render() 返回的是一系列嵌套的組件
this.props 獲取父組件傳遞給子組件的數(shù)據(jù)
this.setState({data: data}); 用于動(dòng)態(tài)更新狀態(tài),設(shè)置數(shù)據(jù)(設(shè)置后UI會(huì)自動(dòng)刷新) - getInitialState() 在整個(gè)組件的生命周期中只會(huì)執(zhí)行一次,用于初始化數(shù)據(jù)
- componentDidMount 會(huì)在 render 后自動(dòng)調(diào)用,用于異步獲取數(shù)據(jù),更新數(shù)據(jù)
操作數(shù)據(jù)的流程
- gitInitialState() 初始化數(shù)據(jù)
- render() 渲染初始化數(shù)據(jù)
- componentDidMount() 異步獲取數(shù)據(jù)
- setState() 更新數(shù)據(jù)
理解一個(gè)組件的狀態(tài)轉(zhuǎn)換
每一個(gè)組件都可以理解為有一個(gè)簡(jiǎn)單的狀態(tài)機(jī)。
調(diào)用 setState(data, callback) 后,data 將會(huì)混入 this.state 中,數(shù)據(jù)得到了更新,render() 就會(huì)被調(diào)用,UI 就能被更新。
組件之間如何通信
<Parent><Child /></Parent>
父組件可以獲取到子組件:this.props.children
render() 永遠(yuǎn)不要手動(dòng)調(diào)用
render() 在 React 創(chuàng)建時(shí)會(huì)調(diào)用一次,在數(shù)據(jù)更新時(shí)調(diào)用 setState() 方法則會(huì)繼續(xù)調(diào)用它來更新網(wǎng)頁(yè)中的真實(shí) DOM。
使用 getInitial() 設(shè)置默認(rèn)值
這個(gè)方法返回的值會(huì)在組件初始化第一次調(diào)用 render() 時(shí)就被使用
class 是關(guān)鍵字,改用 className
// 錯(cuò)誤的寫法
var MyComponent = React.createClass({
render: function () {
return (
<div class="movie">
<h1>速度與激情7</h1>
<p>致敬保羅</p>
</div>
)
}
});
應(yīng)該寫成:
// 正確的寫法
var MyComponent = React.createClass({
render: function () {
return (
<div className="movie">
<h1>速度與激情7</h1>
<p>致敬保羅</p>
</div>
)
}
});
組件名大寫,不然不被識(shí)別
// 錯(cuò)誤的寫法
var myComponent = React.createClass({
render: function () {
return (
<div class="movie">
<h1>速度與激情7</h1>
<p>致敬保羅</p>
</div>
)
}
});
React.render(<myComponent />, document.getElementById('demo'));
應(yīng)該寫成:
// 正確的寫法
var MyComponent = React.createClass({
render: function () {
return (
<div className="movie">
<h1>速度與激情7</h1>
<p>致敬保羅</p>
</div>
)
}
});
React.render(<MyComponent />, document.getElementById('demo'));
怎么隱藏或顯示菊花
var MyComponent = React.createClass({
getInitialState: function () {
return {
loading: true
}
},
showLoading: function () {
this.setState({loading: true})
},
hideLoading: function () {
this.setState({loading: false})
},
render: function () {
return (
{
this.state.loading ?
<div>大家好我是菊花,我在轉(zhuǎn)</div>
:
null
}
)
}
});
插入原生的 HTML 片段的方式
React 會(huì)為我們過濾 XSS,要讓一段 HTML 片段直接顯示出來,需要這樣:
<div dangerouslySetInnerHTML={{__html: 'First · Second'}} />
讓 React 支持移動(dòng)觸摸實(shí)踐
React.initializeTouchEvents(true);
處理表單
表單因?yàn)闀?huì)因用戶交互而變化,所以有特定的一些屬性
- input 和 textarea 組件具有 value
- input[type=checkbox] 和 input[type=radio] 具有 checked
- option 具有 selected,如果要支持多選,可以傳入數(shù)組:
<select multiple={true} value={['B', 'C']}>
表單項(xiàng)具有 onChange 事件
注意如果這么寫:
render: function() {
return <input type="text" value="Hello!" />;
}
那每次 render 的時(shí)候 input 的 value 都會(huì)被重置為 "Hello!",所以需要這么控制:
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function() {
var value = this.state.value;
return <input type="text" value={value} onChange={this.handleChange} />;
}
利用這點(diǎn),可以無縫地接入一些驗(yàn)證規(guī)則,比如限制文字為 140 字:
handleChange: function(event) {
this.setState({value: event.target.value.substr(0, 140)});
}
如果不想這么被控制呢?那就在返回 input 的時(shí)候,不要設(shè)置 value 屬性,這樣隨著用戶輸入,value 不會(huì)被重置:
render: function() {
return <input type="text" />;
}
也可以設(shè)置默認(rèn)值:
render: function() {
return <input type="text" defaultValue="Hello!" />;
}
除了 defaultValue
之外,還支持 defaultChecked
理解虛擬 DOM
React 會(huì)在內(nèi)存里維護(hù)一個(gè)代表 DOM 的結(jié)構(gòu),調(diào)用
render 方法時(shí)才生成真正的 DOM 插入到網(wǎng)頁(yè)中。
理解組件的生命周期
一個(gè)組件的聲明周期可以理解為三個(gè)階段:
- mounting 組件正在插入到 DOM 中
- updating 組件正在重新注入新數(shù)據(jù)后更新到 DOM 中
- unmounting 組件從 DOM 中移除
mounting 階段
getInitialState() 被調(diào)用,返回原始數(shù)據(jù)
componentWillMount() 在組件 mounting 前調(diào)用
componentDidMount() 在組件 mounting 完成后調(diào)用
updating 階段
- componentWillReceiveProps(nextProps) 在接收到新的 props 時(shí)調(diào)用
- shouldComponentUpdate(nextProps, nextState) 在組件需要更新 DOM 時(shí)調(diào)用,若這個(gè)函數(shù)返回 false 則告訴 React 不要更新
- componentWillUpdate(nextProps, nextState) 在更新發(fā)生時(shí)調(diào)用,可以在這里調(diào)用 this.steState() 刷新數(shù)據(jù)
- componentDidUpdate(prevProps, prevState) 在更新完成后調(diào)用
unmounting 階段
- componentWillUnmount() 在組件移除時(shí)被調(diào)用,在這里可以對(duì)數(shù)據(jù)進(jìn)行清理
強(qiáng)制使用數(shù)據(jù)更新組件
forceUpdate() 強(qiáng)制使用數(shù)據(jù)更新組件,而不用調(diào)用 this.setState()
獲取原生 DOM 元素
React.findDOMNode(component)
返回原生的 DOM 元素
注意要獲取原生的 DOM 元素,必須在 render 被調(diào)用, 真正的 DOM 已經(jīng)被插入到頁(yè)面中時(shí)。
理解 refs
可以把 refs 理解為我們?cè)?HTML 中的id,用于定位到指定的組件。
<form onSubmit={this.onAdd}>
{/* 注意這里指定 ref 屬性,然后我們就可以使用 this.refs.xxx 訪問到 */}
<input type="text" ref="name" placehlder="輸入你喜歡的電影"/>
<input type="text" ref="date" placeholder="上映時(shí)間"/>
<input type="submit" value="提交"/>
</form>
ref 屬性可以是一個(gè)回調(diào)函數(shù)而不是名字,這個(gè)回調(diào)會(huì)在組件 mounted 后被調(diào)用。回調(diào)函數(shù)使用被引用的組件作為參數(shù)。
<input ref={ function(component){ React.findDOMNode(component).focus();} } />
注意不要在 render 方法中訪問 refs 屬性。