nodejs基础
1. 简介
- Node.js 是运行在服务端的 JavaScript
- Node.js是一个Javascript运行环境(runtime),既不是语言,也不是框架,不是库,它是一个平台
- Node.js对一些特殊用例进行了优化,提供了替代的API,使得V8在非浏览器环境下运行得更好。
- Node.js是一个基于Chrome JavaScript运行时建立的平台,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
- Node.js 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。
- Node.js可以解析和执行javascript代码(以前只有浏览器可以解析和执行javascript)
注意: @types/node 是vscode里面自动提示的第三方包
js代码
10. Nodejs 主要组成部分
引入 required 模块
我们可以使用 require 指令来载入 Node.js 模块
创建服务器
服务器可以监听客户端的请求类似于 Apache 、Nginx 等 HTTP 服务器
接收请求与响应请求
服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据
2. Nodejs中的Javascript特性
- 没有BOM和DOM
- 支持EcmaScript 基本的 JavaScript 语言部分
- 变量、方法、数据类型、内置对象、Array、Object、Date、Math
- 在 Node 中为 JavaScript 提供了一些服务器级别的 API,主要可以做服务端一些事情
- 文件的读写 fs.readFile() fs.writeFile()
- 特有的模块系统
Nodejs提供的一些服务器级别的API
(1) 文件的读写
浏览器中的 JavaScript 是没有文件操作的能力的, 但是 Node 中的 JavaScript 具有文件操作的能力
读取文件:
/*
1. 浏览器中的 JavaScript 是没有文件操作的能力的,但是 Node 中的 JavaScript 具有文件操作的能力
2. 在 fs 这个核心模块中,提供了所有的文件操作相关的 API,在 Node 中如果想要进行文件操作,就必须引入 fs 这个核心模块
3.例如:fs.readFile 就是用来读取文件的
*/
// 1. 使用 require 方法加载 fs 核心模块
var fs = require('fs')
// 2. 读取文件:fs.readFile
/*
第一个参数就是要读取的文件路径
第二个参数是一个回调函数
如果读取文件 成功
data 数据
error null
如果读取文件 失败
data undefined没有数据
error 错误对象
*/
fs.readFile('./data/name.txt', function (error, data) {
//
// 文件中存储的其实都是二进制数据 0 1,但是我们看到的数据是转化为16进制后的数据,所以可通过toString()转换字符串
if (error) {
console.log('读取文件失败了')
} else {
console.log(data.toString());
}
})
写文件:
var fs = require('fs')
/*
fs.writeFile写文件:向文件中写入数据
第一个参数:文件路径
第二个参数:文件内容
第三个参数:回调函数
成功:
文件写入成功
error 是 null
失败:
文件写入失败
error 就是错误对象
*/
fs.writeFile('./data/name.txt', '大家好,给大家介绍一下,我是Node.js', function (error) {
if (error) {
console.log('写入失败')
} else {
console.log('写入成功了')
}
})
(2) 网络服务的构建
(3) 网络通信:http服务器:可以快速创建一个Web服务器
/*
使用Nodejs构建一个Web 服务器
在 Node 中专门提供了一个核心模块:http,这个模块的主要功能就是创建编写服务器的
*/
// 1. 加载 http 核心模块
var http = require('http')
// 2. 使用 http.createServer() 方法创建一个 Web 服务器,返回一个 Server 实例
var server = http.createServer()
// 3. 注册 request 请求事件,当客户端请求过来,就会自动触发服务器的 request 请求事件,然后执行第二个参数:回调处理函数
// request 请求事件处理函数,需要接收两个参数:
// Request 请求对象
// 请求对象可以用来 获取客户端的一些请求信息,例如请求路径
// Response 响应对象
// 响应对象可以用来 给客户端发送响应消息
server.on('request', function(request, response) {
// request.url获取的是端口号后面的那部分的路径
console.log('收到客户端的请求了,请求路径是:' + request.url);
console.log('请求我的客户端的地址是:', request.socket.remoteAddress, request.socket.remotePort)
// response 对象有一个方法:write 可以用来给客户端发送响应数据
// write 可以使用多次,但是最后一定要使用 end 来结束响应,否则客户端会一直等待
// response.write('hello')
// response.write(' nodejs')
// response.end(); // 结束响应
// 一般是推荐下面这样的写法:直接 end 的同时发送响应数据
res.end('hello nodejs')
})
// 4. 绑定端口号,启动服务器
server.listen(3000, function() {
console.log('服务器启动成功了,可以通过 http://127.0.0.1:3000/ 来进行访问')
})
配置简单的路由:
/*
使用Nodejs构建一个Web 服务器
在 Node 中专门提供了一个核心模块:http,这个模块的主要功能就是创建编写服务器的
*/
// 1. 加载 http 核心模块
var http = require('http')
// 2. 使用 http.createServer() 方法创建一个 Web 服务器,返回一个 Server 实例
var server = http.createServer()
// 3. 注册 request 请求事件,当客户端请求过来,就会自动触发服务器的 request 请求事件,然后执行第二个参数:回调处理函数
// request 请求事件处理函数,需要接收两个参数:
// Request 请求对象
// 请求对象可以用来 获取客户端的一些请求信息,例如请求路径
// Response 响应对象
// 响应对象可以用来 给客户端发送响应消息
server.on('request', function(req, res) {
const req_url = req.url;
console.log('收到客户端的请求了,请求路径是:' + req_url);
if (req_url === '/') {
res.end('index page')
} else if (req_url === '/login') {
res.end('login page')
} else if (req_url === '/products') {
var products = [{
name: '苹果 X',
price: 8888
},
{
name: '菠萝 X',
price: 5000
},
{
name: '小辣椒 X',
price: 1999
}
]
// 响应内容只能是二进制数据或者字符串
res.end(JSON.stringify(products)) //此处直接就是返回数据接口了
} else {
res.end('404 Not Found.')
}
})
// 4. 绑定端口号,启动服务器
server.listen(3000, function() {
console.log('服务器启动成功了,可以通过 http://127.0.0.1:3000/ 来进行访问')
})
5. nodejs之模块系统
5.1 模块作用域
在 Node 中没有全局作用域的概念,只有模块作用域(即文件作用域)
-
外部访问不到内部,内部也访问不到外部
-
默认都是封闭的
e.js
// 定义变量foo var foo = '我的值是foo' // 定义函数add function add(x, y) { return x + y; }f.js
require('./e') console(foo) // foo is not definded console(add(1, 2)) // add is not definded 主要是因为 作用域的原因执行: node f.js
结果: foo is not definded add is not definded
在 Node 中,只能通过 require 方法来加载执行多个 JavaScript 脚本文件
-
require 是一个方法,它的主要作用就是用来加载模块的
var fs = require('fs') // 加载核心模块 var jquery = require('jquery') // 加载第三方模块 var a = require('./a') // 加载自定义模块 -
require的另外一个作用就是获取被加载文件模块导出的对象接口
var e = require('./e') console.log(e.foo) // 返回值:我的值是foo console.log(e.add(1, 2)) // 返回值:3
-
require 加载只能是执行其中的代码,文件与文件之间由于是模块作用域,所以不会有污染的问题
-
模块完全是封闭的
-
外部无法访问内部
-
内部也无法访问外部
-
可以加载执行多个文件,可以完全避免变量命名冲突污染的问题
-
模块与模块是的通信,主要是通过 向外暴露模块exports 实现的
-
在每个模块中,都提供了一个对象:
exports,该对象默认是一个空对象 -
该对象默认是一个空对象
console.log(exports)返回是{} -
把需要被外部访问使用的成员手动的挂载到
exports接口对象中 -
然后谁来
require这个模块,谁就可以得到模块内部的exports接口对象e.js
// 定义变量foovar foo = '我的值是foo'// 定义函数addfunction add(x, y) { return x + y;}向外暴露对象:exports.add = add exports.foo = foof.js
var e = require('./e')console.log(e.foo) // 返回值:我的值是fooconsole.log(e.add(1, 2)) // 返回值:3执行: node f.js
5.2 模块分类
在 Node 中,模块有三种:核心模块、第三方模块、自定义模块
5.3 核心模块
核心模块是由 Node 提供的一个个的具名的模块,它们都有自己特殊的名称标识,例如常见的:
-
fs 文件操作模块
-
http 网络服务构建模块
-
os 操作系统信息模块
-
path 路径处理模块
// 用来获取机器信息的var os = require('os')// 获取当前机器的 CPU 信息console.log(os.cpus())// memory 内存console.log(os.totalmem())// 用来操作路径的var path = require('path')// 获取一个路径中的扩展名部分// extname 扩展名console.log(path.extname('c:/a/b/c/d/hello.txt'))
-
所有核心模块在使用的时候都必须手动的先使用
require方法来加载,然后才可以使用,例如:var fs = require('fs') // 文件操作模块var http = require('http') // 网络服务构建模块var os = require('os') // 操作系统信息模块var path = require('path') // 路径处理模块
5.4 自定义模块
(1)引入自定义模块注意事项
-
相对路径必须加 ./ (相对路径)
-
可以省略后缀名 (.js) -- 推荐
// 可以省略后缀名,但是相对路径必须加 ./ 这个一定不可以省略// require('./b.js') require('./b') // 省略了 后缀名,推荐使用这种方式
(2) 模块是按照顺序执行的
a.js
// 可以省略后缀名,但是相对路径必须加 ./ 这个一定不可以省略// require('./b.js')// require('./b') // 省略了 后缀名,推荐使用这种方式console.log('start a')require('./b')console.log('end a')
b.js
console.log('start b')require('./c')console.log('end b')
c.js
console.log('ccc')
执行node a.js 结果如下:(是按照顺序往下执行的)
start astart bcccend bend a
5.5 模块标识:文件路径标识
模块中的路径标识就是相对于当前文件模块,不受执行 node 命令所处路径影响
文件操作路径中,相对路径设计的就是相对于执行 node 命令所处的终端路径
var fs = require('fs')var path = require('path')// 模块中的路径标识和文件操作中的相对路径标识不一致// 1. 模块中的路径标识就是相对于当前文件模块,不受执行 node 命令所处路径影响require('./b')// 2. 在文件操作路径中,相对路径设计的就是相对于执行 node 命令所处的终端路径 fs.readFile('./a.txt')
3. IP地址和端口号
- ip 地址用来定位计算机
- 端口号用来定位具体的应用程序,所有用来联网通信的应用程序都必须具有端口号
- 端口号的使用范围在0~65536之间
// ip 地址用来定位计算机// 端口号用来定位具体的应用程序// 所有需要联网通信的应用程序都会占用一个端口号var http = require('http')var server = http.createServer()// 2. 监听 request 请求事件,设置请求处理函数server.on('request', function (req, res) { console.log('收到请求了,请求路径是:' + req.url) console.log('请求我的客户端的地址是:', req.socket.remoteAddress, req.socket.remotePort) res.end('hello nodejs')})server.listen(5000, function () { console.log('服务器启动成功,可以访问了。。。')})
4. Content-Type设置
- 不同的资源对应的 Content-Type 是不一样,具体参照:http://tool.oschina.net/commons
- 对于文本类型的数据,最好都加上编码,目的是为了防止中文解析乱码问题
- 在服务端默认发送的数据,其实是 utf8 编码的内容,但是浏览器不知道你是 utf8 编码的内容,浏览器在不知道服务器响应内容的编码的情况下会按照当前操作系统的默认编码去解析,所以就出现了乱码
- 中文操作系统默认是 gbk
- 在 http 协议中,Content-Type 就是用来告知对方我给你发送的数据内容是什么类型,图片不需要指定编码,一般只为字符数据才指定编码 :
res.setHeader('Content-Type', 'image/jpeg') - 解决乱码的问题:res.setHeader('Content-Type', 'text/plain; charset=utf-8')
- text/plain:普通文本 text/html: html 格式的字符串
var http = require('http')var server = http.createServer();server.on('request', function(req, res) { /** * 在服务端默认发送的数据,其实是 utf8 编码的内容,但是浏览器不知道你是 utf8 编码的内容,浏览器在不知道服务器响应内容的编码的情况下会按照当前操作系统的默认编码去解析,所以就出现了乱码 * 中文操作系统默认是 gbk * 在 http 协议中,Content-Type 就是用来告知对方我给你发送的数据内容是什么类型 * 解决乱码的问题:res.setHeader('Content-Type', 'text/plain; charset=utf-8') * text/plain:普通文本 text/html: html 格式的字符串 */ var req_url = req.url if (req_url == '/') { var products = [{ name: '苹果', price: 3000 }, { name: '橘子', price: 4000 }, { name: '大栗子', price: 5000 }, ] // text/plain 就是普通文本 res.setHeader('Content-Type', 'text/plain; charset=utf-8') res.end(JSON.stringify(products)) } else { // 如果你发送的是 html 格式的字符串,则也要告诉浏览器我给你发送是 text/html 格式的内容 res.setHeader('Content-Type', 'text/html; charset=utf-8') res.end('hello html 点我
') }})server.listen(3000, function() { console.log('Server is running......')})
6. 服务端渲染和客户端渲染的区别
- 客户端渲染不利于 SEO 搜索引擎优化
- 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
- 所以大部分的网站既不是纯异步也不是纯服务端渲染出来的,而是二者结合的
- 例如京东的商品列表就采用的是服务端渲染,目的了为了 SEO 搜索引擎优化;而它的商品评论列表为了用户体验,而且也不需要 SEO 优化,所以采用是客户端渲染
- 客户端渲染:最少两次请求,发起 ajax 在客户端使用模板引擎渲染
- 服务端渲染:客户端拿到的就是服务端已经渲染好的
7. nodejs常用第三方模块
7.1 url.parse解析url
(1) 介绍
-
由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET请求的参数。
-
url 模块中的 parse 函数可以用于解析url中的参数
(2) 参数
url.parse(urlStr, [parseQueryString], [slashesDenoteHost])
参数使用说明如下:
- urlStr - 需要接收的url字符串。
- parseQueryString - 为true时将使用查询模块分析查询字符串,默认为false。
- shashesDenoteHost
? -默认为false,//foo/bar 形式的字符串将被解释成 { pathname: ‘//foo/bar' }
? -如果设置成true,//foo/bar 形式的字符串将被解释成 { host: ‘foo', pathname: ‘/bar' }
(3) 使用
var url = require('url') // 主要是可以获取 链接地址 后面的参数var mm = url.parse('/pinglun?name=Jack &age=90', true)console.log(mm.query.name) // 返回Jackconsole.log(mm.query.age) // 返回90
7.2 处理post请求体参数:body-parser
使用post请求的时候,服务端需要引入中间件body-parser,主要原因是post请求发送的数据,是在http的body里面,所以需要进行解析,否则获取不到数据(数据为空)
body-parser 是 专门用来解析表单 POST 请求体
配置并使用 body-parser
// 安装npm install body-parser// 配置 body-parser 中间件(插件),配置完毕后,就会在req 请求对象上多出一个body属性// 我们就可以通过 req.body 获取post请求体参数了app.use(bodyParser.urlencoded({ extended: false }))app.use(bodyParser.json())// 当以 POST 请求 /post 的时候,执行指定的处理函数// 这样的话我们就可以利用不同的请求方法让一个请求路径使用多次app.post('/post', function(req, res) { // 1. 获取表单 POST 请求体数据 // 2. 处理 // 3. 发送响应 // req.query 可以获取 get 请求参数 // req.query 可以获取 post 请求参数 console.log(req.body) // 重定向,同时自动结束响应 res.redirect('/')})
7.3 MD5加密:blueimp-md5
node.js通过可以通过blueimp-md5对密码加密
使用:
1. 下载工具包npm i blueimp-md52. 导入var md5 = require("blueimp-md5")3.加密操作md5(req.body.password) // 这里加密的密码是通过body_parser获取到的password的值
如果你觉得加密程度不够的话,可以再次嵌套一个md5加密,格式如下:
md5(md5(req.body.password))
7.4 express-session :运行在服务端
7.4.1 Session简介:
介绍
session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 session 保存在服
务器上
默认sesstion数据是内存存储的,服务器一旦重启就会丢失,一般的生产机环境都会把session数据做持久化存储的
在Express框架中,默认是不支持 Session 和 Cookie 的,因此此处我们是使用第三方中间件:express-session
用途
- session 运行在服务器端,当客户端第一次访问服务器时,可以将客户的登录信息保存。 当客户访问其他页面时,可以判断客户的登录状态,做出提示,相当于登录拦截
- session 可以和 Redis 或者数据库等结合做持久化操作,当服务器挂掉时也不会导致某些客户信息丢失
工作流程
当浏览器访问服务器并发送第一次请求时,服务器端会创建一个 session 对象,生成一个类似于 key,value 的键值对,然后将 key(cookie)返回到浏览器(客户)端,浏览器下次再访问时,携带 key(cookie), 找到对应的 session(value)。 客户的信息都保存在 session 中
7.4.2 Cookie和Session的区别
1、cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
2、cookie 不是很安全,别人可以分析存放在本地的 COOKIE 并进行 COOKIE 欺骗 考虑到安全应当使用 session。
3、session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用 COOKIE。
4、单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 cookie
7.4.3 express-session 的使用
安装
npm i express-session
引入
var session = require("express-session");
配置
/*该插件会为req请求对象添加一个成员:req.session 默认是一个对象secret参数:配置加密字符串,它会在原有加密的基础上再和这个字符串拼接一起进行加密;目的是为了增加安全性,防止客户端恶意伪造saveUninitialized参数:如果为true,则无论是否使用session,都会默认分配一把钥匙*/app.use(session({ secret: 'keyboard cat', resave: true, saveUninitialized: true}))
使用
// 设置sessionreq.session.foo='bar'// 获取sessionreq.session.foo// 清空sessionreq.session.foo=null
7.5 nodemon工具:实施监控node服务的变化
nodemon 是一款非常实用的工具,主要是用来监控 nodejs 源代码的任何变化,一旦用户保存相关文件,就会自动重启你的服务器
使用nodemon可以解决我们频繁修改代码,频繁重启服务器的问题
7.5.1 全局安装
npm install -g nodemon# 或者 局部安装 npm install nodemon --save# 查看是否安装成功nodemon -v # 或者 nodemon --version
7.5.2 直接使用
# 使用node 执行app.js文件,只要app.js发生,就必须重新执行命令node app.js# 使用nodemon 执行app.js文件的时候,用户修改了app.js只要ctrl+s保存文件,nodemon就会自动重新执行app.jsnodemon app.js
7.5.3 其他相关操作
# 如果没有在应用中指定端口,可以在命令中指定:nodemon ./app.js localhost 8080# 可以运行 debug 模式:nodemon --debug ./app.js# 查看帮助nodemon -h 或者 nodemon --help
7.6 使用nodejs操作mysql数据库
7.6.1 下载安装mysql第三方模块
# 安装mysql包npm i mysql
7.6.2 连接并操作数据库
var mysql = require('mysql');// 1. 创建连接var connection = mysql.createConnection({ host: '', port: '', user: '', password: '', database: ''});// 2. 连接数据库connection.connect();// 3. 执行数据操作:增删改查// 3.1 查询const sql = 'SELECT * from member WHERE id=1;';// 3.2 增加一条数据const sql = `INSERT INTO member VALUES(NULL, "admin", "123456")`;// 3.3 修改const sql = `UPDATE member SET name='lisi' where id=1`;// 3.4 删除const sql = `DELETE FROM member WHERE id=2`;// 执行操作connection.query(sql, function(error, results, fields) { if (error) throw error; console.log('The solution is: ', results);});// 4. 关闭连接connection.end();
7.7 qs序列化对象
7.7.1 安装引用模块
npm install --save qsimport qs from'qs'或者const qs = require("qs")
7.7.2 将对象或者数组序列化成URL的格式:qs.stringify()
// 1. 对象序列化const obj = { methods: 'query_stu' id: 1, name: 'chenchen'}qs.stringify(obj) // 返回数据:methods=query_stu&id=1&name=chenchen// 2. 数组序列化const arr = [2,3]qs.stringify({a:arr}) // 返回数据:'a[0]=2&a[1]=3'
7.7.3 qs.parse()则就是反过来啦,将我们通过qs.stringify()序列化的对象或者数组转回去
const url = 'id=1&name=chenchen'qs.parse(url) // 返回数据 {id:1,name:chenchen}
7.8 Browsersync:浏览器同步测试工具
7.8.1 介绍
省时的浏览器同步测试工具
Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面
更重要的是 Browsersync可以同时在PC、平板、手机等设备下进项调试
BrowserSync是基于Node.js的, 是一个Node模块, 如果您想要快速使用它,也许您需要先安装一下Node.js
Browsersync官网介绍
7.8.2 使用
1 下载安装
npm install -D browser-sync或者 npm install --save-dev browser-sync
2 配置scriptrs package.json文件
"scripts": { "dev":"browser-sync start --server --files \"*.html,css/*.css,js/*.js\"", "start":"npm run dev" },
3 启动开发服务
npm run dev或者npm run start 或者 npm start (只有 start 可以省略 run)
8. nodejs之交互式解释器 Read Eval Print Loop
-
Node.js REPL表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应
-
Node 自带了交互式解释器,可以执行以下任务
- a. 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
- b. 执行 - 执行输入的数据结构
- c. 打印 - 输出结果
- d. 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。
-
Node 的交互式解释器可以很好的调试 Javascript 代码
-
启动方式
node 按键enter即可
8.1 常用命令
# 退出当前终端ctrl + c# 按下两次,退出 Node REPctrl + c # 退出 Node REPLctrl + d# 查看输入的历史命令向上/向下 键# 列出当前命令tab 键# 列出使用命令.help# 退出多行表达式.break# 退出多行表达式.clear# 保存当前的 Node REPL 会话到指定文件.save filename # 载入当前 Node REPL 会话的文件内容.load filename
9. require方法加载规则
9.1 优先从缓存加载
9.2 判断模块标识:require('模块标识')
9.2.1 核心模块
-
核心模块的本质也是文件
-
核心模块文件已经被编译到了二进制文件中了,所以我们只需要按照名字来加载就可以了
require('fs')require('http')
9.2.2 自定义模块:路径形式的模块
# 当前目录,不可省略./ # 上一级目录,不可省略 ../ # 磁盘根目录/ # 绝对路径 几乎不使用# .js 后缀名可以省略require('./foo.js') 等价于require('./foo')
9.2.3 第三方模块
- 凡是第三方模块都必须通过 npm 来下载,使用的时候就可以通过 require('包名') 的方式来进行加载
第三方模块(例如:art-template)查找机制:
- 先找到当前文件所处目录中的 node_modules 目录
- 然后 node_modules/art-template
- 找到 node_modules/art-template/package.json 文件
- 查看 node_modules/art-template/package.json 文件中的 main 属性,main 属性中就记录了 art-template 的入口模块,然后加载使用这个第三方包
- 如果 package.json 文件不存在或者 main 指定的入口模块是也没有,则 node 会自动找该目录下的 index.js,也就是说 index.js 会作为一个默认备选项
- 如果以上所有任何一个条件都不成立,则会进入上一级目录中的 node_modules 目录查找,继续步骤2
- 如果上一级还没有,则继续往上上一级查找,如果直到当前磁盘根目录还找不到,最后报错:can not find module xxx
注意:我们一个项目中,有且只有一个 node_modules,而且是放在根目录中
9.2.4 模块标识中的 / 和文件路径中的 /
模块中的路径标识 就是相对于当前文件模块,不受执行 node 命令所处路径影响
文件操作路径中,相对路径设计的就是相对于执行 node 命令所处的终端路径
-
文件操作中的
相对路径可以省略// 也可以是 ./data/a.txt 此处的 ./ 是可以省略的fs.readFile('data/a.txt', function (err, data) { if (err) { return console.log('读取失败') } console.log(data.toString())})// 在文件操作的相对路径中// ./data/a.txt 相对于当前目录// data/a.txt 相对于当前目录// /data/a.txt 绝对路径,当前文件模块所处磁盘根目录// c:/xx/xx... 绝对路径 -
在模块加载中,相对路径中的 ./ 不能省略
// 引入自定义模块时, ./一定不可以省略require('data/foo.js') // Error: Cannot find module 'data/foo.js'// 正确的写法:(.js后缀名是可以省略的)require('./data/foo')// 这里如果忽略了 . 则也是磁盘根目录require('/data/foo.js')
9.3 模块查找机制
-
优先从缓存加载
-
核心模块
-
路径形式的文件模块
-
第三方模块
11. 事件循环
11.1 简介
- Node.js 是 单进程单线程 应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。
- Node.js 几乎每一个 API 都是支持回调函数的。
- Node.js 基本上所有的事件机制都是用设计模式中 观察者模式 实现。
- Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.
11.2 事件驱动程序
- Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。
- 当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
- 这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)
- 在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。
12. 回调函数:获取 异步操作 的结果
12.1 简介
-
Node.js 异步编程的直接体现就是回调。
-
异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。
-
回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。
- 例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。
-
回调函数一般作为函数的最后一个参数出现
function foo1(name, age, callback) { }function foo2(value, callback1, callback2) { } -
阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内
// 如果需要获取一个函数中 异步操作 的结果,则必须通过 回调函数 来获取 function fn(callback) { // setTimeout 异步操作函数 setTimeout(function() { var data = 'hello' callback(data) }, 1000) } fn(function(data) { console.log(data) })// 注意:凡是需要得到一个函数内部异步操作的结果// setTimeout// readFile// writeFile// ajax// 这种情况必须通过:回调函数
阻塞代码案例
data.txt
hello nodejs
main.js
var fs = require('fs')var data = fs.readFileSync('./data.txt') // 同步读取文件console.log(data.toString())console.log('end ......')// 返回结果: // hello nodejs // end ......// 文件读取完以后,才执行后面的代码
非阻塞代码案例
data.txt
hello nodejs
main.js
var fs = require('fs')// 异步读取文件fs.readFile('./data.txt', function(err, data) { if (err) { console.log('file is not find ......') } console.log(data.toString())})console.log('end ......')// 返回结果: // end ......// hello nodejs // 不要等待文件读取完毕,就可以直接执行下面的代码
13. 常见的console 输出
// 常用的console命令console.log("输出信息");console.info("信息");console.error("错误");console.warn("警告");// 占位符console.log("%年%月%日", 2017, 3, 28);// 查看对象的信息var info = { name: "song", age: '20', message: '这是使用console.log打印对象的信息'};console.dir(info);
14. nodejs之path模块
14.1 path.basename():返回路径的最后一部分
// 返回路径的最后一部分。 第二个参数可以过滤掉文件的扩展名require('path').basename('/test/something') //somethingrequire('path').basename('/test/something.txt') //something.txtrequire('path').basename('/test/something.txt', '.txt') //something
14.2 path.dirname():返回路径的目录部分
require('path').dirname('/test/something') // /testrequire('path').dirname('/test/something/file.txt') // /test/something
14.3 path.extname():返回路径的扩展名部分
require('path').extname('/test/something') // ''require('path').extname('/test/something/file.txt') // '.txt'
14.4 path.isAbsolute():如果是绝对路径,则返回 true
require('path').isAbsolute('/test/something') // truerequire('path').isAbsolute('./test/something') // false
14.5 path.join():连接路径的两个或多个部分
const name = 'joe'require('path').join('/', 'users', name, 'notes.txt') //'/users/joe/notes.txt'
14.6 path.normalize()
当包含类似 .、.. 或双斜杠等相对的说明符时,则尝试计算实际的路径
require('path').normalize('/users/joe/..//test.txt') // '/users/test.txt'
14.7 path.parse():解析对象的路径为组成其的片段
/* root: 根路径。 dir: 从根路径开始的文件夹路径。 base: 文件名 + 扩展名 name: 文件名 ext: 文件扩展名*/require('path').parse('/users/test.txt')/*返回结果:{ root: '/', dir: '/users', base: 'test.txt', ext: '.txt', name: 'test'}*/
14.8 path.resolve(): 获得相对路径的绝对路径计算
path.resolve('joe.txt') //'/Users/joe/joe.txt' 如果从主文件夹运行// 通过指定第二个参数,resolve 会使用第一个参数作为第二个参数的基准path.resolve('tmp', 'joe.txt') //'/Users/joe/tmp/joe.txt' 如果从主文件夹运行// 如果第一个参数以斜杠开头,则表示它是绝对路径:path.resolve('/etc', 'joe.txt') //'/etc/joe.txt'
14.9 其他:__dirname和__filename
-
__dirname:用来动态获取当前文件所处目录的绝对路径 -
__filename:用来动态获取当前文件的绝对路径 -
__dirname和__filename是不受执行node命令的所属目录影响的 -
主要是用来解决文件操作路径的相对路径问题:因为在文件操作中,相对路径是相对于执行
node命令所处的终端目录,所以为了尽量避免这个问题,都建议文件操作的相对路径都转为:动态的绝对路径var fs = require('fs')// 在文件操作路径中,此处的相对路径不是相对于当前文件的相对路径,而是相对于执行node命令的所处的终端路径fs.readFile('./a.txt') -
方式:
path.join(__dirname, '文件名')
15. 如何通过服务器让客户端重定向
-
状态码301:永久重定向 浏览器会记住
a.com => b.com a 浏览器不会请求 a 了 直接去跳到 b 了 -
状态码302:临时重定向 浏览器不记忆
a.com => b.com a.com 还会请求 a a 告诉浏览器你往 b
// 1. 状态码改为 302,302是临时重定向// 2. 在响应头中通过设置 Location 实现服务器端重定向res.statusCode = 302;res.setHeader('Location', '/');
16. HTML处理静态资源 /public/请求资源
用户留言列表
-
一次请求对应一次响应,响应结束这次请求也就结束了
res.end('hello') // 结束响应