在前面咱們經過四篇文章入門了React的大部分主要API,如今則開始進入實踐環節。css
實踐系列的開篇打算拿我司的FrozenUI來試驗,將其部分UI組件進行React化,做爲第一篇實踐文章,將以較簡單的Loading組件來入手,官網demo的效果以下圖:html
爲了更好地開發,後續將以webpack工具來輔助,對其不瞭解的童鞋能夠先查閱個人《webpack 入門指南》一文。node
鑑於咱們將複用 FrozenUI 的樣式,因此在DOM結構、class命名上都應當儘可能和原版的保持一致,在這個基礎上來實現具備一樣功能的React組件。react
因而咱們先下載好 frozen.css(方便示例因此直接用全局的樣式)和圖片資源,並定義一個簡單的 webpack.config.js:webpack
module.exports = { entry: { loading : './src/js/page/loading.js' }, output: { path: 'dist/js/page', filename: '[name].js' }, module: { loaders: [ { test: /\.css$/, loader: 'style-loader!css-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' }, { test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} ] }, resolve: { extensions: ['', '.js', '.json', '.scss'] } };
須要下載的模塊大體有這些(儘管有幾個咱們暫時還用不上,先裝上無所謂):git
"dependencies": { "css-loader": "^0.15.2", "expose-loader": "^0.7.0", "file-loader": "^0.8.4", "jsx-loader": "^0.13.2", "node-sass": "^3.2.0", "react": "^0.13.3", "sass-loader": "^1.0.2", "style-loader": "^0.12.3", "url-loader": "^0.5.6" }
咱們的文件目錄結構也很簡單:github
其中 src 爲源文件文件夾,dist 用於存放 webpack 最終處理後的輸出文件。web
src/js 中又分了 component 和 page 兩個文件夾,用於存放組件腳本和html頁面上要引用的入口腳本。json
./loading.htmlsass
這是最終執行頁面,做爲Demo能夠作的簡單點:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Demo</title> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"> </head> <body> <div class="wrap"></div> <script src="dist/js/page/loading.js"></script> </body> </html>
其中類名爲wrap的div是方便咱們掛載Loading組件的容器,整個頁面也只有一個script腳本入口(樣式也將最終打包在裏面)。
./src/js/page/loading.js
咱們先寫好該頁面入口腳本,肯定好Loading組件的使用錐形:
require('../../css/frozen.css'); //把樣式引進來 var React = require('react'), Loading = require('../component/Loading'); //這是組件模塊,下一步要寫的東西 var wrap = document.querySelector('.wrap'), hideCallback = function(){ //卸載組件後的回調 alert('done!!'); }; React.render( <Loading content='哈嘍' onHide={hideCallback}/>, wrap ); setTimeout(function(){ //3秒後卸載組件,模擬觸發回調 React.unmountComponentAtNode(wrap) }, 3000);
咱們但願可以自定義Loading組件上所顯示的文字,以及它被隱藏掉時觸發的回調,故咱們使用兩個props——「content」和「onHide」來綁定(事實上還有一個判斷是否只在局部顯示「加載中」的props屬性「isPart」,但新版的FrozenUI取消了該功能)。
./src/js/component/Loading.js
這塊是Loading組件模塊,是最重要的模塊,用於實現Loading組件的所有功能。
注意常規咱們要求React組件模塊的首字母必須大寫。
初步寫出一個簡單的組件結構:
var React = require('react'), PropTypes = React.PropTypes; var Loading = React.createClass({ propTypes: { onHide: PropTypes.func, //組件卸載後的回調 content: PropTypes.string // 展現內容 }, componentWillUnmount: function(){ //卸載時的回調 if(typeof this.props.onHide === 'function'){ setTimeout(this.props.onHide, 10); } }, render: function () { var content = this.props.content || '正在加載中...', component = (<div>{content}</div>); return component } }); module.exports = Loading;
對於兩個綁定的props,咱們分別在 componentWillUnmount 和 render 中作了對應處理,從而決定了組件卸載時是否觸發回調,以及加載時顯示什麼內容(若未傳props.content,則默認爲「正在加載中...」),接着咱們要處理的是最終渲染的DOM結構(總不能只有一個div對吧),這塊咱們得分析現有的 Frozen-Loading組件的DOM結構,儘可能與其一致(包括類名的定義):
那麼咱們只須要在 render 裏直接套用這塊DOM結構,把<p>標籤裏的內容換成 {content} 便可。
不過這樣好像太簡單了,不怎麼好玩呢~
在上個版本的Frozen-Loading組件裏,是有區分全局展現/局部展現加載界面的,局部加載是醬紫的:
我還記得局部展現狀況下的DOM結構和樣式(實際上它們只是類名不一樣),因而打算增長個 props.isPart 來判斷用戶是否要局部展現,而且這樣改寫組件代碼:
var React = require('react'), loadingCN = require('../component/styleMaps').loadingCN, //引入加載組件類名對象 PropTypes = React.PropTypes; var Loading = React.createClass({ propTypes: { isPart: PropTypes.bool, //是否局部加載 onHide: PropTypes.func, //組件卸載後的回調 content: PropTypes.string // 展現內容 }, componentWillUnmount: function(){ if(typeof this.props.onHide === 'function'){ setTimeout(this.props.onHide, 10); } }, render: function () { var content = this.props.content || '正在加載中...', flag = this.props.isPart ? 'partial' : 'global', component = (<div className={loadingCN.block[flag]}> <div className={loadingCN.wrap[flag]}> <i className={loadingCN.i[flag]}></i> <p>{content}</p> </div> </div>); return component } }); module.exports = Loading;
留意一個比較有趣的地方,咱們通關一個變量flag來判斷用戶是但願全局顯示仍是局部顯示加載界面,而後經過這個標籤來獲取到對應的類名:
flag = this.props.isPart ? 'partial' : 'global', component = (<div className={loadingCN.block[flag]}> <div className={loadingCN.wrap[flag]}> <i className={loadingCN.i[flag]}></i> <p>{content}</p> </div> </div>);
而此處的 loadingCN 是咱們在開頭引入的一個共用模塊:
loadingCN = require('../component/styleMaps').loadingCN
該模塊的定義也很是簡單:
./src/js/component/styleMaps.js
module.exports = { globalCN: {}, loadingCN: { block: { partial: 'demo-block', global: 'ui-loading-block show' }, wrap: { partial: 'ui-loading-wrap', global: 'ui-loading-cnt' }, i: { partial: 'ui-loading', global: 'ui-loading-bright' } } };
其返回了一個存放各組件類名對象,所以咱們能夠經過 require('../component/styleMaps').loadingCN.block['global'] 的形式來獲取到Loading組件全局加載時最外層div的類名。
因而乎咱們爲啥要這麼折騰多搞個樣式模塊呢?直接寫在 Loading.js 裏不行麼?
答案是能夠,可是多出一個樣式模塊能夠方便咱們後期統一在一個文件裏維護全部組件的類名,其實是爲後期維護提供了必定便捷度。
另外該樣式管理模塊咱們也暫時騰出了一個叫 globalCN 的對象屬性,能夠做爲存放多個組件間共用的類名。
咱們執行 webpack 打包後訪問根目錄的 loading.html(模擬移動端),效果正合咱們預期呢:
咱們給 page/loading.js 要渲染的組件加上 isPart={true} ,讓其走局部加載形式:
React.render( <Loading content='哈嘍' onHide={hideCallback} isPart={true}/>, wrap );
運行結果也是666:
本次的實踐就這麼愉快的結束吧~ 本節的代碼能夠在個人Github下載到。
下次分享下稍複雜點的 Tab 面板的React化的實現。共勉~!
若是以爲有幫助,就幫忙點下推薦吧,否則感受每次寫這種系列的文章好吃虧都沒人支持。。。都不太想繼續寫了\("▔□▔)/