Node.js

Posted 煜成'Studio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js相关的知识,希望对你有一定的参考价值。

Node.js

nvm用于切换node版本

在安装nvm前需要卸载node,否则之后会出现一些问题

查看nvm版本

nvm -v

查看当前安装的nodejs所有版本

nvm list

安装指定版本的nodejs

nvm install [版本号]

卸载指定版本的nodejs

nvm uninstall [版本号]

选择正在使用的node版本

nvm use v[版本号]

查看当前使用的node版本

node -v

查看node地址

where node

清除屏幕中前面的命令,使得屏幕更加清晰

cls

1全局变量

JS中全局对象为windowNode.js中的全局对象是global,之前所使用的的console、setTimeout都是global对象下的属性或者成员

Nodejs里面声明的变量不会挂载到global里面,在JS中申明的变量是会挂载到window上的

let a = 10;
console.log(global.a); //undefined

可以向global添加成员,可以在任何地方去使用

global.a = 60;
console.log(a); //60

nodejs分为两种模式:

一个解释js文件(这个方式是平时用的模式。在当前js文件的路径下,可以通过Vscode中的“在文件资源管理器中显示”找到文件路径,在上面的路径里删除所有输入cmd回车,执行node [文件名].js,就会执行这个文件。这个和下一种模式的路径不一样,这个是当前路径,下一个是在C盘);

另一个交互模式/repl模式(win键+R键,输入node,即进入交互模式,在这个模式下可以输入js代码,这个模式是用在简单的代码验证的)

在JS中执行一个js文件的时候,window和this是相等的;但是在nodejs中执行一个js文件,里面的this和global是不相等的,console.log(this === global); //false,this在文件中其实指向的是这个模块exports/module.exports;但在nodejs的交互模式的窗口中,this和global是相等的true

process对象(了解)

process.argv 是一个数组获取到执行文件的时候命令行的所有的参数,包括node的运行路径、文件路径

在命令行中假如写 node “m1.js” 10,20得到的数组中除了node的运行路径、文件路径、还会收集命令行中的这个10、20,作为数组中的每一个参数放在数组中

因为是数组,所以可以进行数组的操作,在做一些命令行工具效果的时候会用到,例如process.argv[2]取到数组的第三个参数

process.arch 打印系统的位数,比如x64

2Nodejs模块化

//导出的是一个对象
//m1.js
var num = 10;

function sum1(a, b) 
	return a + b;


class Animal 
	constructor() 
		this.age = 10;
	


//导出的第一种方式
//就是相当于给对象添加一个属性
console.log(exports); //
console.log(this); //
//模块中的this相当于模块的导出的对象
console.log(this === exports); //true
exports.num = num; //前面的num是我们自己定义的,后面的是上面的变量名,一般二者写为一致的
exports.sum1 = sum1;
exports.Animal = Animal;
console.log(exports); //num: num, sum1: sum1, Animal: Animal



//导出的第二种方式
module.exports = 
	num, //简写形式
	sum1,
	Animal

//或者写在一行上
module.export = num, sum1, Animal;
//如果console.log(module.exports);也是一个
//实际上exports是module.exports的引用,他俩的指向是一样的
//如果console.log(exports === module.exports);得到的是true
//文件中才有exports,交互模式中没有exports,只有module.exports,所以说真正指向一个空对象的是module.exports
//在交互模式下this === module.exports是false,因为交互模式的this === global,this指向全局变量

//还有一点,如果是用第二种方式的话应该多加一句
//因为不加下面一句的话,exports就相当于跟丢了module.exports,此时的exports就只为一个,
//一般只会使用第一种或者第二种方式,如果你用第二种方式,却还需要使用exports的话,后面加这一句,exports才会一直和module.exports相等的
module.exports = 
	num, 
	sum1,
	Animal

exports = module.exports;



//m2.js
//导入

const m1 = require("路径/m1.js"); //常量一般为大写,但这里是定义的模块,一般直接为导入的模块名一致,这个模块文件的扩展名/后缀名可以不写,直接"路径/m1"就行

console.log(m1); //num: 10, sum: [Function: sum], Animal: [Function: Animal],可以看出导出的是一个对象
console.log(m1.num); //需要取到num的话,要用对象的方式来获取

console.log(m1.sum1(10, 20)); //30

const obj = new m1.Animal();
console.log(obj.age); //10

3nodejs常用内置模块

项目中模块一般会分为3种:nodejs内置模块、自定义模块、第三方模块(使用专门的工具npm进行统一管理)

fs: 文件系统操作

http: 网络操作

path: 路径操作

querystring: 查询参数解析(get请求中携带的参数,查询参数/查询字符串)

url: url解析

const fs = require(“fs”); //直接引入

nodejs内置模块的文档地址:http://nodejs.cn/api/

两个特殊变量

__dirname //得到当前执行文件的绝对路径,不包括文件名,例如console.log(__dirname); //D:\\Desktop

__filename //文件路径和文件名,比上面的多个文件名,例如D:\\Desktop\\test.js

内置模块用require(“内置模块”)引入

const path = require("path");
let extname = path.extname(__filename); //文件的后缀名,例如.js
let basename = path.basename(__filename); //文件名(包含后缀名),例如01.js
let dirname = path.dirname(__filename); //或缺当前文件路径,不包含文件名,例如例如D:\\Desktop,和__dirname结果一样
let parseObj = path.parse(__filename); //是一个对象,包含上面所有的信息
//*******
//拼接效果,可以得到完整路径等
let fullpath = path.join(__dirname, '文件名'); //使用当前路径拼接一个自定义的文件名
//注意不要有路径的斜杠,一个参数就是一层目录
//let fullpath = path.join(__dirname, 'modules', 'm1.js'); //__dirname下的modules下的m1.js

Buffer数据类型

javascript语言本身只有字符串数据类型,没有二进制数据类型,但在处理像文件流(文件读写操作)必须用到二进制数据,因此在Nodejs中,定义一个Buffer类,该类用来创建一个专门存放二进制数据的缓存区。Buffer类似于一个整数数组。

//当你看到一个<Buffer xx xx>这是Buffer对象,可以转成看的懂的形式,用Buffer对象.toString()
let buf1 = Buffer.from([97, 98, 99]); //创建一个Buffer对象
console.log(buf1); //<Buffer 61 62 63>,十六进制的数,这种方式看不懂用下一句
console.log(buf1.toString()); //abc

let buf2 = Buffer.from("nodejs");
console.log(buf2); //<Buffer 6e 6f 64 65 6a 73>
console.log(buf2.toString()); //nodejs

//以下仅作了解
let buf3 = Buffer.alloc(10); //创建一个可以存放10个字符的buffer对象
buf3.write("abc"); //向Buffer对象里面写入信息(内部是先转2进制再转16进制)
console.log(buf3); //<Buffer 61 62 63 00 00 00 00 00 00 00>,没用到的用00表示,用到的直接占位了
console.log(buf3.toString()); //abc

4同步、异步读取文件信息,异步写入文件信息

同步读取(sync 同步):读取文件的时候,要等到文件读取完毕才会执行后面的代码;

异步读取(async 同步):不用等到文件读取完毕,就会执行后面的代码,比如定时器、ajax、事件、jQuery动画函数,异步需要回调函数

const fs = require("fs");
const path = require("path");
let filePath = path.join(__dirname, "hello.txt"); //该文件同级目录下有个hello.txt文件,里面内容是hello world, hello NodeJs 你好

//同步读取
const content = fs.readFileSync(filePath); //同步读取文件信息,把整个文件读取完毕之后才继续往下执行
console.log(content); //<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64 2c 20 68 65 6c 6c 6f 20 4e 6f 64 65 4a 73>
console.log(content.toString()); //hello world, hello NodeJs 你好
//********
//每一次转都很麻烦,直接用下面方式
const content = fs.readFileSync(filePath, "utf-8");
console.log(content); //hello world, hello NodeJs 你好

//异步读取
//需要传入第三个参数,为一个回调函数,第一个参数是读取失败的信息,第二个参数是读取成功读取到的数据
fs.readFile(filePath, "utf-8", (error, data) => 
    if(error) 
    	console.log(error); //error是错误信息,如果读取没发生错误就为null;有错误就报错
    	return; //有错直接报错,然后返回终止代码运行,可以用if...else...但没必要,要学会这种写法
    
    //读取文件信息完毕的时候执行这个回调函数
    console.log(data); //hello world, hello NodeJs 你好,当报错的时候为undefined
)
//异步写入文件信息,是信息覆盖
const fs = require("fs");
const path = require("path");

let filePath = path.join(__dirname, "hello.txt");
//fs.writeFile(文件路径文件名,写入的内容,"utf-8",回调函数)
fs.writeFile(filePath, "hello write", "utf-8", (error) => 
    if(error) 
        console.log(error);
        return;
    
    console.log("写完了!");
)

其他常用方法

const fs = require("fs");

//改名字
//fs.renameSync(旧文件名,新文件名);
fs.renameSync("hello.txt", "hello2.txt");

//获取__dirname这个路径下的所有文件名,是个数组
let namelist = fs.readdirSync(__dirname);
console.log(namelist);

let str1 = "hello.js";
console.log(str1.endsWith("alo")); //判断字符串是否以某子串结尾
console.log(str1.startsWith("heo")); //判断字符串是否以某子串开始

//判断str1是不是js文件名
//console.log(str1.endsWith(".js"));

console.log(str1.substring(2, 4)); //左闭右开
console.log(str1.substring(2)); //从下标为2一直取到结束

批量修改文件名–添加和删除前缀名

//当前目录下所有js文件都添加一个前缀[day03]
const fs = require("fs");
let namelist = fs.readdirSync(__dirname);
namelist.forEach(item => 
    if(item.endsWith(".js")) 
        fs.renameSync(item, `[day03]$item`);
    
)
//当前目录下所有js文件都删除掉一个前缀[day03]
const fs = require("fs");
let namelist = fs.readdirSync(__dirname);
let startStr = "[day03]";
namelist.forEach(item => 
	if(item.endsWith(".js")) 
		fs.renameSync(item, item.substring(startStr.length)); //直接使用前缀长度来判断
	
)

回调地狱(一个回调函数内部含有回调函数,一级一级很多个,影响美观、可读性、可维护性)

需求:现在存在三个文件1.txt(我)、2.txt(爱)、3.txt(nodejs),每个文件里面有一个字符。使用fs.readFile(异步)顺序读取1.txt 2.txt 3.txt里面的内容,组成我爱nodejs,把这个字符串异步写入data.txt文件里面。

const fs = require("fs");
const path = require("path");

let pathName1 = path.join(__dirname, "files", "1.txt");
let pathName2 = path.join(__dirname, "files", "2.txt");
let pathName3 = path.join(__dirname, "files", "3.txt");
let pathName4 = path.join(__dirname, "files", "data.txt");

fs.readFile(pathName1, "utf-8", (error1, data1) => 
    if(erro1) 
        console.log(error1);
        return;
    
    //读取完第一个文件数据后,继续读取第二个文件数据
    fs.readFile(pathName2, "utf-8", (error2, data2) => 
        if(error2) 
            console.log(error2);
            return;
        
        //继续读取第三个文件数据
        fs.readFile(pathName3, "utf-8", (error3, data3) => 
            if(error3) 
                console.log(error3);
                return;
            
            
            let str1 = data1 + data2 + data3;
            fs.writeFile(pathName4, str1, "utf-8", (error4) => 
                if(error4) 
                    console.log(error4);
                    return;
                
                console.log("写完啦!");
            )
        )
    )
)

把异步代码从外观上同步化 Promise

//基础版
const fs = require("fs");
const path = require("path");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");

let p1 = new Promise((resolve, reject) => 
	fs.readFile(filePath1, "utf-8", (error, data) => 
		if(error) 
			reject(error);
		
		resolve(data);
	)
)

let p2 = new Promise((resolve, reject) => 
	fs.readFile(filePath2, "utf-8", (error, data) => 
		if(error) 
			reject(error);
		
		resolve(data);
	)
)

let p3 = new Promise((resolve, reject) => 
	fs.readFile(filePath3, "utf-8", (error, data) => 
		if(error) 
			reject(error);
		
		resolve(data);
	)
)

//then链式调用的特点:
1.第一个then执行完毕后(不管成功还是失败),会执行第二个then里函数的代码
2.then的函数里面可以由返回值,被一下then的形参接收
3.**如果返回的是一个Promise对象,下一个then的形参接收到的不是这个Promise对象,而是这个Promise对象内部的resolve函数的实际参数

let str1 = "";

p1.then((data) => 
	str1 += data; //"我"
	return p2;
, (error) => 
	console.log("读取文件失败", error);
).then((data) =>  //因为上面是return p2,所以这里data是p2对象resolve的值,也就是"爱"
	str1 += data; //"我爱"
	return p3;
).then((data) => 
	str1 += data; //"我爱nodejs"
	console.log(str1);
)
//函数版,把上面的创建Promise对象部分封装成函数
const fs = require("fs");
const path = require("path");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");

function readFilePromise(filePath) 
	return new Promise((resolve, reject) => 
	
        fs.readFile(filePath, "utf-8", (error, data) => 
            if(error) 
                reject(error);
            
            resolve(data);
        )
        
    );

let p1 = readFilePromise(filePath1);
let p2 = readFilePromise(filePath2);
let p3 = readFilePromise(filePath3);

let str1 = "";

p1.then((data) => 
	str1 += data; //"我"
	return p2;
, (error) => 
	console.log("读取文件失败", error);
).then((data) =>  //因为上面是return p2,所以这里data是p2对象resolve的值,也就是"爱"
	str1 += data; //"我爱"
	return p3;
).then((data) => 
	str1 += data; //"我爱nodejs"
	console.log(str1);
)
//util版--util是实用工具的意思
//nodejs里自带的模块,传入一个遵循常见的错误有限的回调函数(即以(error, value) => ...回调作为最后一个参数),并返回一个Promise对象。
const fs = require("fs");
const path = require("path");
const util = require("util");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");
let filePath4 = path.join(__dirname, "files", "data.txt");

let readFilePromise = util.promisify(fs.readFile); //返回一个Promise对象
//等同于下面这个函数,其内部已经封装为函数的这个样子了,下面可以直接正常使用
let writeFilePromise = util.promisify(fs.writeFile); //增加一个写入
//function readFilePromise(filePath) 
//	return new Promise((resolve, reject) => 
//	
//        fs.readFile(filePath, "utf-8", (error, data) => 
//            if(error) 
//                reject(error);
//            
//            resolve(data);
//        )
//        
//    );
//

let p1 = readFilePromise(filePath1);
//let p1 = readFilePromise(filePath1, "utf-8"); //增加"utf-8"
let p2 = readFilePromise(filePath2);
let p3 = readFilePromise(filePath3);

let str1 = "";

p1.then((data) => 
	str1 += data; //"我"
	return p2;
, (error) => 
	console.log("读取文件失败", error);
).then((data) =>  //因为上面是return p2,所以这里data是p2对象resolve的值,也就是"爱"
	str1 += data; //"我爱"
	return p3;
).then((data) => 
	str1 += data; //"我爱nodejs"
	//console.log(str1);
	writeFilePromise(filePath4, str1);
)
//Promise.all版

const fs = require("fs");
const path = require("path");
const util = require("util");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");
let filePath4 = path.join(__dirname, "files", "data.txt");

let readFilePromise = util.promisify(fs.readFile);
let writeFilePromise = util.promisify(fs.writeFile);

let p1 = readFilePromise(filePath1, "utf-8");
let p2 = readFilePromise(filePath2, "utf-8");
let p3 = readFilePromise(filePath3, "utf-8");

Promise.all([p1, p2, p3]).then((data) => 
	//data是一个数组
	//console.log(data.join(""));
	writeFilePromise(filePath4, data.join(""));
).catch((error) => 
	console.log(error);
)
//async + await版本
const fs = require("fs");
const path = require("path");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");

function readFilePromise(filePath) 
	return new Promise((resolve, reject) => 
	
        fs.readFile(filePath, "utf-8", (error, data) => 
            if(error) 
                reject(error);
            
            resolve(data);
        )
        
    );


//await必须放在async函数内部,async函数和之前的函数的使用方式是一致的
//await后面放promise对象,
async function func() 
	let data1 = await readFilePromise(filePath1); //得到成功之后的数据(resolve的实参)
	let data2 = await readFilePromise(filePath2);
	let data3 = await readFilePromise(filePath3);	
	console.log(data1 + data2 + data3);

func();
//util + async + await版本(异步代码外观上同步化的最终版)
const fs = require("fs");
const path = require("path");
const util = require("util");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");
let filePath4 = path.join(__dirname, "files", "data.txt");

let readFilePromise = util.promisify(fs.readFile);
let writeFilePromise = util.promisify(fs.writeFile);

async function func() 
	let data1 = await readFilePromise(filePath1);
	let data2 = await readFilePromise(filePath2);
	let data3 = await readFilePromise(filePath3);	
	writeFilePrimise(filePath4, data1 + data2 + data3);


func();

async和await注意事项

1.如果await后面只写一个基本数据类型,会将其包装为Promise对象

async function func() 
	let data1 = await 123; //异步的
	//data1相当于new Promise((resolve, reject) => resolve(123));
	
	console.log("data1": data1); //data1: 123,后执行(这句放在了微任务队列中),相当于包装的Promise对象resolve返回了123
	
	return data1;

let ret = func();
console.log(ret); //Promise <pending>,先执行

2.如果await后面是一个Promise对象,会把resolve的值返回

3.async函数里面的await是异步的,不是同步

const fs = require("fs");
const path = require("path");
const util = require("util");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");

let readFilePromise = util.promisify(fs.readFile);

async function func() 
	console.log(3);
	let data1 = await readFilePromise(filePath1, "utf-8"); //遇到await下面的代码就都停了,跳出函数
	console.log(4); //放在微任务队列,不明白见附
	let data2 = await readFilePromise(filePath2, "utf-8");
	let data3 = await readFilePromise(filePath3, "utf-8");
	
	console.log(data1 + data2 + data3);

console.log(1);
func();
console.log(2);
//1 3 2 4 我爱nodejs

4.错误处理–外部捕获异常

const fs = require("fs");
const path = require("path");
const util = require("util");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");

let readFilePromise = util.promisify(fs.readFile);

async function func() 
	let data1 = await readFilePromise(filePath1, "utf-8");
	let data2 = await readFilePromise(filePath2, "utf-8");
	let data3 = await readFilePromise(filePath3, "utf-8");
	
	console.log(data1 + data2 + data3);


func().catch(error => 
	console.log(error);
).finally(() => 
	console.log("finally---");
);

5.错误处理–内部捕获异常

const fs = require("fs");
const path = require("path");
const util = require("util");

let filePath1 = path.join(__dirname, "files", "1.txt");
let filePath2 = path.join(__dirname, "files", "2.txt");
let filePath3 = path.join(__dirname, "files", "3.txt");

let readFilePromise = util.promisify(fs.readFile);

async function func() 
	try 
		let data1 = await readFilePromise(filePath1, "utf-8");
		let data2 = await readFilePromise(filePath2, "utf-8");
		let data3 = await readFilePromise(filePath3, "utf-8");
		console.log(data1 + data2 + data3);
	 catch(error) 
		console.log(error);
	 finally 
		console.log("finally---");
	
	


func();

微任务:process.nextTick、MutationObserver、Promise.then catch finally

**宏任务:**I/O、setTimeout、setInterval、setImmediate、requestAnimationFrame

//主线程直接执行
console.log('1');
//丢到宏事件队列中
setTimeout(function() 
    console.log('2');
    process.nextTick(function() 
        console.log('3');
    )
    new Promise(function(resolve) 
        console.log('4');
        resolve();
    ).then(function() 
        console.log('5')
    )
)
//微事件1
process.nextTick(function() 
    console.log('6');
)
//主线程直接执行
new Promise(function(resolve) 
    console.log('7');
    resolve();
).then(function() 
    //微事件2
    console.log('8')
)
//丢到宏事件队列中
setTimeout(function() 
    console.log('9');
    process.nextTick(function() 
        console.log('10');
    )
    new Promise(function(resolve) 
        console.log('11');
        resolve();
    ).then(function() 
        console.log('12')
    )
)
//1 7 6 8 2 4 3 5 9 11 10 12


async function async1() 
  console.log('async1 start')
  await async2()
  console.log('async1 end')

 
async function async2() 
  console.log('async2')

 
console.log('script start')
setTimeout(function() 
  console.log('setTimeout')
, 0)
 
async1(); 
   
new Promise( function( resolve ) 
 console.log('promise1')
 resolve();
 ).then( function() 
 console.log('promise2')
 )
 
console.log('script end')

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

首先,事件循环从宏任务(macrostack)队列开始,这个时候,宏任务队列中,只有一个 script (整体代码)任务。从宏任务队列中取出一个任务来执行。
首先执行 console.log('script start'),输出 ‘script start'
遇到 setTimeout 把 console.log('setTimeout') 放到 macrotask 队列中
执行 aync1() 输出 ‘async1 start' 和 'async2' ,把 console.log('async1 end') 放到 micro 队列中
执行到 promise ,输出 'promise1' ,把 console.log('promise2') 放到  micro 队列中
执行 console.log('script end'),输出 ‘script end'
macrotask 执行完成会执行 microtask ,把 microtask quene 里面的 microtask 全部拿出来一次性执行完,所以会输出 'async1 end' 和 ‘promise2'
开始新一轮的事件循环,去除执行一个 macrotask 执行,所以会输出 ‘setTimeout'

5http模块

ip地址就有唯一性,用来标识不同的设备,例如张三的电脑,相当于快递时每一栋楼的地址,0~255

端口号,标识同一台设备上的不同的网络进程(可联网的运行起来的程序),基于同一台设备来说也有唯一性,相对于例如张三电脑里的微信,相当于门牌号,065535,01024这些知名端口不会用(例如电话号110有确定作用)

服务器,也叫远程主机,就是配置相当高的电脑,但是没有显示器 、键盘

www.baidu.com这个叫域名,由dns服务器把域名解析为ip地址(其实会现在本地的host文件里面找,找不到再去交换机去找,找不到去运营商服务器去找,还找不到最终在域名解析服务器dns上找),客户端这边发送请求报文(我要xxx文件或数据),服务器websever里有服务器代码,解析请求报文、分析请求意图(你要的是什么数据,是不是黑客入侵之类的,在请求报文中请求头中有User-Agent属性,是客户端告诉服务端是哪个浏览器版本,从而服务端知道来源是安全的,爬虫就是伪装成正常的浏览器到服务器上爬取数据),这部分是后端程序员写的。给文件叫响应报文(服务器给浏览器发送文件或数据)。HTTP协议就规定报文应该怎么写。java php python nodejs(http模块)都可以写后端程序

请求方式get(例如打开www.baidu.com?curpage=1&perpage=0网址就是get,get请求可以携带查询参数) post(例如登录,需要提交数据,是post)

浏览器访问页面的基本流程:

用户输入网址;

浏览器请求DNS服务器获取域名对应的IP地址;

请求连接该IP地址服务器;

发送资源请求报文(遵循HTTP协议);

web服务器接收到请求并解析请求,判断用户意图;

获取用户想要的资源(链接数据库从中获取数据);

将资源返回给http服务器程序;

http服务器程序将资源数据通过网络发送给浏览器(响应给浏览器),发送响应报文;

浏览器解析呈现请求的数据。

域名与域名服务器:

1.网站/域名:www.sina.com.cn

www:主机名,ftp、svn、stmp、xmpp服务

sina:机构名

com:机构类型

cn:国家名 tw hk us uk jp

2.域名服务器(DNS):存放域名与IP对应的服务器

HTTP协议(超文本传输协议HyperText Transfer Protocol):

通俗理解为浏览器和web服务器传输数据格式的协议;

HTTP是一个应用层协议;

HTTP协议是基于tcp/ip协议的,发送数据之前需要建立好连接;

(TCP协议是可靠的传输协议,多为一对一,需要确保传输双方都能正常通信;还有不可靠的传输协议UDP,多用于一对多,不需要知道是否能接收的情况,例如直播)

http协议的请求响应模型包含浏览器与http服务器的request、response请求响应模型

请求报文

请求行:格式(请求方式/请求路径 http/版本)

请求头:当时post提交的时候,其中有个属性为Content-Type: application/x-www-form-urlencoded

空行:分隔请求体的作用,代码中表现"\\r\\n"

请求体:post请求才有请求体,浏览器给服务端发送的数据,例如提交的用户名和密码;get请求只有请求行请求头和空行

响应报文

响应行:格式(http/版本 状态码 对状态的描述)

响应头:

空行:

响应体:页面内容

TCP/IP模型 三次握手四次挥手

http协议是基于TCP/IP协议(协议族)的,TCP/IP协议是一个可靠的传输协议(在客户端发送的时候会产生很多有标记的包,一旦服务端没有全部接收到,就是发生了丢包现象,服务端就不响应,过一段时间,客户端看到服务端没有响应,就知道没发成功,就继续再发送一次,所以这是可靠的。而UDP只管发送,不在乎你是没接收到,所以是不可靠的连接)。

在建立连接的过程中,发生了3次握手(客户端=》服务器,服务器知道客户端可以发送了,服务器=》客户端,客户端知道服务器可以接收并发送了,客户端=》服务器,服务器知道客户端可以接收到了),保证双方都知道对方能够正常发送和接收数据

断开连接的时候发生**四次挥手:**客户端=》服务器,请求断开连接,服务器=》客户端,表示接收到断开连接的请求,服务器=》客户端,同意断开,客户端=》服务器,确认断开连接

第二三步不能合并在一起,因为此时服务器哪怕接收到断开请求但是还有任务正在处理中,还不能立刻同意断开

OSI七层模型(Open System Interconnection)

简单来说,就是两台计算机之间通讯的时候经历了哪些工作

OSI七层网络模型TCP/IP四层概念模型对应网络协议
应用层(Application):编码,比如“你好”应用层:开发的时候就是处于应用层开发,通过语法编码来实现,开发的时候最多接触到应用层HTTP、TFTP、FTP、NFS、WAIS、SMTP
表示层(Presentation):数据的加密等操作应用层:同上Telnet、Rlogin、SNMP、Gopher
会话层(Session):在表示层基础上基础添加一些信息,比如是否登录、消息发出者和接收者等应用层:同上SMTP、DNS
传输层(Transport):(传输协议)建立连接,传输数据,关闭连接传输层TCP、UDP
网络层(Network):通过ip地址找到对应的那台计算机网络层IP、ICMP、ARP、RARP、AKP、UUCP
数据链路层(Data Link):将代码转换为计算机认识的二进制数据链路层FDDI、Ethernet、Arpanet、PDN、SLIP、PPP
物理层(Physical):网线、网口,需要寻尊一些规范数据链路层IEEE802.1A、IEEE 802.2到IEEE 802.11

//搭建第一个后端程序。写完代码之后在交互模式下执行一次,再在浏览器端输入localhost:8081

//这里的代码什么时候执行?每接收到一次请求就来执行一次这里的代码

//搭建第一个后端程序。写完代码之后在交互模式下执行一次,再在浏览器端输入localhost:8081
//1引入http模块
const http = require("http");

//2配置服务器程序的端口号 
const port = 8081; //一些典型端口,如mysql 3306 mongodb 27017

//3创建服务器对象
const server = http.createServer((request, Response) => 
    //request请求对象,response响应对象
    //执行代码
    //这里的代码什么时候执行?每接收到一次请求就来执行一次这里的代码
    
    Response.write("hello http"); //可以往浏览器书写一些响应体内容
    Response.write("hello nodejs"); 
    Response.end("hello nodejs http"); //网页中显示这个,表示响应工作已经结束,后面不要再协关于响应的操作了
    //write和end都可以传入参数表示往浏览器书写内容,不同点:write表示连续操作,end表示响应结束,一般放最后,end也可以不传参数
)

//4调用服务器对象的监听方法,让服务器监听浏览器的请求
server.listen(port, (error) => 
    //console.log(error);
    console.log(`WebServer is listening at port $port!`); //代码是否启动
)

获取请求信息

const http = require("http"); //获取请求路径;获取请求方式
const url = require("url"); //解析请求参数时用的
const port = 8081;
const server = http.createServer((request, Response) => 
    //想要获取请求对象相关的一些信息
    //******
    //获取请求路径
    let reqUrl = request.url;
    //请求路径(请求报文中第一行的第二个信息),可以在浏览器检查=》Network=》网址下的Headers里的Request下的view source的GET中看到
    
    //*******
    //获取请求方式
    
    let reqMethod = request.method;
 	console.log(reqUrl, reqMethod); //这里是服务端,所以在黑窗口看到
    //   /,这个是localhost:8081的请求路径,访问首页的请求路径就是一个斜杠/
    //   /favicon.ico,浏览器自发请求了这个东西
    //若是请求网址为localhost:8080/detail.html,则请求路径为/detail.html
    
    //******
    //获取get请求的请求参数,这些参数在url网址上
    //localhost:8081/list.html?curPage=1&perPage=10
    
    let obj = url.parse(reqUrl, true); //一大堆属性
    console.log(obj.query); //curPage: '1', perPage: '10',获取哪个参数可以用boj.query.curPage的方式获取,其实会请求两次,第二次还是请求favicon.ico
    
    //******
    //获取post请求的请求参数,这些参数是在请求报文的请求体里
    //以事件的方式来接收,一旦html文件的表单post提交就会触发该事件执行,事件名是data
    
    request.on("data", (postData) => 
    	//这里的代码一旦接收post请求,就会触发这里的代码执行
    	//postData就是接收到的请求参数,是一个BUffer对象,需要toString一下
    	console.log(postData.toString()); //username=laoli&password=1233
    )
    
    
    Response.end("hello nodejs http"); //在浏览器上显示
)

server.listen(port, (error) => 
    console.log(`WebServer is listening at port $port!`); 
)

nodemon(在项目文件夹下cmd安装)

nodemon是一种工具,通过在检测到目录中的文件更改时自动重新启动节点应用程序来帮助开发基于node.js的应用程序。

nodemon并没有要求任何对你的代码或开发的方法中的额外变化。nodemon是一个替换包装器node,用于在执行脚本时nodemon替换node命令行上。

//nodemon将全局安装到您的系统路径
npm install -g nodemon
//将nodemon安装为开发依赖项:
npm install --save-dev nodemon
//用法
nodemon app.js
//使用帮助
nodemon -h

搭建一个http服务器(一个服务器雏形,底层原理,之后使用框架实现)

const http = require("http");
const fs = require("fs");
const path = require("path");

const port = 8081;

const server = http.createServer((request, response) => 
	//每来一个请求就执行一次这里的代码
	//下面这句是要遵循http协议,设置响应头信息
	//在检查》Network》协议地址下的Response Headers中的Content-type可以看到
	//response.setHeader("Content-type", "text/html; charset=utf-8");
	//response.end("<h1>返回数据</h1>"); //如果想要在这里设置中文的话,需要加上上面这句,否则没遵循http协议,就会乱码
	
	//读取页面内容,返回信息,直接将上面返回的文字换为需要返回的页面就可以了
	
	let filePath = path.join(__dirname, "assets", "html", "index.html");
	let content = fs.readFileSync(filePath); //这里可以不用写utf-8,因为读取的index.html中的meta标签里设置了
	response.end(content); //上面需要进行http协议的句子response.setHeader也可以不用加了,因为index.html里面设置了
)

server.listen(port, (error) => 
	console.log(`WebServer is listening at port &port!`);
)
const http = require("http");
const fs = require("fs");
const path = require("path");

const port = 8081;

const server = http.createServer((request, response) => 
	//每来一个请求就执行一次这里的代码
	//判断浏览器需要哪个资源文件,把下面这些代码进行封装的话就是路由
	let reqUrl = request.url;
	if(reqUrl === '/' || reqUrl === '/index.html') 
		//读取页面内容,返回信息
		
		let filePath = path.join(__dirname, "assets", "html", "index.html");
		let content = fs.readFileSync(filePath);
		response.end(content);
	 else if(reqUrl === '/detail.html') 
		//读取页面内容,返回信息
		
		let filePath = path.join(__dirname, "assets", "html", "detail.html");
		let content = fs.readFileSync(filePath);
		response.end(content); //这句不能放在外面,因为使用let定义的content有块级作用域
	 else if(reqUrl.endsWith(".css")) 
		//如果读取的是一个css文件,也是一个请求,也会执行一次代码
		//哪些标签会造成请求?就有href(link)和src(src img)属性的标签,浏览器是自发的请求;还有像按钮、a标签的话需要在表单里而且需要点击过后才会请求
		//如果用a标签实现跳转的话,index.html中<a href="/detail.html">跳转到详情页</a>
        //这里的跳转地址不是看前端的路径,而是看后端的路由是怎么配的,和这里的if else里的判断内容一致,点击之后也是一次请求
        
		let filePath = path.join(__dirname, "assets", "css", "index.css");
		let content = fs.readFileSync(filePath);
		response.end(content);
	 else 
		response.setHeader("Content-type", "text/html; charset=utf-8"); //不加这句下面的语句会出现乱码
		response.end("404错误:该资源找不到!");
	
)

server.listen(port, (error) => 
	console.log(`WebServer is listening at port &port!`);
)

npm

npm全称Node Package Manager,是一个基于Node.js的第三方模块的包管理器

nodejs=ECMAScript+核心模块

去https://www.npmjs.com/package/md5上面搜索和下载包

npm不需要单独安装,在安装node的时候会连带一起安装了npm

npm init 
//初始化项目(在项目文件夹下cmd,然后输入这个命令),后续会有一些选择,直接默认就行,或者直接npm init -y所有都是默认
npm install [包名] 
//安装后会多一个node_modules文件夹,里面有[包]文件夹,另外的文件夹是[包]依赖的其他的包,也拉过来了
//一般项目共享前,先把node_modules文件夹删除,在别人给的它的项目打包里是没有的,那么启动项目缺少项目依赖包就会报错。在项目文件夹中执行npm install,后面不跟包名,则自动查找package.json文件中的dependencies字段所对应的值的这些包,进行下载安装。

切换npm镜像源

例如使用淘宝的npm镜像源下载jquery

npm install jquery --registry=https://registry.npm.taobao.org

但是每次手动往后面添加==registry=https://registry.npm.taobao.org和麻烦,所以可以通过修改配置文件的方式来处理解决

//配置到淘宝服务器
npm config set registry https://registry.npm.taobao.org
//查看registry是否配置正确
npm config get registry

只要经过上面命令的配置,以后所有的npm install都会使用你配置的registry下载。

cnpm

cnpm是淘宝自己基于npm开发的一个命令,目前不够完善,是为了完成所有的npm功能的一个中国版的npm

如何启动项目?

npm run start
//npm run是固定写法,startk看的是package.json文件中script字段下的启动项目那句命令的属性。实际上这句命令的本质就是在执行start所对应的这个命令
//三个注意点:1.所有引号必须是双引号;2.最后一项键值对后面不能有逗号;3.这个package.json文件中不能有注释

yarn

新的JS包管理工具,旨在取代npm这种包管理工具

npm install -g yarn 
//安装yarn(管理员模式运行cmd)
yarn init -y 
//初始化项目
yarn add [包名] 
//安装包

yarn全局安装后,命令不生效

解决办法:

1.执行下面命令得到yarn全局安装的命令的所处的安装目录

yarn global bin

2.复制安装目录至电脑的环境变量中系统变量的path中

假如发现还是不成功,是因为此时这个路径在最下面,可能上面的某个路径对它产生了影响,就将刚刚path中的路径一直上移到最上面,就可以

3.重新启动终端,全局命令就可以生效了

nodejs不支持ES6模块化规范的解决方案

NODE引擎默认下支持ES6的语法,但不支持ES6的模块化规范

执行node src下面的文件会报错,SyntaxError: Unexpected token …

可以把代码装换一下,把ES6规范转换为commonjs规范

学语法,兼容性如何不用管,可以交给第三方的转换工具(balel-cli和browserify)实现

解决方法:

1.在项目文件夹下生成package.json文件

yarn init -y 或者 npm init -y

2.安装第三方工具

在任意目录下执行,全局安装balel-cli和browserify

yarn global add babel-cli browserify 或者 npm install babel-cli browserify -g

在自己项目目录下执行:

yarn add babel-preset-es2015 或者 npm install babel-preset-es2015 --save-dev

3.在项目根目录下新建.babelrc文件


	"presets": [
	 	"es2015"
	]

4.在项目目录下书写完代码后执行:

babel src -d lib
//用babel工具把src目录下的文件都编译到lib文件夹下面去(没有lib文件夹需要自己创建一个)

(如果出现babel不适内部或者外部命令,就是yarn全局安装不成功,按照上面yarn全局安装后命令不生效的解决方法操作一下,其实babel.cmd这个就在yarn global bin得到的目录下。需要关闭黑窗口重新打开执行代码才能生效)

5.运行lib下的.js文件即可 node lib\\app.js

(注意:如果src下的文件修改代码了,需要执行babel src -d lib命令编译一下后,再运行lib下的app.js,每一次修改都要编译一下,后期直接使用脚手架工具,就可以直接写ES6了)

ES6模块化

//导出方式中这种方式
export let name = "nodejs";
export let age = 12;
//可以写为下面这样
let name = "nodejs";
let age = 12;
export name, age; //这是键值对相同的简写形式
//导入的方式依旧不变
import name, age from '路径';

//用export default导出多个
let name = "nodejs";
let age = 12;
export default name, age; 
//导入
import m1 from './m1';
console.log(m1.name);

以上是关于Node.js的主要内容,如果未能解决你的问题,请参考以下文章

nodejs安全吗

前端常见知识点汇总(ES6,Vue,axios,Node.js,npm,webpack)

Node.js的基本使用2

npmwebpackvue-cli快速上手版

typescript写给JS老鸟的TS速成教程

Node.js 入门:就是运行在服务端的 JavaScript