node.js
导出模块
一个js文件就是一个模块,模块内部的所有变量,对象,方法对外界都不可见。如果想暴露出去让别人用,就需要导出模块。语法如下:
module.exports = {
a :a,
foo
}
Demo
-
暴露的模块,mathuilts.js
function getSum(x, y) { return x + y; } const APP_NAME = "nodejs" function getGap(x, y) { return x - y; } // getSum getSum() // 如果要暴露的是方法,一定不能添加(),直接声明方法名即可 module.exports = { getSum, name: APP_NAME, getGap }
-
使用模块
// 在引入自定义模块的时候,相对路径要使用标准的写法,也就是要添加./
var mathutils = require("./mathutils");
// var sum = getSum(1, 2);
console.log(mathutils.name)
console.log(mathutils.getSum(1,2))
console.log(mathutils.getGap(1,2))
crypto模块
NodeJs的crypto模块提供了哈希,加密相关的功能支持。
哈希算法:MD5,SHA1,SHA256,Hmac
哈希算法用来对数据进行签名,确定数据的唯一性,以及是否被篡改。由于其过程不可逆,也常常用来对用户密码进行加密。
let crypto = require("crypto");
let data = "你好,这是一个加密的原文数据";
// 指定算法
// let hash = crypto.createHash("md5");
// let hash = crypto.createHash("sha1");
// let hash = crypto.createHash("sha256");
// let hash = crypto.createHash("sha512");
// 传入原文
// hash.update(data)
// 计算hash
// let digest = hash.digest("hex");
// console.log(digest)
Hmac是基于key和hash的认证算法。它在上面哈希算法的基础上,再传入一个key。只要key变化,即使输入同样的数据也会得到不同的结果。可以将Hmac理解为随机数增强的哈希算法。
// let salt = "salt";
//
// let hmac = crypto.createHmac("md5", salt);
// hmac.update(data)
// let digest = hmac.digest("hex");
// console.log(digest)
let key = "helloworld";
对称加密算法:AES
AES是一种常用的对称加密算法,加解密都用同一个密钥。
AES加密:
let data = '123456'
let password = 'baby'
const cipher = crypto.createCipher('aes192', password)
let encrypted = cipher.update(data, 'utf-8', 'hex')
encrypted += cipher.final('hex')
console.log(encrypted);
AES解密:
const decipher = crypto.createDecipher('aes192', password)
let encrytedData = 'd1b992fe45e85e43f3b73e0716874bc8'
let decrypted = decipher.update(encrytedData, 'hex', 'utf-8')
decrypted += decipher.final('utf-8')
console.log(decrypted);
event模块
大多数 Node.js 核心 API 都采用惯用的异步事件驱动架构,其中某些类型的对象(触发器)会周期性地触发命名事件来调用函数对象(监听器)。例如,fs.ReadStream
会在文件被打开时触发事件;stream
会在数据可读时触发事件。
用法如下:
const EventEmitter = require('events')
class MyEmitter extends EventEmitter{}
const myEmitter = new MyEmitter()
// 注册xxx事件
myEmitter.on('aaa', (a)=>{
console.log('aaa事件被触发,参数:'+a);
})
// 1秒钟之后触发aaa事件,并传递参数
setTimeout(()=>{
myEmitter.emit('aaa', 'aaaaaa')
},1000)
当你想设计一个模块,它具有在某个条件下执行某个操作的功能,那么event模块就派上用场了。例如:你想设计一个用户注册模块,当用户注册成功之后给用户发送一个email。
【案例】美女图片爬虫项目
功能需求
- 爬取http://www.27270.com/ent/meinvtupian/网站页面,处理GBK乱码的问题;
- 从中提取出图片数据,并下载图片
第三方模块简介
iconv-lite : 用于处理gbk编码问题,iconv-lite
cheerio : 用于数据提取 cheerio
let cheerio = require("cheerio");
let html = `
<ul id="fruits">
<li class="apple">Apple</li>
<li class="orange">Orange</li>
<li class="pear">Pear</li>
</ul>
`;
// 加载代码片段
let $ = cheerio.load(html);
// let text = $("li.apple").text();
// console.log(text)
// 使用类似jQuery的语法获取多个对象,并转为数组
let lis = $("ul li").toArray();
// 遍历数组
for (let i = 0; i < lis.length; i++) {
// 将DOM对象转为jQuery对象
let jEle = $(lis[i]);
// 获取数据
console.log(`class:${jEle.attr("class")},text:${jEle.text()}`)
}
### 代码
let http = require("http");
let iconv = require("iconv-lite");
let cheerio = require("cheerio");
let fs = require("fs");
let path = require("path");
// 1.请求目标页面,处理了中文乱码
http.get("http://www.27270.com/ent/meinvtupian/", res => {
let data = [];
res.on("data", chunk => {
data.push(chunk)
})
res.on("end", () => {
let html = iconv.decode(Buffer.concat(data), 'gb2312');
// 2.解析页面,查找图片的地址和图片的标题
let imgData = parseHTML(html);
// 3.下载图片
downloadImgs(imgData)
})
})
// 2.解析页面,查找图片的地址和图片的标题
function parseHTML(html) {
let $ = cheerio.load(html);
let arr = $("div.MeinvTuPianBox>ul>li>a>i>img").toArray();
let imgData = [];
for (let i = 0; i < arr.length; i++) {
let jEle = $(arr[i]);
imgData.push({
src: jEle.attr("src"),
title: jEle.attr("alt")
})
}
return imgData;
}
// 3.下载图片
function downloadImgs(imgData) {
imgData.forEach(imgObj => {
http.get(imgObj.src, res => {
let filePath = path.join("imgs", imgObj.title+path.extname(imgObj.src))
let writeStream = fs.createWriteStream(filePath);
res.pipe(writeStream)
})
})
}
重构
实现爬虫功能,需要实现抓取页面,提取数据,下载图片这些子功能。而这些功能的调用都是当某个步骤完成了,才能执行下一个步骤,因此我们可以使用event模块来重构整个项目.
let EventEmmiter = require("events");
let http = require("http");
let iconv = require("iconv-lite");
let cheerio = require("cheerio");
let fs = require("fs");
let path = require("path");
class GirlSpider extends EventEmmiter {
// 1.请求目标页面,处理了中文乱码
downloadHTML() {
http.get("http://www.27270.com/ent/meinvtupian/", res => {
let data = [];
res.on("data", chunk => {
data.push(chunk)
})
res.on("end", () => {
let html = iconv.decode(Buffer.concat(data), 'gb2312');
// 页面下载成功以后,触发事件
this.emit("downloadHTMLSuccess", html)
})
})
}
// 2.解析页面,查找图片的地址和图片的标题
parseHTML(html) {
let $ = cheerio.load(html);
let arr = $("div.MeinvTuPianBox>ul>li>a>i>img").toArray();
let imgData = [];
for (let i = 0; i < arr.length; i++) {
let jEle = $(arr[i]);
imgData.push({
src: jEle.attr("src"),
title: jEle.attr("alt")
})
}
// 页面解析成功以后,触发事件
this.emit("parseHTMLSuccess", imgData)
}
// 3.下载图片
downloadImgs(imgData) {
imgData.forEach(imgObj => {
http.get(imgObj.src, res => {
let filePath = path.join("imgs", imgObj.title + path.extname(imgObj.src))
let writeStream = fs.createWriteStream(filePath);
res.pipe(writeStream)
})
})
}
// 初始化方法
init() {
// 添加监听
this.on("downloadHTMLSuccess", (html) => {
this.parseHTML(html)
})
this.on("parseHTMLSuccess", (imgData) => {
this.downloadImgs(imgData)
})
// 开始下载页面
this.downloadHTML()
}
}
let girlSpider = new GirlSpider();
girlSpider.init()
MongoDB
介绍
MongoDB是一个由C++编写的高性能的文档型数据库,为web应用提供可扩展数据库解决方案。它和MySQL的区别如下:
数据库 | MongoDB | MySQL |
---|---|---|
存储单元 | 按collection存储,一个collection中包含很多document | 按table存储,一个table中包含很多的记录 |
数据格式要求 | 非常灵活,document可以存储任意json格式的数据 | row中的每一列的数据类型都是限定死的,不够灵活 |
数据字段扩展 | 对数据字段的扩展零消耗 | 数据扩展字段有很大的消耗.比如:上万条数据再增加一列就要消耗几十秒 |
事务支持 | 在4.0之后版本支持,目前已经支持 | 支持 |
读写性能 | 一般情况下,读写性能都要略高于MySQL | 性能平稳 |
具体区别如下:
下载与安装
ORM - mongoose
无论是mysql还是mongodb,传统的与数据库交互的方式都是按照他们提供的API来写代码。它们提供的API往往不是很容易理解,而且难以记忆,如果传错了参数,写错一个符号都要查文档。就像上面我们学习的数据库API一样。
ORM框架允许我们面向对象操作,不需要记忆任何的数据库API,只需要操作对象即可,由框架底层去调用数据库API,这样就大大提高了程序员的开发效率。不过既然多了一层封装,肯定要损失一点点的性能,可以忽略不计。
在NodeJS中,mongodb最好的orm框架就是mongoose。
安装mongoose
npm install mongoose --save
如果你本机没有安装nodejs驱动,会自动安装nodejs驱动,因为mongoose依赖nodejs驱动了。
文档参考:mongoose官方文档
连接数据库
const mongoose = require('mongoose')
//连接数据库
mongoose.connect("mongodb://127.0.0.1:27017/test");
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
console.log('连接mongodb成功!');
});
模型定义
mongoose使用schema来描述数据的格式,字段,规则,有了schema之后可以生成model来操作数据。
const mongoose = require('mongoose')
const schema = mongoose.Schema({
name: {
type: String,
required: true
},
age: Number,
fav: [String],
});
module.exports = mongoose.model('user', schema)
增删改查
直接查看文档中Model相关API。
await User.create()
await User.findOne()
await User.updateOne()
await User.deleteOne()
//增加条件操作符: 所有支持的操作符:https://docs.mongodb.com/manual/reference/operator/query/
await User.findOne({
age: {$gt:11},
tags:{$in:[1,2,3]}
})
高级查询
//分页查询
await Book.find().skip(10).limit(10)
//排序和字段映射
await User.find().skip(0).limit(2).sort({age:-1}).select("-hobby")
Schema和数据类型
https://mongoosejs.com/docs/guide.html
所有的方法
https://mongoosejs.com/docs/api.html#Model
操作符
https://docs.mongodb.com/manual/reference/operator/query/