Node.js

260 阅读25分钟

认知

Node.js是什么

  • Node.js 不是一门语言
  • Node.js不是库、不是框架
  • Node.js 是一个JavaScript运行时环境
  • 简单点来讲就是Node.js 可以解析和执行JavaScript代码
  • 以前只有浏览器可以可以解析执行JavaScript代码
  • 也就是说现在的JavaScript 可以完全脱离浏览器来运行,一切都归功于:Node.js

浏览器中的JavaScript

  • EcmaScript

  • 基本的语法

    • if
    • var
    • function
    • Object
    • Array
  • BOM

  • DOM

Node.js 中的JavaScript

  • 没有BOMDOM

  • EcmaScript

  • node这个JavaScript执行环境中为JavaScript提供了一些服务器级别的操作**API**

    • 例如文件读写
    • 网络服务的构建
    • 网络通信
    • http 服务器
    • 等处理。。。
  • 特点:

    • 事件驱动
    • 非阻塞IO模型(异步)
    • 轻量和高效
    • 随着课程慢慢的学习你会明白什么是事件驱动、非阻I0模型
  • npm

    • npm是世界上最大的开源库生态系统
    • 绝大多数javaScript相关的包都存放在了npm上,这样做的目的是为了让开发人员更方便的去下载使用。

Node.js能做什么

  • Web服务器后台

  • 命令行工具

    • npm(node)
    • git(c语言)
    • hexo(node)
  • 对于前端开发工程师来讲,接触node最多的是它的命令行工具,自己写的很少,主要是使用别人第三方的

    • webpack
    • gulp
    • npm

学到什么

  • B/S编程模型

    • Browser-Server
    • back-end
    • 任何服务端技术这种B/S编程模型都是一样,和语言无关
    • node只是作为我们学习B/S编程模型的一个工具而已
  • 模块化编程

    • RequireJS
    • SeaJS
    • @import(文件路径)
    • 以前认知的JavaScript只能通过script 标签来加载
    • 在Node中可以像@import()一样来引用加载JavaScript脚本文件
  • node常用API

  • 异步编程

  • 回调函数

    • Promise
    • async
    • generator
  • Express Web 开发框架

  • Ecmascript 6

    • 在课程中穿插讲解
    • 它只是一个新的语法而已引
  • 学习node不仅会帮助大家打开服务端黑盒子,同时会帮助你学习以后的前端高级内容

    • Vue.js
    • React
    • angular

使用Node

安装完毕后,在cmd中输入

node --version

若有版本输出,则表示安装成功

  • 新建JS文件

JS文件的命名除了是node.js其他都可以,在命令行中进入文件所在路径,输入node 文件名.js 即可运行

node 文件名.js
  • 与浏览器JS的差异

node中JS没有window对象,更不用说document对象,不操作BOMDOM

node中的JS具有文件操作能力

文件操作

fs 是 file-system 的简写,就是文件系统的意思

在 Node 中如果想要进行文件操作,就必须引入 fs 这个核心模块

在 fs 这个核心模块中,就提供了所有的文件操作相关的API

使用步骤

  • 使用 require 方法加载 fs 的核心模块
var fs = require('fs'); 
  • 读取文件
//path:文件路径
//function 回调函数,读取完毕执行
//读取成功 data 数据 error null
//读取失败 data null error 错误对象
fs.readFile(path,function(error,data));
//读取到的data是十六进制数据
//所以通过toString()方法令人类识别
//通过判断error是否存在来判断,读取是否成功
    
fs.readdir(path,function(error,files));
//读取文件目录
//files为文件目录数组
  • 写入文件
//path 文件路径
//content 内容
//function 回调函数,写入完毕执行
//error错误对象
fs.writeFile(path,content,function(error){
	console.log('success');
})
//通过判断error是否存在来判断,写入是否成功
//一般来说error为不正确

构建web服务器

使用 require 方法加载http 的核心模块

var http = require('http');

使用http.createServer()方法创建一个web服务器,返回一个server实例

var server = http.createServer();

注册request 请求事件

//服务器提供服务
//1、发请求
//2、接受请求
//3、处理请求
//4、给个反馈(发送响应)
//当客户端请求过来,会自动触发服务器的 request 请求事件
//然后执行第二个参数,回调处理
//回调函数 两个参数 request response
server.on('request',function(request, response){
   console.log('收到客户端的请求了');
   console.log('请求路径是:'+request.url);
   //response对象有一个方法:write可以用来给客户端发送响应数据
   //write 可以使用多次,但是最后一定要使用end来结束响应,否则客户端会一直等待
   response.write('hello');
   response.write(' nodejs');
   response.end();
   //可以在end的同时write,效果相同
   response.end('hello nodejs'); 
   //  只能接收字符串  
});
//Request 请求对象
//请求对象可以用来获取客户端的一些请求信息,例如请求路径
//Response响应对象
//响应对象可以用来给客户端发送响应消息

绑定端口号,启动服务器

//端口可以自行设置,只要不被占用即可
server.listen(3000,function(){
    console.log("成功启动服务器,可以通过 http://127.0.0.1:3000/ 访问")
});

根据不同的请求路径发送不同的响应结果

server.on('request',function(req,res){
	// res.write('<h1>asd</h1>');
	// res.end();
	res.end('<h1>asd</h1>');
	//1、根据不同的请求路径发送不同的响应结果
	// req.url 获取到的是端口号之后的一部分路径
	// 也就是说所有的url都是以 / 开头
	var url = req.url;
	if(url === '/'){
		res.end("index page");
	}else if(url === 'login'){
		res,end("login page");
	}else{
		res.end('404 Not Found!');
	}
});

模块化编程

模块之间的关系

//require是一个方法
//他的作用就是用来加载模块的
//在Node中,模块有三种
//	具名的核心模块,如fs,http,os
//	用户自己编写的文件模块
//在Node中,没有全局作用域,只有模块作用域,模块之间互不影响
//	内部无法访问外部,外部无法访问内部
//a.js
console.log('a start');
//  ./表示相对路径,不加则会被误认为模块
require('./b.js');
console.log('a end');
//b.js
console.log('b start');
require('./c.js');
console.log('b end');
//c.js
console.log('ccc');
结果:
a start
b start
ccc
b end
a end

加载与导出

require 方法有两个作用
1.加载文件模块并执行里面的代码
2.拿到被加载文件模块导出的接口对象
在每个文件模块中都提供了一个对象:exports
exports默认是一个空对象
你要做的就是把所有需要被外部访问的成员挂载到exports对象上

IP地址和端口号

ip地址用来定位计算机,端口号用来定位具体的应用程序,一切需要联网通信的软件都会占用一一个端口号,端口号的范围从0- 65536之间,在计算机中有-些默认端口号,最好不要去使用,例如http服务的80,我们在开发过程中使用一些简单好记的就可以了,例如3000、5000 等没什么含义的端口号。

响应内容类型Content-type

在服务端默认发送的数据,其实是utf-8编码的内容,但是浏览器不知道你是utf-8编码的内容,浏览器在不知道服务器响应内容的编码的情况下会按照当前操作系统的默认编码去解,中文操作系统默认是gbk,解决方法就是正确的告诉浏览器我给你发送的内容是什么编码的,在http 协议中,Content-Type 就是用来告知对方我给你发送的数据内容是什么类型,res.setHeader('Content - Type', 'text/plain; charset=utf-8')plain 作为普通文本,html 作为html代码,浏览器会自动解析渲染。

具体查找网址:tool.oschina.net/commons

如果是图片,字符编码不用指定,即后半部分charset不需要指定。

art-template模板引擎

安装

npm install art-template

该命令在哪里执行,默认会下载到node_modules目录中,node_modules 不要改,也不支持改

在浏览器中使用模板引擎

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<!-- 在浏览器中引用以下文件 
			强调:模板引擎不关心你的字符串内容,只关心自己能认识的模板标记语法,例如{{}}
			就算其中有标签或者其他特殊内容,也只是字符串,不会被解析渲染
			{{}}语法称之为mustache语法,八字胡语法。
		-->
		<script src="node_modules/art-template/lib/template-web.js"></script>
		<script type="text/text/template" id="tql">
			大家好,我叫{{name}},我今年{{age}}岁了.
			我的爱好是{{each hobbies}}{{$value}}{{/each}}
		</script>
		<script type="text/javascript">
			var ret=template("tql",{
				name:'Jack',
				age:18,
				hobbies:["电脑","游戏"]
			});
			console.log(ret);
		</script>
	</body>
</html>

在node中使用art-template模板引擎

模板引擎最早就是诞生于服务器领域,后来才发展到了前端

1、安装

2、引用

​ 只需要使用 require 方法加载就可以了:require('art-template')

参数中的 `art-template `就是你下载包的名字    

​ 也就是说你 install 的名字是什么,则你 require 中的就是什么

3、查文档,使用模板引擎的API

var template = require('art-template');
//模板字符串过长可以使用反引号字符串
//可以和fs读取文件模块配合使用,注意fs.readFile读取到的数据是二进制数据,而模板引擎需要字符串,需要使用data.toString()
var ret = template.render("{{temp}}字符串",{
	temp:'模板'
})
console.log(ret);

服务端渲染和客户端渲染的区别

  • 客户端渲染不利于SEO搜索引擎优化
  • 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
  • 所以你会发现真正的网站既不是纯异步也不是纯服务端渲染出来的
  • 而是两者结合来做的
  • 例如京东的商品列表就采用的是服务端渲染,目的了为了SEO搜索引擎优化
  • 而它的商品评论列表为了用户体验,而且也不需要SEO优化,所以采用是客户端渲染

Node中的模块系统

使用Node编写应用程序主要就是在使用:

  • EcmaScript语法

    • 和浏览器不同,没有BOMDOM
  • 核心模块

    • 文件操作的fs
    • http 服务的 http
    • url 路径操作模块
    • path 路径处理模块
    • os操作系统信息
  • 第三方模块

    • art-template
    • 通过npm下载
  • 自己写的模块

    • 自己创建的文件

什么是模块化

  • 文件作用域

  • 通信规则

    • 加载require
    • 导出

CommonJS模块规范

Node中的Javascript还有一个很重要的概念:模块系统

  • 模块作用域
  • 使用require方法用来加载模块
  • 使用exports接口对象用来导出模块中的成员

加载require

var 自定义变量名 = require('模块');

两个作用:

  • 执行被加载模块的代码
  • 得到被加载模块中的exports导出接口对象

导出exports

  • Node中是模块作用域,默认文件中所有的成员只在当前文件模块有效
  • 对于希望可以被其他模块访问的成员,我们就需要把这些公开的成员都挂载到exports接口对象上就可以了

导出多个成员(必须在对象中):

exports.a = 123;
exports.b = "hello";
exports.c = function(){
    console.log("ccc");
}
exports.d={
    foo:'bar'
}

导出单个成员(函数、字符串等):

module.exports="123";
module.exports=function(){};
//单纯的赋值行为,后者会覆盖前者
module.exports={
    a:;
    b:;
    c:;
}
//等价于导出多个成员

原理解析

exportsmodule.exports 属于同一个引用

//每个模块都有一个自己的module对象
//模块最后会return module.exports
//以上行为都是底层隐式的
console.log(exports===module.exports)//==> true

require方法模块加载规则

  • 优先从缓存加载
//main.js
require("./a");
var b = require("./b");//不会加载模块,只会得到exports
//a.js
console.log("a被加载");
require("./b");
//b.js
console.log("b被加载");
exports.add=function(){};
//b已经在a中加载过,所以在main中不会加载,require仅仅为了得到b的exports。
//所以,加载模块时首先从缓存中开始加载
  • 判断模块标识

    路径模块标识

    ./ 当前目录,不可省略,省略会被当做核心模块名加载,下同

    ../ 上一级目录,不可省略

    / 在首位表示当前文件模块所属磁盘根路径,基本不用

    绝对路径 基本不用

核心模块标识

​ 本质也是文件

​ 已经被编译到二进制文件中,只需按照名字来加载即可

第三方模块

​ 凡是第三方模块必须通过npm来下载

​ 使用的时候就可以通过require('包名') 的方式来加载使用

​ 不可能有任何一个第三方包和核心模块的名字是一样的

​ 1、先找到当前文件所处目录中的 node_modules 目录

​ 2、node_modules/模块名/package.json下的main属性

​ 3、main属性中记录了第三方模块名的入口模块

​ 4、加载入口模块就加载了整个模块,实际加载还是文件

​ 5、如果没有package.json文件或文件中没有main属性有错误,会自动寻找该目录下的index.js文件加载

​ 6、如果也找不到则会不停地从上一级目录开始找,规则同上,直到找到根目录都找不到,则会报错 cannot find module 'XXX'

​ PS:一个项目有且只有一个node_modules目录,放在项目根目录,可供子文件加载

npm

​ 全称:node package manager

npm网站

npm命令行工具

​ npm的第二层含义就是一个命令来工具

​ npm 也有版本这个概念

#查看版本
npm --version 
#升级npm
npm install --global npm

常用命令

npm init 
    npm init -y #跳过向导快速生成
npm install  #下载dependencies依赖中的所有包
    npm i
npm install 包名
npm install 包名 --save #下载并保存依赖项(package.json 中的 dependencies选项)    
    npm i 包名 -s
npm uninstall 包名   #删除包
    npm un 包名
npm uninstall 包名 --save  #删除包并删除依赖信息
   npm un 包名 -s
npm help #查看使用帮助
npm 命令 --help   #查看指定命令的帮助   

npm被墙问题

npm存储包文件的服务器在国外,有时候被墙或速度很慢。

淘宝NPM 淘宝开发团队把npm 在国内做了一个备份

安装淘宝的 cnpm:

npm install --global cnpm  #--global 全局安装 并非当前目录 不可省略
#接下来安装包时,都用cnpm命令而不是npm

不想安装cnpm

npm install 包名 --registry=https://registry.npm.taobao.org
#每次手动加参数非常麻烦,所以可以把这个选项加入到配置文件中
npm config set registry https://registry.npm.taobao.orgw
#只要经过了上面命令的配置,则以后所有 npm install 都会默认通过淘宝的服务器来下载
#查看 npm 配置信息
npm config list

包描述文件(package.json)

建议每个项目都要有一个 package.json 文件(包描述文件,就像产品的说明书一样),给人踏实的感觉。

这个文件可以通过 npm init 生成

最有用的是 denpendecies 选项,可以用来帮我们保存第三方包的依赖信息

如果不小心将 node_modules 目录删除了,只要在根目录下直接命令行输入 npm install 会自动根据package.json 来下载相关第三方包

  • 建议每个项目的根目录下都有一个 package.json文件
  • 建议执行 npm install 包名 的时候加上 --save 选项,目的是用来保存依赖信息

package-lock.json

npm 5 以前是不会有package-lock.json这个文件的。

npm 5 以后才加入了这个文件。

  • 当你安装包的时候,npm都会生成或者更新package-lock.json这个文件。

    • npm5以后的版本安装包不需要加--save参数,它会自动保存依赖信息

    • 当你安装包的时候,会自动创建或者是更新package-lock.json 这个文件

    • package-lock.json 这个文件会保存node_modules中所有包的信息(版本、下载地址)

    • 这样的话重新npm install的时候速度就可以提升

    • 从文件来看,有一个 lock 称之为锁

  • 这个lock是用来锁定版本的。如果项目依赖了1.1.1版本

    • 如果你重新install其实会下载最新版本,而不是1.1.1
    • 我们的目的就是希望可以锁住1.1.1这个版本
    • 所以这个 package-lock.json 这个文件的另一个作用就是锁定版本号,防止自动升级新版

Express

​ 原生的http在某些方面表现不足以应对我们的开发需求,所以我们就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码更高度统一。

​ 在Node中,有很多 Web 开发框架,我们这里以学习 Express 为主。

Express官网

hello world

var express = require('express');
var app = express();
app.get('/',function(req,res){
	res.send("hello world");
})
app.listen(3000,function(){
	console.log('app is running at port 3000');
})

基本路由

开一个get方法或post方法,处理一个请求,不开则不处理。就像路由器分发网络的端口

路由器:

  • 请求方法
  • 请求路径
  • 请求处理函数
//当你以 GET 方法请求/时,执行对应的处理函数
app.get("/",function(req,res){
    res.send('Hello Wordld!');
})
//当你以 POST 方法请求/时,执行对应的处理函数
app.post("/",function(req,res){
    res.send('Hello Wordld!');
})

静态服务

//可以用127.0.0.1:3000/public访问public目录下所有资源
app.use("/public/",express.static("./public/"));
//可以用127.0.0.1:3000/a访问public目录下所有资源
app.use("/a/",express.static("./public/"));
//可以用127.0.0.1:3000/访问public目录下所有资源
app.use(static("./public/"));

在express中配置art-template

​ 安装

npm install -s art-template
npm install -s express-art-template

​ 配置

//第一个参数 当渲染以 .art 为拓展名文件时,使用art-template模板引擎
//express-art-template 是专门用来在 Express 中把 art-template 整合到 Express中
//虽然外面这里不需要加载 art-template ,但是也必须安装 ,有依赖关系
app.engine("art",require("express-art-template"))
//Express 为 Response 相应对象提供了 render 方法
//render 方法默认不可使用,必须配置了模板引擎才可以使用
//res.render("html模板名",{模板数据})
//第一个参数不能写别的路径,默认会去项目中views目录中查找,这是规定

//修改默认路径
app.set("views",需要改的路径)

​ 使用

app.get('/',function(req,res){
    res.render('page.html',obj);//不用渲染的页面可不加第二个参数
});

express与原生的区别

var express = require("express");
var app = express();
//响应 GET 请求
app.get('/',function(req,res){
    res.render('page.html',obj);
    //重定向 跳转首页
    res.redirect("/");
    //获得 GET 请求内容
    console.log(req.query);
});
//响应 POST 请求
app.post('/',function(req,res){
    res.render('page.html',obj);
});

在Express获取表单GET请求体数据

​ 在Express 中有内置获取表单get请求体的属性

req.query;

在Express获取表单POST请求体数据

​ 在Express 中没有内置获取表单post请求体的API,需要使用一个第三方包 body-parser

安装

npm install --s body-parse

配置

var bodyParser = require("body-Parser");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//加入这个配置后,在req请求对象上会多出一个属性 body
//通过 req.body 获取post请求体内容

中间件

中间件函数在应用的request--reponse周期中,能够使用request对象(req)response对象(res)next函数(next。能够实现如下功能

  • 执行逻辑代码;
  • 更改request对象和response对象;
  • 结束request--reponse周期;
  • 调用栈中的下一个中间件。

tips: 如果当前的中间件函数没有结束request--reponse周期,则必须调用next(),将控制权交给下一个中间件函数,否则请求将被挂起。

Express的应用可以使用以下类型的中间件:

  • 应用级别中间件
  • 路由级别中间件
  • 错误处理中间件
  • 内置中间件
  • 第三方中间件
  1. 应用级别中间件

将应用级别的中间件绑定到Express实例--app上,使用app.use()app.METHOD(此METHODgetpost等方法)

tips: 从一个路由到另一个路由(另一个路由使用app.METHOD定义的),使用next('route')

app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id === '0') next('route')
  // otherwise pass the control to the next middleware function in this stack
  else next()
}, function (req, res, next) {
  // send a regular response
  res.send('regular')
})

// handler for the /user/:id path, which sends a special response
app.get('/user/:id', function (req, res, next) {
  res.send('special')
})
  1. 路由级别中间件

    同应用级别的中间件用法相同,不同之处的是它是绑定在router实例(express.Router())上的

    tips: 同样也可以使用next('route') 从一个路由到另一个路由(另一个路由使用router.METHOD定义的)

  2. 误处理中间件

    与其他中间件函数的定义基本相同,不同之处在于错误处理函数多了一个变量:err,即它有4个变量:err, req, res, next

    app.use(function(err, req, res, next) {
      console.error(err.stack);
      res.status(500).send('Something broke!');
    });
    

    tips: next()传入的参数除了字符串'route'外,其他参数会认为出错,交由错误处理函数处理。若未显式定义错误处理函数,则函数集的末尾有express隐式包含的默认错误处理程序。

  3. 内置中间件

    express.static()express.json()express.urlencoded()

  4. 第三方中间件

    如操作cookiecookie-parser

    读取解析的cookiereq.cookies(读取被签名的cookiereq.signedCookies),读取cookie字符串用expressreq.headers.cookie;

    设置cookie用express的res.cookie(key, value)res.header('Set-Cookie', 'key=value');

    var express = require('express');
    var app = express();
    var cookieParser = require('cookie-parser');
    app.use('cookieParser ');
    app.get('/', function(req, res, next){
      console.log(req.cookies); // 解析过的object的形式--{ k1: 'v1', k2: 'v2' }。引入了cookie-parser方可使用
      console.log(req.headers.cookie);// string的形式--k1=v1; k2=v2。express原生可以用
      res.cookie('key3', 'val3'); // 设置cookie,express原生可以用
      res.send('cookie example');
    })
    

    tips:使用session可以用express-session中间件,默认是存储在内存中,server重启后会丢失,不适用于生产环境。可以通过设置store参数 将session内容存储在数据库中。

    tips:关于安全问题的两点:

    • app.disable('x-powered-by'); ``reponse中不会返回X-Powered-By字段(如果不做设置会返回server的类型,如X-Powered-By: Express,可能导致针对性的攻击);
    • 使用session时,将cookiesessionidkey设置为通用的字符串如sessionID(不设置的话sessionid默认的keyconnect.sid),同上一条的原因一样,防止黑客针对你的后台类型进行针对性攻击。
    app.use(session({
      name : 'sessionId',
      resave:false,
      saveUninitialized: false
    }));
    

MongoDB

关系型数据库和非关系型数据库

表就是关系,或者说表与表之间存在关系

  • 所有的关系型数据库都需要通过 sql 语句来操作

  • 所有的关系型数据库在操作前都要设计表结构

  • 而且数据表还支持约束

    • 唯一的
    • 主键
    • 默认
    • 非空
    • 等。。。。
  • 非关系型数据库非常灵活

  • 有的非关系型数据库就是 键值对

  • 但是 MongoDB 是最像关系型数据库的非关系型数据库

    • 数据库——数据库
    • 数据表——集合(数组)
    • 记录表——(文档对象)
  • MongoDB不需要设计表结构

  • 也就是说可以任意地往里面存数据,没有结构性这么一说

  • 下载MongoDB

启动与关闭数据库

# mongodb 默认使用执行 mongod 命令所处盘符根目录下的 data/db 作为自己的数据存储目录
# 所以在第一次执行该命令之前 自己先手动新建一个 data/db 目录
mongod --dbpath D:\data\db
# 修改默认数据存储目录
mongod --dbpath = 新目录
# 如何停止
直接Ctrl+c
或者直接关闭命令行窗口

连接和退出数据库

# 启动数据库后开启一个新的命令行窗口,该命令默认连接本机的 MongoDB 服务
mongo
# 在连接状态下输入该命令
exit

基本命令

  • show dbs

    • 查看显示所有数据库
    • 空数据库不会被显示,除了系统数据local admin config
  • db

    • 查看当前操作的数据库
  • use 数据库名称

    • 切换到指定的数据库(没有则新建)

在Node中如何操作MongoDB数据库

使用官方的 mongodb 包来操作

使用第三方包 mongoose 来操作

第三方包:mongoose 基于 MongoDB 官方的 mongodb 包再一次做了封装

​ 网址:官网

const mongoose = require('mongoose');
// 连接 MongoDB 数据库
mongoose.connect('mongodb://localhost/test', {useUnifiedTopology: true,useNewUrlParser: true});

//设计数据库
const Cat = mongoose.model('Cat', { name: String });

//实例化一个 cat
const kitty = new Cat({ name: 'lly2' });
//持久化保存 kitty 实例
kitty.save().then(() => console.log('meow'));

MongoDB 数据库基本概念

{//MongoDB
    qq:{//数据库,可以有多个
        users:[//集合,相当于关系型数据库中的表,没有表的限制,很灵活
            {},//文档,相当于表记录
            {},
            {}
        ],
        products:[
            
        ]
        ...
    },
    taobao:{
        
    }
}
//操作时,比如插入文档,不用像关系型数据库一样,新建库,新建表,设计表,插入
//MongoDB 会自动帮你创建设计

MongoDB 数据库基本使用

var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/test', {useUnifiedTopology: true,useNewUrlParser: true});

var Schema = mongoose.Schema;

//设计文档结构(表结构)
var userSchema = new Schema({
	username: {
		type:String,
		required:true //不得为空
	},
	password: {
		type:String,
		required:true //不得为空
	},
	email:{
		type:String
	}
});

//将文档结构发布为模型
// mongoose.model 方法将一个架构发布为模型
// 参数1:传入一个开头大写名词字符串表示数据库名称
//			mongoose 会自动生成 小写复数 形式的集合名称
//			此处User 数据库 集合名称 users
var User = mongoose.model('User', userSchema)

var admin = new User({
	username:"wcwcwc",
	password:"123456",
	email:"1311313@qq.com"
});

//新增数据
admin.save(function(err,ret){
	if(err)
		return console.log(err);
	console.log(ret);
})

//查询数据
//返回所有符合条件的文档 返回值为数组 结果只有一个也返回数组
User.find({
	username:"wcwcwc" ,//查找username为wcwcwc
	//省略该参数为 查询所有
},function(err,ret){
	if(err)
		return console.log(error);
	console.log(ret);
})
//只返回符合条件的第一个 返回值为对象
User.findOne({
	username:"wcwcwc" //查找username为wcwcwc
},function(err,ret){
	if(err)
		return console.log(error);
	console.log(ret);
})

//删除数据 
User.remove({
	username:"wcwcwc"
},function(err,ret){
	if(err)
		console.log(err);
})

//更新数据
User.updateMany({//被更新数据条件
	username:"wcwcwc"
},{//更新为
	username:"zszszs"
},function(err){
	if(err)
		console.log(err);
	console.log("success");
})

杂项

获取可以判断的url

​ 表单的actionget时,发送请求的url会变成 /pathname?key1=value1&key2=value2的形式

​ 在服务器端无法判断直接url中的pathname

​ 所以使用url模块的中的parse(string,true)方法解析url

​ 可以直接获取pathname属性进行判断

​ 请求的数据可以直接使用query,用JSON.parse转为js对象

如何通过服务器让客户端重定向

  1. 状态码设置为302临时重定向

    res.statusCode=302

​ ps: 301为永久重定向

​ 比如打开新浪主页www.sina.com会直接跳转到www.sina.com.cn

  1. 在响应头中通过Location 告诉客户端往哪儿重定向

    res.setHeader("Location","/")

    如果客户端发现收到服务器的响应的状态码是302就会自动去响应头中找Location,发起新的请求

    所以你就能看到客户端自动跳转了

修改完代码自动重启

使用第三方命令行工具 nodemon 来帮我们解决频繁修改代码重启服务器问题。

nodemon 是一个基于Node.js 开发的一个第三方命令行工具,使用时需独立安装

npm install --global nodemon
#原本
node app.js
#使用 nodemon 可以在全局使用
nodemon app.js

nodemon 会监视文件的变化,当文件每次保存时,会自动重启服务器

浏览器中JS的模块化

require.js 第三方库 AMD

sea.js 第三方库 CMD

不论是 CommonJS(Node 模块化规范)AMDCMDUMDES6 Modules 官方规范

都是为了解决JavaScript 的模块化问题

解决回调地狱——promise

//Promise 是ES6 提供异步编程的一种解决方案
//Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。
var p1 = new Promise(function(resolve,reject){
    fs.readFile("./asd.txt",function(err,data){
        if(err)
            reject(err);//reject传递的参数将在then的第二个函数中使用
        resolve(data);//resolve传递的参数将在then的第一个函数中使用
    });
});
//then 方法接收两个函数作为参数,
//第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,
//两个函数只会有一个被调用。
p1.then(function(data){//成功调用
    console.log(data);
},function(err){       //失败调用
    console.log(err)
})
//then 方法将返回一个 resolved 或 rejected 状态的 Promise 对象用于链式调用,且 Promise 对象的值就是这个返回值。

Promise封装的readFile

var pReadFile = function(path){
    return new Promise(
        function(resolved,rejected){
            fs.readFile(path,function(err,data){
                if(err)
                    rejected(err);
                else
                    resolved(data);    
            })
        }
    );
}

path 路径操作模块

var path = require('path');
var p="C:a\\b\\c\\index.html";
//获得文件名部分
path.basename(p[,'html'])// -->index[.html]
//获得目录部分
path.dirname(p)// --> C:a\\b\\c
//获得文件拓展名
path.extname(p)// --> html
//格式化路径
path.parse(p)
//{ root: 'C:\\',
//  dir: 'C:\\a\\b\\c',
//  base: 'index.html',
//  ext: '.html',
//  name: 'index' }
//拼接路径
path.join("C:\\a","b")// --> C:\\a\\b
//判断是否为绝对路径
path.isAbsolute(p) // --> true

__dirname__filname

在每个模块中,除了 requireexports等模块相关API 之外,还有两个特殊成员:

__dirname:

​ 动态获取当前文件模块所属目录的绝对路径

__filename:

​ 动态获取当前文件模块的绝对路径

区别:前者无文件名,后者有

在文件操作中,使用相对路径是不可靠的,因为在Node中 文件操作模块被设计 ./ node命令所处的路径

为了解决这个问题,只需要把相对路径变为绝对路径就可以了

require文件模块,则没有这些问题。

表单异步请求和异步请求

同步请求:

​ 在form标签的 method 中填写getpost ,均为同步请求。同步请求发出后,服务端回应该请求前,无法做别的事情

异步请求:

​ 给form中的submit按钮绑定ajax事件,为异步请求。异步请求发出后,服务端未回应前,也可以做别的事情。但是无法在服务器中给页面重定向,需要在该ajax事件上用浏览器JS进行跳转/

cookies和session

  1. 数据存放位置不同:

cookie数据存放在客户的浏览器上,session数据放在服务器上。不持久化保存的话,都在内存里,服务器一旦重启就会消失。

  1. 安全程度不同:

cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session

  1. 性能使用程度不同:

session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie

  1. 数据存储大小不同:

单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20cookie,而session则存储与服务端,浏览器对其没有限制。

Express 默认不支持 session 需要安装第三方插件 express-session

#安装
npm i express-session
//配置
app.use(session({
    secrect: 'keyboard cat',//配置加密字符串,在原有加密基础上加上这个字符串再加密。增加安全性
    resave: false,
    saveUninitialized: true //不论是否使用session ,默认直接给一个session
}));
//使用 配置好后req参数会有一个新的空对象 session
//添加 session 数据
req.session.login=true;
//获取 session 数据
req.session.login;