前端筆記之NodeJS(四)MongoDB數據庫&Mongoose&自制接口&MVC架構思想|實戰

1、MongoDB數據庫

1.1 NoSQL簡介

隨着互聯網web2.0網站的興起,傳統的SQL數據庫(關係數據庫)在應付web2.0網站,特別是超大規模和高併發的SNSsocial network system,人人網)類型的web2.0純動態網站已經顯得力不從心,暴露了不少難以克服的問題,而非關係型的數據庫則因爲其自己的特色獲得了很是迅速的發展。NoSQL數據庫的產生就是爲了解決大規模數據集合多重數據種類帶來的挑戰,尤爲是大數據應用難題。javascript

 

傳統的數據庫MySQLSQL ServerOracleAccess都是SQL結構型數據庫php

這些結構型數據庫的特色,就是「表」有明確的「字段()」的概念,並且字段是固定的。若是要增長字段,全部已經存在的條目都要改變。css

SQL的優勢就是查詢能夠很複雜,好比檢索全部「語文成績大於數學成績且英語成績在班級前三名的女生」。html

可是在如今,你會發現不少時候檢索能夠很簡單,不須要那麼複雜。好比話費詳單,沒有任何需求去檢索:通話時長在5分鐘以上且是夜間主動撥打的。前端

 

結構型數據庫的缺點:若是從此須要增長字段,以前的全部的條目,也要一塊兒增長。好比你的條目已經有2萬條,此時若是忽然要增長一個語文成績字段,此時20000條已經存在的條目都要進行字段的改變,此時特別耗費時間。也就是說靈活性不夠!java

結構型數據庫的優勢:查找快、有主鍵的概念、從鍵、主從查找、映射等等的高級數據庫的概念。node

 

SQL在這個時代:優勢被縮小了,缺點被放大jquery

若是隻須要保存,並不須要精確查找,NoSQL數據庫很是合適。git

 

中國聯通的上網記錄,每一個人都會有一堆數據,這個數據就是:只要存,基本上不須要查找。而且常常結構會常常變。github

 

因此MySQL等等結構型數據,他們的優勢(查找快、主從查找高級功能)咱們愈來愈不用了,缺點(結構難以變化)咱們需求愈來愈大。因此人類開始反思SQL型數據庫了。

因此誕生叫作NoSQL,非關係型數據庫。數據沒有行、列的概念,使用k-v對兒或者JSON進行存儲。優缺點正好和SQL型反過來了,適合結構變化,不適合精確查找。恰好適應了時代「只要存儲,不要查找」。

NoSQL有一個暱稱,叫作Not Only SQL,不只僅是數據庫。其實是玩笑話,功能比SQL弱太多了。

 

介紹兩種工做中最經常使用的數據庫:MongoDB(文檔型數據庫)Redis(K-V對型數據庫),他們都是NoSQL

文檔型數據庫(好比MongoDB)優勢:數據結構要求不嚴格,表結構可變,不須要像關係型數據庫同樣須要預先定義表結構。

 

非關係型數據庫如今已經普遍的應用於:社交平臺、APP通訊記錄、銀行流水、電信通話詳單等等。任何只須要保存,可是不須要精確查找的使用場景,均可以用非關係型數據庫。

NoSQL非關係型數據庫,每個條目的字段是不固定的。以MongoDB爲例子:

{"name":"小明","age":12,"sex":"男"}
{"name":"小紅","age":13,"sex":"女"}
{"name":"小剛","age":14,"sex":"男", "chengji":99}
{"name":"小黑","age":14,"sex":"男", "gongzi":5000}

1.2 MongoDB數據庫安裝

工做的時候是嚴格先後端分開的,咱們學習前端不須要學習數據庫的知識,可是爲了模擬一個項目,更重要的是,讓你瞭解後臺和前端的交互模式,因此必須學習數據庫,要有全棧思惟。

 

MongoDB是一種NoSQLNo Only SQL,非關係型數據庫)數據庫。

MongoDB並非什麼軟件,而是一堆CLI(命令行程序)。

 

MongoDB是綠色的,也就是說不須要安裝,解壓縮就能用。

官網:https://www.mongodb.com/

下載地址:https://www.mongodb.com/download-center

 

解壓縮到你電腦盤符(不要出現中文):C:\Program Files

 

解壓縮以後的畫風是這樣的:

bin文件夾中提供的exe文件,不要雙擊運行,而是應該在cmd中運行,它們都是CLI程序: 

 

 

此時要將這個bin文件夾設置爲系統的環境變量,就能保證CMD在任何盤符下能夠運行他們。

 

 

計算機右鍵點擊屬性

 

 

如圖更改環境變量

 

 

bin文件夾的路徑複製進去。

 

 

打開CMD,在任意盤符下輸入

mongo -version

就能查看版本,

 

若是環境變量沒有設置對,會報錯。

 

數據庫的使用須要你安裝windows補丁:KB2731284

若是補丁安裝不上,查看一下方法:

 https://jingyan.baidu.com/article/fd8044fa3c47e15031137acd.html


1.3數據庫的開機

c盤建立一個文件夾叫database,打開cmd輸入:

mongod --dbpath c:\database

mongod 表示開機命令,數據庫會被打開,--dbpath表示選擇一個數據庫的位置。

 

 

不要關閉這個CMD,關閉了數據庫也就關閉了,此時請打開一個新的CMD窗口作其餘操做:

mongo

進入MongoDBREPL環境

 


1.4數據庫的使用

先開機,而後新建一個CMD倉庫,用mongo命令管理數據,cmd中輸入mongo回車

下面的操做試着看,在mongoREPL環境中是 > ,而不是C:\Users\admin>CMD環境:

建立(切換)數據庫:

use student

 

插入數據:

db.banji0902.insert({"name":"小明", "age":12})

 

查詢全部:

db.banji0902.find()

 

精確查詢:

db.banji0902.find({"name":"小明"})

1.5 MongoDB層次結構

MongoDB中從大到小:Database > collections > documents > records

來自: https://docs.mongodb.com/manual/introduction/

3.5.1 records條目和documents 文檔

record(條目)

MongoDB中記錄是一個文件,這是一個數據結構由字段和值對。MongoDB文檔相似於JSON對象。字段的值能夠包括其餘文檔、數組和文檔數組。

 

documents中的一個document也稱爲一個record條目,條目相似JSON結構,是k-v對的,值能夠是其餘對象、數組、對象數組。


1.5.2 collections 集合(表)

MongoDB使用collections集合存儲文檔(records),集合就至關於SQL中的「表格」。可是不像表格同樣,集合不須要文檔擁有相同的結構(schema)。

 

下面是一個集合,裏面有4個文檔,文檔中有3個條目(nameagesex

{"name":"小明","age":12,"sex":"男"}
{"name":"小紅","age":14,"sex":"女"}
{"name":"小黑","age":14,"sex":"男"}
{"name":"小籃","age":13,"sex":"男"}

文檔存儲在集合(表)中,集合又存儲在數據庫中。


1.5.3 database數據庫

數據庫中能夠有不少個集合(表)。


1.6常見的CMD命令

控制mongodb通常都是使用CMD命令,但也有可視化數據庫軟件。

 

命令有兩種:

CMD命令

REPL命令(用mongo就能夠進入此環境,在小尖角號「>」提示符下輸入)

 

先學習CMD中能運行哪些命令?

mongod表示開機,mongo表示進入數據庫的REPL環境、mongoimport導入數據、mongoexport導出數據。

 

先開機,從此一切操做必須先開機:

mongod --dbpath c:\database

首先要準備好外部的數據,寫幾個文檔,必須是.txt文件,而不是.json文件

 

導入數據,新建一個CMD窗口,輸入:

mongoimport -d student -c banji0902 ./模擬數據.txt

 

 刪除舊數據,導入新數據

mongoimport -d student -c banji0902 ./模擬數據.txt --drop

 

 導出指定的數據庫和集合(表)中的數據:

mongoexport -d student -c banji0902 -o c:\haha.txt
-d     表示指定的數據庫
-c  表示指定的表名
-o  表示導出路徑
--drop 表示清空以前的數據

1.7常見的REPL命令

在開機以後,另外一個CMD窗口,輸入mongo按回車進入數據庫的REPL環境,便可操做數據庫。

 

切換(建立)某一個數據庫:

use student

 

查詢當前有哪些數據庫:

show dbs

 

查看當前數據庫有哪些集合(表)

show collections

 

刪除數據庫:

db.dropDatabase()

 

在表中插入數據:

db.banji0902.insert({"name":"小明", "age":12})

 

查詢表中全部的數據:

db.banji0902.find()

 

精確查詢:

db.banji0902.find({"name":"小明"})

 

且查找:查找年齡是18,而且是男的

db.banji0902.find({"sex":"男", "age":18})

 

或($or)查找:查找年齡是18歲或「男」

db.banji0902.find({$or:[{"sex":"男"}, {"age":38}]})

 

大於($gt):

db.banji0902.find({"age":{$gt:18}})

 

大於等於($gte)、小於($lt)、小於等於($lte

 

 

查找男生,而且年齡介於1825之間:

db.banji0902.find({"sex":"男", "age":{$gte:18, $lte:25}})

 

查詢中能夠用正則表達式:

db.banji0902.find({"name":/小/g})

 

查詢喜歡鬥地主的人:

db.banji0902.find({"hobby":"鬥地主"})

 

修改($set):

db.banji0902.update({"name":"小1"}, {$set:{"sex":"不男不女"}})

 

刪除某條數據:

db.banji0902.remove({"name":"小1"})


1.8 MongoDBNodejs鏈接

官方APIhttp://mongodb.github.io/node-mongodb-native/2.2/

增刪改查(CRUD)操做:http://mongodb.github.io/node-mongodb-native/2.2/tutorials/crud/

安裝mongodb的依賴,而且安裝指定的版本號,不要安裝最新版:

npm install mongodb@2.2.28 --save

注意:mongodb版本的API是不同的,因此要看官方文檔:

 http://mongodb.github.io/node-mongodb-native/3.0/

 

var MongoClient = require("mongodb").MongoClient; //引包

//數據庫地址
var dbUrl = 'mongodb://127.0.0.1:27017/student';
//鏈接數據庫
MongoClient.connect(dbUrl, function(err,db){
    if(err){
        console.log("數據庫鏈接失敗");
        return;
    }

    console.log("數據庫鏈接成功!");
    db.close();
})

 

查詢數據:

var MongoClient = require("mongodb").MongoClient; //引包

//數據庫地址
var dbUrl = 'mongodb://127.0.0.1:27017/student';
//鏈接數據庫
MongoClient.connect(dbUrl, function(err,db){
    if(err){
        console.log("數據庫鏈接失敗");
        return;
    }

    console.log("數據庫鏈接成功!");

    //查詢當前數據庫中banji0902表的數據
    db.collection("banji0902").find({"age":{$gt:18}}).toArray(function(err,data){
        console.log(data);
        db.close();
    })
})

傳統的鏈接形式限制通常不用了,由於:

l 回調函數難看。

不方便MVC編程

原生API複雜


2、Mongoose數據庫

2.1新增數據

官網:https://mongoosejs.com/

node.js的優雅mongodb對象建模。

安裝依賴:

npm install --save mongoose@4

 

寫一個Hello World。套路:引包、建立schema、建立模型、實例化模型、調用save()方法。

var mongoose = require("mongoose"); //引包

//鏈接數據庫,數據庫叫iqd,若是數據庫不存在會自動建立
mongoose.connect("mongodb://127.0.0.1:27017/iqd", {useMongoClient:true});

//建立一個數據庫結構(Schema)
var studentSchema = {
    name:String,
    age:Number,
    sex:String
}

//建立模型(會返回一個類),student是表名稱(集合)
//注意:在nodejs中建立的表面叫「student」,可是到了數據庫中會自動加上s,變成「students」
var Student = mongoose.model('student', studentSchema);

//new一個實例
var xiaoming = new Student({"name":"小明","age":12,"sex":"男"});

//保存數據
xiaoming.save();

 

運行node app.js

iqd數據自動建立,而且建立了一個叫student的表(數據庫會自動加s),因此變成students

 

開機、REPL、控制檯各一個CMD

 

mongoose就是這樣的哲學:讓開發者感受不到在操做數據,僅僅經過new save等類、實例的方法,就能管理數據庫,除了newsvae()能建立數據,用Student.insert({})也能建立數據庫。


2.2查找

//查詢數據庫的方法1:
Student.find({"age":{$gt:18}}, function(err,data){
    console.log(data)
})

//查詢數據庫的方法2:
Student.find({"age":{$gt:18}}).exec(function(err,data){
    console.log(data)
})
查找

2.3刪除

刪除方法很靈活,能夠用類打點調用Studentremove方法

//刪除的方法1
Student.remove({"name":"小黑"}, function(err,data){
    console.log(data)
})

//刪除的方法2:
Student.remove({"name":"小黑"}).exec(function(err,data){
    console.log(data)
})
類打點

 

也能夠調用實例的remove()方法刪除

Student.find({"name":"小6"}, function(err,data){
    var xiao6 = data[0];
    xiao6.remove(); //刪除
})
調用實例

2.4修改

Student.find({"name":"小3"}, function(err,data){
    var xiao3 = data[0];
    xiao3.sex = "不男不女"; //賦值修改
    xiao3.save();
})
mongoose修改

 

若是不用mongoose,用原生

db.collections("students").update({"name":"小3"}, {$set:{"sex":"不男不女"}})

2.5封裝靜態方法

剛剛發現能夠用類名(構造函數)打點調用不少方法

Student.find()
Student.remove()

那麼如何本身封裝一些方法呢?而且能用類打點調用,mongoose提供自定義封裝類的方法(靜態方法),步驟:

建立schema的時候,必須用new mongoose.Schema()建立

schema的實例上,用statics.***綁定靜態方法

var mongoose = require('mongoose'); //引包
//鏈接數據庫,數據庫叫iqd,若是數據庫不存在會自動建立
mongoose.connect('mongodb://localhost/iqd',{useMongoClient:true});

//建立一個數據庫結構(Schema)
var studentSchema = new mongoose.Schema({
    name: String,
    age : Number,
    sex : String
}) //封裝自定義的靜態方法
studentSchema.statics.check = function(name,callback){ //this表示Student類
    this.find({"name":name}, function(err,data){
        var exsit = data.length > 0;
        //將結果經過參數傳遞給回調函數
        callback(exsit)
    })
} var Student = mongoose.model('student', studentSchema);

//驗證某我的是否存在
Student.check("小明", function(exsit){
    console.log(exsit)
})

2.6封裝動態方法

實例打點調用的都叫動態方法:

var mongoose = require('mongoose'); //引包
//鏈接數據庫,數據庫叫iqd,若是數據庫不存在會自動建立
mongoose.connect('mongodb://localhost/iqd',{useMongoClient:true});

//建立一個數據庫結構(Schema)
var studentSchema = new mongoose.Schema({
    name: String,
    age : Number,
sex : String,
})

//封裝自定義的動態方法
studentSchema.methods.bianxing = function(name,callback){
    //this表示實例
    this.sex = this.sex == "男" ? "女" : "男";
}

var Student = mongoose.model('student', studentSchema);

//使用動態方法
Student.find({"name":"小明"}, function(err,data){
    var ren = data[0]; //實例
 ren.bianxing();
    ren.save();
    console.log(data)
})

Nodejs + MongoDB技術棧在工做中不多用到,由於有後臺哥哥姐姐,學習NodejsMongoDBHTTP服務器開發的目的:

理解前端後端如何工做、配合、數據如何傳遞、數據庫的增刪改查是什麼狀況。

爲了寫一個JSON接口,給前端調用


 

3、製做一套增刪改查的接口

作一套RESTful風格的路由接口,建立模擬數據:

{"id":1001,"name":"小明","age":12,"sex":"男","job":"前端"}
{"id":1002,"name":"小紅","age":15,"sex":"女","job":"設計"}
{"id":1003,"name":"小剛","age":12,"sex":"男","job":"老師"}
{"id":1004,"name":"小強","age":13,"sex":"男","job":"後端"}
{"id":1005,"name":"小綠","age":18,"sex":"男","job":"老師"}
{"id":1006,"name":"小青","age":18,"sex":"女","job":"前端"}
{"id":1007,"name":"小黑","age":18,"sex":"女","job":"產品"}
{"id":1008,"name":"小花","age":18,"sex":"女","job":"設計"}

它們都是用戶,因此導入到數據庫時,名字叫users的表中,注意表名最後一個字母必須是s

 建立models文件夾,文件夾中建立User.js,建立數據結構

var mongoose = require('mongoose'); //引包
//默認暴露
module.exports = mongoose.model('user', {
    id:Number,
    name: String,
    age : Number,
    sex : String,
    job : String
});
數據結構

 

 app.js查詢接口

var mongoose = require('mongoose'); //引包
var User = require('./models/User.js');
var express = require('express'); //引包
var app = express();

//鏈接數據庫,數據庫叫gziqd,若是數據庫不存在會自動建立
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/gziqd',{useMongoClient:true});

//查詢數據庫所有的用戶
app.get("/users", function(req,res){
   User.find().exec(function(err,data){
        res.json({"data": data}) })
});

//查詢某一個id的用戶
app.get("/users/:id", function(req,res){
    var id = req.params.id;
    //指定id查詢
    User.find({"id": id}).exec(function(err,data){
        res.json(data[0]) })
});

app.listen(3000)

 

 

發出POST請求建立數據

index.html前端頁面:

<html>
<head>
    <title>Document</title>
</head>
<body>
    <!-- 增長 -->
    <p>ID :<input type="text" id="idTxt" /></p>
    <p>姓名:<input type="text" id="name" /></p>
    <p>工做:<input type="text" id="job" /></p>
    <button id="btn1">建立用戶</button>
    <hr>

    <!-- 刪除 -->
    <p>
        刪除的人的id :<input type="text" id="delId"/>
        <button id="btn2">刪除</button>
    </p>
    <hr>

    <!-- 修改 -->
    <p>
        更名的人的id :<input type="text" id="changeId"/><br>
        要改成什麼名 :<input type="text" id="changeToName"/>
        <button id="btn3">修改</button>
    </p>
    <hr>

    <!-- 查詢 -->
    <p>
        查詢id :<input type="text" id="checkId"/>
        <button id="btn4">查詢</button>
    </p>
</body>
<script type="text/javascript" src="js/jquery-2.2.4.min.js"></script>
<script type="text/javascript">
     //發出POST請求,建立用戶,提交給數據庫(服務器)
     $("#btn1").click(function(){
  $.post("/users",{
            id : $("#idTxt").val(),
            name: $("#name").val(),
            job : $("#job").val()
        }, function(data){
            if(data == "ok"){
                alert("建立成功!");
            }
        })
     });
</script>
</html>

 

後端攔截post增長數據的請求,在數據庫插入數據

app.post("/users", function(req,res){
    var form = new formidable.IncomingForm();
    form.parse(req, function(err, data){
        //插入數據的方法:User.create()
        User.create(data, function(){
            res.send("ok")
        })
    });
});

 

發出delete刪除請求:

請求的類型一共有26種,下面要用到delete請求、patch請求。

$("#btn2").click(function(){
    $.ajax({
        url : "/users/" + $("#delId").val(), //要刪除的id
        type : "delete",
        success: function(data){
             if(data == "ok"){
                 alert("成功");
            }
        }
    });
});

 

後端攔截delete刪除數據的請求,在數據庫刪除指定id的用戶

app.delete("/users/:id", function (req, res){
    var id = req.params.id;
    //刪除數據的語法:User.remove();
    User.remove({ "id": id } , function (err, data){
        res.send("ok");
    });
});

 

發出patch修改請求:

$("#btn3").click(function(){
    $.ajax({
        url : "/users/" + $("#changeId").val() ,
 type : "patch",
        data : {
            "name": $("#changeToName").val()
        },
        success : function(data){
            if(data == "ok"){
                    alert("成功");
            }
        }
    });
});

 

後端攔截patch修改數據的請求,在數據庫修改指定id的用戶

app.patch("/users/:id", function (req, res) {
    var id = req.params.id; 
    var form = new formidable.IncomingForm();
    form.parse(req, function (err, jsonobj) {
        var name = jsonobj.name; //獲得前端發來的name
        User.update({"id": id }, {"$set": {"name":name} }, function (err, results) {
            res.send("ok");
        });
    });
});

 

完整的app.js,實現了不少接口。

下面紅色代碼是express的路由,綠色代碼是formidable獲得數據,藍色代碼是關於數據庫的。

var formidable = require('formidable');
var mongoose = require('mongoose');
var User = require('./models/Users.js'); //引入數據結構
var express = require('express');
var app = express();

//靜態化www文件夾
app.use(express.static("www"));

//鏈接數據庫,數據庫叫作gziqd。如數據庫不存在會自動建立。
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/gziqd', {useMongoClient:true});

//查詢數據庫所有的用戶
app.get("/users" ,function(req,res){
    User.find().exec(function(err, data){
        res.json({"data" : data}) })
}); //查詢某一個id的用戶
app.get("/users/:id" ,function(req,res){
    var id = req.params.id;
    // 指定id查詢
    User.find({"id" : id}).exec(function(err, data){
        res.json(data[0]) })
}); // 後端接收請求,插入數據,建立用戶
app.post("/users" ,function(req,res){
    var form = new formidable.IncomingForm();
    form.parse(req, function(err, data){
       //插入數據的語法:User.create();
       User.create(data, function(){
            res.send("ok"); })
    });
}); //刪除請求
app.delete("/users/:id" ,function(req,res){
    var id = req.params.id;
    //刪除數據的語法:User.remove();
    User.remove({"id":id}, function(){
        res.send("ok"); })
}); //修改請求
app.patch("/users/:id" ,function(req,res){
    var id = req.params.id;
    var form = new formidable.IncomingForm();
    form.parse(req, function(err, data){
       var name = data.name; //獲得前端提交的name數據
       //修改數據的語法:User.update();
       User.update({"id" : id}, {$set:{"name":name}}, function(err, data){
            res.send("ok"); })
    });
});

app.listen(3000);

RESTful風格的路由:

 

功能

URL

請求類型

增長

http://127.0.0.1/users

POST請求

刪除

http://127.0.0.1/users/1001

DELETE請求

修改

http://127.0.0.1/users/1001

PATCH請求

查詢

http://127.0.0.1/users

http://127.0.0.1/users/1001

GET請求

 

 

一個URL地址能夠幹多個工做,靠HTTP請求類型區分開。

 

RESTful是個英語單詞的縮寫,語義化的、美觀的URL路由形式。

 

 

 

2015RESTful流行以前的路由是這樣的:

 

 

功能

URL

請求類型

增長

http://127.0.0.1/adduser.php

POST請求

刪除

http://127.0.0.1/deluser.php?id=1001

GET請求

修改

http://127.0.0.1/changeuser.php?id=1001&name=新名字

GET請求

查詢

http://127.0.0.1/users.php

http://127.0.0.1/users.php?id=1001

GET請求

 

 

缺點:

 

① 每一個業務用到了不一樣的php文件,不方便MVC編程。

 

② 暴露技術細節,人家輕鬆知道你的處理頁面和傳參數的狀況。

 

總結一下mongoose中的寫法:

 

功能

寫法

增長

User.create({})

刪除

User.remove({"id" : id})

修改

User.update({"id":id}, {"$set" : {"name" : 前端發來的新名字}})

查找

User.find()

User.find({"id":id})


4、MVC的架構思想

4.1 MVC理論知識

Models 模型

 

 

Views 視圖

 

 

Controllers 控制器(包工頭)

 

 

 

Model(模型)是應用程序中用於處理應用程序數據邏輯的部分。

一般模型對象負責在數據庫中存取數據、進行計算等內容。最髒最累的活是models在完成

MVCM表示可插拔的、可本身調試的單獨的邏輯單元,model

 

model層的內容僅對本身的函數負責,不須要對整個項目業務邏輯負責,它本身可以單元測試(unit test)。

 

View(視圖)是應用程序中處理數據顯示的部分。

一般視圖是依據模型數據建立的。

 

Controller(控制器)是應用程序中處理用戶交互的部分。

一般控制器負責從視圖讀取數據,控制用戶輸入,並向模型發送命令請求。從模型獲得數據以後,要渲染視圖。

 

 

控制器會向多個模型發送請求,請求他們的數據,交給視圖去呈遞。

MVC 分層同時也簡化了分組開發。不一樣的開發人員可同時開發視圖、控制器邏輯和業務邏輯。

 


4.2 MVC小項目-項目介紹

作一個「帶有機器學習能力的因數計算器」。

項目的邏輯:

 

首頁上面點擊查詢按鈕的時候,由前端js調用window.location進行跳轉頁面/cha/9954路由頁面:

 

Nodejs不擅長計算,因此咱們考慮一個事:機器學習。

把用戶查詢過的數字,存儲起來,好比用戶查詢過9954,此時就在data文件夾中建立9954.txt的文件。此時將因數數組存放於此。當有其餘用戶查詢9954的時候,不須要計算,而是直接呈遞結果!這樣一來,用的人越多,機器知道的答案也越多,從而愈來愈快。由於Nodejs找一個文件遠比計算一個什麼快多了,I/O操做是Nodejs的強項

 

邏輯:

 


4.3 MVC小項目-模型層編寫

建立一個models模型車文件夾,新建2個文件:

match.js 裏面提供計算因數的函數

file.js 裏面提供文件的操做函數(讀取文件的數組、將數組寫入文件、判斷文件是否存在)

 

也就是說:「模型層員工」就兩個:文件操做函數、數學計算函數。

 

模型層只對本身的函數負責,不須要看見全局業務。

建立data文件夾,負責存儲全部已經計算過的答案:

 

fs.stat()函數能夠檢查一個文件是否存在,存在返回JSON,不存在返回undefined

fs.stat(path, callback)

file.js文件,封裝一個isExist()函數,這個函數接收數字、回調函數,經過回調函數返回true/false

表示文件是否存在,true/false返回值最終須要提供給控制器使用。

 

var fs = require("fs");
//判斷一個文件是否存在,接收一個數字參數,好比886,能夠判斷886.txt是否存在
function isExist(n,callback){
    //異步函數不容許return,因此只能提供回調函數傳遞參數
    fs.stat("./data/"+ n +".txt",function(err,stats){
        //文件存在stats會返回json,不存在返回undefined
        var result = stats === undefined ? false : true;
        callback(result);
    });
};

//單元測試,檢查886.txt是否存在
isExist(886,function(result){ console.log(result); });
//向外暴露函數
exports.isExist = isExist;

 

寫一個函數,單元測試一個函數。

var fs = require("fs");
//判斷一個文件是否存在,接收一個數字參數,好比886,能夠判斷886.txt是否存在
function isExist(n, callback){
   ...
}
//向外暴露
exports.isExist = isExist;

// 單元測試。檢查886.txt是否存在
// isExist(886,function(result){
//     console.log(result)
// });


//將數組寫入到某個文件中
function writeArray(n, arr, callback){
    fs.writeFile("./data/"+ n +".txt", JSON.stringify(arr), function(err){
        if(err){
            callback(false)
        }else{
            callback(true)
        }
    })
}
//向外暴露
exports.writeArray = writeArray;

// 單元測試,寫入數據
// writeArray(8888,[2,2,2],function(result){
//     console.log(result)
// });

//讀取某個文件中的數組
function readArray(n, callback){
    fs.readFile("./data/" + n + ".txt", function(err,data){
        if(err){
            //若是文件不存在
            callback(false)
        }else{
            //若是文件存在,返回讀取到的數組結果
            var arr = JSON.parse(data)
            callback(arr)
        }
    })
}

//單元測試
// readArray(888, function(arr){
//    console.log(arr)
// })
//向外暴露
exports.readArray = readArray;

math.js模塊,裏面就提供了一個函數:
//計算因數函數,返回一個數組
function calcFactor(n){
    var arr = [];
    for (var i = 1; i <= n; i++) {
        if(n % i == 0){
            arr.push(i)
};
    }
    return arr;
}
console.log(calcFactor(48));
exports.calcFactor = calcFactor;

4.4 MVC小項目-控制器和視圖層

控制器就是中間件,咱們將路由的回調函數單獨放在外部的js文件中,就是控制器,當項目大的時候能夠分出不少。

var express = require("express");
var app = express();
var mainCtrl = require("./controllers/mainCtrl.js");

//設置模板引擎
app.set("view engine", "ejs");

//路由清單,把中間件的回調函數放在外部文件
app.get("/", mainCtrl.showIndex); //顯示首頁
// app.get("cha/:num", mainCtrl.showCha); //顯示結果的頁面

app.listen(3000);

 

向外暴露中間件:

exports.showIndex = function(req,res){
    res.render("index.ejs")
}

exports.showCha = function(req,res){

}

 

index.html視圖

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <style type="text/css">
        body{background: #ccc;}
        .wrap{
            background: #fff;
            width: 900px;height: 400px;
            border-radius:10px;
            padding:10px;margin:20px auto;
            text-align:center;
        }
        input{
            width: 60%;font-size:22px;border-radius:5px;
            border:1px solid #eee;padding:0 5px;
        }
        button{width: 60px;height: 30px;background: skyblue;border:none;}
    </style>
</head>
<body>
    <div class="wrap">
        <h1>因數計算器</h1>
        <div class="box">
            <p>請輸入查詢的數字</p>
            <p>
                <input type="text" autofocus id="numTxt">
                <button id="btn">查詢</button>
            </p>
        </div>
    </div>
</body>
<script type="text/javascript">
     var btn = document.getElementById("btn");
     var numTxt = document.getElementById("numTxt");

     btn.onclick = function(){
        var num = Number(numTxt.value); //獲得數字

        if(isNaN(num)){ //isNaN 若是輸入的不是數字返回true
            alert("請輸入正確的數字");
            return;
        }

        //點擊查詢跳轉到查詢頁面
 window.location = "/cha/" + num;
     }
</script>
</html>

能夠跳轉頁面了。

 

核心就是去路由/cha/:num,控制器在調用全部「小兵」,用小兵暴露的API來組合上層業務:

 

cha.ejs視圖:

<style type="text/css">
    .wrap{...    min-height:400px;}
</style>
<body>
    <div class="wrap">
        <h1>因數計算器 - 結果</h1>
        <div class="box">
            <p>你查詢的數字是:<%= num %>。它的因數有<%= results.length %>個,分別是:</p>
            <p>本次查詢耗時:<%= time %>毫秒</p>
            <ul>
                <% 
                    for(var i = 0 ; i < results.length; i++){ 
                %>
                        <li><%= results[i] %></li>
                <% 
                    } 
                %>
            </ul>
        </div>
    </div>
</body>

 

mainCtrl.js控制器:

var file = require("../models/file.js");
var match = require("../models/match.js");

//顯示結果頁面
exports.showCha = function(req,res){
    //獲得查詢的數字
    var num = req.params.num;
//渲染查詢頁面,先用假數據測試
res.render("cha" ,{
    num : num,
    results : [1,2,3,4,5],
});
}

var file = require("../models/file.js");
var match = require("../models/match.js");

exports.showCha = function(req,res){
    //獲得查詢的數字
    var num = req.params.num;
    var time = new Date();
    //檢查文件是否存在
    file.isExist(num , function(exist){
        if(exist){//這個數字(文件)已經存在
            //直接讀取這個文件:
            file.readArray(num , function(arr){
                res.render("cha" ,{ //字典
                    num : num,
                    results : arr,
                    time : new Date() - time
                });
            });
        }else{//若是文件不存在!
            var arr = math.calcFactor(num);//計算!
            //向文件寫入數組
            file.writeArray(num , arr , function(){
                res.render("cha" ,{
                    num : num,
                    results : arr,
                    time : new Date() - time
                });
         });
        }
  });
}
相關文章
相關標籤/搜索