随着互联网web2.0网站的兴起,传统的SQL数据库(关系数据库)在应付web2.0网站,特别是超大规模和高并发的SNS(social network system,人人网)类型的web2.0纯动态网站已经显得力不从心,暴露了不少难以克服的问题,而非关系型的数据库则因为其自己的特色获得了很是迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤为是大数据应用难题。javascript
传统的数据库MySQL、SQL Server、Oracle、Access都是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}
工做的时候是严格先后端分开的,咱们学习前端不须要学习数据库的知识,可是为了模拟一个项目,更重要的是,让你了解后台和前端的交互模式,因此必须学习数据库,要有全栈思惟。
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
在c盘建立一个文件夹叫database,打开cmd输入:
mongod --dbpath c:\database
mongod 表示开机命令,数据库会被打开,--dbpath表示选择一个数据库的位置。
不要关闭这个CMD,关闭了数据库也就关闭了,此时请打开一个新的CMD窗口作其余操做:
mongo
进入MongoDB的REPL环境
先开机,而后新建一个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":"小明"})
MongoDB中从大到小:Database > collections > documents > records
来自: https://docs.mongodb.com/manual/introduction/
record(条目)
在MongoDB中记录是一个文件,这是一个数据结构由字段和值对。MongoDB文档相似于JSON对象。字段的值能够包括其余文档、数组和文档数组。
documents中的一个document也称为一个record条目,条目相似JSON结构,是k-v对的,值能够是其余对象、数组、对象数组。
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":"男"}
文档存储在集合(表)中,集合又存储在数据库中。
数据库中能够有不少个集合(表)。
控制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 表示清空以前的数据
在开机以后,另外一个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"})
官方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复杂
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({})也能建立数据库。
//查询数据库的方法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) })
删除方法很灵活,能够用类打点调用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(); //删除 })
Student.find({"name":"小3"}, function(err,data){ var xiao3 = data[0]; xiao3.sex = "不男不女"; //赋值修改 xiao3.save(); })
若是不用mongoose,用原生
db.collections("students").update({"name":"小3"}, {$set:{"sex":"不男不女"}})
刚刚发现能够用类名(构造函数)打点调用不少方法
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) })
实例打点调用的都叫动态方法:
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}) |
Models 模型
|
Views 视图
|
Controllers 控制器(包工头)
|
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
一般模型对象负责在数据库中存取数据、进行计算等内容。最脏最累的活是models在完成。
在MVC中M表示可插拔的、可本身调试的单独的逻辑单元,model。
model层的内容仅对本身的函数负责,不须要对整个项目业务逻辑负责,它本身可以单元测试(unit test)。
View(视图)是应用程序中处理数据显示的部分。
一般视图是依据模型数据建立的。
Controller(控制器)是应用程序中处理用户交互的部分。
一般控制器负责从视图读取数据,控制用户输入,并向模型发送命令请求。从模型获得数据以后,要渲染视图。
控制器会向多个模型发送请求,请求他们的数据,交给视图去呈递。
MVC 分层同时也简化了分组开发。不一样的开发人员可同时开发视图、控制器逻辑和业务逻辑。
作一个“带有机器学习能力的因数计算器”。
项目的逻辑:
首页上面点击查询按钮的时候,由前端js调用window.location进行跳转页面到/cha/9954路由页面:
Nodejs不擅长计算,因此咱们考虑一个事:机器学习。
把用户查询过的数字,存储起来,好比用户查询过9954,此时就在data文件夹中建立9954.txt的文件。此时将因数数组存放于此。当有其余用户查询9954的时候,不须要计算,而是直接呈递结果!这样一来,用的人越多,机器知道的答案也越多,从而愈来愈快。由于Nodejs找一个文件远比计算一个什么快多了,I/O操做是Nodejs的强项。
逻辑:
建立一个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;
控制器就是中间件,咱们将路由的回调函数单独放在外部的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 }); }); } }); }