一、MongoDB數據庫
1.1 NoSQL簡介
隨着互聯網web2.0網站的興起,傳統的SQL數據庫(關系數據庫)在應付web2.0網站,特別是超大規模和高並發的SNS(social network system,人人網)類型的web2.0純動態網站已經顯得力不從心,暴露了很多難以克服的問題,而非關系型的數據庫則由於其本身的特點得到了非常迅速的發展。NoSQL數據庫的產生就是為了解決大規模數據集合多重數據種類帶來的挑戰,尤其是大數據應用難題。
傳統的數據庫MySQL、SQL Server、Oracle、Access都是SQL結構型數據庫。
這些結構型數據庫的特點,就是“表”有明確的“字段(列)”的概念,而且字段是固定的。如果要增加字段,所有已經存在的條目都要改變。

SQL的優點就是查詢可以很復雜,比如檢索所有“語文成績大於數學成績且英語成績在班級前三名的女生”。
但是在現在,你會發現很多時候檢索可以很簡單,不需要那么復雜。比如話費詳單,沒有任何需求去檢索:通話時長在5分鍾以上且是夜間主動撥打的。
結構型數據庫的缺點:如果今后需要增加字段,之前的所有的條目,也要一起增加。比如你的條目已經有2萬條,此時如果突然要增加一個語文成績字段,此時20000條已經存在的條目都要進行字段的改變,此時特別耗費時間。也就是說靈活性不夠!
結構型數據庫的優點:查找快、有主鍵的概念、從鍵、主從查找、映射等等的高級數據庫的概念。
SQL在這個時代:優點被縮小了,缺點被放大。
如果只需要保存,並不需要精確查找,NoSQL數據庫非常合適。

中國聯通的上網記錄,每個人都會有一堆數據,這個數據就是:只要存,基本上不需要查找。並且經常結構會經常變。
所以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是一種NoSQL(No Only SQL,非關系型數據庫)數據庫。
MongoDB並不是什么軟件,而是一堆CLI(命令行程序)。
MongoDB是綠色的,也就是說不需要安裝,解壓縮就能用。
下載地址: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
進入MongoDB的REPL環境

1.4數據庫的使用
先開機,然后新建一個CMD倉庫,用mongo命令管理數據,cmd中輸入mongo回車
下面的操作試着看,在mongo的REPL環境中是 > ,而不是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個條目(name、age、sex)
{"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)

查找男生,並且年齡介於18到25之間:
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 MongoDB和Nodejs連接
官方API:http://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 回調函數難看。
l 不方便MVC編程
l 原生API復雜
二、Mongoose數據庫
2.1新增數據
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等類、實例的方法,就能管理數據庫,除了new和svae()能創建數據,用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刪除
刪除方法很靈活,可以用類打點調用Student的remove方法
//刪除的方法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,用原生
db.collections("students").update({"name":"小3"}, {$set:{"sex":"不男不女"}})
2.5封裝靜態方法
剛剛發現可以用類名(構造函數)打點調用很多方法
Student.find()
Student.remove()
那么如何自己封裝一些方法呢?並且能用類打點調用,mongoose提供自定義封裝類的方法(靜態方法),步驟:
l 創建schema的時候,必須用new mongoose.Schema()創建
l 在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技術棧在工作中很少用到,因為有后台哥哥姐姐,學習Nodejs、MongoDB的HTTP服務器開發的目的:
理解前端后端如何工作、配合、數據如何傳遞、數據庫的增刪改查是什么情況。
為了寫一個JSON接口,給前端調用
三、制作一套增刪改查的接口
做一套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路由形式。
在2015年RESTful流行之前的路由是這樣的:
| 功能 |
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}) |
四、MVC的架構思想
4.1 MVC理論知識
| Models 模型
|
Views 視圖
|
Controllers 控制器(包工頭)
|
Model(模型)是應用程序中用於處理應用程序數據邏輯的部分。
通常模型對象負責在數據庫中存取數據、進行計算等內容。最臟最累的活是models在完成。
在MVC中M表示可插拔的、可自己調試的單獨的邏輯單元,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 }); }); } }); }







