react+redux實(shí)戰(zhàn)(二)----搭建express并將應(yīng)用連接到數(shù)據(jù)庫mongodb

上一篇文章:
react+redux實(shí)戰(zhàn)(一)----基本流程

主要完成了基本頁面的搭建,但是對于數(shù)據(jù)的交互還并不支持,因此為了構(gòu)建一個完整的數(shù)據(jù)流,開始嘗試將應(yīng)用連接到數(shù)據(jù)庫。

數(shù)據(jù)庫選型(可以略過)

選型最基本的要求就是可用js操作。在選型階段發(fā)現(xiàn)了wilddog(野狗),以簡單的實(shí)時通信著稱。

野狗確實(shí)特別合適flux,flux是為了給react或者其他前端框架做全局?jǐn)?shù)據(jù)同步用的,野狗做的事情恰好就是各個客戶端和服務(wù)器的數(shù)據(jù)同步,試想一下,我在手機(jī)上點(diǎn)了一個贊,更新了本地?cái)?shù)據(jù),然后本地?cái)?shù)據(jù)自動同步了線上數(shù)據(jù),線上數(shù)據(jù)又自動同步了你手機(jī)上的數(shù)據(jù),然后state的改變觸發(fā)重繪,在你的界面上彈出一個小紅點(diǎn)。而這一系列的數(shù)據(jù)比對,傳遞,同步,重繪都是野狗和框架自動完成的。(摘自知乎)

而且它是也是js操作,使用事件方式來對數(shù)據(jù)庫進(jìn)行操作,而且數(shù)據(jù)庫是以后臺云方式存在的,如果只是構(gòu)建react的完整數(shù)據(jù)流,完全可以不用后臺,只搭配野狗就實(shí)現(xiàn)。我又沒用野狗,還說這么多是想表達(dá)什么呢?感慨這實(shí)在是簡單,用過的朋友希望能介紹下經(jīng)驗(yàn),后邊有機(jī)會想嘗試一把敏捷開發(fā)。

言歸正傳。

目前前端的主流方法還是利用ajax或者fetch這種方式向指定api請求數(shù)據(jù),然后router處理請求返回?cái)?shù)據(jù),如果使用野狗提供的方法,可以將這一步和對數(shù)據(jù)庫的讀寫操作綜合為一步。確實(shí)高效快捷,然而這次本著學(xué)習(xí)的目的,想一探web應(yīng)用的整個流程,還是選擇走主流,所以這次選擇使用mongodb,然后使用express作為后臺。

將webpack-dev-server集成到整個web應(yīng)用

既然選定后臺使用express作為自己的web服務(wù)器,那么第一步就需要將webpack dev server集成進(jìn)來,否則就不能使用webpack提供的模塊實(shí)時打包和熱加載功能。最簡單的方式如下:

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

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

告訴頁面應(yīng)該去開發(fā)服務(wù)器地址獲得腳本資源文件。使用過程中發(fā)現(xiàn),bundle.js文件是webpack會打包生成出來的,如果應(yīng)用中就只有這個一個輸出,沒有chunks的話,這樣集成是可以的。但是要知道 webpack dev server 把編譯后的靜態(tài)文件是保存在內(nèi)存里的,如果使用按需加載,就會找不到這些chunkfiles,抽取的公共文件也會找不到,所以我們不得不繼續(xù)前行。

express本身就是一系列middleware的集合,而webpack-dev-server就是一個小型的express服務(wù)器,它就是用的webpack-dev-middleware來處理webpack編譯后的輸出,所以,我們在express中使用webpack的開發(fā)工具:webpack-dev-middlewarewebpack-hot-middleware。webpack-hot-middleware是結(jié)合webpack-dev-middleware使用的,用來實(shí)現(xiàn)熱更新。

使用前記得安裝。dev模式下,webpack的配置如下,只需注意注釋地方(其它地方不用關(guān)注):

'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',
   //入口文件修改,原來對應(yīng)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個插件,這個原來應(yīng)該也有,如果沒有就加上
    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的啟動文件中配置(為方便,貼出完整代碼,關(guān)于配置主要是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))
      //設(shè)置靜態(tài)資源地址
    app.use(express.static(__dirname+'/public'));

    app.use('/',routes);

    app.listen(3000,function(){
        console.log("runing")
    })
}

note:兩個middleware應(yīng)在routes之前配置。
至此,webpack-dev-server整合到了nodeJs后臺。

使用mongodb數(shù)據(jù)庫

mongodb我們都聽過,但是使用時,我們安裝和使用的是mongoose,這是一個提供了和mongodb相映射的nodeJs庫,它可以將數(shù)據(jù)庫中的數(shù)據(jù)類型轉(zhuǎn)換為js對象供我們在應(yīng)用中使用。安裝mongoose(npm install mongoose)之前要首先安裝好mongodb(brew install mongodb)。

向express啟動文件中添加

var mongoose=require('mongoose');
//導(dǎo)入定義的模型
global.dbHandle=require('./models/haddledb.js');
//連接數(shù)據(jù)庫,默認(rèn)端口號是27017,mediumReact是自己的數(shù)據(jù)庫名稱
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定義了文檔的結(jié)構(gòu),是數(shù)據(jù)庫的骨架,不具備操作數(shù)據(jù)庫的能力;Model是由Schema生成的模型,具備操作數(shù)據(jù)庫能力;Entity是Model生成的實(shí)體,其操作也能影響數(shù)據(jù)庫。一般我們都是操作model。

那么現(xiàn)在的當(dāng)務(wù)之急,就是向數(shù)據(jù)庫中添加一些數(shù)據(jù),mongoose提供的CRUD方法也很簡潔:

//可以使用model創(chuàng)建一個實(shí)體
ArticlesEntity=new ArticlesModel({
    "id": 2001,
    "title": "生活不是等待暴風(fēng)雨過去而是讓我們學(xué)會在雨中翩翩起舞,生活不是等待暴風(fēng)雨過去而是讓我們學(xué)會在雨中翩翩起舞。",
    "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"
});
//然后保存到數(shù)據(jù)庫
ArticlesEntity.save();

但是,對于我們初來乍到的人來說,最好的還是可視化工具,我使用的是Robomongo,稍微看兩下其菜單,就能知道如何操作了。

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

新聞列表頁請求數(shù)據(jù)的antion:

import fetch from 'isomorphic-fetch'
import { createActions } from 'redux-actions';

export const { fetchArticles } = createActions({
    FETCH_ARTICLES: async () => {
        try {
            //express后臺中需要建立'/articles'路由,來處理請求數(shù)據(jù)
            let response = await fetch('/articles');
            let articles = await response.json();

            return {  articles }
        } catch (err) {
            console.log(err);
        }
    }
});

對應(yīng)的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,因?yàn)槭褂昧藃eact-router來處理處理做單頁應(yīng)用,在express中我們就只用給其一個入口路徑
router.get('/',function(req,res,next){
    res.render('index',{title:"medium-react"});
});
//列表頁get數(shù)據(jù)的請求地址
router.get('/articles',function(req,res,next){
        //使用find()方法有點(diǎn)簡單了,因?yàn)槭醉摶緯婕胺猪摚@個以后再改進(jìn)
    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應(yīng)用就已經(jīng)搭建完成,這一章讓我體驗(yàn)了一把完整的web應(yīng)用的基本流程,感覺不錯。但是遺留問題也還很多,比如連接數(shù)據(jù)庫的初衷,就是更好的觀察、處理異步交互,然而在文章詳情頁,整個結(jié)構(gòu)樹太深

用工具畫圖好累,就手繪湊合看吧。。

目前只在文章詳情頁connect到數(shù)據(jù)庫,也就意味著dispatch方法是在這個容器中獲取的,但是對于評論中的點(diǎn)贊事件可能發(fā)生在主回復(fù)Comment組件中,也可能是從回復(fù)Reply組件中,如果將dispatch 方法一層層傳遞下去,不僅路徑深,而且中間都不曾用到該方法?;蛟S也可以這樣:點(diǎn)贊之后將數(shù)據(jù)傳遞(比如觀察者模式)到container ,然后dispatch這個請求。你還有什么好的方法么?

下一節(jié)先實(shí)現(xiàn)最基礎(chǔ)的異步交互:

react+redux實(shí)戰(zhàn)(三)----異步交互

【主要參考資源】
Express結(jié)合Webpack的全棧自動刷新
express使用指南
Mongoose學(xué)習(xí)參考文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容