美文网首页程序员让前端飞Web
react+redux实战(二)----搭建express并将应

react+redux实战(二)----搭建express并将应

作者: RichardBillion | 来源:发表于2016-10-01 21:06 被阅读5161次

    上一篇文章:
    react+redux实战(一)----基本流程

    主要完成了基本页面的搭建,但是对于数据的交互还并不支持,因此为了构建一个完整的数据流,开始尝试将应用连接到数据库。

    数据库选型(可以略过)

    选型最基本的要求就是可用js操作。在选型阶段发现了wilddog(野狗),以简单的实时通信著称。

    野狗确实特别合适flux,flux是为了给react或者其他前端框架做全局数据同步用的,野狗做的事情恰好就是各个客户端和服务器的数据同步,试想一下,我在手机上点了一个赞,更新了本地数据,然后本地数据自动同步了线上数据,线上数据又自动同步了你手机上的数据,然后state的改变触发重绘,在你的界面上弹出一个小红点。而这一系列的数据比对,传递,同步,重绘都是野狗和框架自动完成的。(摘自知乎)

    而且它是也是js操作,使用事件方式来对数据库进行操作,而且数据库是以后台云方式存在的,如果只是构建react的完整数据流,完全可以不用后台,只搭配野狗就实现。我又没用野狗,还说这么多是想表达什么呢?感慨这实在是简单,用过的朋友希望能介绍下经验,后边有机会想尝试一把敏捷开发。

    言归正传。

    目前前端的主流方法还是利用ajax或者fetch这种方式向指定api请求数据,然后router处理请求返回数据,如果使用野狗提供的方法,可以将这一步和对数据库的读写操作综合为一步。确实高效快捷,然而这次本着学习的目的,想一探web应用的整个流程,还是选择走主流,所以这次选择使用mongodb,然后使用express作为后台。

    将webpack-dev-server集成到整个web应用

    既然选定后台使用express作为自己的web服务器,那么第一步就需要将webpack dev server集成进来,否则就不能使用webpack提供的模块实时打包和热加载功能。最简单的方式如下:

    就是在入口html文件载入打包的js文件时指定完整的url地址,如

    <script src="http://127.0.0.1:3000/assets/bundle.js"></script>
    

    告诉页面应该去开发服务器地址获得脚本资源文件。使用过程中发现,bundle.js文件是webpack会打包生成出来的,如果应用中就只有这个一个输出,没有chunks的话,这样集成是可以的。但是要知道 webpack dev server 把编译后的静态文件是保存在内存里的,如果使用按需加载,就会找不到这些chunkfiles,抽取的公共文件也会找不到,所以我们不得不继续前行。

    express本身就是一系列middleware的集合,而webpack-dev-server就是一个小型的express服务器,它就是用的webpack-dev-middleware来处理webpack编译后的输出,所以,我们在express中使用webpack的开发工具:webpack-dev-middlewarewebpack-hot-middleware。webpack-hot-middleware是结合webpack-dev-middleware使用的,用来实现热更新。

    使用前记得安装。dev模式下,webpack的配置如下,只需注意注释地方(其它地方不用关注):

    'use strict';
    let path = require('path');
    let webpack = require('webpack');
    let baseConfig = require('./base');
    let defaultSettings = require('./defaults');
    
    // Add needed plugins here,reload为true意思是,如果遇到不能hot load 的情况,就整页刷新
    var hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true';
    let config = Object.assign({}, baseConfig, {
      entry: [
        './src/index',
       //入口文件修改,原来对应webpack-dev-server的是
      //'webpack-dev-server/client?http://0.0.0.0:8000', 
      //'webpack/hot/only-dev-server',改为如下:
        hotMiddlewareScript
      ],
      cache: true,
      devtool: 'eval-source-map',
      plugins: [
        //添加下面3个插件,这个原来应该也有,如果没有就加上
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin()
      ],
      module: defaultSettings.getDefaultModules()
    });
    
    config.module.loaders.push({
      test: /\\.(js|jsx)$/,
      loader: 'react-hot!babel-loader',
      include: [].concat(
        config.additionalPaths,
        [ path.join(__dirname, '/../src') ]
      )
    });
    
    module.exports = config;
    

    note:就只有注释的几个地方需要注意修改,webpack原来如何配置的,其它地方维持不变就行。

    接下来在express的启动文件中配置(为方便,贴出完整代码,关于配置主要是if判断中的语句):

    var express=require('express');
    
    var routes=require('./routes/index');
    var app=express();
    
    //nodeJs模板语言,选用ejs(需要安装),如下配置可正常使用.html文件作为入口
    app.engine('.html', require('ejs').__express);
    //change the template main catelog
    app.set('views',__dirname+'/src');
    app.set('view engine','html')
    
    var isDev = process.env.NODE_ENV !== 'production';
    
    if(isDev){
        const webpack = require('webpack');
        const webpackDevMiddleware = require('webpack-dev-middleware');
        const webpackHotMiddleware = require('webpack-hot-middleware');
        const config = require('./webpack.config');
    
        const compiler=webpack(config);
    
        app.use(webpackDevMiddleware(compiler,{
            publicPath:config.output.publicPath,
            noInfo:true,
            stats:{
                colors:true
            }
        }));
        app.use(webpackHotMiddleware(compiler))
          //设置静态资源地址
        app.use(express.static(__dirname+'/public'));
    
        app.use('/',routes);
    
        app.listen(3000,function(){
            console.log("runing")
        })
    }
    

    note:两个middleware应在routes之前配置。
    至此,webpack-dev-server整合到了nodeJs后台。

    使用mongodb数据库

    mongodb我们都听过,但是使用时,我们安装和使用的是mongoose,这是一个提供了和mongodb相映射的nodeJs库,它可以将数据库中的数据类型转换为js对象供我们在应用中使用。安装mongoose(npm install mongoose)之前要首先安装好mongodb(brew install mongodb)。

    向express启动文件中添加

    var mongoose=require('mongoose');
    //导入定义的模型
    global.dbHandle=require('./models/haddledb.js');
    //连接数据库,默认端口号是27017,mediumReact是自己的数据库名称
    global.db=mongoose.connect('mongodb://localhost:27017/mediumReact');
    

    models/haddledb.js文件中是定义好的模型

    var mongoose=require('mongoose');
    var Schema=mongoose.Schema;
    //定义一个Schema
    var ArticlesSchema=new Schema({
        id:{type:Number},
        title:{type:String},
        genre:{type:Number},
        source:{type:String},
        praise_count:{type:Number},
        comment_count:{type:Number},
        publish_time:{type:Date},
        banner_pic:{type:String}
    });
    //定义一个model
    var ArticlesModel=mongoose.model("Articles",ArticlesSchema);
    

    主要就是理解Schema定义了文档的结构,是数据库的骨架,不具备操作数据库的能力;Model是由Schema生成的模型,具备操作数据库能力;Entity是Model生成的实体,其操作也能影响数据库。一般我们都是操作model。

    那么现在的当务之急,就是向数据库中添加一些数据,mongoose提供的CRUD方法也很简洁:

    //可以使用model创建一个实体
    ArticlesEntity=new ArticlesModel({
        "id": 2001,
        "title": "生活不是等待暴风雨过去而是让我们学会在雨中翩翩起舞,生活不是等待暴风雨过去而是让我们学会在雨中翩翩起舞。",
        "genre": 1,
        "source": "片刻",
        "praise_count": 234,
        "comment_count": 65,
        "publish_time": "2016-08-10 14:08:36 +0800",
        "banner_pic": "/images/grid-article-banner.jpg"
    });
    //然后保存到数据库
    ArticlesEntity.save();
    

    但是,对于我们初来乍到的人来说,最好的还是可视化工具,我使用的是Robomongo,稍微看两下其菜单,就能知道如何操作了。

    建立路由api,完善请求流程

    新闻列表页请求数据的antion:

    import fetch from 'isomorphic-fetch'
    import { createActions } from 'redux-actions';
    
    export const { fetchArticles } = createActions({
        FETCH_ARTICLES: async () => {
            try {
                //express后台中需要建立'/articles'路由,来处理请求数据
                let response = await fetch('/articles');
                let articles = await response.json();
    
                return {  articles }
            } catch (err) {
                console.log(err);
            }
        }
    });
    

    对应的reducer:

    import { handleActions } from 'redux-actions';
    import { FETCH_ARTICLES } from '../actions/index.js';
    
    export default handleActions({
        FETCH_ARTICLES: (state, action) => {
            let payload = action.payload;
    
            return {...state,isFecting:false,articles:payload.articles}
        }
    }, {});
    

    express的routes文件:

    var express=require('express');
    var router=express.Router();
    var mongoose=require('mongoose');
    var articles=mongoose.model('Articles');
    
    //get home page,因为使用了react-router来处理处理做单页应用,在express中我们就只用给其一个入口路径
    router.get('/',function(req,res,next){
        res.render('index',{title:"medium-react"});
    });
    //列表页get数据的请求地址
    router.get('/articles',function(req,res,next){
            //使用find()方法有点简单了,因为首页基本会涉及分页,这个以后再改进
        articles.find({},function(err,results){
            if(err){
                console.log('error message',err);
                return;
            }
            res.json(results);
        })
    });
    module.exports=router;
    

    这个routes文件是在express的启动文件中使用的:

    var routes=require('./routes/index');
    app.use('/',routes);
    

    写在最后

    至此,整个web应用就已经搭建完成,这一章让我体验了一把完整的web应用的基本流程,感觉不错。但是遗留问题也还很多,比如连接数据库的初衷,就是更好的观察、处理异步交互,然而在文章详情页,整个结构树太深

    用工具画图好累,就手绘凑合看吧。。

    目前只在文章详情页connect到数据库,也就意味着dispatch方法是在这个容器中获取的,但是对于评论中的点赞事件可能发生在主回复Comment组件中,也可能是从回复Reply组件中,如果将dispatch 方法一层层传递下去,不仅路径深,而且中间都不曾用到该方法。或许也可以这样:点赞之后将数据传递(比如观察者模式)到container ,然后dispatch这个请求。你还有什么好的方法么?

    下一节先实现最基础的异步交互:

    react+redux实战(三)----异步交互

    【主要参考资源】
    Express结合Webpack的全栈自动刷新
    express使用指南
    Mongoose学习参考文档

    相关文章

      网友评论

        本文标题:react+redux实战(二)----搭建express并将应

        本文链接:https://www.haomeiwen.com/subject/jclhyttx.html