這是第一次寫React和Node,選用的是前端Material-ui框架,后端使用的是Express框架,數據庫采用的是Mongodb。
項目代碼在:GitHub/lilu_movie , 歡迎大家關注或提問題。
這是一個通過從電影天堂抓取數據並顯示的電影網站,demo部署在heroku上面。
安裝:
首先安裝express框架;
npm install express --save
生成文件后,可以通過npm start啟動應用。
注意:ejs 從3.x后不支持layout,可以通過express-partials ,但是不支持4,4之后用include
緊接着我迫不及待安裝material-ui:
npm install material-ui --save
然后出現錯誤:
所以必須安裝react依賴:
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