本文介绍如何使用nodejs
简单的HttpServer
创建一个app.js文件输入如下内容
const http = require('http');
http.createServer((req,res) => {
res.writeHead(200, {
'Content-Type': 'text/plain'
})
res.write('Hello, World!\n'); // 返回给客户端的消息
res.end();
}).listen(8000);
用浏览器访问http://localhost:8000/即可
调试nodejs
运行 node --inspect-brk app.js
然后在你的程序里面随便写个断点debugger
使用谷歌访问about://inspect,然后点击inspect进入你的程序
使用前端访问,即可进入刚才打的断点即可
基础路由
-
回调函数形式
const http = require('http'); function index(req, res) {
res.writeHead(200);
res.end('Hello, World!');
} http.createServer(function(req,res){
res.setHeader("Access-Control-Allow-Origin","*");
res.setHeader("Access-Control-Allow-Headers","content-type");
res.setHeader("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS"); if(req.url === '/') {
return index(req, res);
} res.writeHead(404);
res.end(http.STATUS_CODES[404]);
}).listen(8000) -
对象形式
const http = require('http');
var routes = {
'/': function index(req, res) {
res.writeHead(200);
res.end('Hello, World');
},
'/test': function test(req, res) {
res.writeHead(200);
res.end("test page");
}
} http.createServer(function(req,res){
res.setHeader("Access-Control-Allow-Origin","*");
res.setHeader("Access-Control-Allow-Headers","content-type");
res.setHeader("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS"); if(req.url in routes) {
return routes[req.url](req, res)
} res.writeHead(404);
res.end(http.STATUS_CODES[404]);
}).listen(8000)
nodejs配置开发和生产环境
-
项目目录
-
development.js
module.exports={
args: {
a: 'development'
}
} -
production.js
module.exports={
args: {
a: 'production'
}
} -
config.js
var path = require('path');
var env = process.env.NODE_ENV || 'production';
env=env.toLowerCase(); var file = path.resolve(__dirname, env); try{
module.exports = require(file+'.js');
}catch(err) {
throw err;
} -
app.js
const http = require('http'); var args = require('./config/config.js');
console.log(args.args.a); var routes = {
'/': function index(req, res) {
res.writeHead(200);
res.end('Hello, World');
},
'/test': function test(req, res) {
res.writeHead(200);
res.end("test page");
}
} http.createServer(function(req,res){
res.setHeader("Access-Control-Allow-Origin","*");
res.setHeader("Access-Control-Allow-Headers","content-type");
res.setHeader("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS"); if(req.url in routes) {
return routes[req.url](req, res)
} res.writeHead(404);
res.end(http.STATUS_CODES[404]);
}).listen(process.env.PORT || 8001) -
运行
set NODE_ENV=development&&set PORT=8881&&node app.js
nodejs核心模块一览
'assert' 断言测试模块
'buffer' 操作二进制数据
'c/c++_addons' 提供在js中运行c/c++库接口
'child_process' 生成子进程
'cluster' 利用此模块可以把nodejs集群进程部署在同一端口的多核计算机上
'console' 提供一个console功能,类似浏览器的
'crypto' 提供加密功能
'deprecated_apis' 废弃的api
'dns' 通过dns.lookup()访问本机的dns,通过提供的其他api访问网络dns
'domain' 弃用了
'Events' 异步事件驱动架构
'fs' 文件I/O操作
'http' 方便操作http协议
'https' 方便操作https协议
'module' 文件加载系统
'net' 异步网络包装器
'os' 提供操作系统api接口
'path' 提供工具操作文件和文件夹路径
'punycode' 废弃
'querystring' 提供工具转化和格式化URL查询字符串
'readline' 提供接口用来从可读流中读取数据一次一行,例如process.stdin
'repl' 提供一个REPL实现
'stream' 操作流对象
'string_decoder' 提供api用来解码Buffer对象转化成字符串
'timers' 提供全局api用来执行定时任务
'tls_(ssl)' 提供api用来实现OpenSSL
'tracing' 用来追踪程序中产生的信息,添加--trace-events-enabled开启
'tty' 几乎没啥用
'dgram' 提供实现UDP数据报
'url' 提供api操作URL
'util' node.js的内部api
'v8' 提供v8api
'vm' 提供api在v8虚拟环境中编译和运行js
'zlib' 提供实现Gzip的函数接口
express用法
-
路由方法处理
npm install express --save const express = require('express');
const app = express();
const port = 8000; app.all("*",function(req,res,next){
// 所有路由
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers","content-type");
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options') res.send(200);
else next();
}); app.get("/testget", function(req, res, next){
res.send('testget');
}) app.post("/testpost", function(req, res, next){
res.send('testpost');
}) app.put("/testput", function(req, res, next){
res.send('testput');
}) app.delete("/testdelete", function(req, res, next){
res.send('testdelete');
}) app.use("/testuse", function(req, res, next){
// 所有路由
res.send('testuse');
}) // 路由链
app.route('/testroute').get(function (req, res, next) {
res.send('testrouteget');
}).post(function (req, res, next) {
res.send('testroutepost');
}).put(function (req, res, next) {
res.send('testrouteput');
}) // 函数链
app.get('/mypath', function (req, res, next) {
console.log('1');
next();
}, function (req, res, next) {
console.log('2');
res.send('....');
}) app.listen(port, function() {
console.log('Server listening on http://localhost:' + port);
}); -
模块化(直接传递值)
app.js
const express = require('express');
const otherMiddleware = require('./other.js');
const app = express(); app.all("*",function(req,res,next){
// 所有路由
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers","content-type");
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options') res.send(200);
else next();
}); app.use('/api/v1/', otherMiddleware({ data:'Hello world' })).listen(8000); other.js
const express = require('express');
module.exports = function(options={}){
const router = express.Router(); router.get('/test', (req, res, next) => {
res.end(options.data);
}) return router;
} -
模块化(传递类实例)
app.js
const express = require('express');
const otherMiddleware = require('./other.js');
const app = express(); app.all("*",function(req,res,next){
// 所有路由
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers","content-type");
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options') res.send(200);
else next();
}); class CreateDataService {
constructor(data='Hello'){
this.data=data;
} createData(outdata) {
return `${this.data}, ${outdata}!`;
}
} app
.use('/api/v1/service1', otherMiddleware({ service: new CreateDataService('Hello')}))
.use('/api/v1/service2', otherMiddleware({ service: new CreateDataService('Hi')}))
.listen(8000); other.js
const express = require('express'); module.exports = function(options={}){
const router = express.Router(); const {service} = options; router.get('/test', (req, res, next) => {
res.end(service.createData(req.query.outdata || "haven't any data"));
}) return router;
} 请求
http://localhost:8000/api/v1/service1/test?outdata=World
http://localhost:8000/api/v1/service2/test?outdata=World -
模板引擎(jade)
安装 npm install jade --global app.js
const express = require('express');
const app = express(); const PORT = 8000; app.all("*",function(req,res,next){
// 所有路由
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers","content-type");
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options') res.send(200);
else next();
}); app.set('view engine','jade');
app.set('views',__dirname); app.get('/', function(req, res) {
res.render('test');
}); app.listen(PORT, function(err){
if(!err) {
console.log('Server is running at port', PORT);
}else{
console.log(JSON.stringify(err));
}
}) test.jade
doctype html
html
title hello,jade
body
h1 Hello World
a(href='https://www.baidu.com') baidu
input(type='text') - var a = 1
case a
when 0: p 0
when 1: p 1
default: p #{friends} 输入http://localhost:8000/即可看到页面 -
模板引擎(ejs)
npm install ejs app.js
const express = require('express');
const app = express();
var cors = require('cors');
const PORT = 8000;
app.use(cors()); app.set('view engine','ejs');
app.set('views',__dirname); app.get('/', function(req, res) {
res.render('test', {
title: 'ejs测试成功',
arr: ["a", "b", "c"]
});
}); app.listen(PORT, function(err){
if(!err) {
console.log('Server is running at port', PORT);
}else{
console.log(JSON.stringify(err));
}
}) test.ejs
<!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>Document</title>
</head>
<body>
<h1><%= title %></h1> <ul>
<% for(var i=0; i<arr.length; i++) { %>
<li>
<%= arr[i] %>
</li>
<% } %>
</ul>
</body>
</html> -
处理json数据
const express = require('express');
const app = express();
var cors = require('cors');
const PORT = 8000;
app.use(cors()); app.get('/', function(req, res){
var info={
'a': 'aaaa',
'b': 1111
} // 方式一 res.json(info);
// 方式二 res.send(JSON.stringify(info))
// 方式三 res.status(200).json(info); }) app.listen(PORT, function() {
console.log('Node.js listening on port ' + PORT)
}) -
请求静态文件
新建一个public文件夹,在里面放置你的静态文件,比如index.html
只需要一句代码就搞定一切了 app.use(express.static('public'));
直接访问http://localhost:8000/index.html即可 你可以添加一个前缀比如 app.use('/static', express.static('public'));
直接访问 http://localhost:8000/static/index.html -
错误处理
使用pug模板引擎
创建一个views文件夹,在里面创建一个error.pug文件,输入如下内容
html
body
h1= message
h2= error.status
p= error.stack 测试代码
app.set('view engine','pug');
app.set('views',path.resolve(__dirname, 'views')); // 制造一个404错误,并将err抛出
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
//pass error to the next matching route.
next(err);
});
// 接收错误,渲染错误页面
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
}); -
从request中获取信息
最原始的方法获取request属性
请求路径 /test/11?a=1
app.get('/test/:id', function(req, res){
console.log(req.originalUrl); // /test/1?a=1
console.log(req.params.id); // 11
console.log(req.query.a); // 1
res.send(req.get('Content-Type'));
}) 使用body-parser中间件处理,可以方便地处理请求体的参数
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/test', function(req, res, next) {
console.log(req.body);
}) 同样何以使用cookie-parser,可以方便地处理cookie对象
npm install cookie-parser
var cookieParser = require('cookie-parser')
app.use(cookieParser());
app.use(express.static('public'));
app.get('/setcookie', function(req, res) {
res.cookie('name', 'yjw', {
maxAge: 100000, // 过期时间
httpOnly: true
});
return res.send('cookie设置成功');
});
app.get('/getcookie', function(req, res) {
var name = req.cookies['name'];
if(name) {
return res.send(name);
}
return res.send("cookie中无数据")
}); -
请求之前和完成之后的回调
在api最前面添加
app.use(function (req, res, next) {
function afterResponse() {
res.removeListener('finish', afterResponse);
res.removeListener('close', afterResponse);
// 这里输入请求完成要执行的代码
console.log("请求完毕");
}
res.on('finish', afterResponse);
res.on('close', afterResponse);
// 这里输入请求之前要执行的代码
console.log("准备请求");
next();
}); -
自定义中间件
只需要使用app.use传递一个函数即可 app.use(function(req, res, next){
req.data="testdata";
next();
}) -
Django风格的路由
实际上就是命名一个路由,然后实现跳转
npm install express-reverse require('express-reverse')(app);
app.get('test','/getdata/:data', function(req, res) {
return res.send(req.params.data);
}) app.get('/test-redirect', function(req, res, next) {
res.redirectToRoute('test', { data: 'world' });
}); 然后请求test-redirect即可
文件I/O
-
异步读取文件
const fs = require('fs');
const path = require('path');
app.get('/test', function(req, res) {
fs.readFile(path.resolve(__dirname, 'test.txt'), {encoding: 'utf-8'}, (err, content) => {
if(err) return console.error(err);
res.send(content);
})
}) -
文件目录
app.get('/test', function(req, res) {
fs.readdir(__dirname, (err, files) => {
if(err) return console.error(err);
res.send(files.join(' '));
})
}) -
使用流复制文件
// highWaterMark是设置缓冲区
var readable = fs.createReadStream(path.resolve(__dirname, 'test.txt'), { encoding: 'utf8', highWaterMark: 16 * 1024 });
var writable = fs.createWriteStream(path.resolve(__dirname, 'testbak.txt'));
readable.pipe(writable); -
检查文件是否有权限
fs.constants.F_OK 读/写/可执行权限
fs.constants.R_OK 读权限
fs.constants.W_OK 写权限
fs.constants.X_OK 可执行权限 var filepath = path.resolve(__dirname, 'test.txt');
fs.access(filepath, fs.constants.R_OK | fs.constants.W_OK, (err) => {
if(err) {
console.log("%s 文件不存在", filepath);
}else {
console.log("可以读或者写 %s 此文件", filepath);
}
}) -
检查文件和目录是否存在
var filepath = path.resolve(__dirname, 'test.txt');
fs.stat(filepath, function(err) {
if(!err) {
console.log('文件或者文件夹存在');
}else if(err.code === 'ENOENT') {
console.log('文件或者文件夹不存在')
}
}) -
按行读取
const readline = require('readline');
var filepath = path.resolve(__dirname, 'test.txt');
var linesCount = 0;
var rl = readline.createInterface({
input: fs.createReadStream(filepath),
output: process.stdout,
terminal: false
}); rl.on('line', function (line) {
console.log(line);
linesCount++;
});
rl.on('close', function () {
console.log(linesCount);
}); -
获取用户输入信息
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('What is your name?', (name) => {
console.log(`Hello ${name}!`);
rl.close();
}); -
创建文件夹
fs.mkdir(path.resolve(__dirname, 'test'), (err) => {
if(err && err.code !== 'EEXIST' ? err : null){
return console.error(err);
}else {
// 此处文件夹已经创建成功,可以对刚创建的文件夹做操作 }
}) -
往文件中写数据
// 直接覆盖
fs.writeFile(path.resolve(__dirname, 'test.txt'), '我是写入的数据', function(err){
if(err) return console.error(err);
}); -
文件内容替换
// 将文件中部分文字替换成其他的文字,其中正则flags的gim分别表示全局、不区分大小写、多行匹配
var filepath = path.resolve(__dirname, 'test.txt');
fs.readFile(filepath, 'utf-8', function(err, data) {
if (err) throw err;
var newValue = data.replace(/11111111/gim, 'a');
fs.writeFile(filepath, newValue, 'utf-8', function(err, data) {
if (err) throw err;
console.log('搞定!');
})
}) -
删除文件
fs.unlink(path.resolve(__dirname, 'test.txt'),function(err){
if(err) console.error(err);
console.log("已删");
}) -
使用流读取文件
let fileBuffer, chunks = [], stream = fs.createReadStream(path.resolve(__dirname, 'test.txt'));
stream.once('error', (err) => {
// 出错了就执行此方法
console.error(err);
})
stream.on('data', (chunk) => {
// 将数据一点点读入到chunks中
chunks.push(chunk);
});
stream.once('end', ()=>{
// 读取结束后,调用此方法
fileBuffer = Buffer.concat(chunks);
console.log(fileBuffer.toString());
})
nodejs模块
```txt
node自带的模块处理功能
module.exports={ // 只能导出一个对象
a: 'a',
b: 'b'
}
等价写法如下
exports.a='a';
exports.a='b';
导入模块
const test = require('./test.js');
es6的模块处理
```
node集群
app.js
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// 根据电脑cpu的核心数创建多个Worker分支
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
if (signal) {
console.log(`worker was killed by signal: ${signal}`);
} else if (code !== 0) {
console.log(`worker exited with error code: ${code}`);
} else {
console.log(`worker success ${worker.process.pid} died`);
}
});
} else {
// 让workers共享一个端口
require('./server.js')();
}
server.js
// const http = require('http');
const express = require('express');
const app = express();
var cors = require('cors');
app.use(cors());
app.use(express.static('public'));
function startServer() {
app.get("/test", function(req, res, next){
res.send('test');
})
app.listen(8000, function() {
console.log('Server listening on http://localhost:' + 8000);
});
}
if (!module.parent) { // 判断此文件是否被其他文件引用
// 这个文件是直接运行的,就直接启动server
startServer();
} else {
// 这个文件是通过其他文件导入执行的,就导出server
module.exports = startServer;
}
事件发射器
const EventEmitter = require('events').EventEmitter;
class Test extends EventEmitter {};
let t = new Test();
t.on('fn', (data) => { // 如果你需要设置一次性的监听器用once
console.log(data);
})
console.log(t.eventNames()); // 查看所有的订阅者(函数)
console.log(t.listenerCount("fn")); // 获取指定监听器的数量
t.emit('fn', "abc");
自动重载
自动刷新代码
npm install -g nodemon
nodemon app.js
环境变量
系统环境变量
console.log(process.env)
命令行参数
console.log(process.argv)
从属性文件中获取配置参数
npm install properties-reader
app.js
const path = require('path');
var PropertiesReader = require('properties-reader');
var properties = null;
process.argv.forEach(function (val, index, array) {
var arg = val.split("=");
if (arg.length > 0) {
if (arg[0] === 'env') {
properties = PropertiesReader(path.resolve(__dirname, arg[1] + '.ini'));
}
}
});
var someVal = properties.get('yjw.data.a');
console.log(someVal)
dev.ini
[yjw]
data.a = 1
运行 node app.js env=dev
Promise的简单使用
var fn = function(flag) {
return new Promise(function(resolve, reject) {
if(flag) {
resolve("it is true");
}else {
reject(new Error("it is false"));
}
})
}
fn(false).then(function(data){
console.log(data);
}).catch(function(err){
console.log(err);
})
node异常重启
node是单线程的程序,如果node进程出问题了,那么我们开发的程序自然就崩盘了
这个时候可以安装一个监视进程,如果node进程挂了,就再次启动
有n种监视进程,这里就列举一个
forever
npm install forever -g
process.on('uncaughtException', function (err) {
console.log(err);
process.exit(1); // 捕获node进程异常之后,结束此进程,目的是利用forever重启node,避免数据库不重连
});
forever start index.js // 启动程序
forever list // 产看forever监视进程
forever stop 0 // 停止监视进程
NODE_ENV环境变量
在程序中可以使用process.env.NODE_ENV来辨别当前环境是生产环境还是开发环境
if(process.env.NODE_ENV === 'production') {
} else {
}
设置NODE_ENV的几种方式
方式一:直接设置
set NODE_ENV=development&&node app.js
方式二:.env文件
npm install env-cmd -g
编写.env文件,输入NODE_ENV=development
运行env-cmd .env node app.js即可
方式三:cross-env插件
npm install cross-env -g
cross-env NODE_ENV=development node app.js
异步平行流
多个任务一起执行
var async = require('async');
function fn1(callback) {
setTimeout(function(){
callback(null, 'a') // 第一个参数是错误值,后面的参数都是成功值
}, 100)
}
function fn2(callback) {
setTimeout(function(){
callback(null, 'b')
}, 200)
}
function fn3(callback) {
setTimeout(function(){
callback(null, 'c')
}, 300)
}
async.parallel([ fn1, fn2, fn3 ], function(err, results) {
if (err) {
return console.error(err);
}
console.log(results); // [ 'a', 'b', 'c' ]
});
同步平行流
多个任务一个接一个执行
function fn1(callback) {
setTimeout(function(){
callback(null, 'a')
}, 100)
}
function fn2(callback) {
setTimeout(function(){
callback(null, 'b')
}, 200)
}
function fn3(callback) {
setTimeout(function(){
callback(null, 'c')
}, 300)
}
async.series([ fn1, fn2, fn3 ], function(err, results) {
if (err) {
return console.error(err);
}
console.log(results); // [ 'a', 'b', 'c' ]
});
同步值传递平行流
多个任务一个接一个执行,并且上一个任务的返回值传递给下一个任务
function fn1(callback) {
setTimeout(function(){
callback(null, 'a')
}, 100)
}
function fn2(data, callback) {
setTimeout(function(){
callback(null, data+'b')
}, 200)
}
function fn3(data, callback) {
setTimeout(function(){
callback(null, data+'c')
}, 300)
}
async.waterfall([ fn1, fn2, fn3 ], function(err, results) {
if (err) {
return console.error(err);
}
console.log(results); // abc
});
异步遍历
async.each
async.each(['a', 'b', 'c'], function(item, callback){
console.log(item);
callback(null);
}, function(err){
console.log(err);
})
async.times
长for循环,必须使用这个,要不然你的node程序就卡机了
async.times(5, function(n, next) {
next(null, n);
}, function(err, results) {
console.log(err);
console.log(results); // [ 0, 1, 2, 3, 4 ]
});
使用socket.io实现一个钟表功能
npm install socket.io
服务端代码
const express = require('express');
const app = express();
var cors = require('cors');
app.use(cors());
const server = app.listen(8000, console.log("Socket.io Hello World server started!"));
const io = require('socket.io')(server);
io.on('connection', (socket) => {
console.log("Client connected!");
socket.on('receivefromclient', (msg) => {
console.log(msg);
})
setInterval(function(){
var today=new Date()
var h=today.getHours()
var m=today.getMinutes()
var s=today.getSeconds()
socket.emit('receivefromserver', h+":"+m+":"+s);
}, 1000)
});
前端代码
<!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>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
</head>
<body>
<p id="message"></p>
</body>
<script>
var socket = io("http://localhost:8000");
setInterval(function(){
socket.on("receivefromserver", function(msg) {
document.getElementById('message').innerHTML = msg;
});
}, 1000)
socket.emit('receivefromclient', '收到消息!');
</script>
</html>