node学习笔记
node知识点
一些基础知识
node.js的所有api都支持回调函数,可以实现异步编程,并通过回调函数对值进行处理。一般回调函数是作为最后一个参数传入异步操作;其中接收两个参数;一个是错误对象,另一个才是正确数据
const fs = require("fs")
// 测试下同步读取文件数据;会先读取data数据,并通过console打印出来;如果是异步则会输出undefined
let syncData = fs.readFileSync('test.txt')
console.log(data.toString)
// 体验下异步
fs.readFile('test.txt', function (err, data) {
if (err) return console.log(err)
console.log(data.toString())
})
node.js是单进程单线程;但 V8 引擎提供异步执行回调接口,通过这些接口可以处理大量的并发。
node.js的事件机制几乎都采用观察者模式;
事件模块(events)
使用方法和原理
先引入events模块,该模块只提供了EventEmitter对象;通过new 创建一个实例event;
通过event.on(‘xxx’, ()=>{}) 创建一个事件
使用event.emit(xxx) 触发一个事件
/*
const events = require('events') // 只提供一个对象events.EventEmitter
const event = new events.EventEmitter(); // 核心就是事件触发与事件监听器功能的封装
*/
// 也可以写成:
const eventEmitter = require('events').EventEmitter
const event = new eventEmitter ();
event.on('some_event', function() {
console.log('some_event 事件触发1');
});
event.on('some_event', function() {
console.log('some_event 事件触发2');
}); // 相同事件可注册多个,触发后依次执行
setTimeout(() => {
event.emit('some_event')
}, 2000)
event.addListener('some_event', () => {
console.log('some_event 事件触发3');
})
let listenerList = event.listeners('some_event')
let listenerCount = event.listenerCount('some_event')// 获取监听器数量
console.log(listenerList, listenerCount) // [ [Function], [Function], [Function] ], 3
原理:EventEmitter 的每个事件由一个事件名(some_event)和若干个参数组成。
事件名是一个字符串,通常表达一定的语义。
EventEmitter 支持相同事件中存在若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递
EventEmitter的其他参数
- addListener(event, listener)为指定事件添加一个监听器到监听器数组的尾部。
- on(event, listener) 为指定事件注册监听器
- once(event, listener) 作用同上,只触发一次
- removeListener(event, listener) 移除某个事件的某个监听器;
- removeAllListeners([event]) 移除所有事件的监听器;也可以指定某个事件
- listeners(event) 返回指定事件的监听器数组
Buffer(缓冲区)
作用:js没有二进制数据类型,而处理TCP和文件流时必须使用二进制数据;所以node定义了一个Buffer类来创建一个专门存放二进制数据的缓存区。
创建:
- Buffer.alloc(size[, fill[, encoding]]) 返回指定大小的Buffe实例,不设置fill,默认填充0
- Buffer.from(array) 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)
- Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。
- Buffer.from(buffer): 复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例
- Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例
const buf1 = Buffer.alloc(10);// 创建一个长度为 10、且用 0 填充的 Buffer。
const buf2 = Buffer.from([1, 2, 3]);// 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。
写入缓冲区
buf.write(string[, offset[, length]][, encoding])
string - 写入缓冲区的字符串。
offset - 缓冲区开始写入的索引值,默认为 0 。
length - 写入的字节数,默认为 buffer.length
encoding - 使用的编码。默认为 ‘utf8’ 。
返回实际写入的大小,若空间不足写到满后不再写入
const buf = Buffer.alloc(5);
let len = buf.write("test");
console.log("写入字节数 : "+ len); // 写入字节数 : 4
读取文件
buf.toString([encoding[, start[, end]]])
入参:
encoding - 使用的编码。默认为 ‘utf8’ 。
start - 指定开始读取的索引位置,默认为 0。
end - 结束位置,默认为缓冲区的末尾。
encoding: ‘utf8’ ‘ascii’ ‘base64’ ‘hex’
返回:解码缓冲区数据并使用指定的编码返回字符串。
将 Buffer 转换为 JSON 对象
buf.toJSON()
缓冲区合并
Buffer.concat(list[, totalLength])
list - 用于合并的 Buffer 对象数组列表。
totalLength - 指定合并后Buffer对象的总长度。
返回值
返回一个多个成员合并的新 Buffer 对象。
Stream(流)
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)
读取流中数据
写入流
管道流和链式流
模块系统
作用:可以共享出某文件的变量、方法、构造函数、类。
使用:通过exports.xxx或者module.exports = xxx方法导出(共享);使用require方式引入,不同的导出方法使用时不同
// child.js
function child1() {
console.log("hello world")
}
exports.child1 = child1 // 导出的第一种
module.exports= child1 // 导出的第二种 也可以module.exports.child1= child1
// parents.js
const child = require("./child.js")
child.child1() // 使用第一种
child() // 使用第二种
注1:exports对于本身是一个变量(对象),它不是module的引用,它是{}的引用,指向module.exports的{}模块。module是一个变量,指向一块内存,exports是module中的一个属性,存储在内存中;exports属性指向{}模块
注2:多次exports.aaa = xxx;会被最后一个覆盖
注3:require加载时是有优先级的,文件系统缓存的模块>原生模块(例:http)>文件加载
全局变量
__filename:正在执行的文件名及其他的绝对路径
__dirname:正在执行的文件名绝对路径
文件系统(fs)
读取文件:
fs.readFile(‘xxx’, (err, data) => {}) //异步读取
const syncData = fs.readFileSync(‘xxx’) //同步读取
打开文件:
fs.open(path, flags[, mode], callback)
path - 文件的路径。
flags - 文件打开的行为。例:r, r+, w, w+等。
mode - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。
callback - 回调函数,带有两个参数如:callback(err, fd)。
获取文件信息:
const fs = require('fs');
fs.stat('xxx/fs.js', function (err, stats) {
console.log(stats.isFile()) //true
console.log(stats.isDirectory()); //false
})
写入:
fs.writeFile(file, data[, options], callback)file - 文件名或文件描述符。
data - 要写入文件的数据,可以是 String(字符串) 或 Buffer(缓冲) 对象。
options - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 ‘w’
callback - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。
关闭:
fs.close(fd, callback)
删除
fs.unlink(path, callback)
创建目录
fs.mkdir(path[, options], callback)
读取目录
fs.readdir(path, callback)
callback - 回调函数,回调函数带有两个参数err, files,err 为错误信息,files 为 目录下的文件数组列表。
删除目录
fs.rmdir(path, callback)
连接数据库
连接mysql
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '123456',
database : 'test'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
连接MongoDB
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("runoob");
var myobj = { name: "菜鸟教程", url: "www.runoob" };
dbo.collection("site").insertOne(myobj, function(err, res) {
if (err) throw err;
console.log("文档插入成功");
db.close();
});
});
express
Express 是一个保持最小规模的灵活的 Node.js Web 应用程序开发框架,他在node.js基础功能上进行扩充,本质上讲使用express就是为了调用各种中间件。
安装:npm installexpress –g npm install express-generator –g
创建脚手架:express -e projectName
生成的目录:
app.js:项目的主入口,一般都是在这里面做修改,比如新增路由规则等。
bin:项目启动脚本所在目录。比如启动服务的 npm start,其实运行的就是 bin/start 脚本。
public:存放项目的静态文件,比如js、css、img等。
routes:用来存放项目的路由配置。
views:存放项目的模板文件。
node_modules: 项目依赖的node模块,比如各种中间件等。
package.json:项目依赖声明。
bin目录和http模块
其中bin目录是项目启动目录,里面有文件www.js
#!/usr/bin/env node
var app = require('../app');
var debug = require('debug')('myapp:server');
var http = require('http');
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var server = http.createServer(app); // 创建http server
server.listen(port);
...
...
这里引入了http模块和app.js文件
http模块
http模块主要用于创建http server服务;支持更多特性,不缓冲请求和响应,处理流相关。
var server = http.createServer(): 创建一个server
server.on:绑定事件处理函数
server.listen:监听事件
创建一个服务的简单例子:
var http = require('http');
// 创建http server
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(8080, '127.0.0.1');
app.js文件和path模块的用法
项目的主入口;
app = express()创建一个express实例。我们可以在这个实例中引入中间件,在处理http请求的函数时会一个接一个调用中间件,最终得到我们想要的结果。
每个中间件接收三个参数:request对象(http请求),response对象(http响应),next回调函数(跳到下一个中间件)
如何注册中间件:use方法;例:app.use(logger(‘dev’)); 当收到http请求后,会依次调用(如果有next()的话)
指定变量的值:set方法,例:app.set(‘views’, path.join(__dirname, ‘views’));
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');// 这是一个解析Cookie的工具。通过req.cookies可以取到传过来的cookie,并把它们转成对象。
var logger = require('morgan'); // 在控制台中,显示req请求的信息
var bodyParser = require('body-parser');//node.js 中间件,用于处理 JSON, Raw, Text 和 URL 编码的数据。
var indexRouter = require('./routes/index'); // 引入路由主文件
var usersRouter = require('./routes/users'); // 引入关于用户的路由文件
var app = express(); // 创建一个express实例
var cors = require('cors')
app.use(cors()) // 解决跨域问题方法一
// view 处理
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// 设置跨域 解决跨域问题方法二
app.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'X-Requested-With')
res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
res.header('X-Powered-By', '3.2.1')
res.header('Content-type', 'application/json;charset=utf-8')
next()
})
app.use('/', indexRouter); // 页面路由处理,当首页时处理index.js中的路由
app.use('/users', usersRouter);
path模块的用法
-
ath.join([path1][, path2][, …]) 连接路径;正确使用当前系统的路径分隔符,Unix系统是"/",Windows系统是"\"
例:上图app.set(‘views’, path.join(__dirname, ‘views’));
__dirname为正在执行文件的文件夹的绝对路径;
在__dirname后面接入views路径名 -
path.resolve([from …], to) 将 to 参数解析为绝对路径,给定的路径的序列是从右往左被处理的,后面每个 path 被依次解析,直到构造完成一个绝对路径
routes目录下index.js 和mysql、url模块
在app.js中引入了路由级中间件的文件,包含routes中的index.js和users.js文件
其中需要注意的有app.get和router.get的区别
require(‘express’).get(’/’, (res,req,rext)=> {})…是应用级中间件,
而require(‘express’).Router().get(’/’, (res,req,rext)=> {})是路由级中间件。一般项目用他
const express = require('express')
const app = express()
const router = express.Router()
var express = require('express');
var router = express.Router();
var URL = require('url');
var mysql = require('mysql'); //加载mysql模块
var connection = mysql.createConnection({ // 创建连接设置参数
host : 'localhost',
user : 'root',
password : 'tgf123456',
database : 'nodedemo'
});
connection.connect(); // 连接到mysql服务器
var sql = 'SELECT * FROM stude';
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
router.get('/index', function(req, res, next) {
res.render('index', { title: 'Expressindex' });
});
router.get('/add', function(req, res, next) {
//解析请求参数
var params = URL.parse(req.url, true).query;
//查
connection.query(sql,function (err, result) { // 插入sql语句;返回错误信息,查询结果
if(err){
console.log('[SELECT ERROR] - ',err.message);
return;
}
console.log(params.id);
//把搜索值输出
res.send(result);
});
});
router.all('*', (req, res, next) => {
res.json('请求错误')
})
module.exports = router;
mysql模块
如上代码;
//加载mysql模块
var mysql = require('mysql');
// 创建连接设置参数
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'tgf123456',
database : 'nodedemo'
});
// 连接到mysql服务
connection.connect();
// 查询
let sql = 'select * from stude'
router.get('/search',function(req,res,next){
connection.query(sql, (err, result) => {res.send(result)})
});
通过connection.query(sql, (err, result) =>{})插入sql语句并进行返回报错信息或者查询结果
传入的sql语句:
除了普通查询外,还有占位符模型
例:let sql = ‘select * from heroes where id < ?’; 有一个占位符
可写为:connection.query(sql, 10, (err, result) =>{})
多个占位符的情况:传递数组
let sql = ‘select * from heroes where id < ? and sex = ?’;
connection.query(sql, [10, 30], (err, result) =>{})
如果一次执行多个sql语句,用;隔开
例:let sql = select id,name from students; select * from boy;
;
增加数据时可以用set ?提供键值对
例:const addSql = ‘INSERT INTO stude set ?’;
let addSqlParams = {
name: “zhangsan”,
age: 12,
number: 100
}
connection.query(addSql,addSqlParams,(req, res) => {})
mysql使用优化
在使用mysql模块时可以使用连接池的方法
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'tgf123456',
database:'nodedemo', // 前面建的user表位于些数据库中
});
// 接口中使用时用pool.getConnection连接
let sql = 'select * from stude'
router.get('/search',function(req,res,next){
pool.getConnection((err, connection) => {
connection.query(sql, (err, result) => {}) // 正常用
connection.release(); // 释放连接
})
});
在开发web应用程序时,连接池是一个很重要的概念。建立一个数据库连接所消耗的性能成本是很高的。在服务器应用程序中,如果为每一个接收到的客户端请求都建立一个或多个数据库连接,将严重降低应用程序性能。
因此在服务器应用程序中通常需要为多个数据库连接创建并维护一个连接池,当连接不再需要时,这些连接可以缓存在连接池中,当接收到下一个客户端请求时,从连接池中取出连接并重新利用,而不需要再重新建立连接。
所以代替var connection = mysql.createConnection({xxx})的连接方式
其中mysql.createPool的两个入参分别是createConnection方法中可以使用的各种属和数据库连接的必要参数;也可只传后者.
其中,connection.release()是归还到连接池;connection.destroy()当连接不在需要,从连接池中移除;connection.end()当连接池不在需要时,关闭连接池
url模块
- url.parse(urlString,boolean,boolean) 将一个url的字符串解析并返回一个url的对象
例:var params = url.parse(req.url, true).query;
第一个参数url:传入请求的url地址
第二个参数传入布尔值(选填):默认false,为true时返回的url对象中,query的属性为一个对象
第三个参数传入布尔值(选填):默认false,为true时不用指定协议,即请求时不用加http
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?name=%E5%94%90%E5%A4%A7&age=17&number=100',
query: [Object: null prototype] { name: '唐三', age: '17', number: '100' },
pathname: '/add',
path: '/add?name=%E5%94%90%E5%A4%A7&age=17&number=100',
href: '/add?name=%E5%94%90%E5%A4%A7&age=17&number=100'
}
- url.format(urlObj): 将传入的url对象编程一个url字符串并返回
let newUrl = url.format({
protocol:"http:",
host:"localhost:3000",
port:"3000"
});
console.log(newUrl) // 'http://localhost:3000'
- url.resolve(from,to): 返回一个格式为"from/to"的字符串,感觉是切换路由,将传入的from的host:port和to(要去的)的用"/"符号进行拼接,并返回
例:search替代了原来的add接口
console.log(url.resolve("http://localhost:3000/add", "search"));
// http://localhost:3000/search;