node学习整理(二)

334 阅读12分钟

16. http模块

引入http模块

const http = require('http');

创建服务

const server = http.createServer((req,res)=>{})

http.createServer()

参数为一个回调函数
回调函数有两个参数
  1. req <http.IncomingMessage> =>HTTP请求的参数
    • 它可用于访问响应状态、消息头、以及数据。
  2. res <http.IncomingMessage> =>当收到此请求的响应时触发。 此事件仅触发一次。(响应)
    • 此对象由 HTTP 服务器在内部创建,而不是由用户创建。 它作为第二个参数传给回调函数。
回调函数内部 :
  1. req
    • req.url => 请求的 URL 字符串。 它仅包含实际 HTTP 请求中存在的 URL。
/* 
GET /status?name=ryan HTTP/1.1\r\n
Accept: text/plain\r\n
\r\n */
// 如req.url 请求的是上述则返回:
'/status?name=ryan'
  1. res
    • res.writeHead(状态码,[响应头]) => 向请求发送响应
      • 状态码:一个3位的HTTP状态码,如200(类型为number);
      • 响应头:一个对象,用来设置响应头的内容
        • 例如:{'Content-Type': 'text/html'}
    • res.end()
      • 此方法向服务器发出信号,表明已发送所有响应头和主体,该服务器应该视为此消息已完成
      • 必须在每个响应上调用此方法

17. url (统一资源定位符)

┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                              href                                              │
├──────────┬──┬─────────────────────┬────────────────────────┬───────────────────────────┬───────┤
│ protocol │  │        auth         │          host          │           path            │ hash  │
│          │  │                     ├─────────────────┬──────┼──────────┬────────────────┤       │
│          │  │                     │    hostname     │ port │ pathname │     search     │       │
│          │  │                     │                 │      │          ├─┬──────────────┤       │
│          │  │                     │                 │      │          │ │    query     │       │
"  https:   //    user   :   pass   @ sub.example.com : 8080   /p/a/t/h  ?  query=string   #hash "
│          │  │          │          │    hostname     │ port │          │                │       │
│          │  │          │          ├─────────────────┴──────┤          │                │       │
│ protocol │  │ username │ password │          host          │          │                │       │
├──────────┴──┼──────────┴──────────┼────────────────────────┤          │                │       │
│   origin    │                     │         origin         │ pathname │     search     │ hash  │
├─────────────┴─────────────────────┴────────────────────────┴──────────┴────────────────┴───────┤
│                                              href                                              │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(all spaces in the "" line should be ignored — they are purely for formatting)

引入url模块

  • const url=require("url");

将url字符串转换为url对象

  • url.parse(url字符串)
let urlString = 'https:47.95.207.1:3000/fcj/recommend/hot/hehe?us=123&ps=456#nihao';
let urlObj = url.parse(urlString);
console.log(urlObj);
/* 
	urlObj = {
  protocol: 'https:',
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: '#nihao',
  search: '?us=123&ps=456',
  query: 'us=123&ps=456',
  pathname: '47.95.207.1:3000/fcj/recommend/hot/hehe',
  path: '47.95.207.1:3000/fcj/recommend/hot/hehe?us=123&ps=456',
  href: 'https:47.95.207.1:3000/fcj/recommend/hot/hehe?us=123&ps=456#nihao' }
*/

将url对象转为url字符串

  • url.format(url对象)
let urlObj = {
  protocol: 'https:',
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: '#nihao',
  search: '?us=123&ps=456',
  query: 'us=123&ps=456',
  pathname: '47.95.207.1:3000/fcj/recommend/hot/hehe',
  path: '47.95.207.1:3000/fcj/recommend/hot/hehe?us=123&ps=456',
	href: 'https:47.95.207.1:3000/fcj/recommend/hot/hehe?us=123&ps=456#nihao' 
	}
let urlString = url.format(urlObj);
console.log(urlString);
	/* 
	urlString = https:47.95.207.1:3000/fcj/recommend/hot/hehe?us=123&ps=456#nihao
	*/

18. querystring 查询字符串

引入querystring模块

  • const qs = require('querystring');

将query字符串转换为query对象

  • qs.parse(query字符串,键值对的分隔符,键与值的分隔符)
let string = 'name=qwe&pass=123&sex=0';
let obj = qs.parse(string,'&','=');
console.log(obj);
/* 
{ name: 'qwe', pass: '123', sex: '0' }
*/

将query对象转为query字符串

  • qs.stringify(query对象,键值对的分隔符,键与值的分隔符)
let obj = { name: 'qwe', pass: '123', sex: '0' };
let string = qs.stringify(obj,'&','=');
console.log(string);
/* 
name=qwe&pass=123&sex=0
*/

执行 URL 百分比编码字符的解码

  • qs.unescape(url字符串);
let string = 'w%3D%E4%BD%A0%E5%A5%BD%26foo%3Dbar';
let result = qs.unescape(string);
console.log(result);
/* 
w=你好&foo=bar
*/

执行 URL 百分比编码

  • qs.unescape(url字符串);
let string = 'w=你好&foo=bar';
let result = qs.escape(string);
console.log(result);
/* 
w%3D%E4%BD%A0%E5%A5%BD%26foo%3Dbar
*/

19. express

express 环境搭建

  1. 安装
	$ npm install express --save
  1. 引入模块
	const express = require('express')
    const app = express()
  1. 开启服务
    • app.listen()
      • 第一个参数是端口号
      • 第二个参数是主机名(可省略 默认为127.0.0.1)
      • 第三个参数是回调函数
app.listen(80,()=>{
	console.log("success");
});

get访问

  • app.get()
    • 第一个参数是访问的地址
      • 不区分大小写
      • 地址相同时,会执行第一个
    • 第二个参数是一个回调函数
      • req是请求对象
      • res是响应对象
    • 第三个参数是next(可省略)
      • 可以通过next继续向下查找是否还有符合条件的路由
app.get("/",function(req,res){
	res.end("get->/");
});
/***************************/
// 只会执行第一个
app.get("/weibo",function (req,res) {
    res.end("get->weibo");
    res.end("lala");
})
app.get("/weibo",function (req,res) {
    res.end("get->weibo2");
})
/***************************/
app.get("/first",function (req,res,next) {
    console.log(11111111111);
    // res.end("first");
    next();
})
app.get("/first",function (req,res) {
    console.log(2222222);
    res.end("first2");
})

post访问

  • app.post()
    • 第一个参数是访问的地址
      • 不区分大小写
      • 地址相同时,会执行第一个
    • 第二个参数是一个回调函数
      • req是请求对象
      • res是响应对象
    • 第三个参数是next (可省略)
      • 可以通过next继续向下查找是否有符合条件的路由
app.post("/",function(req,res){
	res.end("post");
})

以get/post形式访问所有地址

app.get("*",function(req,res,next){
	res.end();
})
///////////
app.post("*",function(req,res,next){
	res.end();
})

通过 * 来对找不到访问地址时输出404

// 当访问地址不是/first时 输出404
app.get("/first",function (req,res) {
    console.log(2222222);
    res.end("first2");
})
app.get("*",function (req,res) {
    res.end("404")
})

不限制访问形式,不限制访问地址

app.all("*",function(req,res){
	res.end("all");
})

get 接收query值

1. 当传参方式为:http://127.0.0.1/sum?a=1&b=2

  • 用req.query接受
app.get("/sum",function(req,res){
	const data = req.query;
	console.log(data); // { a: '1', b: '2' }
	res.end();
})

2. 当传参方式为:http://127.0.0.1/sum/1/2

  • 用req.params接收
app.get("/sum/:a/:b",function(req,res){
	const data = req.params;
	console.log(data); // { a: '1', b: '2' }
	res.end();
})

post接收数据

需要依赖 body-parser模块

  1. $ cnpm install body-parser -S
  2. 引入模块
    • const bodyParser = require("body-parser")

常用的有两种,分别对应两种传输方式

1. 传输参数为json格式
const xhr = new XMLHttpRequest();
xhr.open("post","http://127.0.0.1/sum?a=1&b=200");
xhr.setRequestHeader("content-type","application/json")
xhr.send('{"e":12}');
xhr.onload = function () {
		console.log(xhr.responseText);
}
const bodyParser = require("body-parser")
app.use(bodyParser.json());// 接收JSON数据,将结果放到req.body中
app.post("/sum",function (req,res) {
    console.log(req.body);
    res.end("sum");
})
2. 传输参数为urlencoded格式
const xhr = new XMLHttpRequest();
xhr.open("post","http://127.0.0.1/sum?a=1&b=200");
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded")
xhr.send("c=3&d=4");
xhr.onload = function () {
		console.log(xhr.responseText);
}
const bodyParser = require("body-parser")
app.use(bodyParser.urlencoded());// 接收urlencoded数据,将结果放到req.body中
app.post("/sum",function (req,res) {
    console.log(req.body);
    res.end("sum");
})

请求模拟工具 postman

静态资源配置


app.use(express.static('public'))

app.use('/static', express.static('public'))

app.use('/static', express.static(path.join(__dirname, 'public')))

res.json()

  • 效果相当于res.end();
  • 表示结束响应
  • 并输出
  • 可直接写入json格式的数据

20. mongod

安装配置

  • Mongodb官网下载最新版本的Mongodb下载地址
  • 下载msiwindow安装包,可以装到C盘或者D盘目录下

配置

- 由于我是安装在D盘的环境下
D:\Program Files (x86)\MongoDB\Server\3.2\bin
  • 所以在bin文件夹下找到mongod.exe命令,然后通过管理员执行mongod --dbpath x路径x,路径可以是任何地方,我这里选择在D盘的MongoDB目录下,当然路径不要包含特殊的字符串,比如Program Files (x86)也不行
mongod --dbpath D:\mongodb\data\db

image

命令行

  • 经过上面的配置之后,就可以返回bin目录下找到mongo.exe命令,并管理员下执行,就可以出现mongodb的命令行模式
D:\Program Files (x86)\MongoDB\Server\3.2\bin
  • 然后就可以使用下面的命令来测试了

mongod

db.help()//帮助
db.stats()//统计

显示数据库

  • show dbs

检查当前选择的数据库

  • db

添加数据库

  • 数据库名为数据库创建的名字,使用该命令后会默认切换到对应的数据库,并且在数据库中添加选项,数据库信息才显示,如果默认就有该数据库,那就是切换到对应的数据库里面
 use 数据库名

删除数据库

  • 先切换到对应的数据库,然后再执行db.dropDatabase()删除该数据库
use 数据库名
//switched to db 数据库名
db.dropDatabase()

显示集合

  • 用一下命令可以检查创建的集合
show collections

添加集合

  • 在创建完数据库之后,我们就可以创建集合
db.createCollection(集合名字name,设置参数options[对象类型])
  • name是要创建的集合的名称。 options是一个文档,用于指定集合的配置

  • 参数 类型描述

    • name (String) 要创建的集合的名称
    • options (Document) (可选)指定有关内存大小和索引的选项
      • options参数是可选的,因此只需要指定集合的名称。 以下是可以使用的选项列表:
        • 字段 类型 描述
          • capped (Boolean) (可选)如果为true,则启用封闭的集合。上限集合是固定大小的集合,它在达到其最大大小时自动覆盖其最旧的条目。 如果指定true,则还需要指定size参数。
          • autoIndexId (Boolean) (可选)如果为true,则在_id字段上自动创建索引。默认值为false。
          • size (数字) (可选)指定上限集合的最大大小(以字节为单位)。 如果capped为true,那么还需要指定此字段的值。
          • max (数字) (可选)指定上限集合中允许的最大文档数。
        • 由于option是可选,我们也可以不带配置项创建集合
db.createCollection("mycollection")

删除集合

  • db.collection.drop()用于从数据库中删除集合
db.集合名.drop()
  • 比如我们可以测试以下操作
db.createCollection("wscats")//创建名为wscats的集合
show collections//显示该数据库所有集合   wscats
db.wscats.drop()//删除名为wscats的集合

查看文档

  • 最简单查看文档的方法就是find(),会检索集合中所有的文档结果
db.集合名.find()
  • 要以格式化的方式显示结果,可以使用pretty()方法。
db.集合名.find().pretty()
1.固值寻找
  • 寻找age集合里面所有含有属性值为wscats的文档结果,相当于where name = 'wscats'
db.age.find({name:"wscats"})
2.范值寻找
  • 操作 语法 示例 等效语句
- 相等	{ : } =>db.age.find({"name":"wscats"}).pretty()	
    - 相当于: where name = 'wscats'
- 小于	{:{$lt:}} =>db.age.find({"likes":{$lt:50}}).pretty()	
    - 相当于: where likes < 50
- 小于等于	{:{$lte:}} =>db.age.find({"likes":{$lte:50}}).pretty()
    - 相当于: where likes <= 50
- 大于	{:{$gt:}} =>db.age.find({"likes":{$gt:50}}).pretty()
    - 相当于: where likes > 50
- 大于等于	{:{$gte:}} =>db.age.find({"likes":{$gte:50}}).pretty()
    - 相当于: where likes >= 50
- 不等于	{:{$ne:}} =>db.age.find({"likes":{$ne:50}}).pretty()	
    - 相当于: where likes != 50
3.AND和OR寻找
1. AND
  • 在find()方法中,如果通过使用将它们分开传递多个键,则mongodb将其视为AND条件。 以下是AND的基本语法

  • 寻找_id为1并且name为wscats的所有结果集

db.age.find(
   {
      $and: [
         {"_id": 1}, {"name": "wscats"}
      ]
   }
)
2. OR
  • 在要根据OR条件查询文档,需要使用$or关键字。以下是OR条件的基本语法
  • 寻找name为corrine或者name为wscats的所有结果集
db.age.find(
   {
      $or: [
         {"name": "corrine"}, {“name“: "wscats"}
      ]
   }
)
3. AND和OR等结合
  • 相当于语句where title = "wscats" OR ( title = "corrine" AND _id < 5)
db.age.find({
  $or: [{
    "title": "wscats"
  }, {
    $and: [{
      "title": "corrine"
    }, {
      "_id": {
        $lte: 5
      }
    }]
  }]
})

插入文档

文档的数据结构和JSON基本一样。 所有存储在集合中的数据都是BSON格式。 BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON

  • 要将数据插入到mongodb集合中,需要使用mongodb的insert()save()方法。
db.集合名.insert(document)
  • 比如我们可以插入以下数据
db.wscats.insert({
   _id: 100,
   title: 'MongoDB Tutorials', 
   description: 'node_tutorials',
   by: 'Oaoafly',
   url: 'https://github.com/Wscats/node-tutorial',
   tags: ['wscat','MongoDB', 'database', 'NoSQL','node'],
   num: 100,
})
  • 也可以支持插入多个,注意传入的是数组形式
db.wscats.insert([{
   _id: 100,
   title: ‘Hello’
},{
   _id: 101,
   title: ‘World’
}])
  • 在插入的文档中,如果不指定_id参数,那么mongodb会为此文档分配一个唯一的ObjectId
  • 要插入文档,也可以使用db.post.save(document)。如果不在文档中指定_id,那么save()方法将与insert()方法一样自动分配ID的值。如果指定_id,则将以save()方法的形式替换包含_id的文档的全部数据。
db.wscats.save({
   _id: 111,
   title: 'Oaoafly Wscats', 
})

更新文档

1.update()方法
  • 寻找第一条title为wscats的值,并且更新值title为corrine和age为12
db.age.update({
  'title': 'wscats'
}, {
  $set: {
    'title': 'corrine',
    'age': 12
  }
})
  • 默认情况下,mongodb只会更新一个文档。要更新多个文档,需要将参数multi设置为true,还可以配合find方法里面的各种复杂条件判断来筛选结果,然后更新多个文档

  • 寻找所有title为wscats的值,并且更新值title为corrine和age为12

db.age.update({
  'title': 'wscats'
}, {
  $set: {
    'title': 'corrine',
    'age': 12
  }
}, {
  multi: true
})
2.save()方法
  • _id主键为3的文档,覆盖新的值,注意_id为必传
db.age.save({
  '_id':3,
  'title': 'wscats'
})

删除文档

  • 删除主键_id为3的文档,默认是删除多条
db.age.remove({
  '_id':3
})
  • 建议在执行remove()函数前先执行find()命令来判断执行的条件是否正确

  • 如果你只想删除第一条找到的记录可以设置justOne为1,如下所示

db.age.remove({...},1)
  • 全部删除
db.age.remove({})

Limit与Skip方法

Limit
  • 如果你需要在mongodb中读取指定数量的数据记录,可以使用mongodb的Limit方法,limit()方法接受一个数字参数,该参数指定从mongodb中读取的记录条数。
db.age.find().limit(数量)
Skip
  • 我们除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。
db.age.find().limit(数量).skip(数量)
//skip()方法默认值为0
  • 所以我们在实现分页的时候就可以用limit来限制每页多少条数据(一般固定一个值),用skip来决定显示第几页(一个有规律变动的值)

排序

  • 在mongodb中使用使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排列,而-1是用于降序排列。

1 升序排列 -1 降序排列

db.集合名.find().sort({键值(属性值):1})
  • age集合表重新根据_id主键进行降序排列
db.age.find().sort({
  "_id": -1
})

Node.js连接

安装mongodb的模块
npm install mongodb
1.连接数据库
var MongoClient = require('mongodb').MongoClient;
//结尾是选择数据库名
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';
MongoClient.connect(DB_CONN_STR, function(err, db) {
  console.log("连接成功!");
});
2.查询数据
  • 注意查询回来的结果需要toArray来遍历处理
var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';

MongoClient.connect(DB_CONN_STR, function(err, db) {
  console.log("连接成功!");
  //选中age集合,并用find方法把结果集拿回来进行处理
  db.collection("age").find({title: "cba"}).toArray(function(err, result) {
    if (err) {
      console.log('Error:' + err);
      return;
    }
    console.log(result);
  });
});

经过测试,读取大于100条的时候会出现报错官网解释,可以尝试用forEach代替

db.collection('pokemon').find({})
  .forEach(function(item){
      console.log(item)
  })
查询ID
  • 查询自动生成的ObjectId
var ObjectId = require('mongodb').ObjectId;
let _id = ObjectId("5bcae50ed1f2c2f5e4e1a76a");
db.collection('xxx').find({
    "_id": _id
}).forEach(function (item) {
    console.log(item)
})
3.插入数据
  • insert函数第一个参数是需要插入的值(可以一个也可以多个),第二个参数是接受一个回调函数,当值插入成功后回返回插入值得一些关键信息,比如_id
var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';

MongoClient.connect(DB_CONN_STR, function(err, db) {
  console.log("连接成功!");
	const db = client.db("demo");
  db.collection("age").insert([
    { 
      title: "插入的值A"
    }, {
      title: "插入的值B"
    }
  ], function(err, result) {
    if (err) {
      console.log('Error:' + err);
      return;
    }
    console.log(result)
  })
});
4.更新数据
  • 注意如果不加set就是完全替换原来的那份(没有设置的属性值将会丢失),加上set则只是更新对应的属性值,其余不做改变
var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';

MongoClient.connect(DB_CONN_STR, function(err, db) {
  console.log("连接成功!");
  db.collection("age").update({
    "_id": 1
  }, {
    $set: {
      title: "你好,世界",
      skill: "js"
    }
  }, function(err, result) {
    if (err) {
      console.log('Error:' + err);
      return;
    }
    //console.log(result);
  });
});
5.删除数据
var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27017/wscats';

MongoClient.connect(DB_CONN_STR, function(err, db) {
  console.log("连接成功!");
  db.collection("age").remove({
    "_id": 1
  }, function(err, result) {
    if (err) {
      console.log('Error:' + err);
      return;
    }
    //console.log(result);
    //关闭数据库
    db.close();
  });
});
6.关闭数据库
db.close();

封装自定义模块

  • 新建mongo.js写入以下代码,封装自定义模块,方便其他路由复用,注意assert是node自带的断言模块,用于测试代码

  • 参考

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
const url = 'mongodb://localhost:27017';
const dbName = 'shop';
function query(callback) {
	MongoClient.connect(url, function(err, client) {
		assert.equal(null, err);
		console.log("Connected successfully to server");
		const db = client.db(dbName);
		callback(db);
		client.close();
	});
}
module.exports = {
	query
}
  • 在路由文件中引入和使用
var mongo = require('./mongo.js')
router.post('/addproduct', function(req, res, next) {
	mongo.query(function(db) {
		db.collection("product").insertMany([req.body], function(err, result) {
			console.log("Inserted 1 document into the collection");
			res.send('respond with a resource');
		});
	})
});

mongoose

可视化

21. Socket

socket.io

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>socket.io</title>
  <script src="socket.io.js" charset="utf-8"></script>
</head>
<body>
  <h1>gp6 交流区</h1>
  <div id="content" name="name" style="overflow-y: scroll; width: 400px; height: 300px; border: solid 1px #000"></div>
  <br />
  <div>
    <input type="text" id="msg" style="width: 200px;">
  </div>
  <button id="submit">提交</button>
  <script>
    var socket = io.connect('http://10.9.164.98:8081');
    const content = document.getElementById('content')
    document.querySelector('#submit')
      .addEventListener('click', function () {
        var msg2 = msg.value
        socket.emit('receive', msg2)
        msg.value = ''
        content.innerHTML += msg2 + '<br/>'
      }, false)

      socket.on('message', function(msg){
        content.innerHTML += msg + '<br/>'
      })
  </script>
</body>
</html>

  • server.js
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);

app.use(express.static(__dirname + '/client'))

io.on('connection', function (socket) {
  setInterval(function () {
    socket.emit('list', 'abc')
  }, 1000)
  socket.broadcast.emit('list', 'test');
  socket.on('backend', (msg) => {
    console.log(msg);
  })

  socket.on('receive', (msg) => {
    socket.broadcast.emit('message', msg);
  })
});

server.listen(8081, '10.9.164.98');

net模块

  • serverCode
const net = require('net')

const server = new net.createServer()

let clients = {}
let clientName = 0

server.on('connection', (client) => {
  client.name = ++clientName
  clients[client.name] = client

  client.on('data', (msg) => {
    // console.log('客户端传来:' + msg);
    broadcast(client, msg.toString())
  })

  client.on('error', (e) => {
    console.log('client error' + e);
    client.end()
  })

  client.on('close', (data) => {
    delete clients[client.name]
    console.log(client.name + ' 下线了');
  })
})

function broadcast(client, msg) {
  for (var key in clients) {
    clients[key].write(client.name + ' 说:' + msg)
  }
}
server.listen(9000)
  • clientCode
var net = require('net')
const readline = require('readline')

var port = 9000
var host = '127.0.0.1'

var socket = new net.Socket()

socket.setEncoding = 'UTF-8'

socket.connect(port, host, () => {
  socket.write('hello.')
})

socket.on('data', (msg) => {
  console.log(msg.toString())
  say()
})

socket.on('error', function (err) {
  console.log('error' + err);
})

socket.on('close', function () {
  console.log('connection closeed');
})

const r1 = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})

function say() {
  r1.question('请输入:', (inputMsg) => {
    if (inputMsg != 'bye') {
      socket.write(inputMsg + '\n')
    } else {
      socket.destroy()
      r1.close()
    }
  })
}

websocket

const ws = new WebSocket('ws://localhost:8080/')

ws.onopen = () => {
  ws.send('大家好')
}

ws.onmessage = (msg) => {
  const content = document.getElementById('content')
  content.innerHTML += msg.data + '<br/>'
}

ws.onerror = (err) => {
  console.log(err);
}

ws.onclose = () => {
  console.log('closed~');
}
ws.send(msg2)
  • server.js
const WebSocket = require('ws')
const ws = new WebSocket.Server({ port: 8080 })

let clients = {}
let clientName = 0

ws.on('connection', (client) => {
  client.name = ++clientName
  clients[client.name] = client

  client.on('message', (msg) => {
    broadcast(client, msg)
  })

  client.on('close', () => {
    delete clients[client.name]
    console.log(client.name + ' 离开了~')
  })
})

function broadcast(client, msg) {
  for (var key in clients) {
    clients[key].send(client.name + ' 说:' + msg)
  }
}

22. SSR 与 SEO

vue 和 react 介绍

23. 项目实战

api接口

  • RestfulApi 规范
  • 接口文档的生成(apidoc)
  • 接口请求方式区别

跨域解决

  • cors
  • jsonp
  • proxy

身份验证

JWT

Cookia+Session

图片上传

  1. 安装multer模块
npm install multer
  1. 引用模块 它是依赖于express的一个模块
//引用express并配置
var express = require("express");
var app = express();
app.listen(3000);
var multer = require('multer');
/*var upload = multer({
	//如果用这种方法上传,要手动添加文明名后缀
        //如果用下面配置的代码,则可以省略这一句
	dest: 'uploads/'
})*/
  1. 配置
  • 设置保存文件的地方,并根据上传的文件名对应文件添加后缀
  • 可以通过filename属性定制文件保存的格式
属性值 用途
destination 设置资源的保存路径。注意,如果没有这个配置项,默认会保存在/tmp/uploads下。此外,路径需要自己创建
filename 设置资源保存在本地的文件名
var storage = multer.diskStorage({
	//设置上传后文件路径,uploads文件夹会自动创建。
	destination: function(req, file, cb) {
		cb(null, './uploads')
	},
	//给上传文件重命名,获取添加后缀名
	filename: function(req, file, cb) {
		var fileFormat = (file.originalname).split(".");
		//给图片加上时间戳格式防止重名名
		//比如把 abc.jpg图片切割为数组[abc,jpg],然后用数组长度-1来获取后缀名
		cb(null, file.fieldname + '-' + Date.now() + "." + fileFormat[fileFormat.length - 1]);
	}
});
var upload = multer({
	storage: storage
});
  1. 接受文件
  • upload.single('xxx'),xxx与表单中的name属性的值对应
  • 这里虽然用到post请求,但实际上不需要bodyParser模块处理
app.post('/upload-single', upload.single('logo'), function(req, res, next) {
	console.log(req.file)
	console.log('文件类型:%s', req.file.mimetype);
	console.log('原始文件名:%s', req.file.originalname);
	console.log((req.file.originalname).split("."))
	console.log('文件大小:%s', req.file.size);
	console.log('文件保存路径:%s', req.file.path);
	res.send({
		ret_code: '0'
	});
});
  1. 多图上传 多图上传只要更改一下地方,前端往file输入框加多一个multiple="multiple"属性值,此时就可以在选图的时候多选了,当然也可以并列多个file输入框(不推荐多个上传图片输入框),这样体验会不好
<input type="file" name="logo" multiple="multiple" />

后端也需要相应的改变

app.post('/upload-single', upload.single('logo'), function(req, res, next) {
//upload.single('logo')变为upload.array('logo', 2),数字代表可以接受多少张图片
app.post('/upload-single', upload.array('logo', 2), function(req, res, next) {

如果不想有图片数量上传限制,我们可以用upload.any()方法

app.post('/upload-single', upload.any(), function(req, res, next) {	
	res.append("Access-Control-Allow-Origin","*");
	res.send({
		wscats_code: '0'
	});
});
  1. 前端部分
  • formData表单提交
<form action="http://localhost:3000/upload-single" method="post" enctype="multipart/form-data">
	<h2>单图上传</h2>
	<input type="file" name="logo">
	<input type="submit" value="提交">
</form>
  • formData表单+ajax提交
<form id="uploadForm">
	<p>指定文件名: <input type="text" name="filename" value="" /></p>
	<p>上传文件: <input type="file" name="logo" /></ p>
	<input type="button" value="上传" onclick="doUpload()" />
</form>

FormData对象,是可以使用一系列的键值对来模拟一个完整的表单,然后使用XMLHttpRequest发送这个"表单"

注意点

  • processData设置为false。因为data值是FormData对象,不需要对数据做处理。
  • <form>标签添加enctype="multipart/form-data"属性。
  • cache设置为false,上传文件不需要缓存。
  • contentType设置为false。因为是由<form>表单构造的FormData对象,且已经声明了属性enctype="multipart/form-data",所以这里设置为false

上传后,服务器端代码需要使用从查询参数名为logo获取文件输入流对象,因为<input>中声明的是name="logo"

function doUpload() {
	$.ajax({
		url: 'http://localhost:3000/upload-single',
		type: 'POST',
		cache: false, //不必须
		data: new FormData($('#uploadForm')[0]),
		processData: false,//必须
		contentType: false,//必须
		success: function(data) {
			console.log(data)
		}
	})
}

参考文档 Github MyDemo Github Multer MDN FormData对象的使用

24. 自动化测试 mocha

  • Mocha('摩卡'),诞生于2011年,现在比较流行的JavaSscript测试框架之一,可以运行于Node环境和浏览器环境 -测试框架:可以运行测试的工具。通过他,可以为JavaScript应用 添加测试,从而保证代码质量

参考文档 mochajs mocha中文文档

安装配置

  • 使用npm 全局安装
$ npm install --global mocha
  • 项目依赖 局部安装
$ npm isntall mocha

基本语法

assert 断言

  • 断言库:chai
  • should 风格断言
  • expect 风格断言
  • 全局安装chai
npm install chai -g 

案例使用

  • 递归执行
$ mocha test --recursive 

25. mongodb 数据库

  • mongodb 非关系型数据库(非结构性数据库)
      • 集合
        • 文档 document
  • mysql 关系型数据库(结构型数据库)
        • 行 列

挂载数据库

  1. 创建一个文件夹
  • eg : d-->mongo
  1. 命令行输入
  • mongod --dbpath d:\mongo
  1. 挂载完毕后将命令行最小化
  2. 在浏览器通过 http://127.0.0.1:27017 来访问

进入mongo环境

  1. 在新的命令行输入 mongo
  • show dbs
    • 查看当前的数据库列表
  • show collections
    • 查看当前库当中的集合列表
  • use 数据库名
    • 使用指定的库
  • db
    • 查看当前所在的数据库
  • db.dropDatabase
    • 删除当前的数据库

增删改查

  • db.<集合名>.insert({userName:"xiaozhang"})
    • <集合名>集合当中增加了一个文档
  • mongoimport --db scoreList --collection <集合名> --file E:\1924\Lession04\data --drop
    • --db 指定数据库
    • --collection 指定集合
    • --file 指定导入的文件
    • --drop 可省略.如果使用该属性则是覆盖,不使用是追加

  • db.<集合名>.remove({})
    • 删除所有文档
  • db.<集合名>.remove({age:10})
    • 删除age等于10的文档
  • db.<集合名>.remove({age:{$gte:16}},{justOne:true})
    • 删除age大于等于16的符合条件的第一个文档

  • db.<集合名>.update({userName:"张三"},{age:10})
    • 完整替换
  • db.<集合名>.update({userName:"王五"},{$set:{sex:"女"}})
    • 将userName为王五的sex修改为女
  • db.<集合名>.update({sex:"男"},{$set:{age:18}})
    • 只能将第一个sex为男的age修改为18
  • db.<集合名>.update({sex:"女"},{$set:{age:16}},{multi:true})
    • 将所有sex为女的age更改为16
  • db.<集合名>.update({userName:"二哈"},{$inc:{age:1}})
    • 将userName为二哈的age加1

  • db.<集合名>.find()
    • 查看当前库当中的<集合名>集合的文档列表
  • db.<集合名>.count()
    • 查看<集合名>集合的文档数量
  • db.<集合名>.find().count()
    • 根据条件查找数量
  • db.<集合名>.find({userName:"二哈"})
    • <集合名>集合当中查找userName为二哈的文档
  • db.<集合名>.find({userName:/常/})
    • 模糊查找 查找userName中包含常的文档
  • db.<集合名>.find({"chengji.yuwen":2})
    • 查找<集合名>集合中的chengji中的yuwen为2的文档
  • db.<集合名>.find({age:{$gt:12}})
    • 查找age大于12
    1. $gt : 大于
    2. $gte : 大于等于
    3. $lt : 小于
    4. $lte : 小于等于
    5. $ne : 不等于
  • db.<集合名>.find({age:{$ne:12},sex:"女"})
    • 多条件,age不等于12,sex为女
  • db.<集合名>.find({$or:[{sex:"女"},{age:12}]})
    • 查找sex为女或者年龄为12的文档
  • db.<集合名>.find().skip(2)
    • 跳到指定的文档数
  • db.<集合名>.find().limit(1)
    • 取指定的文档数
  • db.<集合名>.find().sort({age:1})
    • 按照age的大小排序
    • 1 正序
    • -1 倒序
  • db.<集合名>.find().sort({age:1,"chengji.yuwen":1})
    • 按照age的正序,如果age相同,按照chengji中的yuwen的正序
  • db.<集合名>.find().limit(7).skip(1).sort({age:-1})
    • 先sort,再skip,再limit
分页:
已知:
		1、pageIndex:指定的页数
		2、count:文档数量
		3、pageSum:总页数      pageSum = Math.ceil(count/pageNum)
		4、pageNum:每页显示的条数
求:
		每页要显示的内容。
问:总条数9,每页2条,每一页的内容如何求
		skip = (pageIndex-1)*pageNum;
		1、 db.`<集合名>`.find().sort({age:-1}).skip(0).limit(2)
		2、 db.`<集合名>`.find().sort({age:-1}).skip(2).limit(2)
		3、 db.`<集合名>`.find().sort({age:-1}).skip(4).limit(2)
		4、 db.`<集合名>`.find().sort({age:-1}).skip(6).limit(2)
		4、 db.`<集合名>`.find().sort({age:-1}).skip(8).limit(2)