這是第一次寫React和Node,選用的是前端Material-ui框架,後端使用的是Express框架,數據庫採用的是Mongodb。css
項目代碼在:GitHub/lilu_movie , 歡迎你們關注或提問題。html
這是一個經過從電影天堂抓取數據並顯示的電影網站,demo部署在heroku上面。前端
安裝:node
首先安裝express框架;react
npm install express --save
生成文件後,能夠經過npm start啓動應用。webpack
注意:ejs 從3.x後不支持layout,能夠經過express-partials ,可是不支持4,4以後用includegit
緊接着我火燒眉毛安裝material-ui:es6
npm install material-ui --save
而後出現錯誤:github
因此必須安裝react依賴:web
npm install react@^15.0.0 --save npm install react-dom@^15.0.0 --save npm install react-tap-event-plugin@^1.0.0 --save
安裝nodemon
nodemon ./bin/www #更改會自動重啓服務
本地安裝數據庫mongodb
而後npm安裝操做mongodb的mongoose
npm install mongoose npm install express-mongoose
接着你會發現按照material-ui的import引入報錯,
使用es6查看系統支持哪些es6語法
npm install es6-checker
由於react使用es6和jsx語法,因此須要轉化,安裝以下包:
npm install babel-loader babel-core babel-preset-es2015 —save-dev npm install jsx-loader —save-dev npm install babel-preset-react
安裝webpack
npm install webpack —save-dev npm install css-loader —save-dev npm install webpack-dev-server —save-dev
這裏有必要提一下:-save-dev表明安裝的包適用於開發的,相似於rails中安裝Gem放在:development環境下,這樣生產環境就不會安裝。
而後在webpack配置文件中babels的loaders中query加入presets
由於須要一些css文件,react經過require style文件,須要安裝
npm install style-loader —save-dev npm install css-loader —save-dev // 這個和style一塊兒用纔有效果
在webpack中的config 加上loader: "style-loader!css-loader」,就不用require使用style!css!了
啓動腳本:
配置package.json文件,給script添加命令
"start": ["node ./bin/www", "webpack」],
編寫webpack.config.js配置文件,更改html引入文件
在webpack-dev-server 沒有真正生成文件,還得要引入<script src=「localhost:8080/assets/bundle.js"></script>
運行npm run dev,看webpack-dev-server效果
Express後端流程改變:
剛開始,我用一向的後臺思路經過routes渲染頁面,頁面html引入react的js文件,reactjs文件link後臺js響應;後臺相應經過鏈接mongodb獲取數據庫內容。
很成功,獲取到相應的內容了,可是由於使用react,因此很差每次都取加載一次內容,而後又不用引擎模板,這些數據如何放入state讓react用diff算法本身計算呢?怎樣變成單頁面應用呢?
而後我想到就是ajax;上網google一下,發現用fetch能實現像ajax那樣的請求。同一個component能夠很容易實現fetch數據改變this的state。
這時候發現不知道怎麼經過點擊標籤,渲染新頁面,
因爲react規定父元素只能改變子元素,可是很差將子元素改變父元素;
通常咱們都會把許多內容都搞到最頂級那個父元素的state,這樣其餘都有可能與他有關聯,而子元素改變父元素的state的方法就是經過回調setState;
因此這裏咱們能夠將movies放在最頂的component,而後點擊標籤,就去回調去改變這個父元素的state,用到這個state的子元素就會刷新。
代碼詳見這個commit
可是這樣太hacky了,違背react理念,代碼難理解;
有沒有其餘更好的辦法呢?
Google查找答案發現有兩種方法:
使用react-router
若是不是用react-router,則得這樣寫https://github.com/ReactTraining/react-router/blob/master/docs/Introduction.md
react-route根據history傳入的連接,找到你對應routes的component,而後改變children,成功渲染改組件。
對於不一樣組件改變同個內容仍是使用react-router
使用react-router發現client端經過router的連接,局部更新內容;
這樣子說,徹底不須要server後臺每一個路由每次渲染不一樣頁面了,只須要server不一樣連接給出不一樣內容,而後渲染同一個頁面,這個頁面經過react-router去改變內容便可。
因此刪除後端全部router路由;
按照React Router官方教程實現相應代碼。
這時候發現一個問題:
渲染同一個頁面就要在後端引入前端的routes,也就須要到es6了,可是以前後端沒有經過webpack進行es6的轉化,因此還要對後端的入口文件進行webpack轉化。
對後端server.js進行webpack的bundle後,很容易報錯,首先要在web pack中排除掉node_module的文件,而後須要引入各類loader;
server端要import client端的routes過來,可是route的component會引用相應的component。若是遇到client的內容,有些react-router/server是處理不了的,會報沒有window錯誤。
如下是最終的webpack.server.config.js
var webpack = require("webpack"); var fs = require("fs"); var path = require("path"); module.exports = { entry: [ path.resolve(__dirname, 'server.js') ], output: { filename: 'server.bundle.js' }, target: 'node', externals: fs.readdirSync(path.resolve(__dirname, 'node_modules')).concat([ 'react-dom/server', 'react/addons', ]).reduce(function (ext, mod) { ext[mod] = 'commonjs ' + mod return ext }, {}), node: { __filename: true, __dirname: true }, module: { loaders: [ { test: /\.css$/, loader: "style-loader!css-loader" }, { test: /\.js$/, exclude: /node_modules/, loader: "babel", query: { presets: ['react', 'es2015'] } }, { test: /.json$/, loader: 'json-loader' }, { test: /.node$/, loader: 'node-loader' }] } }
本文內容有待更新,具體代碼和問題詳見Github倉庫的commit