最近構(gòu)思了一個(gè)練手項(xiàng)目偎谁,應(yīng)用react實(shí)現(xiàn)程序的邏輯肿男,從而進(jìn)一步學(xué)習(xí)React框架開(kāi)發(fā)。這次的例子是一個(gè)純前端的簡(jiǎn)單應(yīng)用纤房,模擬實(shí)現(xiàn)圖片上傳和展示功能纵隔。應(yīng)用中,使用sessionStorage存儲(chǔ)圖片數(shù)據(jù)炮姨,并且將sessionStorage中存儲(chǔ)的圖片數(shù)據(jù)捌刮,以列表的形式展示出來(lái)。
用Webpack打包React應(yīng)用
Webpack 是一個(gè)前端資源加載/打包工具舒岸,只需要相對(duì)簡(jiǎn)單的配置就可以提供前端工程化需要的各種功能绅作。之前的例子中,對(duì)react的模塊化吁津,采用的是requirejs的AMD方式棚蓄,使用Webpack之后,我們就可以使用nodejs的commonjs方式編寫(xiě)模塊化的js代碼碍脏。通過(guò)webpack梭依,會(huì)將所有依賴的資源打包成一個(gè)單獨(dú)的bundle,包含應(yīng)用的邏輯代碼典尾,還有依賴的模塊役拴。
如果使用jsx格式編寫(xiě)混編的js和html代碼,還需要安裝相應(yīng)的loader钾埂,才能成功打包河闰,使用的是babel-loader科平。這里的loader除了jsx之外,根據(jù)需要姜性,還可以支持sass等其他格式瞪慧。
安裝相應(yīng)的npm依賴包
npm install react --save-dev
npm install react-dom --save-dev
npm install webpack --save-dev
npm install babel-loader --save-dev
跟所有的應(yīng)用程序一樣,react也需要有一個(gè)入口點(diǎn)部念,webpack會(huì)從入口點(diǎn)開(kāi)始弃酌,對(duì)所有的依賴文件進(jìn)行打包,在webpack.config.js
配置文件中儡炼,進(jìn)行相應(yīng)的配置妓湘;entry就是入口點(diǎn)
module.exports = {
entry:[
'./app/main.js'
],
output: {
path: __dirname + '/assets/',
publicPath: "/assets/",
filename: '[name].bundle.js'
},
resolve:{},
module: {
loaders: [
/*{
test: /\.scss$/,
loaders: ["style", "css", "sass"]
},*/{
test: /\.js$/,
loaders: ['babel-loader'],
include: __dirname + '/app/'
},
{ test: /\.jsx?$/, loaders: ['babel','jsx?harmony']}
]
},
plugins:[]
};
使用Gulp構(gòu)建項(xiàng)目
為了開(kāi)發(fā)和調(diào)試方便,引入構(gòu)建工具gulp乌询,主要使用到的插件包括:gulp-connect
榜贴、gulp-sass
、gulp-webpack
妹田、gulp-uglify
唬党。分別使用npm安裝對(duì)應(yīng)的模塊,npm install gulp --save-dev
鬼佣。
項(xiàng)目開(kāi)發(fā)中初嘹,簡(jiǎn)單的實(shí)現(xiàn)了幾個(gè)Task,進(jìn)行項(xiàng)目打包和構(gòu)建沮趣。
var connect = require('gulp-connect');
var gulpWebpack = require('gulp-webpack');
var bower = require('gulp-bower');
var uglify = require('gulp-uglify');
var sass = require('gulp-sass');
var webpackConfig = require('./webpack.config');
gulp.task('bower', function() {
return bower('./bower_components')
.pipe(gulp.dest('./static/lib/'));
});
gulp.task('easy_webpack',function(){
gulp.src('./app/main.js')
.pipe(gulpWebpack(webpackConfig))
.pipe(uglify())
.pipe(gulp.dest('./assets/scripts/'))
});
gulp.task('easy_sass', function () {
return gulp.src(path.SASS + '/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./assets/styles/'));
});
gulp.task('easy_build',['bower','easy_webpack','easy_sass'], function () {});
gulp.task('staticserver', function() {
connect.server({
port:3333,
});
});
React的組件
搭建好環(huán)境之后,react的使用非常方便坷随,把html根據(jù)需要拆分成不同的模塊就可以直接在jsx語(yǔ)法的代碼中混合編寫(xiě)房铭,ReactDom.render就可以把模版轉(zhuǎn)換成html代碼,并且把html模塊代碼嵌入到指定的dom對(duì)象中温眉,<代表往后是html代碼缸匪,當(dāng)作html解析,{代筆往后是js代碼类溢,當(dāng)作js解析凌蔬,需要注意的是,因?yàn)閏lass是關(guān)鍵字闯冷,所以需要把class替換成className砂心,把for替換成htmlFor。
var React = require('react');
var ReactDOM = require('react-dom');
module.exports = React.createClass({
handleClick:function(event){
console.log(event.target);
},
render: function() {
return (
<div className="img-item">
<div className="img-view" onClick="{this.handleClick}">
<img className="picture" src={this.props.imgData.file}/>
</div>
<div className="img-info">
<span className="img-name">{this.props.imgData.name}</span>
<span className="img-type">{this.props.imgData.format}</span>
</div>
</div>
);
}
});
......
#使用的示例
var imageCtrl = new ImageCtrl();
var imageDataStore = imageCtrl.findAll();
ReactDOM.render(
<div className="img-content card-mode">
<ul className="js-img-list u-clearfix">
{
imageDataStore.map(function (item,index) {
return <li key={item.id}><ImgListItemComponent imgData={item}/></li>
})
}
</ul>
</div>,
document.getElementById('imageList')
);
在React中蛇耀,通過(guò)React.createClass方法就可以生成一個(gè)組件辩诞,通過(guò)這個(gè)方法,我們可以封裝自己的組件纺涤,并且像普通的html標(biāo)簽一樣在網(wǎng)頁(yè)中插入译暂。界面交互的大多數(shù)場(chǎng)景實(shí)現(xiàn)抠忘,就是在組織不同的組件,實(shí)現(xiàn)組件的復(fù)用和狀態(tài)控制外永。對(duì)于組件崎脉,我們可以像普通的html標(biāo)簽一樣,設(shè)置屬性伯顶,然后通過(guò)this.props
就可以讀取對(duì)應(yīng)的屬性囚灼,不同于html標(biāo)簽的是,jsx的屬性砾淌,支持所有js的數(shù)據(jù)類型而不是普通字符串啦撮。
React組件的props可以通過(guò)定義變量的方式,在組建類的外部訪問(wèn)汪厨,var imgList = <ImgListItemComponent imgData={item}/>
赃春,然后讀取imgList.props.imgData,但是官方建議不要在外部對(duì)props進(jìn)行修改劫乱,因?yàn)檫@個(gè)是不可控的织中。
React創(chuàng)新性的將組件看成是一個(gè)狀態(tài)機(jī),頁(yè)面渲染的時(shí)候衷戈,根據(jù)state屬性的初始狀態(tài)來(lái)決定如何渲染狭吼,在組件有互動(dòng)需要的時(shí)候,不同的交互會(huì)導(dǎo)致state的改變殖妇,從而觸發(fā)重新渲染UI刁笙。這樣的實(shí)現(xiàn)機(jī)制和Jquery的事件綁定機(jī)制,設(shè)計(jì)理念是完全不一樣的谦趣。其中g(shù)etInitialState返回的object會(huì)被賦值作為初始化的state疲吸。在交互過(guò)程中,如果有需要改變狀態(tài)的時(shí)候前鹅,執(zhí)行this.setState({fileContent:"",fileStatus:false,fileEntity:null});
摘悴,在傳入的對(duì)象中,制定的state對(duì)應(yīng)的值就會(huì)改變舰绘,從而導(dǎo)致render
中根據(jù)對(duì)應(yīng)的state值來(lái)渲染蹂喻。
......
module.exports = React.createClass({
getInitialState: function() {
return {fileTip: "",fileName: "",fileStatus:false,fileEntity:null,clicked: false,fileContent:"",fileNameShow:""};
},
render:function(){
if (this.state.clicked) {
return (
<div className="simple-modal upload-modal">
...省略...
</div>
);
}else {
return <div />;
}
}
.......
this.props和this.state都是組件類提供的屬性,可以通過(guò)組件直接讀取捂寿,他們的區(qū)別是口四,props是固定的屬性,已經(jīng)設(shè)定就不再變化者蠕,而state根據(jù)交互的需要會(huì)發(fā)生變化窃祝。
React 使用駝峰命名規(guī)范的方式給組件綁定事件處理器。如果需要綁定點(diǎn)擊事件,直接設(shè)置onClick就可以粪小,onClick="{this.handleClick}"
綁定成功大磺,如果用戶點(diǎn)擊onClick就會(huì)調(diào)用React.createClass創(chuàng)建的對(duì)象中的handleClick函數(shù),結(jié)合setState就可以動(dòng)態(tài)的改變組件的狀態(tài)探膊,進(jìn)而更新界面杠愧。
開(kāi)發(fā)這個(gè)應(yīng)用的過(guò)程中,使用了mixin實(shí)現(xiàn)的彈窗組件逞壁,該組件通過(guò)在body的最后面添加一個(gè)div標(biāo)簽來(lái)實(shí)現(xiàn)彈窗流济;基本夠用,接下來(lái)的學(xué)習(xí)中腌闯,還會(huì)深入的學(xué)習(xí)mixin和彈窗的實(shí)現(xiàn)绳瘟。khan學(xué)院的React modal
開(kāi)發(fā)過(guò)程
在開(kāi)發(fā)的過(guò)程中,因?yàn)樽罱K要將html代碼拆分成不同的模塊姿骏,實(shí)現(xiàn)整個(gè)功能的過(guò)程中糖声,可能涉及多個(gè)場(chǎng)景,所以將不同的場(chǎng)景分瘦,分別以純靜態(tài)頁(yè)面的方式實(shí)現(xiàn)蘸泻,這樣就可以快速的調(diào)整頁(yè)面布局,拆分之后保留頁(yè)面原型嘲玫。
保存圖片的時(shí)候悦施,存儲(chǔ)格式是ImageData類,該類扮演了Schema的作用去团;圖片通過(guò)File讀取之后抡诞,以base64形式保存在sessionStorage中,保存的過(guò)程抽象成專門的模塊土陪,這樣以后需要實(shí)現(xiàn)異步上傳的時(shí)候沐绒,可以直接重新實(shí)現(xiàn)該模塊。該模塊使用mocha進(jìn)行單元測(cè)試旺坠。
/*圖片存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)Schema*/
ImageData = function(initData){
this.name = "";
this.id = "";
this.desc = "";
this.url = "";
this.visit = "";
this.createTime = null;
this.format = null;
this.file = "";
this.type = "";
var dataTypeList = ImageData.getParamList();
if(typeof initData == "object"){
for(var key in dataTypeList){
var keyName = dataTypeList[key];
if(initData[keyName]){
this[keyName] = initData[keyName];
}
}
}
};
ImageData.getParamList = function(){
var dataTypeList = ["name","id","desc","url","visit","createTime","format","file","type"];
return dataTypeList;
};
/*數(shù)據(jù)存儲(chǔ)相關(guān)代碼*/
var DataHandler = {
get:function(key,callback){
if(typeof sessionStorage != "object"){
return [];
}
var dataStr = sessionStorage.getItem(key);
var keyData = [];
if(dataStr){
keyData = JSON.parse(dataStr);
}
typeof callback == "function" && callback(keyData);
return keyData;
},
//以下代碼省略
......
預(yù)覽地址:http://pages.liuwill.com/
項(xiàng)目代碼:https://github.com/liuwill/react-webpack-startup
下載源代碼之后,執(zhí)行npm install
安裝需要的依賴包扮超,然后按照順序執(zhí)行g(shù)ulp任務(wù)取刃,就可以啟動(dòng)靜態(tài)服務(wù)器預(yù)覽效果。