React+Node初尝试

这是第一次写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查找答案发现有两种方法:

 

  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'
      }]

  }
}
复制代码

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

posted @ 2017-03-29 12:51  最骚的就是你  阅读(1964)  评论(0编辑  收藏  举报