Node.js
1、终端基本使用
打开应用
- notepad 打开记事本
- mspaint 打开画图
- calc 打开计算机
- write 写字板
- sysdm.cpl 打开环境变量设置窗口
常用命令
- md 创建目录
- rmdir(rd) 删除目录,目录内没有文档。
- echo on > a.txt 创建空文件
- echo 内容 > a.txt 往文件里写内容
- echo 内容 >> a.txt 往文件里追加内容,同时也会覆盖以前的
- del 删除文件
- rm 文件名 删除文件
- cat 文件名 查看文件内容
- cat > 文件名 向文件中写上内容
2、Node.js开发环境搭建
- Node.js 介绍
- Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境
- Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效
- Node.js 的包管理器 npm,是全球最大的开源库生态系统
- javascript 是脚本语言,需要解析器才能执行,浏览器就充当了解析器
- 在Chrome中,解析器就是 V8 引擎,将 javascript 转换成 机器码
- V8 引擎是开源的,由 C++ 语言编写,性能高
- Node.js 高性能,事件驱动,非阻塞,生态圈很好
- Node.js 普通安装
- 官网 下载安装即可,很小不到20M!
- 验证是否成功,命令行输入
node -v
显示版本号如v8.11.4
- 按提示升级 npm,Update available 5.6.0 → 6.4.1,
npm i -g npm
- Node.js多版本安装方式
- 卸载已有的Node.js
- 下载nvm
- 在C盘创建目录dev
- 在dev目中中创建两个子目录nvm和nodejs
- 并且把nvm包解压进去nvm目录中
- 在install.cmd文件上面右键选择【以管理员身份运行】
- 打开的cmd窗口直接回车会生成一个settings.txt文件,修改文件中配置信息
- 配置nvm和Node.js环境变量
- NVM_HOME:C:\dev\nvm
- NVM_SYMLINK:C:\dev\nodejs
- 把配置好的两个环境变量加到Path中
- nvm常用的命令
- nvm list 查看当前安装的Node.js所有版本
- nvm install 版本号 安装指定版本的Node.js
- nvm uninstall 版本号 卸载指定版本的Node.js
- nvm use 版本号 选择指定版本的Node.js
3、全局对象
-
全局对象
- 不用导入,直接使用的对象
- 官方文档
- Buffer 类,用于处理二进制数据
- console,用于打印 stdout 和 stderr
- global, 全局的命名空间对象
- process,进程对象
- setTimeout(callback, delay[, ...args])
- setInterval(callback, delay[, ...args])
- setImmediate(callback[, ...args])
- clearTimeout(timeoutObject)
- clearInterval(intervalObject)
- clearImmediate(immediateObject)
-
以下变量虽然看起来像全局变量,但实际上不是
- 全局变量在所有模块中均可使用
- 以下对象作用域只在模块内,详见 module文档:
- __dirname
- __filename
- exports
- module
- require()
-
运行
.js
脚本文件-
node app
或者node app.js
-
-
实践代码
console.log('hello world'); setTimeout(function () { console.log("3 seconds have passed 2"); }, 3000); // 箭头函数,es6的写法 setTimeout(() => { console.log("3 seconds have passed 1"); }, 3000); // 每间隔2秒不断执行 setInterval(function () { console.log("2 seconds have passed"); }, 2000); var time = 0 var timer = setInterval(function () { time += 2; console.log(time + " seconds have passed"); if (time > 6) { clearInterval(timer); console.log("clearInterval") } }, 2000) // 输出当前目录 和 带绝对路径的文件名 console.log(__dirname) console.log(__filename) console.log('end') console.dir(global)
4、回调函数
function sayHi() {
console.log('Hi')
}
sayHi() // 调用函数
// 将匿名函数赋给变量
var sayBye = function (name) {
console.log(name + ' Bye')
}
sayBye()
// 第一个参数是函数
function callFunction(fun, name) {
fun(name)
}
callFunction(sayBye, 'able')
// 或者
callFunction(function (name) {
console.log(name + ' Bye')
}, 'able')
5、模块化
传统非模块化开发有如下的缺点:
1、命名冲突
2、文件依赖
前端标准的模块化规范:
1、AMD - requirejs
2、CMD - seajs //阿里的
服务器端的模块化规范:
1、CommonJS - Node.js
模块化相关的规则:
1、如何定义模块:一个js文件就是一个模块,模块内部的成员都是相互独立
2、模块成员的导出和引入
模块成员的导出最终以module.exports为准
如果要导出单个的成员或者比较少的成员,一般我们使用exports导出;
如果要导出的成员比较多,一般我们使用module.exports的方式
这两种方式不能同时使用
exports与module的关系:
module.exports = exports = {};
module 对象
- 每个文件都被视为独立的模块
- 每个模块中,module 指向表示当前模块的对象的引用
- module 实际上不是全局的,而是每个模块本地的
- module.exports 导出模块内的对象,方便其他对象引用
- require() 引入模块
- 当 Node.js 直接运行一个文件时,require.main 会被设为它的 module
- 可以通过 require.main === module 来判断一个文件是否被直接运行
- module 提供了一个 filename 属性(通常等同于 __filename)
- 可以通过检查 require.main.filename 来获取当前应用程序的入口点
//例1
//导出 03.js
var sum = function(a,b){
return parseInt(a) + parseInt(b);
}
exports.sum = sum;
//引入
var module = require('./03.js');
var ret = module.sum(12,13);
console.log(ret); //输出 25
//例2
//导出 03.js
var sum = function(a,b){
return parseInt(a) + parseInt(b);
}
module.exports = sum;
// 引入
var module = require('./03.js');
var ret = module(12,13);
console.log(ret); //输出 25
//例3
//导出
var flag = 123;
global.flag = flag;
//引入
require('./07');
console.log(global.flag); //输出 123
//例4
var counter = function(arr){
return "There are" + arr.length +"element in the array";
}
var adder = function(a,b){
return `the sum of the 2 numnber is ${a+b}`;
}
var pi = 3.14;
// module.exports.counter = counter;
// module.exports.adder = adder;
// module.exports.pi = pi;
// 或者
module.exports = {
counter: counter,
adder:adder,
pi:pi
}
//引入
var stuff = require('./9');
console.log(stuff.counter(['1','2','3'])); //输出 There are3element in the array
console.log(stuff.adder(5,5)); //输出 the sum of the 2 numnber is 10
console.log(stuff.pi); // 输出 3.14
注意:已经加载的模块会缓存。
模块文件的后缀3种情况:.js .json .node
上述三种模块的加载优先级(不加文件后缀时的优先级):.js -> .json -> .node
6、buffer基本操作
Buffer对象是Node处理二进制数据的一个接口。它是Node原生提供的全局对象,可以直接使用,不需要require(‘buffer’)。
- 实例化
- Buffer.from(array)
- Buffer.from(string)
- Buffer.alloc(size)
- 功能方法
- Buffer.isEncoding() 判断是否支持该编码
- Buffer.isBuffer() 判断是否为Buffer
- Buffer.byteLength() 返回指定编码的字节长度,默认utf8
- Buffer.concat() 将一组Buffer对象合并为一个Buffer对象
- 实例方法
- write() 向buffer对象中写入内容
- slice() 截取新的buffer对象
- toString() 把buf对象转成字符串
- toJson() 把buf对象转成json形式的字符串
7、事件(events)
- 多数 Node.js 核心 API 都采用异步事件驱动架构
- 所有能触发事件的对象都是 EventEmitter 类的实例
- 事件名称通常是驼峰式的字符串
- 实践代码
//例1
// 导入事件库
var events = require('events');
// 新建事件对象
var myEmitter = new events.EventEmitter();
// 绑定事件,事件名称叫做someEvent,后面是事件触发的回调函数
myEmitter.on('someEvent',function(message){
console.log(message);
})
// 手动触发
myEmitter.emit('someEvent','the event was emmitted'); //the event was emmitted
//例2 (给对象一个事件)
var events = require('events');// 导入事件库
var util = require('util'); // 导入工具库
var Person = function(name){ //定义一个对象
this.name = name;
}
// inherits是继承的意思,让Person类继承事件对象的属性
util.inherits(Person,events.EventEmitter);
// 定义三个对象
var xiaoming = new Person('xiaoming');
var lili = new Person('lili');
var lucy = new Person('lucy');
var persons = [xiaoming,lili,lucy];
// 循环数组,每个对象都绑定一个事件
persons.forEach(function(persons){
persons.on('speak',function(message){
console.log(persons.name +' said '+ message);
})
})
// 触发
xiaoming.emit('speak','hi');
lili.emit('speak','i want a apple');
// 注意,要让对象使用事件,就让对象继承那个事件库的对象就行了,也就是
// events.EventEmitter
8、读写文件(同步和异步)
//例1
var fs = require('fs'); //文件的一个库
var readMe = fs.readFileSync("readMe.txt","utf8"); //参数:文件名,编码
console.log(readMe); // you read me!
fs.writeFileSync("writeMe.txt","这是写入的"); //参数:文件名,写入的东西
// 以上部分是同步执行情况
// node.js执行JavaScript的时候是单线程的,如果读写文件很耗时,他就会阻塞
注意:IO操作是很耗时的,它不能阻塞主线程,应该发起一个新的线程去执行它
noejs中异步处理操作,异步的操作都要加回调函数
//例2
// 解决办法,用异步
var fs = require('fs');
var readMe = fs.readFile("readMe.txt","utf8",function(err,data){
console.log(data); //you read me!
fs.writeFile("writeMe.txt","写入的内容",function(){
console.log('writeMe has finished!');
})
})
console.log("finished"); //这个比上面那句先执行
// why
/*
第二条语句是一个异步的事件,异步的IO操作,nodejs在执行JavaScript的时候
是单线程的,语句还是一行一行往下执行,为什么这里会先执行最后一句呢?这是因为
nodejs维护了一个事件队列,执行第二句的时候,会在事件队列那里注册一个事件,告诉
这个事件队列我将要去读一个文件,但是这个回调函数没有被马上执行,这个操作是
瞬间完成的。完成之后他就会执行主线程的第三个语句。当主线程空闲之后,它就会
去找事件队列里面的事件,把它取出来,然后发起一个线程执行,执行成功后,它就
告诉主线程我已经成功了,你来执行我吧。所以这里先执行的是主线程,然后执行的
是异步操作里面的回调函数。
注意:IO操作是很耗时的,它不能阻塞主线程,应该发起一个新的线程去执行它
noejs中异步处理操作,异步的操作都要加回调函数
*/
//例3
var fs = require('fs');
// 异步删除文件
fs.unlink("writeMe.txt", function () {
console.log("delete writeMe.txt file");
})
// 同步创建和删除目录
fs.mkdirSync('stuff');
fs.rmdirSync('stuff');
// 异步创建目录,但是这种递归回调,嵌套太多会出现问题,以后再说解决办法
fs.mkdir('stuff', function () {
fs.readFile('readMe.txt','utf8',function(err,data){
fs.writeFile('./stuff/writeMe.txt',data,function(){
console.log('copy successfully');
})
})
});
9、流和管道
- 流(stream)
- 处理流式数据的抽象接口
- stream 模块提供了一些基础的 API,用于构建实现了流接口的对象
- 流可以是可读的、可写的、或是可读写的,所有的流都是 EventEmitter 的实例
- 流处理数据通过缓存可以提高性能
- 管道
- 使用管道,代码量更少
- myReadStream.pipe(myWriteStream)
//例1
var fs = require('fs');
// 创建一个输入流
var myReadStream = fs.createReadStream(__dirname+'/readMe.txt');
// 流是一个事件的实例,拥有事件的一些特性,它可以绑定一些监听函数
myReadStream.on('data',function(chunk){
console.log('new chunk received'); //new chunk received
console.log(chunk); //<Buffer 79 6f 75 20 72 65 61 64 20 6d 65 21>
})
// 性能提高的原因是文件分成buffer了
//例2
var fs = require('fs');
// 创建一个读的流
var myReadStream = fs.createReadStream(__dirname+'/readMe.txt');
//创建一个写入的流
var myWriteStream = fs.createWriteStream(__dirname+'/writeMe.txt');
//设置编码
myReadStream.setEncoding('utf8');
var data = ""
// 接收数据时用的监听函数
myReadStream.on('data',function(chunk){
data += chunk;
myWriteStream.write(chunk);//这里写入内容
})
//接受完数据之后用的
myReadStream.on('end',function(){
console.log(data);
})
//例3
var fs = require('fs');
// 创建一个读的流
var myReadStream = fs.createReadStream(__dirname+'/readMe.txt');
//创建一个写入的流
var myWriteStream = fs.createWriteStream(__dirname+'/writeMe.txt');
var writeData = "hello world";
myWriteStream.write(writeData,'utf8'); //向文件中写入 "hello world"
myWriteStream.end();
myWriteStream.on('finish',function(){
console.log('finished');
})
以上三例是用流来写入或读出文件
//管道
var fs = require('fs');
// 创建一个读的流
var myReadStream = fs.createReadStream(__dirname+'/readMe.txt');
//创建一个写入的流
var myWriteStream = fs.createWriteStream(__dirname+'/writeMe.txt');
// 直接将 myReadStream内容 写入 myWriteStream
myReadStream.pipe(myWriteStream);
var crypto = require('crypto'); //加密
var fs = require('fs');
var zlib = require('zlib'); //压缩
var password = new Buffer(process.env.PASS || 'password');
var encryptStream = crypto.createCipher('aes-256-cbc',password);
var gzip = zlib.createGzip();
var readStream = fs.createReadStream(__dirname+"/readMe.txt");
var writeStream = fs.createWriteStream(__dirname+"/out.gz");
// 压缩
readStream
.pipe(encryptStream)
.pipe(gzip)
.pipe(writeStream)
.on('finish',function(){
console.log('done');
});
//解压缩
var crypto = require('crypto'); //加密
var fs = require('fs');
var zlib = require('zlib'); //解压缩
var password = new Buffer(process.env.PASS || 'password');
var decryptStream = crypto.createDeCipher('aes-256-cbc',password);
var gzip = zlib.createGunzip();
var readStream = fs.createReadStream(__dirname+"/out.gz");
// 解压缩
readStream
.pipe(gzip)
.pipe(decryptStream)
.pipe(process.stdout)
.on('finish',function(){
console.log('done');
});
10、http模块,web服务器
下面几个是响应一个纯文本给客户端
var http = require('http');
var server = http.createServer(function(request,response){
console.log("Request received");
response.writeHead(200,{'Content-Type':'text/plain'});
response.write('Hello from out application');
response.end();
})
server.listen(3000);
console.log('Server started on locahost port 3000');
1.png
2.png
//把上面的 server.listen(3000);改成下面这句
server.listen(3000,'127.0.0.1');
3.png
//改用 request 表达式
var http = require('http');
// request表达式
var onRequest = function(request,response){
console.log("Request received");
response.writeHead(200,{'Content-Type':'text/plain'});
response.end('Hello from out application');
}
var server = http.createServer(onRequest);
server.listen(3000,'127.0.0.1');
console.log('Server started on locahost port 3000');
11、web服务器响应json
var http = require('http');
// request表达式
var onRequest = function(request,response){
console.log("Request received");
// 这里改为响应json的
response.writeHead(200,{'Content-Type':'application/json'});
var myObj = {
name:'hfisdwc',
job:'programmer',
age:27
}
response.end(JSON.stringify(myObj)); //将对象转换成json字符串,写入response中
}
var server = http.createServer(onRequest);
server.listen(3000,'127.0.0.1');
console.log('Server started on locahost port 3000');
4.png
序列化:把它转成字符串 stringify
反序列化:把字符串转成对象 parse
- JSON对象
- 字符串必须使用双引号表示,不能使用单引号
- 对象的键名必须放在双引号里面
- 数组或对象最后一个成员的后面,不能加逗号
- JSON对象是 JavaScript 的原生对象,用来处理 JSON 格式数据
- JSON.stringify方法用于将一个值转为 JSON 字符串
- JSON.parse方法用于将 JSON 字符串转换成对应的值
12、web服务器响应HTML
var http = require('http');
var fs = require('fs');
// request表达式
var onRequest = function(request,response){
console.log("Request received");
// 这里改为响应html的
response.writeHead(200,{'Content-Type':'text/html'});
var myReadStream = fs.createReadStream(__dirname+'/index.html','utf8');
myReadStream.pipe(response);
}
var server = http.createServer(onRequest);
server.listen(3000,'127.0.0.1');
console.log('Server started on locahost port 3000');
13、模块化思想组织代码
var http = require('http');
var fs = require('fs');
function startServer() {
// request表达式
var onRequest = function (request, response) {
console.log("Request received");
// 这里改为响应html的
response.writeHead(200, {
'Content-Type': 'text/html'
});
var myReadStream = fs.createReadStream(__dirname + '/index.html', 'utf8');
myReadStream.pipe(response);
}
var server = http.createServer(onRequest);
server.listen(3000, '127.0.0.1');
console.log('Server started on locahost port 3000');
}
// 导出
exports.startServer = startServer;
//导入
var server = require('./server');
server.startServer();
14、web服务器路由
var http = require('http');
var fs = require('fs');
function startServer() {
var onRequest = function (req, res) {
console.log('request received ' + req.url)
if (req.url === '/' || req.url === '/home') {
res.writeHead(200, { 'Content-Type': 'text/html' })
fs.createReadStream(__dirname + '/index.html', 'utf8').pipe(res)
} else if (req.url === '/review') {
res.writeHead(200, { 'Content-Type': 'text/html' })
fs.createReadStream(__dirname + '/review.html', 'utf8').pipe(res)
} else if (req.url === '/api/v1/records') {
res.writeHead(200, { 'Content-Type': 'application/json' })
var jsonObj = {
name: 'able'
}
res.end(JSON.stringify(jsonObj))
} else {
res.writeHead(200, { 'Content-Type': 'text/html' })
fs.createReadStream(__dirname + '/404.html', 'utf8').pipe(res)
}
}
var server = http.createServer(onRequest)
server.listen(3000)
console.log('server started on http://127.0.0.1:3000')
}
// 导出
exports.startServer = startServer;
重构上面的路由代码:
- 将路由、处理函数和主程序分离,单独存放
- 分工明确,各司其职,方便管理
//server.js
var http = require('http')
var url = require('url') //url库
var querystring = require('querystring')
function startServer(route, handle) {
var onRequest = function (req, res) {
console.log('request received ' + req.url)
var pathname = url.parse(req.url,true).pathname
var data = ""
req.on("error", function (err) {
console.error(err)
}).on("data", function (chunk) {
data += chunk
}).on("end", function () {
if (req.mothod === "POST") {
if (data.length > 1e6) {
req.connection.destroy() // 如果数据很大,就断开
}
route(handle, pathname, res, querystring.parse(data))
} else {
var params = url.parse(req.url, true).query
route(handle, pathname, res, params)
}
})
// 或者
// var data = []
// data.push(chunk)
// data = Buffer.concat(data).toString()
}
var server = http.createServer(onRequest)
server.listen(3000)
console.log('server started on http://127.0.0.1:3000')
}
module.exports.startServer = startServer
//router.js
fs = require('fs')
function route(handle, pathname, res, params) {
console.log('Routing a request for ' + pathname)
if (typeof handle[pathname] === 'function') {
handle[pathname](res, params)
} else {
console.log('No handle for ' + pathname)
res.writeHead(200, { 'Content-Type': 'text/html' })
fs.createReadStream(__dirname + '/404.html', 'utf8').pipe(res)
}
}
module.exports.route = route
//handler.js
var fs = require('fs')
function home(res) {
console.log('home')
res.writeHead(200, { 'Content-Type': 'text/html' })
fs.createReadStream(__dirname + '/index.html', 'utf8').pipe(res)
}
function review(res) {
console.log('review')
res.writeHead(200, { 'Content-Type': 'text/html' })
fs.createReadStream(__dirname + '/review.html', 'utf8').pipe(res)
}
function api_records(res, params) {
console.log('api_records')
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(params))
}
module.exports = {
home: home,
review: review,
api_records: api_records
}
//app.js
var server = require('./server')
var router = require('./router')
var handler = require('./handler')
var handle = {}
// key是路径,值是处理函数
handle['/'] = handler.home
handle['/home'] = handler.home
handle['/review'] = handler.review
handle['/api/v1/records'] = handler.api_records
server.startServer(router.route, handle)
15、web服务器使用get或post请求发送数据
有两种方式:通过地址栏和表单
地址栏是用get方法,表单是post方法。因为地址栏的数据是暴露出去的,所以通常用于查询数据,而通过表单传递数据一般用于提交一些信息。
var querystring = require('querystring')
-
querystring.parse(data)
把一个 URL 查询字符串 str 解析成一个键值对的集合
// 接收请求数据,然后处理,查看request 类型
var data = ""
req.on("error", function (err) {
console.error(err)
}).on("data", function (chunk) {
data += chunk
}).on("end", function () {
if (req.mothod === "POST") {
if (data.length > 1e6) {
req.connection.destroy() // 如果数据很大,就断开
}
route(handle, pathname, res, querystring.parse(data))
} else {
var params = url.parse(req.url, true).query
route(handle, pathname, res, params)
}
})
// 或者
// var data = []
// data.push(chunk)
// data = Buffer.concat(data).toString()
16、包管理器 npm
- npm 官网
-
npm install -g xxx
全局安装可执行文件,当作命令行工具 - 使用国内源,解决慢的问题
# Or alias it in .bashrc or .zshrc
echo '\n#alias for npm\nalias npm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/npm \
--disturl=https://npm.taobao.org/dist \
--userconfig=$HOME/.npmrc"' >> ~/.zshrc && source ~/.zshrc
- yarn 也是包管理器,更快下载速度
17、package.json 文件
- 记录项目中使用的包名,发布时不用包内容了,只要名称就行
-
npm init
提问式初始化项目信息,生成package.json
文件,-y 全部默认 -
npm install --save xxx
安装的同时,将信息写入package.json -
npm install --save-dev xxx
安装的同时,将信息写入package.json中的dev开发依赖 -
npm view moduleNames
查看node模块的package.json文件夹 -
npm run start
启动包,执行 package.json scripts 中的 start 命令,还有 stop restart test -
npm install
安装 package.json 中记录的包
18、nodemon监控文件并重启服务
- nodemon 用来监视应用中的任何文件更改并自动重启服务
- 非常适合用在开发环境中,方便啊,不用手动操作了
- 全局安装
npm install -g nodemon
- 本地安装
npm install --save-dev nodemon
- 启动应用
nodemon [your node app]
- 获取修改 package.json 中的启动脚本,添加
nodemon app.js
, 用 npm start 直接启动,方便