这是第一次写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