Bootstrap

nodejs应用级详解

简接:
nodejs是什么,nodejs不是一个语言,首先js是只能运行在浏览器的引擎上,nodejs让js可以运行在操作系统上,所以nodejs是让js可以运行在操作系统上的一个运行环境

init && package.json详解

npm init 后出现的文件

{
  // 项目名称
  "name": "my-project",
  // 项目版本
  "version": "1.5.0",
  //项目描述
  "description": "Express server project using compression",
  //项目入口js文件,定义了项目的入口点,通常是用于启动项目的文件
  "main": "src/index.js",
  //scripts 属性接受一个对象,值可以通过 npm run 运行的脚本,其键为实际运行的命令。这些通常是终端命令,我们把它们放入 scripts 字段,可以既可以记录它们又可以轻松地重用。
  "scripts": {
      "start": "node index.js",
      "dev": "nodemon",
      "lint": "eslint **/*.js"
  },
  //项目使用的所有依赖项及对应版本,在依赖版本中看到的插入符号(^)和波浪号(~)是 SemVer 中定义的版本范围的表示法。
  "dependencies": {
      "express": "^4.16.4",
      "compression": "~1.7.4"
  },
  //与 dependencies 字段类似,但是这里列出的包仅在开发期间需要,而在生产中不需要。
  "devDependencies": {
      "eslint": "^5.16.0",
      "nodemon": "^1.18.11"
  },
  //用于定义源代码所在的 url 及其使用的版本控制系统的类型
  "repository": {
    "type": "git",
    "url": "https://github.com/osiolabs/example.git"
  },
  //这些字段是列出公共项目的联系人以及与贡献者共享信用的有用方法。
  "author": "Jon Church",
  //这些字段是列出公共项目的联系人以及与贡献者共享信用的有用方法。
  "contributors": [{
    "name": "Amber Matz",
    "email": "[email protected]",
    "url": "https://www.osiolabs.com/#team"
  }],
  //NPM 注册表会为该字段建立索引,能够在有人搜索软件包时帮助找到它们。数组中的每个值都是与你的程序包关联的一个关键字。
  "keywords": ["server", "osiolabs", "express", "compression"]
}

util

1.美化输出

const util = require('util');

const obj = {
    name: "aaa",
    age:21
};

let temp = util.inspect(obj,{
    colors: true
});
console.log(temp);

2.获取绝对路径

const path = require('path');
//获取hello.js的绝对路径  E:\data\node_lecture\src\app\hello.js
const outputPath = path.join(__dirname, 'hello.js');
console.log(outputPath);

3.获取扩展名

const path = require('path');
const extInfo = path.extname(path.join(__dirname, 'hello.js'));
console.log(extInfo);

4.文件路径详细信息

const path = require('path');
const filePath = '/Users/helloworld/node/test.js';
const obj = path.parse(filePath);
console.log(obj);
{
  root: '/',
  dir: '/Users/helloworld/node',
  base: 'test.js',
  ext: '.js',
  name: 'test'
}

5.获取域名和ip

//获取ip
const dns = require('dns');
const domain = 'www.baidu.com';
dns.resolve(domain, function (error, address) {
    if (error) {
        console.log(error);
        return;
    }
    console.log(address);
});

//获取域名
const dns = require('dns');
dns.reverse('114.114.114.114', function (error, domain) {
    console.log(domain);
});

导入导出

一.CommonJs规范语法

导出
var myInfo = {
    name: 'zhangsan',
    age: 20
};
var myFunction = function (inputNumber) {
    return inputNumber + 5;
};
整体导出//推荐
exports.myInfo = myInfo;
exports.myFunction = myFunction;

2.导入模块

const myModule = require('./myModule');
console.log(myModule.myInfo);
console.log(myModule.myFunction(8));

二.es6语法
nodejs默认不支持这种写法,需要在package.json中写入一下内容

{
  "type": "module"
}
默认导出:是针对模块的主要功能或值的导出,可以任意命名。
var myInfo = {
    name: 'zhangsan',
    age: 20
};
export default myInfo  
导入:import xxx from './test.js'(可以自定义导入名称)

命名导出:
1.末尾导出:
// 导出
 var myInfo = {
    name: 'zhangsan',
    age: 20
};
var myFunction = function (inputNumber) {
    return inputNumber + 5;
};
export {myInfo,myFunction}
//导入
import {myInfo,myFunction} from './test.js'

2.直接导出:
// 导出
export var myInfo = {
    name: 'zhangsan',
    age: 20
};
export var myFunction = function (inputNumber) {
    return inputNumber + 5;
};
//导入
import {myInfo,myFunction} from './test.js'

文件操作

同步读取 && 异步读取 && 写入 && 删除 && 重命名

Node操纵文件系统是通过一个重要的原生模块来实现的:fs
对于fs中的绝大多数api来说,Node都提供了相同功能的两个版本:同步版本与异步版本。
对于同步版本与异步版本来说,其在方法的命名上存在一个规则:
xxxxx(异步)
xxxxxSync(同步)
尽最大可能去使用异步版本。

//同步
const fs = require('fs');
try {
    const data = fs.readFileSync('test.txt', 'utf8');
    console.log(data);
} catch (e) {
    console.log(e);
}
-------------------------------------------------------
const fs = require('fs');
//异步 文件路径  字符编码格式  回到函数
fs.readFile('test.txt', 'utf8', function (error, data) {
    if (error) {
        console.log(error);
        console.log('error occured')
    } else {
        console.log(data);//test.txt中的内容
    }
    }
});
console.log(data);//undefined,异步调用,执行到这里还没有读取到test.txt中
----------------------------------------------
//写入 向mytest.txt中写入 hello world,会覆盖mytext.txt中的原有内容
fs.writeFile('mytest.txt', 'hello world', function (error) {
    if (error) {
        console.log('write file error');
    } else {
        console.log('write file successful');
    }
});
------------------------------------------------
//追加写入
fs.writeFile('mytest2.txt', 'mytest2, node.js\r\n', {flag: 'a'}, function (error) {
    if (error) {
        console.log('write file error');
    } else {
        console.log('write file successful');
    }
});
//另一种写法
const fs = require('fs');
fs.appendFile('info.txt', 'hello world', 'utf8', (error) => {
    if (error) {
        throw error;
    }
    console.log('success');
});
-------------------------------------------------
//删除文件
const fs = require('fs');
fs.unlink('hello.txt', (error) => {
    if (error) {
        throw error;
    }
    console.log("success");
});
--------------------------------------------------
//重命名
const fs = require('fs');
const util = require('util');
fs.rename('hello.txt', 'world.txt', (error) => {
    if (error) {
        throw error;
    }
    //获取文件信息
    fs.stat('world.txt', (error, stats) => {
        if (error) {
            throw error;
        }
        console.log(util.inspect(stats));
    });
})

创建多级目录 && 读取指定目录下所有文件 && 文件是否存在 && 删除目录及其下的所有文件

//创建多级目录
const fs = require('fs');
fs.mkdir('mydir/hello/world', {recursive: true}, (error) => {
    if (error) {
        throw  error;
    }
    console.log('success');
});
-------------------------------------------
//读取指定目录下所有文件
const fs = require('fs');
fs.readdir('./', (error, files) => {
    if (error) {
        throw error;
    }

    console.log(files);
});
------------------------------------------------
//文件是否存在,存在正常执行,不存在抛出异常
fs.access('./app0.js', (error) => {
    if (error) {
        throw error;
    }
    console.log('success');
});
--------------------------------------------------
// 删除目录及其下的所有文件
const fs = require('fs');
fs.rmdir('mydir', {recursive: true}, (error) => {
    if (error) {
        throw error;
    }
    console.log('success');
});

文件流 && 读入流 && 写出流

//读入流
const fs = require('fs');

const readStream = fs.createReadStream('./app12.js', {encoding: 'utf8'});

readStream.on('open', (fd) => {
    console.log(fd);
});

readStream.on('ready', () => {
    console.log('ready');
});

readStream.on('data', (data) => {
    console.log(data);
});

readStream.on('end', () => {
    console.log('end');
});

readStream.on('close', () => {
    console.log('close');
});

readStream.on('error', (error) => {
    console.log(error);
});
------------------------------------------------------
//读取后写入
const fs = require('fs');
const readStream = fs.createReadStream('./app12.js', {encoding: 'utf8'});
const writeStream = fs.createWriteStream('mytest.js', {encoding: 'utf8'});
readStream.on('data', (data) => {
    writeStream.write(data, () => {
        console.log(data);
    });
});

Buffer

二进制数组

//申请一个128字节大小空间
const buffer = Buffer.alloc(128);
//指定字符集写入
const length = buffer.write('helloworld你好', 'utf8');
console.log('byte count: ' + length);
----------------------------------------------------------
//比较大小 用ASCII码值 返回 0 1 -1
const buffer1 = Buffer.from('hello');
const buffer2 = Buffer.from('world');
const compareResult = buffer1.compare(buffer2);
console.log(compareResult);
------------------------------------------------------------
const buffer = Buffer.alloc(3);
buffer[0] = 65;
buffer[1] = 66;
buffer[2] = 67;
console.log(buffer.toString('utf8'));
//输出ABC
------------------------------------------------------------
//和String比较
const str = 'abcde天';
const buffer = Buffer.from(str);
console.log(str.length);//6
console.log(buffer.length);//8
console.log(buffer);//<Buffer 61 62 63 64 65 e5 a4 a9>
console.log(buffer.toString('utf8'));//abcde天

服务器搭建

写法一

//导入http模块
var http = require('http');

//创建一个服务器
server = http.createServer(function(req, res){
    res.writeHead(200,'Content-Type','text/plain;charset=utf-8');
    res.write("hellowwsssw");
    //一定要加,标志响应内容结束
    res.end();
});

//启动服务器,服务在127.0.0.1,8888端口监听
server.listen(8888,"127.0.0.1");

//服务器正常启动,监听模式下调用
server.on("listening",function(){
    console.log("on");
});

//客户端与服务器建立连接后触发
server.on("connection",function(){
    console.log("connect")
});

//服务器关闭触发
server.on("close",function(){
    console.log("close");
});

console.log("server start on 8888")

写法二

const http = require('http');
const server = http.createServer(function (request, response) {
    //接收数据
    let data = '';
    //接收数据
    request.on('data', function (chunk) {
        data += chunk;
    });

    //返回响应
    request.on('end', function () {
        let method = request.method;
        let headers = JSON.stringify(request.headers);
        let httpVersion = request.httpVersion;
        let requestUrl = request.url;

        response.writeHead(200, {'Content-Type': 'text/html'});

        let responseData = method + ", " + headers + ", " + httpVersion + ", " + requestUrl;

        response.end(responseData);
    });
});

server.listen(3000, function () {
    console.log('Node Server started on port 3000');
});

客户端发送请求实例

const http = require('http');
http.get({
    'host': 'localhost',
    'port': '3000'
}, function(response) {
    let responseData = '';
    //读取响应数据
    response.on('data', function(chunk) {
        responseData += chunk;
    });
    //读取结束后触发
    response.on('end', function() {
        console.log(responseData);
    });
}).end();

事件

Node.js的事件机制中主要有三类角色:事件(Event)、事件发射器(EventEmitter)、事件监听器(EventListener)。

// 引入 events 模块
let events = require("events");
// 创建 eventEmitter 对象
let eventEmitter = new events.EventFmitter();
// 绑定事件及事件的处理程序
eventEmitter.on("eventName", eventHandler);
// 触发事件
eventEmitter.emit("eventName");

EventEmitter类–事件发射器
event:代表事件名,listener:代表事件处理函数
addListener(event,listener) 事件监听函数,一个事件默认最多能有10个监听器
on(event, listener) 对指定对象绑定事件处理函数(addListener方法的别名),一个事件默认最多能有10个监听器
once(event, listener) 对指定对象指定只执行一次的事件处理函数
removeListener(event, listener) 删除事件
setMaxListeners(n) 指定对象处理函数的最大数量,n为正数值,代表最大的可指定事件处理函数的数量
listeners(event) 获取指定对象的所有事件处理函数
emit(event, [arg1], [arg2], […]) 手动触发指定事件

事件处理过程中抛出的错误,可以使用try…catch捕获。

try {
    emitter.emit("abc");
} catch (err) {
    console.log("错误:", err.message);
}

node.js进程

主进程

//node.js的版本号
console.log(process.version);
//nodejs版本号及依赖的版本号
console.log(process.versions);
//当前运行平台
console.log(process.platform);
//node安装版本
console.log(process.execPath);
//配置信息
console.log(process.config);
//node进程pid
console.log(process.pid);
//node进程运行名称
console.log(process.title);
//操作系统的架构 64 32
console.log(process.arch);
//nodejs运行的内存情况
console.log(process.memoryUsage());
//nodejs的当前工作目录
console.log(process.cwd());
//nodejs的当前工作目录到上一级目录
process.chdir('../');
console.log(process.cwd());
//nodejs的环境信息
console.log(process.env);
//指定环境信息为dev环境
// process.env.NODE_ENV = 'dev'
//nodejs执行时间
console.log(process.uptime());

//监听exit 程序退出会执行回调
process.on('exit', () => {
    console.log('node process exited');
});
//显示的执行退出进程
// process.exit(0);

//退出之前回调
process.on('beforeExit', () => {
    console.log('node process before exited');
});

//进程异常处理
process.on('uncaughtException', (error) => {
    console.log(error);
    console.log('=======');
    console.log('uncaughtException occured');
});

//线程中断触发
process.on('SIGINT', () => {
    console.log('received SIGINT info');
});

setTimeout(() => {
    console.log('timeout');
}, 100000);
-----------------------------------------------------------------------
const fs = require('fs');
const myFunction = () => {
    console.log('myFunction invoked');
};
//下一个同步方法执行完毕,异步方法执行之前执行
process.nextTick(myFunction);

console.log(fs.readFileSync('./app1.js').toString('utf8'));

fs.readFile('./app1.js', (error, data) => {
    console.log(data.toString('utf8'));
});
-----------------------------------------------------------------------

子进程

1.spawn:执行任何命令

//引入子进程模块
const childProcess = require('child_process');

//使用子进程执行任何命令,会返回子进程对象
const lsChildProcess = childProcess.spawn('ls', ['-al', './']);

//子进程执行完成后,执行回调获取到 data ,stdout是标准输出,默认输出到控制台
lsChildProcess.stdout.on('data', (data) => {
    console.log(data.toString());
    //子进程pid
    console.log(`child process id: ${lsChildProcess.pid}`);
});

//子进程退出回调
lsChildProcess.on('exit', (code, signal) => {
    console.log(code);
});
----------------------------------------------------------------------------------------
const childProcess = require('child_process');

//创建子进程执行node app2命令,子进程又创建子进程
const nodeChildProcess = childProcess.spawn('node', ['app2']);

nodeChildProcess.stdout.on('data', (data) => {
    console.log(data.toString());
    console.log(`child process id: ${nodeChildProcess.pid}`);
});

nodeChildProcess.on('exit', (code, signal) => {
    console.log(code);
});
----------------------------------------------------------------------------------------

2.fork:只能执行nodejs命令 可以省略node,其余和spawn一样

const childProcess = require('child_process');
const forkProcess = childProcess.fork('./app5', {silent: true});

3.exec: 有回调,且创建一个新的shell去执行命令

const childProcess = require('child_process');

childProcess.exec('node app7', (error, stdout, stderr) => {
    if (error) {
        console.log(error);
        throw error;
    } else {
        console.log(stdout.toString());
    }
});

4.execFile:不会创建一个新的shell去执行命令

const childProcess = require('child_process');
childProcess.execFile('node', ['app9'], (error, stdout, stderr) => {
    if (error) {
        console.log(error);
        throw error;
    } else {
        console.log(stdout.toString());
    }
});

主子进程通信

const childProcess = require('child_process');
const forkProcess = childProcess.fork('./app5', {silent: true});
//接收子进程消息
forkProcess.on('message', (message) => {
    console.log(message);
});

//向子进程发送消息
forkProcess.send('hello world');

子进程文件son.js

[1, 2, 3, 4, 5].forEach(i => {
    console.log(i);
});

//接收主进程消息
process.on('message', (message) => {
    console.log(message);
    //向主进程发送消息
    process.send('welcome');
});

使用cluster创建子进程

const cluster = require('cluster');
const http = require('http');
const os = require('os');

const cpuCount = os.cpus().length;
//cluster.schedulingPolicy = cluster.SCHED_RR;
/*
    Master - Worker 模式
    主进程启动若干个子进程,子进程和主进程可以通信
    此时主进程不会处理请求,而是起到将请求分发到子进程。
    实际上port:3000只有一个线程监听,就是主进程

 */
if (cluster.isMaster) {
    for (let i = 0; i < cpuCount; ++i) {
        //cluster.fork()父进程创建的所有子进程,都会重新执行当前js文件
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(worker.process.pid);
    })
} else {
    //启动cpuCount个服务器
    const httpServer = http.createServer((request, response) => {
        let data = '';

        request.on('data', (chunk) => {
            data += chunk;
        });

        request.on('end', () => {
            response.writeHead(200, {'Content-Type': 'text/plain'});

            response.end(`${process.pid}`);
        });
    });

    httpServer.listen(3000, () => {
        console.log('listening to port 3000');
    });
}

koa

const Koa = require('koa');
const app = new Koa();
/**
 * 调用链路是 中间件1 -> 中间件2 -> 中间件3 -> 中间件4-> 中间件3 -> 中间件2 -> 中间件1
 */

//中间件1
app.use(async (ctx, next) => {
    console.log('myFunction1 started');
    //等待下一个中间件执行完成后,再接着往下执行
    await next();
    console.log('myFunction1 finished');
});

//中间件2
app.use(async (ctx, next) => {
    console.log('myFunction2 started');
    //等待下一个中间件执行完成后,再接着往下执行
    await next();
    console.log('myFunction2 finished');
});

//中间件3
app.use(async (ctx, next) => {
    console.log('myFunction3 started');
    //等待下一个中间件执行完成后,再接着往下执行
    await next();
    console.log('myFunction3 finished');
});

app.use(async (ctx) => {
    // ctx.body = 'Hello Koa';
    console.log('myFunction4 started');
    ctx.response.type = 'text/html';
    ctx.response.body = '<h2>Hello Koa</h2>';
    console.log('myFunction4 finished');
});

app.listen(3000);

node.js socket建立长链接

http是短连接
socket是长连接

1.服务端

const http = require('http');
const io = require('socket.io');
const fs = require('fs');

const server = http.createServer((request, response) => {
    response.writeHead(200, {'Content-Type': 'text/html'});

    if (request.url === '/') {
        fs.readFile('./client.html', 'utf8', (error, data) => {
            if (error) {
                console.log('error occured');
                return;
            } else {
                response.end(data.toString());
            }
        });
    } else {
        response.end('<html><body>Error</body></html>');
    }
});

server.listen(3000, 'localhost');

const socket = io.listen(server);

socket.on('connection', (socket) => {
    console.log('connection has been established');

    socket.on('message', (message) => {
        console.log('message: ' + message);
    });

    socket.on('disconnect', () => {
        console.log('connection has lost');
    });

	//1自定义服务器端事件,让客户端监听,发送this is serverEvent消息
    socket.emit('serverEvent', 'this is serverEvent');

	//2监听客户端写的自定义事件 serverEvent
    socket.on('clientEvent', (data) => {
        console.log(data.address + ", " + data.age);
    });

	//3监听客户端自定义事件broadcastEventClient
    socket.on('broadcastEventClient', (message) => {
        console.log(message);
        //3broadcast:向所有连接的所有客户端发送消息(不包含刚连接的客户端),不加broadcast仅向刚连接的客户端发送消息
        socket.broadcast.emit('broadcastEventServer', 'you are good!');
    });
	
	//
    socket.send('hello client');
});

2.客户端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>

    <script type="text/javascript">
        const socket = io('http://localhost:3000');

        socket.on('message', (message) => {
            console.log('data from server: ' + message);
        });

        socket.on('disconnect', () => {
            console.log('disconnect');
        });

		//1监听服务器写的自定义事件 serverEvent
        socket.on('serverEvent', (data) => {
            console.log('serverEvent: ' + data);
			//2自定义客户端事件,让服务器监听,发送一个对象
            socket.emit('clientEvent', {address: 'taiyuan', age: 20});
        });
        
		//3向服务器端发送消息,自定义事件broadcastEventClient
        socket.emit('broadcastEventClient', 'take care');
		//3接收服务器的响应
        socket.on('broadcastEventServer', (message) => {
            console.log(message);
        });
    </script>
</head>
<body>
</body>
</html>

这个js文件和html文件放到同一个文件夹,node app0.js启动后端服务器,用浏览器访问client.html即可

操作数据库

const mysql = require('mysql');
const uuid = require('uuid');

const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'root',
    database: 'mytest'
});

connection.connect((error) => {
    if (error) {
        console.log(error);
        throw error;
    } else {
        console.log('connection successful');

        const userId = uuid.v1();
        const username = 'nihao';
        const real_name = 'aaa';
        const age = 30;
        const address = 'bb';

        connection.query('insert into users set ?', {
            id: userId,
            username: username,
            real_name: real_name,
            age: age,
            address: address
        }, (error, result) => {
            if (error) {
                console.log('insert error occured: ' + error);
                throw error;
            } else {
                console.log(result);

                connection.query('select * from users', (error, result) => {
                    if (error) {
                        console.log('select error occured: ' + error);
                        throw error;
                    } else {
                        console.log(result);

                        connection.end((error) => {
                            if (error) {
                                console.log('end error occured');
                                throw error;
                            }
                        });
                    }
                });
            }
        });
    }
});

MongoDB

const mongoose = require('mongoose');

const uri = 'mongodb://localhost:27017/mytest';

mongoose.connect(uri, {useNewUrlParser: true, useUnifiedTopology: true}, (error) => {
    if (error) {
        console.log(error);
        throw error;
    } else {
        console.log('connection successful');

        const parentSchema = new mongoose.Schema({
            name: String,
            age: Number,
            address: String
        });

        const studentSchema = new mongoose.Schema({
            name: String,
            age: Number,
            address: String,
            married: Boolean,
            parents: parentSchema
        });

        mongoose.model('student', studentSchema);

        const Student = mongoose.model('student');

        const student = new Student({
            name: 'zhangsan',
            age: 20,
            address: 'tianjin',
            married: false,
            parents: {
                name: 'lisi',
                age: 50,
                address: 'dalian'
            }
        });

        student.save((error) => {
            if (error) {
                console.log(error);
                throw error;
            } else {
                console.log('save successful');

                Student.find({}, (error, docs) => {
                    if (error) {
                        console.log(error);
                        throw error;
                    } else {
                        console.log(docs);

                        // mongoose.connection.close();

                        docs.forEach(doc => {
                            doc.remove();
                        });
                    }
                });
            }
        });
    }
});

操作redis

//获取连接
    getRedisConnection() {
        return new Redis({
            host: 'localhost',
            port: 6379
        });
    }

//增加
const redis = this.getRedisConnection();
redis.set(redisKeyPrefix + userSessionId, userId, 'ex', 1800, (error, result) => {
            redis.quit();
        });
//根据键删除
redis.del(redisKeyPrefix + userSessionId, (error, result) => {
            redis.quit();
        });
//根据键获取
const redis = this.getRedisConnection();
return redis.get(redisKeyPrefix + userSessionId, (error, userId) => {
        	redis.quit();
        	return userId;
        });
//重置过期时间
const redis = this.getRedisConnection();
redis.expire(redisKeyPrefix + userSessionId, 1800, (error, result) => {
      		redis.quit();
      	});
;