在學習react初期,看了一些視頻和資料,react基礎知識差很少學完,跟着網上的一個教程,作了一個小型的問答demo。css
需求看圖說:html
1.點擊「添加」按鈕,顯示問題輸入表單,再次點擊,隱藏表單。同時,點擊「取消」按鈕,隱藏表單。react
2.輸入問題標題和內容後,點擊「確認」按鈕,將問題顯示在下方(按照投票數從高到低)。git
3.每一個問題有加票和減票功能,在點擊的同時,將問題按照投票數從高到低排序。程序員
實現過程:github
1、開發環境和工具npm
1.npm init (生成package.json文件) (按照嚮導填寫各個字段,最後生成 package.json 文件。編程
容易出錯的是: name的值不要和包包同名 。json
好比咱們後續須要使用npm安裝幾個包包:browserify react reactify ...則name值若是寫做「browserify」或「react」,此依賴會安裝失敗!redux
提示以下:
npm WARN install Refusing to install react as a dependency of itself)
2.npm install react --save npm install react-dom --save (最開始就是沒有安裝react-dom,因此一直渲染不出來)
3.npm install -g gulp
4.npm install --save-dev gulp gulp-browserify gulp-concat gulp-react gulp-connect lodash reactify
(這裏注意一下,npm install --save 與 npm install --save-dev 的區別,一個放在package.json 的dependencies , 一個放在devDependencies裏面,產品模式用dependencies,開發模式用devDep。)
5.bower init (生成bower.json文件)
6.bower install bootstrap --save
7.新建app文件夾,再在下面建一個js文件夾,建立main.js
8.新建dist文件(壓縮後的文件放的地方)
9.建立gulpfile.js
2、代碼開發
gulpfile.js內容以下:
1 var gulp = require('gulp'), 2 connect = require('gulp-connect'), 3 browserify = require('gulp-browserify'), 4 concat = require('gulp-concat'), 5 port = process.env.port || 5000; 6 7 gulp.task('browserify',function(){ 8 gulp.src('./app/js/main.js') 9 .pipe(browserify({ 10 transform: 'reactify', 11 })) 12 .pipe(gulp.dest('./dist/js')) 13 }); 14 15 // live reload 16 gulp.task('connect',function(){ 17 connect.server({ 18 // root:'./', 19 port: port, 20 livereload: true 21 }) 22 }) 23 24 // reload Js 25 gulp.task('js',function(){ 26 gulp.src('./dist/**/*.js') 27 .pipe( connect.reload() ) 28 }) 29 30 gulp.task('html',function(){ 31 gulp.src('./app/**/*.html') 32 .pipe( connect.reload() ) 33 }); 34 35 gulp.task('watch',function(){ 36 gulp.watch('./dist/**/*.js',['js']); 37 gulp.watch('./app/**/*.html',['html']); 38 gulp.watch('./app/js/**/*.js',['browserify']); 39 }) 40 41 gulp.task('default',['browserify']); 42 43 gulp.task('serve',['browserify','connect','watch']);
靜態頁面html:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf8"> 5 <title>React問答 app </title> 6 <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"> 7 <style> 8 .container{ 9 max-width: 800px; 10 } 11 .jumbotron .container{ 12 position: relative; 13 max-width: 800px; 14 } 15 #add-question-btn{ 16 position: absolute; 17 bottom: -20px; 18 right: 20px; 19 } 20 form[name="addQuestion"] .btn{ 21 margin: 20px 0 0 15px; 22 } 23 .media-left{ 24 text-align: center; 25 width:70px; 26 float: left; 27 } 28 .media-left .btn{ 29 margin-bottom: 10px; 30 } 31 .vote-count{ 32 display: block; 33 } 34 </style> 35 </head> 36 <body> 37 <div id="app"> 38 <div class="jumbotron text-center"> 39 <div class="container"> 40 <h1>React問答</h1> 41 <button id="add-question-btn" class="btn btn-success">添加問題</button> 42 </div> 43 </div> 44 <div class="main container"> 45 <form name="addQuestion" class="clearfix"> 46 <div class="form-group"> 47 <label for="qtitle">問題</label> 48 <input type="text" class="form-control" id="qtitle" placeholder="您的問題的標題"> 49 </div> 50 <textarea class="form-control" rows="3" placeholder="問題的描述"></textarea> 51 <button class="btn btn-success pull-right">確認</button> 52 <button class="btn btn-default pull-right">取消</button> 53 </form> 54 <div id="questions" class=""> 55 <div class="media"> 56 <div class="media-left"> 57 <button class="btn btn-default"> 58 <span class="glyphicon glyphicon-chevron-up"></span> 59 <span class="vote-count">22</span> 60 </button> 61 <button class="btn btn-default"> 62 <span class="glyphicon glyphicon-chevron-down"></span> 63 </button> 64 </div> 65 <div class="media-body"> 66 <h4 class="media-heading">產品經理與程序員矛盾的本質是什麼?</h4> 67 <p>理性探討,請勿撕逼。產品經理的主要工做職責是產品設計。接受來自其餘部門的需求,通過設計後交付研發。但這裏有好些職責不清楚的地方。</p> 68 </div> 69 </div> 70 71 <div class="media"> 72 <div class="media-left"> 73 <button class="btn btn-default"> 74 <span class="glyphicon glyphicon-chevron-up"></span> 75 <span class="vote-count">12</span> 76 </button> 77 <button class="btn btn-default"> 78 <span class="glyphicon glyphicon-chevron-down"></span> 79 </button> 80 </div> 81 <div class="media-body"> 82 <h4 class="media-heading">熱愛編程是一種怎樣的體驗?</h4> 83 <p>別人對玩遊戲感興趣,我對寫代碼、看技術文章感興趣;把泡github、stackoverflow、v2ex、reddit、csdn當作是興趣愛好;遇到重複的工做,總想着能不能經過程序實現自動化;喝酒的時候把寫代碼當下酒菜,邊喝邊想邊敲;不給工資我也會來加班;作夢都在寫代碼。</p> 84 </div> 85 </div> 86 87 </div> 88 89 </div> 90 </div> 91 92 <!-- 93 <script src="/dist/js/main.js"></script> --> 94 </body> 95 </html>
接下來的react代碼轉化,我就不詳細謝了,在這裏貼出個人項目文件展現:
index.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf8"> 5 <title>React問答 app </title> 6 <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"> 7 <style> 8 .container{ 9 max-width: 800px; 10 } 11 .jumbotron .container{ 12 position: relative; 13 max-width: 800px; 14 } 15 #add-question-btn{ 16 position: absolute; 17 bottom: -20px; 18 right: 20px; 19 } 20 form[name="addQuestion"] .btn{ 21 margin: 20px 0 0 15px; 22 } 23 .media-left{ 24 text-align: center; 25 width:70px; 26 float: left; 27 } 28 .media-left .btn{ 29 margin-bottom: 10px; 30 } 31 .vote-count{ 32 display: block; 33 } 34 </style> 35 </head> 36 <body> 37 <div id="app"> </div> 38 39 <script src="/dist/js/main.js"></script> 40 41 </body> 42 </html>
main.js
1 var React = require('react'); 2 var ReactDOM = require('react-dom'); 3 var QuestionApp = require('./components/QuestionApp.js'); 4 /* 模塊名可以使用相對路徑(以./開頭),或者是絕對路徑(以/或C:之類的盤符開頭)*/ 5 6 var mainCom = ReactDOM.render( 7 <QuestionApp />, 8 document.getElementById('app') 9 );
QuestionApp.js
1 var React = require('react'); 2 var ShowAddBtn = require('./ShowAddBtn.js'); 3 var QuestionForm = require('./QuestionForm.js'); 4 var QuestionList = require('./QuestionList.js'); 5 module.exports = React.createClass({ 6 getInitialState: function(){ 7 var questions = [ 8 { 9 key: 1, 10 title: '產品經理與程序員矛盾的本質是什麼?', 11 description: '理性探討,請勿撕逼。產品經理的主要工做職責是產品設計。接受來自其餘部門的需求,通過設計後交付研發。但這裏有好些職責不清楚的地方。', 12 voteCount: 2 13 }, 14 { 15 key: 2, 16 title: '熱愛編程是一種怎樣的體驗?', 17 description: '別人對玩遊戲感興趣,我對寫代碼、看技術文章感興趣;把泡github、stackoverflow、v2ex、reddit、csdn當作是興趣愛好;遇到重複的工做,總想着能不能經過程序實現自動化;喝酒的時候把寫代碼當下酒菜,邊喝邊想邊敲;不給工資我也會來加班;作夢都在寫代碼。', 18 voteCount: 3 19 } 20 ]; 21 questions = this.questionSort(questions); //sort the init questions 22 return { 23 displayForm: false, 24 questions: questions 25 }; 26 }, 27 onToggleForm: function(){ 28 this.setState({ 29 displayForm: !this.state.displayForm 30 }); 31 }, 32 onQuestionNew: function(newQuestion){ 33 newQuestion.key = this.state.questions.length + 1; 34 var newQuestions = this.state.questions.concat(newQuestion); 35 newQuestions = this.questionSort(newQuestions); 36 this.setState({ 37 questions: newQuestions 38 }); 39 }, 40 questionSort: function(questions){ 41 questions.sort(function(a,b){ 42 return b.voteCount - a.voteCount; 43 }); 44 return questions;//以前一直報錯,這裏忘記return 45 }, 46 onVote: function(nowKey, newVoteCount){ 47 var questions = this.state.questions; 48 var index = 0; 49 for(var i=0; i<questions.length; i++){ 50 if(questions[i].key == nowKey){ 51 index = i; 52 } 53 } 54 questions[index].voteCount = newVoteCount; 55 questions = this.questionSort(questions); 56 this.setState({ 57 questions: questions 58 }); 59 }, 60 render: function(){ 61 return ( 62 <div> 63 <div className="jumbotron text-center"> 64 <div className="container"> 65 <h1>React問答</h1> 66 <ShowAddBtn onToggleForm={this.onToggleForm}/> 67 </div> 68 </div> 69 <div className="main container"> 70 <QuestionForm onQuestionNew={this.onQuestionNew} displayForm={this.state.displayForm} onToggleForm={this.onToggleForm}/> 71 <QuestionList onVote={this.onVote} questions={this.state.questions}/> 72 </div> 73 </div> 74 ) 75 } 76 });
ShowAddBtn.js
1 var React = require('react'); 2 module.exports = React.createClass({ 3 render: function(){ 4 return ( 5 <button id="add-question-btn" className="btn btn-success" onClick={this.props.onToggleForm}>添加問題</button> 6 ) 7 } 8 });
QuestionForm.js
1 var React = require('react'); 2 module.exports = React.createClass({ 3 handleSubmit: function(e){ 4 e.preventDefault(); 5 var newQuestion = { 6 title: this.refs.title.value, 7 description: this.refs.description.value, 8 voteCount: 0 9 }; 10 this.refs.questionForm.reset(); 11 this.props.onQuestionNew(newQuestion); 12 }, 13 render: function(){ 14 return ( 15 <form name="addQuestion" className="clearfix" ref="questionForm" 16 style={{display: this.props.displayForm ? 'block' : 'none'}} 17 onSubmit={this.handleSubmit}> 18 <div className="form-group"> 19 <label htmlFor="qtitle">問題</label> 20 <input ref="title" type="text" className="form-control" id="qtitle" placeholder="您的問題的標題"/> 21 </div> 22 <textarea ref="description" className="form-control" rows="3" placeholder="問題的描述"></textarea> 23 <button className="btn btn-success pull-right">確認</button> 24 <a className="btn btn-default pull-right" onClick={this.props.onToggleForm}>取消</a> 25 </form> 26 ) 27 } 28 });
QuestionList.js
1 var React = require('react'); 2 var QuestionItem = require('./QuestionItem.js'); 3 module.exports = React.createClass({ 4 render: function(){ 5 var questions = this.props.questions; 6 var _this = this;//這裏的this要單獨保存,不然下面的map中的this指的是循環的每一個對象 7 var questionComps = questions.map(function(qst){ 8 return <QuestionItem 9 key={qst.key} 10 questionKey={qst.key} 11 title={qst.title} 12 description={qst.description} 13 voteCount={qst.voteCount} 14 onVote={_this.props.onVote}/> 15 }); 16 //開始一直報錯,是由於這裏,render裏面,return(), 沒有寫最外層div 17 return ( 18 <div id="questions" className=""> 19 {questionComps}/*直接放個數組在這裏,他會自動去循環*/ 20 </div> 21 ) 22 } 23 });
QuestionItem.js
1 var React = require('react'); 2 module.exports = React.createClass({ 3 voteUp: function(){ 4 var newVoteCount = parseInt(this.props.voteCount, 10) + 1; 5 //this.props.questionKey這裏必須重 6 // 新定義一個questionKey屬性, 不能this.props.key 7 this.props.onVote(this.props.questionKey, newVoteCount); 8 }, 9 voteDown: function(){ 10 var newVoteCount = parseInt(this.props.voteCount, 10) - 1; 11 this.props.onVote(this.props.questionKey, newVoteCount); 12 }, 13 render: function(){ 14 return ( 15 <div className="media"> 16 <div className="media-left"> 17 <button className="btn btn-default" onClick={this.voteUp}> 18 <span className="glyphicon glyphicon-chevron-up"></span> 19 <span className="vote-count">{this.props.voteCount}</span> 20 </button> 21 <button className="btn btn-default" onClick={this.voteDown}> 22 <span className="glyphicon glyphicon-chevron-down"></span> 23 </button> 24 </div> 25 <div className="media-body"> 26 <h4 className="media-heading">{this.props.title}</h4> 27 <p>{this.props.description}</p> 28 </div> 29 </div> 30 ) 31 } 32 33 });
到這裏,全部代碼就完成了,在命令行裏面執行gulp serve命令,而後在瀏覽器中訪問localhost:5000。
到此整個小demo就算完成了。對於react-router, react redux等深刻知識點還在進一步學習過程當中,歡迎你們提出問題,一塊兒討論。