1.NodeJS简介
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于 Chrome JavaScript 运行时建立的一个平台。
Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快,性能非常好。
Node.js 的工作原理?
-
Node.js 最大的用武之地在于建设高性能、高扩展性的互联网应用---因为它能够处理庞大的并且高吞吐量的并发连接。主要是因为node.js的以下几大特性:
-
event-driven 事件驱动
-
non-blocking 非阻塞的异步I/O调用
-
lightweight 省空间、省设备,轻量意味着更好的移植性
-
Node.js是跨平台的项目,可以运行在Linux、Unix、Windows
-
efficient 高效(node.js是基于单线程)
在PHP或者JSP中,是每个新增一个连接(请求)便生成一个新的线程,这个新的线程会占用系统内存,最终会占掉所有的可用内存。而 Node.js 仅仅只运行在一个单线程中,使用非阻塞的异步 I/O 调用,所有连接都由该线程处理,在 libuv 的加分下,可以允许其支持数万并发连接(全部挂在该线程的事件循环中),
注意:Node.js也有潜在的缺陷,比如:如果所有客户端的请求共享单一线程时也会有问题, 大量的计算可能会使得 Node 的单线程暂时失去反应, 并导致所有的其他客户端的请求一直阻塞, 直到计算结束才恢复正常;
因此,开发时千万不要让一个 Exception 阻塞核心的事件循环,因为这将导致 Node.js 的应用程序崩溃。比如在 PHP 中某个页面挂掉是不会影响网站运行的,但是 Nodejs 是一个线程一个线程来处理所有的链接,因此一旦异常阻塞了都可能会影响到其他所有的链接。
当然,Node.js中有很多工具和策略来帮助我们解决上述的问题,比如:异常回调传递, Forever 进行进程监视等,下面来简单学习一下,初步不入后端的门槛。
运行js代码
- 用node执行单独的js代码 nodejs运行 JavaScript 脚本文件
1、创建并编写 JavaScript 脚本文件
2、在js脚本文件找到对应的根目录,点击磁盘路径删除,输入cmd命令,打开 cmd 命令窗口
3、直接执行“node js文件名的具体路径”命令,即可执行js脚本文件
nodejs有哪些核心模块
- 内置模块
- 第三方模块
- 自定义模块
nodejs的核心模块:1、HTTP模块,用于处理客户端的网络请求;2、URL模块,用于处理客户端请求过来的URL;3、Query Strings模块;4、File System模块;5、Path模块;6、Global模块等等。
- 首先我们了解这几个模块
Node核心模块之HTTP模块
作用:
处理客户端的网络请求
代码步骤:
1.导入 HTTP 核心模块
2.监听客户端的请求
3.处理客户端的请求
4.开启服务器
需要借助 http 模块 搭建服务器:
var http = require('http');
// req: 客户端向服务器请求
// res: 服务器向客户端响应
var app=http.createServer((req, res) => {
// 设置响应头
res.setHeader(200, {"Content-type":"text/plain; charset=utf-8"})
// 设置编码格式
res.end(data);
// 返回数据
res.end("")
// 终止响应 也可以返回数据
//})
//app.listen(8080)
//服务正确开启,需要监听8080 端口
NodeJS核心模块之File System模块
前提 : 以后的所有静态资源(html,css,js,图片等)都是放在服务端的,如果浏览器需要这些html,css,js,图片等资源,则需要先将其读取到node.exe的内容中,然后再返回给浏览器
作用:
在服务端来操作文件,可能是需要将浏览器上传的图片保存到服务器,也可能是需要将服务器的资源读取之后返回给浏览器
- 代码步骤:
1.导入 fs 核心模块
2.使用相应的方法来写入文件、读取文件等操作
nodejs 的 fs 文件系统
nodejs 的fs 文件系统 可以操作电脑上的文件, 对文件进行增删改查的操作
var fs = require('fs')
对文件的基本操作
- 读取文件
fs.readFile()
fs.readFile(url, (err, result) => {
if(err) {
console.log(err);
return;
}
console.log(result) // 默认的格式是buffer 类型
console.log(result.toString()) // 默认的格式是buffer 类型
})
开始
// 引入模块
var fs = require('fs');
// 1. fs.stat() 检测是文件还是目录
/**
* path 文件路径
* callback 回调函数
*/
fs.stat('./package.json', (err, data) => {
if (err) {
console.log(err);
}
console.log(`是文件: ${data.isFile()}`); // true
console.log(`是目录: ${data.isDirectory()}`); // false
})
// 2. fs.mkdir() 创建目录
/**
* path 将要创建的目录路径
* mode 目录权限(读写权限),默认777,一般不用写
* callback 回调函数,传递异常参数 err
*/
fs.mkdir('./src/util', err => {
if (err) {
console.log(err);
return;
}
console.log('创建成功');
})
// 3. fs.writeFile() 写入操作(有则写入,无则创建并写入)
/**
* path 将要写入文件路径
* content 写入内容
* callback 回调函数,传递异常参数 err
*/
fs.writeFile('./util/format.js', '这是format 文件', err => {
if (err) {
console.log(err);
return;
}
console.log('写入成功');
})
// 4. fs.appendFile() 追加内容操作(有则写入,无则创建并写入)
/**
* path 将要追加文件路径
* content 写入内容
* callback 回调函数,传递异常参数 err
*/
fs.appendFile('./util/format.js', '这是追加内容', err => {
if (err) {
console.log(err);
return;
}
console.log('追加内容成功');
})
// 5. fs.readFile() 读取文件内容
/**
* path 文件路径
* callback 回调函数,传递异常参数 err
*/
fs.readFile('./util/format.js', (err,data) => {
if (err) {
console.log(err);
return;
}
console.log(data); // Buffer 类型
console.log(data.toString()); // 将 Buffer类型 转成 string类型
})
// 6. fs.readdir() 读取目录下的文件及文件夹
/**
* path 文件路径
* callback 回调函数,传递异常参数 err
*/
fs.readdir('./util', (err,data) => {
if (err) {
console.log(err);
return;
}
console.log(data); // ['index.html', 'index.js', 'js']
})
// 7. fs.rename() 重命名/移动文件
/**
* oldPath 旧文件路径
* newPath 新文件路径
* callback 回调函数,传递异常参数 err
*/
fs.rename('./html/old.html', './new/new.html', (err) => {
if (err) {
console.log(err);
return;
}
console.log('移动文件成功');
})
// 8. fs.rmdir() 删除目录(不能删除非空目录)
/**
* oldPath 待删除目录路径
* callback 回调函数,传递异常参数 err
*/
fs.rmdir('./html', (err) => {
if (err) {
console.log(err);
return;
}
console.log('删除目录成功');
})
// 9. fs.unlink() 删除文件
/**
* oldPath 待删除文件路径
* callback 回调函数,传递异常参数 err
*/
fs.unlink('./html/old.html', (err) => {
if (err) {
console.log(err);
return;
}
console.log('删除文件成功');
})
// 9. fs.copyFile() 删除文件
/**
* oldPath 待拷贝文件路径
*newpath 要拷贝到的文件路径
* callback 回调函数,传递异常参数 err
*/
/var oldpath=__dirname+"/src/1.txt"
/var newpath=__dirname+"/wangluo/1.txt"
//fs.copyFile(oldpath,newpath,(err)=>{
console.log(err)
})
//fs.readdir(__dirname+"/src",(err)=>{
console.log(err)
})
Node核心模块之URL模块
作用:
处理客户端请求过来的URL
代码步骤:
1.导入 URL 核心模块
2.导入 HTTP 核心模块
3.监听客户端的请求
在这中间处理客户端请求过来的URL
4.处理客户端的请求
5.开启服务器
nodejs 的 url 模块
url 模块可以解析 url 地址 ; 通过 url.parse() 方法 解析地址 转化为一个对象
var url = require('url');
var path = 'https:://baike.baidu.com/xiaoshuo?kerywods=hello#3';
var res = url.parse(path);
// 网址: URL
//https:://baike.baidu.com/xiaoshuo?kerywods=hello#3
//网址的组成: 协议 域名 pathname querystring hash
//域名 ==>DNS解析 会把域名解析为一个ip port
/*
Url {
protocol: 'https:', // 协议
slashes: null,
auth: null,
host: null, // 主机
port: null, // 端口号
hostname: null, // 主机名
hash: '#3', // 哈希 url地址中 #后的内容
search: '?kerywods=hello', // 搜索 url 地址中 ? 开始往后的内容, 不包含hash
query: 'kerywods=hello', // 查询 url 地址中 ? 后边的内容 传递的数据
pathname: '://baike.baidu.com/xiaoshuo', // 访问的地址(请求资源的路径)
path: '://baike.baidu.com/xiaoshuo?kerywods=hello', // 路径
href: 'https:://baike.baidu.com/xiaoshuo?kerywods=hello#3' // 详细的url地址
}
*/
如果需要获取url地址中传递的数据, 需要传递第二个参数 url.parse(url, true) 对url 中的 query 字段解析为对象
let res = url.parse(path, true);
Url {
protocol: 'https:',
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: '#3',
search: '?kerywods=hello',
query: [Object: null prototype] { kerywods: 'hello' },
pathname: '://baike.baidu.com/xiaoshuo',
path: '://baike.baidu.com/xiaoshuo?kerywods=hello',
href: 'https:://baike.baidu.com/xiaoshuo?kerywods=hello#3'
}
通过 url 模块对 url 地址进行解析, 实现路由的操作
const http = requrie('http')
const url = requrie('url')
http.createServer((req, res) => {
// 对请求的路径进行解析
let pathname = url.parse(req.url).pathname;
if(pathname == '/'){
// 相关的处理
}else if(pathname == '/login'){
// 相关的处理
}....
}).listen(8000, () => {
console.log('server runnning ....')
})
// var url=require("url")
// // console.log(url)
// var str="http://www.hqyj.com/20220728/news/page1/index.html?count=20&maxid=123456"
// var obj=url.parse(str)
// console.log(obj)
var http = require("http")
var fs = require("fs")
var url=require("url")
//"http://ip:port/20220728/news/page1/index.html?count=20&maxid=123456"
var app = http.createServer((req, res) => {
console.log(req.url)//"/20220728/news/page1/index.html?count=20&maxid=123456"
var path=url.parse(req.url).pathname
fs.readFile(__dirname + path, (err, data) => {
res.end(data)
})
})
app.listen(8080)
NodeJS核心模块之Query Strings模块
作用:
处理客户端通过get/post请求传递过来的参数
使用关键点:
1.需要导入 'querystring' 这个核心模块
2.get请求时 querystring 一般是配合 url 核心模块一起使用的
3.get/post请求最终都需要调用 querystring.parse方法,将请求传递过来的键值对字符串转成js对象,方便操作
注意:
get/post的请求方式是不一样的,客户端传递过来时,参数放在
的地方是不一样的,所以服务器端处理方式也不太一样
nodejs核心模块 querystring模块
用来对url中的查询字符串这部分进行处理。nodejs中提供了querystring这个核心模块来帮助我们处理这个需求。
示例
const qs= require('querystring');
let obj = qs.parse('id=18&name=zs');
console.log(obj) // {id:18, name:"zs"}
querystring模块所有方法:
{
unescapeBuffer: [Function: unescapeBuffer],
unescape: [Function: qsUnescape],
escape: [Function: qsEscape],
stringify: [Function: stringify],
encode: [Function: stringify],
parse: [Function: parse],
decode: [Function: parse]
}
querystring 模块 字面意思就是 查询字符串,一般是对http请求所带的数据进行解析
引入模块:require('querystring'); querystring模块仅有四个方法
-
querystring.parse(str); parse函数:是将一个字符串反序列化为一个对象。
-
querystring.escape(str); escape函数:可使传入的字符串进行编码。
-
querystring.stringify(str); stringif函数:将一个对象序列化成一个字符串,与querystring.parse相对
-
querystring.unescape(); unescape函数:可将含有%的字符串进行解码
例 1 querystring.parse(str)
const http = require('http');
const url = require('url');
const qs = require('querystring');
//
const strurl = 'http://www.baidu.com?a=1&b=2';
const r = qs.parse(strurl);
console.log(r); //{ 'http://www.baidu.com?a': '1', b: '2' }
const server = http.createServer(function(request,response){
const r2 = qs.parse(url.parse(request.url).query);
console.log(r2);//{ newsid: '1', autuor: 'zs' } 相当于parst的true的效果
response.end();
});
server.listen(8000);
例 2 querystring.stringify()(str)
const http = require('http');
const url = require('url');
const qs = require('querystring');
const strbm = 'http://www.baidu.com/s?wd=中国$a=10';
const server = http.createServer(function(request,response){
var params = {
a:50,
b:50
}
const rsgf = qs.stringify(params);
//也可以指定连接符,同理 可以指定参数名和值的连接符
const rsgf2 = qs.stringify(params,'@');
console.log(rsgf);//a=50&b=50
console.log(rsgf2);//a=50@b=50
response.end();
});
server.listen(8000);
还可以用json来解析
// var querystring=require("querystring")
// var obj=querystring.parse("username=jack&count=20&maxid=123456")
// //obj[xxxx]
// console.log(obj)
// var str2=querystring.stringify({name:"jack",age:20})
//返回的是一个网址
// var str3=JSON.stringify({name:"jack",age:20})
//返回的是一个对象
// console.log(str2,str3)
NodeJS核心模块之Path模块
作用
操作文件的路径,为文件操作服务
常用的属性:
__dirname : 文件所在的文件夹路径
__filename : 文件所在的路径
require() : 导入需要的模块
module : 自定义模块时用到
exports : 自定义模块时用到
path 模块
提供了一些用于处理文件路径的小工具,我们可以通过以下方式引入该模块:
var path = require("path")
在使用 fs 模块操作文件时,如果提供的操作路径是以 ./ 或 ../ 开头的相对路径时,很容易出现路径动态拼接错误的问题。因为代码在运行的时候,会以 node 命令时所处的目录,动态拼接出被操作文件的完整路径
于是我们可以用绝对路径来代替相对路径,但是这样做又会出现移植性差、不利于维护的问题
因此我们可以利用 __dirname 来完美解决
示例:
// 在node.js环境中 有两个全局变量 __dirname __filename
//1.他们保存的是字符串
//2.
//__dirname 当前js文件所在的目录:绝对路径 文件夹(directory)
//__filename 当前js文件的目录:绝对路径
console.log(111111,__dirname,__filename,2222)
var fs=require("fs")
fs.readFile(__dirname+"/index.html",(err,data)=>{
console.log(err,data.toString())
})
fs.readFile(__filename,(err,data)=>{
console.log(err,data.toString())
})
let fs = require("fs");
let path = require("path");
// 拿到需要读取的文件路径 __dirname-表示当前文件所处的目录
let str = path.join(__dirname, "./readMe.txt");
//读取文件
// fs.readFile(__dirname + './readMe.txt', "utf8", function(err, data) { 或
fs.readFile(str, "utf8", function (err, data) {
if (err) {
throw new Error("读取文件失败");
}
console.log(data.toString());
});
NodeJS核心模块之mime模块
作用:
mime是一个互联网标准,通过设定它就可以设定文件在浏览器的打开方式
注意点
可以通过mime.getType()来判断输入网址的文件的类型,从而来根据网址数据设置类型:
也可以用mime.getExtension() 来获取你输入的类型:
但是mime是外部资源模块,需要自行下载,在小黑窗输入 npm i mime 来下载安装此模块
var mime=require("mime")
var re=mime.getExtension("text/css")
console.log(re)
var re2=mime.getType("htpp://123.123.12.3:8080/css/index.html")
console.log(re2)
//引入模块
const mime = require('mime');
//指定文件类型
let type = mime.getType(realPath);
res.writeHead(200, {
'content-type': type
});
mime.getType可以通过路径返回资源类型
var mime=require("mime")
var url=require("url")
var querystring=require("querystring")
var http=require("http")
var fs=require("fs")
var app = http.createServer((req,res) => {
let urlobj = url.parse(req.url)
let pathname = urlobj.pathname
let path = __dirname + pathname
fs.readFile(path,(err,data) => {
if (!err) {
// 获取当前访问网址的文件类型
let type1 = mime.getType(path)
//设置一个变量来转化,可以实现任何类型,不用去考虑
res.setHeader("conten-Type",type1)
res.end(data)
}else {
res.end("404")
}
})
})
app.listen(8081)
路径
1.文件操作中的./xx相对路径问题
查看以下场景:
# /js/foo/a.txt
hello World
# /js/foo/index.js
const fs = require('fs');
fs.readFile('./a.txt',(err,data) => {
if (err) {
console.log('error');
} else {
console.log(data.toString());
}
})
在/js目录下执行node foo/index.js,会出现文件找不到的情况,原因:/js/foo/index.js文件中读文件是写的相对路径也即:./a.txt,而这个相对路径实际上是相对于执行node命令所处的路径,也即以上的执行node时,进行文件操作时查找的路径是:js/a.txt显然/js目录下没有该文件,也就查找失败。
# /js/foo/a.txt
hello World
# /js/foo/index.js
const fs = require('fs');
fs.readFile('./a.txt',(err,data) => {
if (err) {
console.log('error');
} else {
console.log(data.toString());
}
})
# /js/other.js
require('./foo/index'); // 引入/foo目录下的index.js文件
在/js目录下执行node other.js命令,执行结果为:
找不到该文件
原因和之前的一样,虽然在js下执行该命令,但是在该文件中还是在引入并执行index.js文件,由于node命令执行的目录是:/js目录,所以在文件操作的时候,查找的文件目录是:/js/a.txt,显然又是找不到的结果
所以在文件操作中,相对路径是不可靠的,为了解决这个问题,则需要将相对路径改为绝对路径。但是如果仅仅是将文件操作的路径改为C:\node\js\foo\a.txt路径,则当交付项目的时候,还需要将该路径改为当前项目所处计算机的绝对路径,显然这是不可行的,因此__dirname开始发挥作用。
而什么__dirname是什么呢? 在每个模块中,除了require、exports等模块相关API之外,还有两个特殊的成员
__dirname获取当前文件所处目录(绝对路径)
__filename获取当前文件所处目录,包括当我文件(绝对路径)
__dirname和__filename是不受执行node命令所属路径影响的
以上两种获取路径的方式都是动态获取的
由于__dirname不受node命令所属路径影响,同时又可以动态的获取当前文件的绝对路径,因此可以是个不错的选择,将/foo/index.js修改:
# /js/foo/index.js
const fs = require('fs');
const path = require('path');
// 采用path.join()对于拼接的路径自动进行修复,避免不必要的失误操作造成的文件访问不到的问题
fs.readFile(path.join(__dirname + './a.txt'),(err,data) => {
if (err) {
console.log('error');
} else {
console.log(data.toString());
}
})
2.require()中的路径问题
模块中require中所写的路径跟文件操作的路径是没有关系的,其路径是相对于文件模块的,也即相对于当前文件模块(文件)所处目录的相对路径。
# /js/other.js
require('./foo/index.js');
# /js/foo/index.js
console.log('1');
此时查找./foo/index.js就是相对于/js目录
老师笔记详细解答:
/*
本地相对路径
在这个页面中写路径: file://x1/x2/x2/index.html
"./src/18.jpg" 写这个路径的文件的页面是在本地打开的==> file://x1/x2/x2/src/18.jpg
"src/18.jpg" 写这个路径的文件的页面是在本地打开的 ==> file://x1/x2/x2/src/18.jpg
"file://c:/"
本地绝对路径
从根盘符开始写路径
"C:/Users/Administrator/Desktop/%E4%BB%A3%E7%A0%81/14-%E5%90%84%E7%A7%8D%E8%B7%AF%E5%BE%84(%E7%9B%B8%E5%AF%B9%E7%BB%9D%E5%AF%B9)/index.html"
相对网络路径
当前页面的网址: "协议://ip:port /src/news/index.html querystring hash"
页面内部的路径:
"./src/18.jpg" ==> "协议://ip:port /src/news/src/18.jpg"
"src/18.jpg" ==> "协议://ip:port /src/news/src/18.jpg"
思考1:
用户输入网址:
"http://192.168.6.60:8080/user/20220728/newspage.html?n=20"
打开了一个页面,在这个页面中有一个img的src是 : "./src/18.jpg"
请问192.168.6.60:8080这个服务器会受到req.url是什么?
答: "/user/20220728/src/18.jpg"
思考2:
用户输入网址:
"http://192.168.6.60:8080/user/20220728/newspage"
打开了一个页面,在这个页面中有一个img的src是 : "./src/18.jpg"
请问192.168.6.60:8080这个服务器会受到req.url是什么?
答: "/user/20220728/src/18.jpg"
它真正的网址:"http://192.168.6.60:8080/user/20220728/src/18.jpg"
绝对网络路径
"协议://ip:port /src/news/src/18.jpg"
易错思考:
用户输入网址: http://192.168.6.60:8080/user/20220728/newspage
打开了一个页面,在这个页面中有一个img的src是 : "192.168.6.60:8080/src/18.jpg"
请问192.168.6.60:8080这个服务器会受到req.url是什么?
答: "/user/20220728/192.168.6.60:8080/src/18.jpg"
它真正的网址:"http://192.168.6.60:8080/user/20220728/192.168.6.60:8080/src/18.jpg"
本地相对根路径
思考:用户本地打开: "file:///c:/xx/xx2/index.html"
页面中有一个img的src是 : "/src/18.jpg"
它真正的路径:"file:///c:/src/18.jpg"
网络相对根路径
"/src/18.jpg"
思考:
用户输入网址: http://192.168.6.60:8080/user/20220728/newspage
打开了一个页面,在这个页面中有一个img的src是 : "/src/18.jpg"
请问192.168.6.60:8080这个服务器会受到req.url是什么?
答:"/src/18.jpg"
它真正的网址:"http://192.168.6.60:8080/src/18.jpg"
*/
详细讲解页面加载过程
说一说从输入URL到页面呈现发生了什么?(知识点)
这个题可以说是面试最常见也是一道可以无限难的题了,一般面试官出这道题就是为了考察你的前端知识的深度与广度。
-
1.浏览器接受URL开启网络请求线程(涉及到:浏览器机制,线程与进程等)
-
2.开启网络线程到发出一个完整的http请求(涉及到:DNS解析,TCP/IP请求,5层网络协议等)
-
3.从服务器接收到请求到对应后台接受到请求(涉及到:负载均衡,安全拦截,后台内部处理等)
-
4.后台与前台的http交互(涉及到:http头,响应码,报文结构,cookie等)
-
5.缓存问题(涉及到:http强缓存与协商缓存等)(请看上一篇文章这些浏览器面试题,看看你能回答几个?)
-
6.浏览器接受到http数据包后的解析流程(涉及到html词法分析,解析成DOM树,解析CSS生成CSSOM树,合并生成render渲染树。然后layout布局,painting渲染,复合图层合成,GPU绘制,等)
-
在浏览器地址栏输入URL: 当我们在浏览器地址栏输入URL地址后,浏览器会开一个线程来对我们输入的URL进行解析处理。
浏览器中的各个进程及作用:(多进程)
浏览器进程:负责管理标签页的创建销毁以及页面的显示,资源下载等。 第三方插件进程:负责管理第三方插件。 GPU进程:负责3D绘制与硬件加速(最多一个)。 渲染进程:负责页面文档解析(HTML,CSS,JS),执行与渲染。(可以有多个)
这只是一个小小的开头,后面学了再补充总结