这是第一次写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(React适合做SPA),所以不好每次都取加载一次内容,所以就不用引擎模板,这些数据如何放入state让react用diff算法自己计算呢?怎样变成单页面应用呢?

这时候我想到就是ajax;上网google一下,发现用fetch能实现像ajax那样的请求。同一个component可以很容易实现fetch数据改变this的state。

详情更改请看我的这个commit:add client store to fetch movie's data from server

这时候发现不知道怎么通过点击侧边栏标签,渲染新页面(注意:侧边栏是一个组件,而右边的电影列表又是一个组件,他们是两个不同的组件

使用React-Router

由于react规定父元素只能改变子元素,但是不好将子元素改变父元素;

一般我们都会把许多内容都搞到最顶级那个父元素的state,这样其他都有可能与他有关联,而子元素改变父元素的state的方法就是通过回调setState;

所以这里我们可以将movies放在最顶的component,然后点击标签,就去回调去改变这个父元素的state,用到这个state的子元素就会刷新。

代码详见这个commit

但是这样太hacky了,违背react理念,代码难理解;

有没有其他更好的办法呢?

Google查找答案发现有两种方法:

  1. 使用react-router

  2. 如果不是用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'
}] }
}

事实上使用react react-router已经足够完成功能,但是考虑到如果之后想要扩展,例如现在我又要增加用户注册,登录页面;那么很有可能随着页面组件增多,这个项目将会变成意大利面;

使用Redux

这时候应该引入redux;

所以我又npm install react-redux, redux;

关于redux,其实应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。惟一改变 state 的办法是触发 action,一个描述发生什么的对象。

为了描述 action 如何改变 state 树,你需要编写 reducers

通过写reducer,可以很好的管理actions,这样更加条理。可以看我的redux目录

  • actions:用来fetch后台数据或者直接返回改变的内容
  • reducers:通过actions返回的数据,改变state
  • constants:每个actions的常量名
  • configureStore.js:通过传入reducers和初始的states,返回store(包含改变的states)

然后前台app.js渲染之前,必须先通过<Provide>传入store;

const store = configureStore(window.__INITIAL_STATE__)

/* 这里store存储某一时刻的state树,所以这里面通过action得到的states是不一样的
* 例如:如果一开始给tags,后面action没有给tags,则不会有原来的初始值tags,所以不用担心数据冗余。
*/ ReactDOM.render(
<Provider store={store} >
<Router routes={routes} history={browserHistory} />
</Provider>,
document.getElementById('app')

后台呢?也要相应根据么次fetch数据返回新的store。

      const store = configureStore(initialState)
const state = store.getState()
const params = Object.assign(req.query, renderProps.params) fetchComponentDataBeforeRender(store.dispatch, renderProps.components, params)
.then(() => {
// 这里redux的store给Provider,再通过mapStateToProps给对应的容器组件
const html = renderToString(
<Provider store={store}>
<RouterContext {...renderProps} />
</Provider>
)

有了后台传回来新的store,如何应用到前台组件呢?

就在组件中引入connect:

import { connect } from 'react-redux'
class MoviesGridList extends React.Component {
... ....
// 将store最新的state.movies给props,
function mapStateToProps(state, props) {
let tags = props.location.query.tags
let movies
if (tags === undefined) {
movies = state.movies
} else {
movies = getMoviesByTag(state, tags)
}
return {
tags: tags,
movies: movies
}
} export default connect(mapStateToProps)(MoviesGridList)

本文内容有待更新,具体代码和问题详见Github仓库的commit

React+Node初尝试的更多相关文章

  1. R语言爬虫初尝试-基于RVEST包学习

    注意:这文章是2月份写的,拉勾网早改版了,代码已经失效了,大家意思意思就好,主要看代码的使用方法吧.. 最近一直在用且有维护的另一个爬虫是KINDLE 特价书爬虫,blog地址见此: http://w ...

  2. React+Node.js+Express+mongoskin+MongoDB

    首发:个人博客,更新&纠错&回复 采用React + Node.js + Express + mongoskin + MongoDB技术开发的一个示例,演示地址在这里,项目源码在这里. ...

  3. React + Node 单页应用「二」OAuth 2.0 授权认证 & GitHub 授权实践

    关于项目 项目地址 预览地址 记录最近做的一个 demo,前端使用 React,用 React Router 实现前端路由,Koa 2 搭建 API Server, 最后通过 Nginx 做请求转发. ...

  4. SQLSERVER2012里的扩展事件初尝试(下)

    SQLSERVER2012里的扩展事件初尝试(下) SQLSERVER2012里的扩展事件初尝试(上) 我们继续文章扩展事件在Denali CTP3里的新UI(二)里的这个实验 脚本文件下载:http ...

  5. SQLSERVER2012里的扩展事件初尝试(上)

    SQLSERVER2012里的扩展事件初尝试(上) SQLSERVER2012里的扩展事件初尝试(下) 周未看了这两篇文章: 扩展事件在Denali CTP3里的新UI(一) 扩展事件在Denali ...

  6. react + node + express + ant + mongodb 的简洁兼时尚的博客网站

    前言 此项目是用于构建博客网站的,由三部分组成,包含前台展示.管理后台和后端. 此项目是基于 react + node + express + ant + mongodb 的,项目已经开源,项目地址在 ...

  7. MERN——MongoDB && React && Node && Express

    原文链接:Let’s build a full stack MongoDB, React, Node and Express (MERN) app github源码地址:jelorivera08/re ...

  8. [浅学] 1、Node.js尝试_安装&运行第一个helloworld

    官网:https://nodejs.org/ 介绍:Node.js® is a platform built on Chrome's JavaScript runtime for easily bui ...

  9. codefirst初尝试

    Code First 约定 借助 CodeFirst,可通过使用 C# 或Visual Basic .NET 类来描述模型.模型的基本形状可通过约定来检测.约定是规则集,用于在使用 Code Firs ...

随机推荐

  1. Swift 实现俄罗斯方块详细思路解析(附完整项目)

    一:写在开发前 俄罗斯方块,是一款我们小时候都玩过的小游戏,我自己也是看着书上的思路,学着用 Swift 来写这个小游戏,在写这个游戏的过程中,除了一些位置的计算,数据模型和理解 Swift 语言之外 ...

  2. MongoDB学习总结(四) —— 索引的基本用法

    说到索引,大家肯定都在关系型数据库或多或少接触过,它的主要目的是加速查询的速度.MongoDB作为一种数据库,当然也提供了索引的操作. 我们先插入1万条测试数据. 首先,我们先来看看不加索引查找nam ...

  3. shell监控网卡流量

     #!/bin/bashRx=`ifconfig  eno16777736 | grep RX | grep packets | awk '{print $5}'`Tx=`ifconfig  eno1 ...

  4. git链接GitHub命令及基本操作

    Git是一款不错的代码管理工具,下面引用百科的一段话:  Git是用于Linux内核开发的版本控制工具.与CVS.Subversion一类的集中式版本控制工具不同,它采用了分布式版本库的作法,不需要服 ...

  5. BZOJ 3402: [Usaco2009 Open]Hide and Seek 捉迷藏(最短路)

    这个= =一看就是最短路了= = PS:最近有点懒 = = 刚才看到一道平衡树的裸题还嫌懒不去写= =算了等刷完这堆水题再去理= = CODE: #include<cstdio>#incl ...

  6. JNI调用的helloworld(JNI_OnLoad映射方式)

    本示例展示JNI的基本示例,helloworld级别的,不过是用JNI_OnLoad映射的方式. 直接看代码,先看包含native method的Person.java的代码: package hel ...

  7. 【Unity优化】Unity优化技巧进阶开篇

    版权声明:本文为博主原创文章,欢迎转载.请保留博主链接:http://blog.csdn.net/andrewfan 做游戏好多年了,关于游戏优化一直是令开发者头疼的一个问题.因为优化牵扯的内容很多, ...

  8. 如何用一张图片代替 'input:file' 上传本地文件??

    今天去面试,碰到了一道题,也许是因为紧张或者喝水喝多了,一时竟然没有转过弯来,回来之后一细想原来这么简单,哭笑不得,特此记录一下! 原题是这样的:  如何用一张图片代替 'input:file' 上传 ...

  9. 大型ERP系统在线体验

    ERP简单说明: AIO7构建了基于SOA三层架构的管理软件平台.客户通过网络即可得到ERP服务,不用安装服务器.不用建立数据中心.不用安装软件.无需专业IT支持,任何上网设备就可以使用高性能.功能集 ...

  10. 徒手用Java来写个Web服务器和框架吧<第一章:NIO篇>

    因为有个不会存在大量连接的小的Web服务器需求,不至于用上重量级服务器,于是自己动手写一个服务器. 同时也提供了一个简单的Web框架.能够简单的使用了. 大体的需求包括 能够处理HTTP协议. 能够提 ...