Node.js学习笔记(一) + 超简单服务器搭建
本篇笔记只适用于想搭建一个能够收发简单请求服务器的前端初学者。
文章目录
Node.js适合人群
- 想创建自己服务的前端开发人员。(也是本笔记适合人群)
- 想部署高性能服务的后端开发人员。
安装
为什么介绍两种?
初学时,我先在本机(Windows)上直接安装,配置环境变量,学习+练习node,在cmd上运行node.js实现了开放端口和接发本地的服务请求的功能后,最后再配置在云服务器(Linux)上。
Node.js安装的官网:https://nodejs.org/en/download/
Node.js中文网:http://nodejs.cn/download/
1.Windows安装
LTS表示长期维护版,Current表示最新版。依据系统选择 “Windows Installer (.msi) 64-bit ”,msi就是安装包,安装起来比较便捷。
随后编辑环境变量path,让变量值为node.js目录即可。
此电脑–属性–高级系统设置–环境变量–用户变量/系统变量–Path
由于windows版本的nodeJS集成了npm包管理器,而且在同一个目录下,所以只需要配置一项环境变量,最后在cmd上输入如下命令,验证就行。
# 查看版本
node -v
npm -v
2.Linux安装
linux服务器的版本一定要了解清楚,否则会费很大的劲去配置结果最后显示“无法执行二进制文件”(实际上系统不适配,两个小时的教训)。
通过如下命令可以查看自己的linux版本。
uname -a
根据官网可以选择不同的版本(我居然每个都试了一遍…最后去服务器上重新查看规格才知道的。)中文网上有docker镜像,也可以在开启docker之后使用。
小知识:ARM和x86是不同的CPU架构,由于内置的指令集不同,编译环境不同,所以一般软件代码不互用。
下载下来的tar.xz文件上传到服务器上解压:
tar -xvf node-v14.17.5-linux-x64.tar.xz
进入解压目录,查看bin下面有没有npm和node文件:
cd node-v...
配置环境变量
#第一种方法:编辑profile参数
vim /etc/profile
#在最下面添加 export PATH=$PATH: 后面跟上 node 下 bin 目录的路径
#export PATH=$PATH: 接上 Node目录/bin
export PATH=$PATH:/root/node-v14.17.5-linux-x64/bin
#保存后退出文件,通过下面命令生效。
source /etc/profile
#第二种方法:建立软链接
#前面的分别为node和npm的地址,后面的为用户自身的bin目录。
①ln -s /app/software/node-v14.17.5-linux-x64/bin/npm /usr/local/bin/
②ln -s /app/software/node-v14.17.5-linux-x64/bin/node /usr/local/bin/
最后通过查看版本验证即可:
# 查看版本
node -v
npm -v
简介
简单地说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js 是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
Node.js是一个能够在服务器端运行JavaScript的开放源代码、跨平台JavaScript运行环境。
Node采用Google开发的V8引擎运行js代码,使用事件驱动、非阻塞和异步I/O模型等技术来提高性能,可优化应用程序的传输量和规模。
Node大部分基本模块都用JavaScript编写。在Node出现之前,JS通常作为客户端程序设计语言使用,以JS写出的程序常在用户的浏览器上运行。
目前, Node已被IBM、Microsoft、 Yahoo!、 Walmart、Groupon、SAP_ LinkedIn、 Rakuten、 PayPal、 Voxer和GoDaddy等企业采用。
功能
-
Node是对ES标准一个实现,Node也是一个JS引擎。
-
通过Node可以使js代码在服务器端执行。
-
Node仅仅对ES标准进行了实现,所以在Node中不包含DOM和BOM。
-
Node中可以使用所有的内建对象:
String Number Boolean Math Date RegExp Function object Array
而BOM和DOM都不能使用,
但是可以使用console 也可以使用定时器(setTimeout() setInterval () )。 -
Node可以在后台来编写服务器,
Node编写服务器都是单线程的服务器。(因为JS就是单线程的嘛。)Node处理请求时是单线程,但是在后台拥有一个I/O线程池。(所以宏观上看上去是多线程的。)
模块
node中一个JS文件就是一个模块。
在node中,通过require()函数来引入外部的模块。require()可以传递一个文件的路径作为参数,node将会自动根据该路径来引入外部模块。如果使用相对路径,必须以.或者…开头。
使用require()引入模块后,该函数会返回一个对象,这个对象代表的是引入的模块。
var md = require("./xxx.js")
console.log(md) // 输出一个对象{...}
在Node中,每一个js文件中的JS代码都说独立运行在一个函数中。而不是全局作用域,所以一个模块中的变量和函数在其他模块中无法访问。
我们可以通过exports来向外部暴露变量和方法。只需要将需要暴露给外部的变量火方法设置为exports的属性即可。
exports.add = function(a,b){
return a+b;
}
exports.x = "this is x."
模块分为两大类:核心模块,文件模块。
核心模块的标识就是模块的名字,所以引入require的时候参数直接就是名字,而不是文件路径。
在node中有一个全局对象global, 它的作用和网页中window类似。在全局中创建的变量/函数都会作为global的属性/方法保存
小知识: 封装函数的实参查看方法: arguments.callee + “”
实际上模块中的代码都是包装在一个函数中执行的, 并且在函数执行时,同时传递进了5个实参
module
- module代表的是当前模块本身
exports就是module的属性
既可以使用exports 导出,也可以使用module. exports导出
当node在执行模块中的代码时,它会首先在代码的最顶部,添加如下代码
function (exports, require, module,__filename,__dirname) {
在代码的最底部,添加如下代码
}
实际上模块中的代码都是包装在一个函数中执行的, 并且在函数执 行时,同时传递进了5个实参
-
exports
该对象用来将变量或函数暴露到外部
-
require
函数,用来引入外部的模块
-
module
module代表的是当前模块本身
exports就是module的属性
既可以使用exports 导出,也可以使用module. exports导出(这个比较稳一点。注意改对象和改变量的区别。)
通过exports只能使用.的方式来向外暴露内部变量
通过module.exports可以直接赋值。module.exports = {}
-
__filename
例子值:C: \Users\yy\Projects\node\module. js
当前模块的完整路径
-
__dirname
例子值:C: \Users\yy\Projects\node
当前模块所在文件夹的完整路径
包npm
package
●CommonJS的包规范允许我们将一组相关的模块组合到一起,形成一组完整的工具。
●CommonJS的包规范由包结构和包描述文件两个部分组成。
包结构
- 用于组织包中的各种文件
包描述文件
- 描述包的相关信息,以供外部读取分析
包实际上就是一个压缩文件,解压后还原为目录。
规范的包的需要
- package.json 描述文件,用于表达非代码相关信息,位于包的根目录下。
- bin 可执行二进制文件
- lib js文件
- doc 文档
- test 单元测试
npm命令
NPM命令
●npm -v
-查看版本
●npm
-帮助说明
●npm search包名
-搜索模块包,
●npm install 包名 --save
-在当前目录安装包 并添加到json描述的依赖中。
●npm install 包名 -g
-全局模式安装包(全局安装的包一般都是一些工具)
●npm remove包名 (remove可以简写为 “r” )
-删除一个模块
●npm install文件路径
-从本地安装
●npm install 包名-registry=地址
-从镜像源安装
●npm config set registry=地址
-设置镜像源
npm镜像源
可以使用淘宝的镜像源,日后如果npm美国服务器慢的话,可以使用国内的淘宝镜像,使用cnpm即可。
网址是:npm.taobao.org
npm install -g cnpm --registry=https://registry.npm.taobao.org
通过npm下载的包都会放到node_module文件夹中
下载的包直接通过包名引用即可。
var math = require("math");
node在使用模块名字来引入模块时,它会首先在当前目录的node_modules中寻找,如果由则直接使用。如果没有则去上一级目录中寻找,直到找到为止。直到找到磁盘的根目录为止。
常用模块/工具介绍
1.自动重启工具nodemon
背景:编辑一个server.js文件,再通过node server.js运行,但是如果在修改server.js文件后又需要我们手动退出并使用node重启server.js文件。这样十分麻烦。
解决工具:我们可以通过npm下载一个工具nodemon,它除了正常运行JS文件外还会监听js文件是否被修改,如果被修改了它会自动重启js文件的。这里-g表示全局使用。(一般工具性的包都是使用-g的)
npm install -g nodemon
安装成功后,后续运行JS文件命令可为:nodemon
#正常操作
node server.js
#安装后的操作
nodemon server.js
2.express 模块
基于 Node.js 平台,快速、开放、极简的 Web 开发框架。
Express官网:https://www.expressjs.com.cn/
里面有许多API,可以用来搭建服务器,可以说是前端小白的必备框架了。
下载命令:
npm install express --save
初始化操作
npm init --yes
该命令会创建一个package.json文件,用来记录node.js的信息。其中-yes或者-y都表示默认选项,可以试试不加的情况,会有许多配置项。
Buffer缓冲区
1.Buffer的结构和数组很像,操作的方法也和数组类似
2.数组中不能存储二进制的文件,而buffer就是专门用来存储二进制数据
3.使用buffer不需要引入模块, 直接使用即可
4.在buffer中存储的都是二进制数据,但是在显示时都是以1 6进制的形式显示
buffer中每一个元素的范围是从00 - ff 。 0-255
var str = "hello buffer"
var buf = Buffer.from(str); //将一个字符串转换为buffer
console.log(buf);
Buffer.alloc(size) //创建一个指定大小的Buffer
Buffer.alloUnsafe(size) //创建一个指定大小的Buffer,但是可能包含敏感数据。
buf.toString() //将缓冲区的数据转化为字符串
文件系统(File system)
-
文件系统简单来说就是通过Node来操作系统中的文件
-
使用文件系统,需要先引入fs模块,fs是核心模块,直接引入不需要下载
const fs = require("fs");
同步文件的写入
- 手动操作的步骤
1.打开文件
2.向文件中写入内容
3.保存并关闭文件
1.打开文件
fs.openSync(path,flags{,mode})
path
要打开的文件路径flags
要打开的文件要操作的类型- r:只读的
- w:可写的
2.向文件中写入内容
fs.writeSync(fd, string {, position {, encoding } })
fd
文件的描述符,需要传递要写入的文件的描述符,其实就是数字string
要写入的内容position
写入的起始位置encoding
写入的编码,默认utf-8
3.保存并关闭文件
fs.closeSync (fd)
- fd要关闭的文件的描述符
var fs = require("fs");
//打开文件
var fd = fs.openSync("hello.txt","w");
//向文件中写入内容
fs.writeSync(fd,"今天天气真不错~~~");
//关闭文件
fs.closeSync(fd);
异步文件的写入
fs.open(path,flags {,mode},callback)
- 用来打开一个文件
- 异步调用的方法,结果都是通过回调函数arguments返回的。
- 回调函数两个参数:err 错误对象,如果没有错误则为null;fd文件描述符
fs.write(fd, string[, position[, encoding]], callback)
- 用来异步写入一个文件
fs.close(fd, callback)
- 用来关闭文件的
var fs = require("fs");
//异步方法没有返回值
fs.open("hello2.txt","w",function(err,fd){
if(!err){
console.log(fd);
fs.write(fd,"这是异步写入的内容",function(err,length,string){
if(!err){
console.log("写入成功")
}
})
fs.close(fd,function(err){
if(!err){
console.log("文件已关闭。")
}
})
}
});
简单文件写入
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])
- file要操作的文件的路径
- data要写入的数据,此处的data是Buffer
- options选项,可以对写入进行一些设置
- callback 当写入完成以后执行的函数
- flag
- r只读
- w可写
- a追加
var fs = require("fs");
fs.writeFile("hello3.txt","这是通过writeFile写入的内容",{flag:"a"},function(err){
if(!err){
console.log("写入成功");
}
})
流式文件写入
同步,异步和简单文件写入都不适合大文件写入,性能较差,容易导致内存溢出。
var fs = require("fs");
//流式文件写入
//创建一个可写流
//fs.createReadStream(path[,options])
//fs.createWriteStream(path[,options])
var ws = fs.createWriteStream("hello3.txt");
//可以通过监听流的open和close事件来监听流的打开和关闭
ws.once("open",function(){
console.log("流打开了~~~");
})
ws.once("close",function(){
console.log("流关闭了。")
})
//通过ws向文件中输出内容
ws.write("通过可写流写入文件");
//..可写多次
//关闭流
ws.close(); //此方法可能会残留write
ws.end();
文件读取类似 写入
流式文件读取
//创建一个可读流
var rs = fs.createReadStream ("an.jpg");
//创建一个可写流
var ws = fs.createWriteStream ("an2.jpg");
//监听流的开启和关闭
rs.once("open", function () {
console.log("可读流打开了~~");
});
rs.once ("close", function () {
console.log ("可读流关闭了~~") ;
ws.end();//数据读取完毕,再关闭可写流
}) ;
I
//如果要读取一个可读流中的数据,必须要为可读流绑定-个data事件, data 事件绑定完毕,它会自动开始读取数据
//当
rs.on("data",function(data){
console.log(data);
//将读取的数据写入流中
ws.write(data);
});
//创建一个可读流
var rs = fs.createReadStream ("an.jpg");
//创建一个可写流
var ws = fs.createWriteStream ("an2.jpg");
//pipe()可以将可读流的内容直接写到可写流中
rs.pipe(ws);
fs的其他方法
- 检查一个文件是否存在
fs.existsSync(path)
- 获取文件的状态
fs.stat(path,callback)
fs.statSync(path)
(它会返回一个对象,对象保存当前状态对象的相关信息。)
fs.stat("a.mp3",function(err,stats){
console.log(stats.isFile());
console.log(stats.isDirectory());
console.log(stats.size);
})
●删除文件
fs.unlink(path,callback)
fs.unlinkSync(path)
●列出文件
fs.readdir(path[ options], callback)
fs.readdirSync(path[ options])
fs.readdir(".",function(err,files){
//files是一个字符串数组,存文件或文件夹的名字。
if(!err){
console.log(files);
}
})
●截断文件
fs.truncate(path, len, callback)
fs.truncateSync(path, len)
●建立目录
fs.mkdir(path[, mode], calback)
fs.mkdirSync(path[, mode])
● 删除目录
fs.rmdir(path, callback)
fs.rmdirSync(path)
●重命名文件和目录
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
●监视文件更改写入
fs.watchFile(filename[, options], listener)
fs.watchFile("hello2.txt",function(curr,prev){
//curr 当前文件状态对象
//prev 之前文件状态对象
})
背景知识
命令行窗口
-
别名:小黑屏、CMD窗口、终端、shell
-
打开方式
- 开始菜单–>运行–> CMD -->回车
- 打开文件夹目录,在上方直接输入cmd后回车。(超便捷定位!)
-
常用的指令:
-
dir列出当前目录下的所有文件
-
cd目录名进入到指定的目录
-
md目录名创建一个文件夹
-
rd目录名删除一个文件夹
-
-
目录
.表示当前目录
…表示上一级目录 -
环境变量小知识 (windows系统中变量)
path:当我们在命令行窗口打开一个文件,或调用一个程序时,系统会首先在当前目录下寻找文件程序,如果找到了则直接打开。
如果没有找到则会依次到环境变量path的路径中寻找。最终没找到就报错。
四步搭建最简单的服务器
假定已经学完了上述知识,注意如果出错了就在英文目录下进行。
1.初始化环境
初始化
进入自己创建的目录,打开终端,输入
npm init --yes
创建了存储信息的package.json文件后
下载express框架
npm i express
2.编辑服务端JS代码
编辑server.js文件(先编辑txt文本,后重命名修改后缀即可。)
//1.引入express
const express = require('express');
//2.创建应用对象
const app = express();
//设置端口号
const port = 8002;
//3.创建路由规则
//request 是对请求报文的封装
//response 是对响应报文的封装
app.get('/server',(request,response)=>{
//设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin','*');
response.send('hello this is GET request!');
});
//4.监听端口启动服务
app.listen(port,()=>{
console.log("服务已经启动,"+port+"端口监听中...")
})
3.运行代码
通过node在cmd上运行该server.js代码,模拟服务器。
# 二选一,下面的需要 npm install nodemon工具
node server.js
nodemon server.js
如果没有下载nodemon工具,上述的nodemon可以替换为node
4.验证服务器消息
访问网址来验证服务器: 本机地址+端口+相对请求地址
http://127.0.0.1:8002/server
- 本机地址:127.0.0.1
- 端口:8002(js文件里的port)
- 请求地址:/server (js文件里的get请求第一个参数)
访问成功。
显然,本机地址可以根据云服务器的地址(IP)来修改;端口也可以根据情况来修改。至于请求地址以及后面的请求参数,可以通过编辑server.js文件来扩展功能来实现。
总结
为了学习Ajax发送请求才学习的Node.js,充当了后端服务器的角色。没有较多的理论性知识,相反主要都是一些实用性的API介绍。当然上述的服务器请求太过简单,利用好Node.js的“文件系统”和express框架已经足够满足我这样的前端小白的服务器需求了。后续还需要配合ajax来定义更多更复杂的请求。