Node.js基础笔记

1:模块系统

JS这门语言最开始是作为浏览器脚本存在的,本身并没有模块系统,模块的划分靠html中的script标签控制,然而node.js将js作为后台开发语言,必须要有模块划分,如果一个文件要将本文件定义的变量和函数暴露出去,可以使用export:

//文件a.js
let a = 10;
function f(){};
module.exports = {
	a:a,
	f:f
}

或者

module.exports.a = a;
module.exports.f = f;

如果不涉及函数和数组,直接使用export也可,因此下面这种写法也是可以的

exports.a = a;	//a不是数组和函数,可以省略module关键字
module.exports.f = f;

node中模块的实现,本质是将文件包含在了一个函数里,上面文件中的代码编译后会用函数进行包装,转化为下面这种形式,并预先生成module对象,作为参数传入。

module={
	id:'a.hs',
	exports:{}
}
function load(module){
	let a = 10;
	function f(){};
	return module.exports;
}

如果要使用上面的模块,则需要用到require关键字:

const a = require('./a.js');
console.log(a.a);
a.f();

这是用require导入自定义模块的情况,如果需要使用系统模块,比如文件系统,则不需要添加 ./ 表明在当前目录内:

const fs = require('fs');

在es6之后有了和其他语言一样的import关键字,import是编译时调用,require是运行时调用,因而可以放在文件的任何一个位置(这里其实有个疑惑,import难道不能放在文件的任何一个位置么?)。

2:文件系统

一共分为读、写、文件状态获取、读流、写流、pipe几个部分。
读文件:

const fs = require('fs');
//异步读取
//如果是二进制数据,或者没有指定解析格式为utf-8,则会返回Buffer对象,本质是一个数组,数组中的每个元素是16进制值。
fs.read('文件路径','utf-8',function (err,data) {
    if(err){
		console.log(err);
	}else{
		console.log(data);
	}
});
//同步读取
try {
    let data =fs.readFileSync('文件路径','utf-8');
}catch (e) {
    console.log(e)
}
//string和buffer之间相互转化
let data2 = Buffer.from(data,'utf-8');	//将字符串转化为二进制
data = data2.toString('utf-8');

写文件

//同步写入
fs.write('文件路径','utf-8',function (err) {
    console.log(err);
});
//异步写入
try {
    fs.writeFileSync('文件路径','utf-8');    
}catch(e){
    console.log(e);
};

readStream

如果文件很大,不可能一次性从磁盘读取到内存中,这个时候就需要使用流的方式每次读一部分。流是一种抽象,就好像从自来水管接一桶水一样。

const rs = fs.createReadStream('文件路径','utf-8');
re.on('data',function (chunk) {});	//读取一段文件
re.on('end',function () {});	//文件读取完毕
re.on('error',function (err) {});	//文件读取出错

writeStream

const ws = fs.createWriteStream('');
ws.write('','utf-8');
ws.write('','utf-8');
ws.end();

pipe

pipe表示管道,如果需要把一个文件的内容读出来,然后写入另一个文件中,就好像在两个文件中建立了一根水管一样,所以叫管道。
过程:使用读流从文件A中读取数据,使用写流将读取的数据写入文件B中

const fs = require('fs');
const rs = fs.createReadStream('文件A','utf-8');
const ws = ts.createWriteStream('文件B');
rs.pipe(ws);

3:http

服务器自然离不开网络,这部分用于接收和响应http请求。
最简单的http服务器:

const http = require('http');
//创建服务器
//这里res对象本质是一个writeStream
const server = http.createServer(function (req,res) {
    console.log(req.method);
    console.log(req.url);
    res.writeHead(200,{
        'Content-Type':'text-html'
    });
    res.end('

hello world

'
); }); server.listen(8080);

文件服务器:

const
    fs = require('fs');
    url =require('url');
    path = require('path');
    http = require('http');

//||符号:如果左边表达式为真返回左边表达式,否则返回右边表达式,这里如果行命令启动的时候指定了文件路径,那么使用行命令指定的文件路径,否则使用当前代码文件所在的根目录为文件目录 path.resolve('.') 即是获取当前跟目录
const root = path.resolve(process.argv[2] || '.');
const server = http.createServer(function (req,res) {
	
	//获取浏览器中输入的文件路径,这里的路径不包含host和端口因而不需要特殊处理
    const pathname = req.url;
    const filePath = path.join(root,pathname);

	//判断文件是否存在
    fs.stat(filePath,function (err,stat) {
        if (err){
            res.writeHead('404');
            res.end('404 not find');
        }else{
            res.writeHead('200');
            const rs = fs.createReadStream(filePath,'utf-8').pipe(res);
        }
    });
});
server.listen(8080);
console.log('http://127.0.0.1:8080');

4:加密和哈希 —— crypto

hash: 用户验证文件的正确性,同样的文件生成和哈希值是相同的,如果哈希值不同,那么文件肯定也不一样,因而可以用于校验文件的完整性和正确性,hash算法有许多,常用的如MD5、SHA1、SHA256、SHA512等。
MD5:

const crypto = require('crypto');
const md5 = crypto.createHash('md5');

//输入用户MD5加密的内容,这里可以更新多次
md5.update('hello');
md5.update('world');

//使用16进制(hex)输出
console.log(md5.digest('hex'));

Hmac:
Hmac基于MD5、SHA等哈希算法,区别在于Hmac多了一个秘钥,相同的内容如果秘钥不同,生成的哈希也不一样。

const crypto = require('crypto');

//第二个参数即是秘钥
const hmac= crypto.createHmac('md5','goldfish');
hmac.update('hello');
hmac.update('word');
console.log(hmac.digest('hex'));

对称加密aes

let crypto = require('crypto');

function aesEncrypt(data, key, iv) {
	//生成加密器,算法为aes192,秘钥为key,初始向量为iv
	//只有key和iv都相同,生成的密文才是相同得1
    let cipher = crypto.createCipheriv('aes192', key, iv);

	//
    let crypted = cipher.update(data,'utf-8','hex');
    crypted += cipher.final('hex');
    return crypted;
}

function aesDecrypt(encrypt, key, iv) {
    let decipher = crypto.createDecipheriv('aes192', key, iv);
    let decryped = decipher.update(encrypt,'hex','utf-8');
    decryped += decipher.final('utf-8');
    return decryped;
}

还有一些加密api用到的时候再查

5. web开发 —— KOA2

之前nodejs的web框架为express,但由于express存在callback hell的问题,因此这从KOA2开始学起。
node.js可以使用package.json文件管理包依赖,在js目录下新建该文件,写入如下内容:

{
    "name": "hello",
    "version": "1.0.0",
    "description": "Hello Koa 2 example with async",
    "main": "app.js",
    "scripts": {
        "start": "node app.js"
    },
    "keywords": [
        "koa",
        "async"
    ],
    "author": "Tang He",
    "license": "Apache-2.0",
    "repository": {
        "type": "git",
        "url": ""
    },
    "dependencies": {
        "koa": "2.0.0"
    }
}

然后同样在js目录下输入npm install即可,这种方式在管理多个包的时候更为方便,其实主要就是最后的dependencies。
使用koa2新建一个简单的web服务器:

const koa = require('koa');
const app = new koa();

//async 表示这里使用的是一个异步函数
app.use(async function (ctx, next) {
	//调用下一个async函数
    await next();
    ctx.response.type = 'text/html';
    ctx.response.body = '

hello koa

'
; }); app.listen(3000);

koa把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能,看下面的例子。

const koa = require('koa');
const app = new koa();

app.use(async function(context,next) {
    console.log(context.request.url+context.request.method);
    await next();
});

app.use(async function(context, next) {
   const timeBefore = new Date().getTime();
   await next();
   const timeAfter = new Date().getTime();
   console.log(timeAfter-timeBefore);
});
app.use(async function(context, next) {
    await next();
    context.response.type = 'text/html';
    context.response.body = 'hello koa';
});
app.listen(8000);

这里有三个use,先调用第一个use,在该use中调用next()拿到第二个use,同样在第二个use中调用next拿到第三个。如果第二个或者第一个use中的next没有被调用,后面的代码就不会被执行。

koa-router:
提供了路由相关功能,这里路由的实现其实和iOS中的路由框架很像,如果根据用户的请求路径一个个比较效率很低,底层使用哈希表或者二叉查找树就会简单许多,这里koa-router就提供了路由查找的功能。

const koa = require('koa');
const router = require('koa-router')();
const app = new koa();

//接收到请求后打印请求内容,然后转发给下一个use
app.use(async function(context,next) {
    console.log(context.request.url+context.request.method);
    await next();
});

//将请求转发给路由,路由会选择接收请求的处理函数
app.use(router.routes());

router.get('/',function (context,next) {
    context.response.body = '

hello world

'
}); router.get('/goldfish',function (context,next) { context.response.body = '

hello goldfish

'
; }); app.listen(8000);

koa-bodyparser
上面的代码只能用户处理get请求,如果是post请求呢?请求内容在请求体重,无论是node原生还是koa-router都没有提供body解析的功能,这时候就需要一个解析工具 —— koa-bodyparse:

const koa = require('koa');
const bodyParse = require('koa-bodyparser');
const router = require('koa-router')();

const app = new koa();

app.use(async function(context,next) {
    console.log(context.request.url+context.request.method);
    await next();
});

//请求先被转发到body解析
app.use(bodyParse());

//解析后的请求被转发给路由处理
app.use(router.routes());

router.get('/', async (ctx, next) => {
    ctx.response.body = `

Index

Name:

Password:

`
; }); router.get('/goldfish',function (context,next) { context.response.body = '

hello goldfish

'
; }); router.post('/login',function (context,next) { const username = context.request.body.name || ''; const password = context.request.body.password || ''; if (username === 'koa' && password === '123'){ context.response.body = 'success!'; } else { context.response.body = 'login fail'; } }); app.listen(8000);

上面的代码,如果响应的请求很多,通常不放在一个文件内,分多个文件写,其中请求处理部分称为controller,单独拉一个文件管理。

MVC
模板引擎部分可以使用 Nunjucks: 官方文档
Node.js基础笔记_第1张图片
其中 async 的响应函数就是controller,用于处理业务方面的逻辑,比如判断用户是否存在,从数据库或文件系统中读取数据等。模板就是view,用户视图展示。数据从数据库中取出来,拼接成model,再传递给view。

你可能感兴趣的:(JavaScript)