nodejs
安装并使用nodejs
安装Nodejs
下载
官网: https://Nodejs.org/en/
中文网:http://Nodejs.cn/
-
版本说明
- LTS: 长期稳定版。 实际项目开发建议使用长期稳定版
- Current: 最新版。最新版包含了一些新功能,如果想学习最新的功能,则可以使用该版本。但是,最新版可能会有一些未知的bug
安装
-
双击安装文件开始安装(不同系统选择对应的安装文件)
-
傻瓜式安装,一路 ‘next’ 即可
注意: 安装在英文路径下;
测试是否安装成功
在桌面上或者任意一个文件夹,按住 shift 键 , 点击鼠标右键,选择 ‘在此处打开命令窗口’
进入黑窗口后输入: node -v
或node --version
能够看到Nodejs版本号即为安装成功。
在node中运行js
方法1: 进入Node.js REPL。
REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应。
Node 自带了交互式解释器,可以执行以下任务:
-
读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
-
执行 - 执行输入的数据结构
-
打印 - 输出结果
-
循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。
-
通过node 进入REPL。
-
按下ctrl+c 两次可退出
方法2:执行一个JS文件
先准备好一个js文件,例如:e:/index.js
var a = 1;
console.info(a + 2);
接下来 通过 node
`js文件的路径 的格式来执行这个js文件。
注意事项:
- 输入node回车后,要按两次Ctrl+C,才能回到目录中(Ctrl一直按着不放也可以,松开从新按也可以)
- 执行js文件时,如果当前命令行目录和js文件不在同一个盘符下,要先切换盘符
- 执行js文件时,如果当前命令行目录和js文件在同一个盘符中,则可以使用相对路径找到js文件并执行
体会,此时执行的js代码或文件和浏览器没有任何关系,他们是通过node执行的
Node概述
node是什么
Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine.
Node.js® 是一个基于 Chrome V8 引擎 的 JavaScript 运行时
-
Node全名是Node.js,但它不是一个js文件,而是一个软件
-
Nodejs是一个基于Chrome V8引擎的ECMAScript的运行环境,在这个环境中可以执行js代码
-
Nodejs提供了大量的工具(API),能够让我们完成文件读写、Web服务器创建等功能。
node让javascript程序员能够一统前后端。
理解node和浏览器的区别
- 安装了浏览器这个软件,它不但可以执行ECMAScript,浏览器这个软件内置了window对象,所以浏览器有处理DOM和BOM的能力。
- 安装了NodeJs这个软件,它不但可以执行ECMAScript,NodeJS这个软件也内置了一些东西,包括全局成员和模块系统,同时还可以载入第三方模块来完成更强大的功能。
node的主要内容
node中的全局变量
node中有一个全局变量global,是node中最大的一个对象,相当于浏览器中的window对象。global中的成员在使用时,可以省略global,这点也类似浏览器中的window
下面介绍几个全局对象global中的成员:
- console,我们在初体验时,使用了console,它可不是浏览器中的console对象,使用的是node中的console
- process,和进程相关的对象
- setInterval,同理,也是node中的,不是浏览器中的
- require(),它是全局对象global中的一个方法,用于在js文件中引入另外的文件
- __dirname,当前执行文件的绝对路径(在js文件中使用)
- __filename,当前执行文件的绝对路径,包含文件名(在js文件中使用)
console
用于输出。功能与浏览器中的console功能类似。
process
process.argv
包含命令行参数的数组。第一个元素会是’node’,第二个元素将是.js文件的名称,接下来的参数依次是命令行参数。
node index.js a=1 b=2
process.env
获取当前系统环境信息的对象,常规可以用来进一步获取环境变量、用户名等系统信息
process.platform
获取运行程序所在的平台系统 ‘darwin’, ‘freebsd’, ‘linux’, ‘sunos’ or ‘win32’
process.execPath
获取开启当前进程的执行文件的绝对路径
__dirname , __filename
注意是双
下划线。
-
__dirname,当前执行文件的绝对路径
-
__filename,当前执行文件的绝对路径,包含文件名
它们必须是在某个js文件中使用。
nod中的模块
模块是Node.js 平台自带的一套基本的 API(功能模块)。
- 如何理解"模块"?
- 它的功能相对封闭
- 它是node的内置对象
- 它是一个个独立的文件
- 所有模块的源代码 https://github.com/nodejs/node/tree/master/lib
- 模块的分类
- 内置模块
- 下载使用的第三方模块
- 按node规范自定义模块
- 使用模块的基本步骤
- 导入模块,格式是:
const 模块名 = require("模块名");
- 使用模块中的各个功能
- 导入模块,格式是:
path模块
Path:路径。 它提供了我们处理文件,文件夹的路径的工具。
官方手册: http://nodejs.cn/api/path.html
// 使用模块之前,首先加载模块
const path = require('path');
获取文件的文件名
let filename = path.basename(__filename);
console.info(filename)
console.info( path.basename('/a/b/c/d/index.html') )
获取文件的目录名
let dirname = path.dirname(__filename);
console.info(dirname)
获取文件的扩展名
let extname = path.extname(__filename);
console.info(extname)
路径拼接
let p = path.join("a","b")
console.info(p)
其它方法列表
方法 | 作用 |
---|---|
path.basename(path[, ext]) | 获取返回 path 的最后一部分(文件名) |
path.dirname(path) | 返回目录名 |
path.extname(path) | 返回路径中文件的扩展名(包含.) |
path.format(pathObject) | 将一个对象格式化为一个路径字符串 |
path.join([…paths]) | 拼接路径 |
path.parse(path) | 把路径字符串解析成对象的格式 |
path.resolve([…paths]) | 基于当前工作目录拼接路径 |
工作目录:当前运行 Node 程序的目录
const path = require('path');
// extname -- 获取文件后缀
console.log(path.extname('index.html')); // .html
console.log(path.extname('index.coffee.md')); // .md
// join -- 智能拼接路径
console.log(path.join('/a', 'b', 'c')); // \a\b\c
console.log(path.join('a', 'b', 'c')); // a\b\c
console.log(path.join('/a', '/b/../c')); // \a\c
console.log(path.join('/a', 'b', 'index.html')); // \a\b\index.html
console.log(path.join(__dirname, 'a', 'index.html')); // 得到一个绝对路径
fs模块
文件操作模块。 Filesystem,提供对文件,文件夹的操作。
手册:http://nodejs.cn/api/fs.html
// 引入模块,引入模块的时候,可以使用var、let,但是建议使用const,因为我们不希望它改变
const fs = require('fs');
fs模块中操作文件(或者文件夹)的方法,大多都提供了两种选择:
- 同步版本的
- 异步版本的
文件内容读取 - readFile
异步格式
fs.readFile('文件路径'[,选项], (err, data) => {
if (err) throw err;
console.log(data);
});
说明:
-
第一个参数:读入的文件路径。 相对路径和绝对路径均可。
-
第二个参数: 配置项,主要用来配置字符集。可选参数。一般可设置为’utf8’
如果不设置该参数,文件内容会以二进制形式返回。
-
参数3: 读取完成后触发的回调函数。有两个参数 — err 和 data
-
读取成功
-
err: null
-
data: 文件内容,如果不设置参数2,则返回二进制数据。可以使用 toString() 方法将二进制数据
转为正常字符串
-
读取失败
- err: 错误对象
- Data: undefined
-
-
示例:
const fs = require("fs")
fs.readFile('文件路径',"utf8", (err, data) => {
if (err) throw err;
console.log(data);
});
异步方法与同步方法
文件写入
覆盖写入 writeFile
const fs = require('fs')
fs.writeFile('./a.txt', 'hello world niahi \n asfsdf', err => {
if (err) {
console.info(err)
throw err
}
})
//向指定文件中写入字符串(覆盖写入), 如果没有该文件则尝试创建该文件
fs.writeFile(var1, var2, var3, var4);
参数1: 要写入的文件路径 — 相对路径和绝对路径均可,推荐使用绝对路径
参数2: 要写入文件的字符串
参数3: 配置项,设置写入的字符集,默认utf-8
参数4: 写入完成后触发的回调函数,有一个参数 — err (错误对象)
文件追加 appendFile
const fs = require('fs')
fs.appendFile('./a.txt', '\n 为天地立命', err => {
if (err) {
console.info(err)
throw err
}
})
//向指定文件中写入字符串(追加写入), 如果没有该文件则尝试创建该文件
fs.appendFile(var1, var2, var3, var4);
参数1: 要写入的文件路径 — 相对路径和绝对路径均可,推荐使用绝对路径
参数2: 要写入文件的字符串
参数3: 配置项,设置写入的字符集,默认utf-8
参数4: 写入完成后触发的回调函数,有一个参数 — err (错误对象)
文件夹操作
状态
创建
删除
调用fs模块的方法,下面列举fs模块中的常用方法
API | 作用 | 备注 |
---|---|---|
fs.access(path, callback) | 判断路径是否存在 | |
fs.appendFile(file, data, callback) | 向文件中追加内容 | |
fs.copyFile(src, callback) | 复制文件 | |
fs.mkdir(path, callback) | 创建目录 | |
fs.readDir(path, callback) | 读取目录列表 | |
fs.rename(oldPath, newPath, callback) | 重命名文件/目录 | |
fs.rmdir(path, callback) | 删除目录 | 只能删除空目录 |
fs.stat(path, callback) | 获取文件/目录信息 | |
fs.unlink(path, callback) | 删除文件 | |
fs.watch(filename[, options][, listener]) | 监视文件/目录 | |
fs.watchFile(filename[, options], listener) | 监视文件 |
// readFile -- 异步读取文件
fs.readFile('./test.json', (err, data) => {
if (err) {
console.log('读取文件出错');
} else {
console.log(data); // 读取到的二进制数据
}
});
fs.readFile('./test.json', 'utf-8', (err, data) => {
if (err) {
console.log('读取文件出错');
} else {
console.log(data); // 读取到的原始数据
}
});
// writeFile -- 异步写入文件
fs.writeFile('./abc.html', 'hello world', (err) => {
if (err) {
console.log('写入文件失败');
} else {
console.log('文件写入成功');
}
});
querystring模块
查询字符串处理模块
加载模块
const querystring = require('querystring');
调用querystring模块中的方法
// parse -- 将查询字符串解析成JS对象
console.log(querystring.parse('id=1&name=zs&age=20'));
// { id: '1', name: 'zs', age: '20' }
// stringify -- 将JS对象转成查询字符串
console.log(querystring.stringify({ id: '1', name: 'zs', age: '20' }));
// id=1&name=zs&age=20
url模块
url模块用来处理url地址
一个完整的url 协议://主机地址:[端口]/文件地址?参数
- 提供两套处理url的API
- 遗留的API,提供url.parse();方法解析url
- 新的API,通过实例化URL,来解析url
加载模块
const url = require('url');
遗留API使用方法
let myURL = url.parse('/test.html?id=11&age=22'); // 返回一个包含url各个部分的对象
新的API使用方法,实例化的时候,必须传递一个完整的url
// 直接提供一个完整的url
let myURL = new URL('http://www.xxx.com/test.html?id=11&age=22');
// 或
// 提供两个参数,一是文件路径及参数部分,二是域名,总之,二者组合必须是完整的url
let myURL = new URL('/test.html?id=11&age=22', 'http://www.xxx.com');
// 得到的myURL是一个对象,包含url中的各个部分
// 如果需要解析参数部分,则使用querystring模块,或使用URL的一个子对象searchParams中的get方法
let age = myURL.searchParams.get('age'); // 22
http模块
http服务器处理模块
node不同于Apache,安装完node并没有一个能够提供Web服务环境,需要使用http模块自己来搭建Web服务器
http是一个系统模块,让我们能够通过简单的流程创建一个Web服务器
使用http模块搭建Web服务器
创建 Web 服务器的步骤
- 导入 http 核心模块
- 创建 server 对象(server 对象负责建立连接,接收数据)
- 注册 request 事件,当浏览器发送请求到服务器执行,设置处理请求的函数
- 监听端口(这个步骤也可以放到注册request事件之前)
// 0. 加载 http 核心模块
var http = require('http')
// 1. 创建服务器,得到 Server 实例
var server = http.createServer()
// 2. 监听客户端的 request 请求事件,设置请求处理函数
server.on('request', function (req, res) {
// 形参req是请求request的简写
// 形参res是响应response的简写
console.log('收到客户端的请求了')
})
// 3. 绑定端口号,启动服务器
server.listen(3000, function () {
console.log('Server is running at port 3000.')
})
- 当服务器接收到浏览器的请求后,如果没有做出响应,浏览器会等待
- 服务器的最终目的是要根据请求做出响应
如何对浏览器的请求做出响应
当收到浏览器的请求后,会调用处理请求的函数(有两个核心参数 request 和 response)
请求已经能够收到了,现在急需给浏览器的请求做出响应,这里就使用到了处理请求的函数,具体的是使用到了该函数的第二个参数,见下面的代码。
// 代码片段
server.on('request', function (req, res) {
// 该函数就是处理请求响应的函数
// 形参res是响应response的简写
})
- 形参res
- 形参res是response的缩写
- 响应对象,服务器给浏览器返回的响应内容,可以通过该对象设置
- res.write() 设置响应体(返回给浏览器的内容)的内容,可以多次调用,但是只调用write不会做出响应,发送响应要调用 end()
- res.end() 把响应报文(响应行、响应头、响应体)发送给浏览器
- res.setHeader() 设置响应头,比如设置响应体的编码
- statusCode 设置状态码
PS:浏览器在请求服务器的时候,默认会请求网站根目录下的 /favicon.ico
网站图标
-
响应代码
// 0. 加载 http 核心模块 var http = require('http') // 1. 创建服务器,得到 Server 实例 var server = http.createServer() // 2. 监听客户端的 request 请求事件,设置请求处理函数 server.on('request', function (req, res) { // 设置响应头,设置编码,否则浏览器收到的是乱码 res.setHeader('Content-Type', 'text/html; charset=utf-8'); // end中就是返回给浏览器的数据 res.end('hello,我是服务器,我收到你的请求了'); }) // 3. 绑定端口号,启动服务器 server.listen(3000, function () { console.log('Server is running at port 3000.') })
// 如果需要将一个html页面返回给浏览器,则读取文件,然后返回即可 var http = require('http') var fs = require('fs'); // 1. 创建服务器,得到 Server 实例 var server = http.createServer() // 2. 监听客户端的 request 请求事件,设置请求处理函数 server.on('request', function (req, res) { fs.readFile('./test.html', (err, data) => { // 如果读取中出现错误,则抛出错误 if (err) throw err; // 没有错误,则返回读取的html,因为html中设置编码,所以这里不需要设置编码 res.end(data); }); }) // 3. 绑定端口号,启动服务器 server.listen(3000, function () { console.log('Server is running at port 3000.') })
根据不同 url 地址处理不同请求
前面已经可以对浏览器的请求做出响应了,但是响应的内容总是一样的。能不能根据url的不同,做出合适的响应呢?当然可以,那么首先就需要知道浏览器请求的url是什么。
涉及到和请求相关的信息,都是通过请求响应处理函数的第一个参数完成的。
server.on('request', function (req, res) {
// 形参req 是 请求request的意思,所有和请求相关的信息,都在req对象中
})
-
- 请求对象,浏览器发送的请求报文中的数据已经被解析到该对象上
- request.url 获取请求行中的路径
- request.method 获取请求行中的请求方法
- request.headers 获取请求头
-
代码实例
// 服务器中有3个html文件,分别是a.html b.html c.html // 根据浏览器发来的不同的请求,分别返回这三个文件的内容 var http = require('http') // 1. 创建服务器,得到 Server 实例 var server = http.createServer() // 2. 监听客户端的 request 请求事件,设置请求处理函数 server.on('request', function (req, res) { // 根据请求对象req,得到请求url let url = req.url; if (url === '/a') { // 读取a.html,并通过res.end()响应浏览器的请求 } else if (url === '/b') { // 读取b.html,并通过res.end()响应浏览器的请求 } else if (url === '/c') { // 读取c.html,并通过res.end()响应浏览器的请求 } }) // 3. 绑定端口号,启动服务器 server.listen(3000, function () { console.log('Server is running at port 3000.') })
处理浏览器POST方式提交的数据
上一节,我们根据不同的url地址,对不同的请求,做出了不同的响应。但这些请求都是GET方式的请求,如果浏览器使用POST方式向服务器发送了一次请求,又该如何处理呢?
POST请求一般会提交数据给服务器,服务器在接收数据的时候也是分块接收的
var http = require('http')
// 1. 创建服务器,得到 Server 实例
var server = http.createServer()
// 2. 监听客户端的 request 请求事件,设置请求处理函数
server.on('request', function (req, res) {
// 假设 /add 是POST方式的请求
if (req.url === '/add') {
// 注册req的data事件,分块接收数据
let str = '';
req.on('data', (chunk) => {
str += chunk; // chunk 是块的意思,这里将分块接收到的数组拼接到str字符串中
});
// 注册req的end事件,当数据都接收到了,会触发end事件
req.on('end', () => {
// 这里处理用户提交的数据
console.log(str);
});
}
})
// 3. 绑定端口号,启动服务器
server.listen(3000, function () {
console.log('Server is running at port 3000.')
})
处理静态资源
-
静态资源指的是html文件中链接的外部资源,如css、js、image文件等等。
-
如果请求的a.html 文件中链接了外部文件(静态资源),比如css、images、js文件等,浏览器会自动再次发送请求,向服务器请求这些文件
-
服务器要判断浏览器请求的路径是否是静态资源,如果是静态资源把静态资源的内容返回给浏览器
浏览器请求了静态资源文件,服务器就得做出响应,而且还要指定响应数据的类型,否则浏览器会把字符串当做纯文本处理。
建议每个响应都告诉客户端我给你发送的 Content-Type 内容类型是什么
为不同的文件类型设置不同的 Content-Type
- .html:text/html
- .css:text/css
- .js:application/javascript
- .jpg:image/jpg
response.setHeader('Content-Type', 'text/css');
处理 404
-
404是一个响应状态码,表示请求的资源不存在
-
如果请求未处理的路径,服务器不会做任何的响应,此时浏览器处于等待状态
-
如果浏览器请求未处理的路径,统一设置响应码
404
,并做友好提示// 设置状态码为404 response.statusCode = 404; response.end('对不起,您请求的页面未找到');
留言板案例
ajax学习中,做过这个案例。当时使用的是老师提供的接口来完成的。现在,学习了node,我们自己来处理浏览器的请求。
此次开发,使用不使用ajax无所谓,关键是如何来处理浏览器的请求
准备工作
- 新建一个文件夹,比如msg
- 将使用到的html文件、css文件、图片文件、js文件等复制到准备好的文件夹msg中
- 将模拟数据db.json文件复制到msg中
- 在msg文件夹中,创建app.js,搭建Web服务环境
实现留言列表
- 当输入地址,回车,会向服务器发送一次GET请求
- 服务器接收到请求,要给浏览器返回它要的数据
实现添加留言功能
- 点击界面中的“添加”时,向服务器发送请求,并提交数据给服务器,注意设置表单的action和method
- 服务器接收到请求,完成数据添加,并作出响应,告知浏览器添加成功与否