在前面咱们经过四篇文章入门了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化的实现。共勉~!
若是以为有帮助,就帮忙点下推荐吧,否则感受每次写这种系列的文章好吃亏都没人支持。。。都不太想继续写了\("▔□▔)/