NodeJS入门学习教程

一、概念

1.什么是nodejs
  • Node.js是JavaScript 运行时环境,通俗易懂的讲,Node.js是JavaScript的运行平台
  • Node.js既不是语言,也不是框架,它是一个平台
2.nodejs特点
  • 没有Bom,Dom
  • 在Node中这个JavaScript执行环境为JavaScript提供了一些服务器级别的API
    • 例如文件的读写
    • 网络服务的构建
    • 网络通信
    • http服务器
  • 构建在Chrome的V8引擎之上,意味着nodejs的执行效率很高
  • 基于事件驱动envent-driven ,non-blocking I/O mode 非阻塞I/O模型(异步)ightweight and efficent. 轻量和高效
  • 包含NPM包管理器,npm 是世界上最大的开源生态系统,绝大多数JavaScript相关的包都存放在npm上,这样做的目的是为了让开发人员更方便的去下载使用
3.nodejs可以做什么
  • web服务器后台
  • 命令行工具,执行npm命令等
  • 借助npm包管理器构建项目,和上传自己的组件

二、安装和使用

到官网下载Node,并一路next就能完成安装,如果安装过后再次安装新版本则会自动升级安装。官网:https://nodejs.org/en/

确认是否安装成功,输入以下命令查看版本号:

node --version

配置目录

在nodejs安装目录创建【node_global】及【node_cache】两个文件夹

在这里插入图片描述

创建完两个空文件夹之后,打开cmd命令窗口,输入

npm config set prefix "D:\Develop\nodejs\node_global"
npm config set cache "D:\Develop\nodejs\node_cache"

接下来设置环境变量,关闭cmd窗口,“我的电脑”-右键-“属性”-“高级系统设置”-“高级”-“环境变量”

进入环境变量对话框,在【系统变量】下新建【NODE_PATH】,输入【D:\Develop\nodejs\node_global\node_modules】

在这里插入图片描述

将【用户变量】下的【Path】修改为【D:\Develop\nodejs\node_global】

在这里插入图片描述

三、核心API的使用

Global 全局对象

Global是node.js的全局对象,使用它里面的属性或方法或子对象,可以不用加global

例如

global.console.log('你好');

**另外:**这些对象在所有的模块中都可用。 以下的变量虽然看似全局的,但实际上不是。 它们仅存在于模块的作用域中

console.log('Dirname:',__dirname);
console.log('Filename:',__filename);
console.log('Exports:',exports);
console.log('Module:',module);
console.log('require:',require);

打印结果:

Dirname: F:\vue_project\learn_nodeJs\src\01-NodeJs初步使用
Filename: F:\vue_project\learn_nodeJs\src\01-NodeJs初步使用\5-全局变量.js
Exports: {}
Module: Module {
  id: '.',
  path: 'F:\\vue_project\\learn_nodeJs\\src\\01-NodeJs初步使用',
  exports: {},
  parent: null,
  filename: 'F:\\vue_project\\learn_nodeJs\\src\\01-NodeJs初步使用\\5-全局变量.js',
  loaded: false,
  children: [],
  paths: [
    'F:\\vue_project\\learn_nodeJs\\src\\01-NodeJs初步使用\\node_modules',
    'F:\\vue_project\\learn_nodeJs\\src\\node_modules',
    'F:\\vue_project\\learn_nodeJs\\node_modules',
    'F:\\vue_project\\node_modules',
    'F:\\node_modules'
  ]
}
require: [Function: require] {
  resolve: [Function: resolve] { paths: [Function: paths] },
  main: ...,//省略内容
  extensions: ...,//省略内容
  cache: ...,//省略内容
}
1.fs模块的使用

浏览器中的JavaScript是没有文件操作能力的,但是Node中的JavaScript具有文件操作能力,在Node中如果想要进行文件的操作就必须引用fs这个核心模块。fs模块提供了了所有文件操作相关的API

下面是一些基本的代码示例:

1.1文件读取
//  1.使用fs核心模块
var fs = require('fs');

/**
 * filename, 必选参数,文件名
 * [options],可选参数,可指定flag(文件操作选项,如r+ 读写;w+ 读写,文件不存在则创建)及encoding属性
 * callback 读取文件后的回调函数,参数默认第一个err,第二个data 数据
 */
fs.readFile('./data/a.txt',function(err,data){
   if(err){
        console.log('文件读取失败');
   }
    else{
         console.log(data.toString());
    }
})


// readFileSynsc();同步操作a也是就是说会等当前执行完后在,才会输出2,(阻塞)
let data = fs.readFileSync('./text.txt');
console.log(data.toString())
console.log(2);
1.2文件写入
//  1.使用fs核心模块
var fs = require('fs');

/**
 * filename, 必选参数,文件名
 * data, 写入的数据,可以字符或一个Buffer对象
 * [options],flag,mode(权限),encoding
 * callback 读取文件后的回调函数,参数默认第一个err,第二个data 数据
 * 文件不存在则会自动创建一个文件
 */
fs.writeFile('./data/a.txt','我是文件写入的信息',function(err){
   if(err){
        console.log('文件写入失败');
   }
})


// 同步版本的写入
fs.writeFileSync('./2.text','我是同步写入的文件')
1.3以追加方式写文件
let fs = require('fs');
/*
* 追加文件内容
* */
fs.appendFile('../data/a.txt', '\n使用fs.appendFile追加文件内容', function (error) {
    if(error){
        console.log('追加文件内容出错');
    }else {
        console.log('追加内容完成');
    }

});

// 同步方式
// fs.appendFileSync('../data/a.txt','追加的内容');
1.4创建文件目录
let fs = require('fs');

/**
 * fs.mkdir(path, [mode], callback);
 * path, 被创建目录的完整路径及目录名;
 * [mode], 目录权限,默认0777
 * [callback(err)], 创建完目录回调函数,err错误对象
 */
fs.mkdir('../data/test1',function (error) {
    if(error){
        console.log('创建文件夹失败');
    }else {
        console.log('创建文件夹成功');
    }
});

/*
* 同步方法
* */
fs.mkdirSync('../data/test2');
1.5读取文件目录
// 异步读取文件夹里面的所有文件
fs.readdir('./',(err,files)=>{
    if (err) {
        console.log(err);
    } else{
        // console.log(files); 返回的文件是个数组,可以用forEach循环输出文件名
        files.forEach((x)=>{
            console.log('有'+ x +'这个文件');
        })
    }
});
// 同步获取文件夹里面的所有文件
let files = fs.readdirSync('./public');
console.log(files); //返回的是一个数组
1.6删除文件
let fs = require('fs');

//异步操作删除1.text
fs.unlink('./1.text',(err)=>{
	if (err) {
		console.log(err);
	} else{
		console.log('删除文件成功');
	}
})

//同步操作删除2.text
fs.unlinkSync('./2.text');
1.7删除文件目录
let fs = require('fs');

// 异步删除文件夹
fs.rmdir('../data/test1',function (error) {
    if(error){
        console.log('删除文件夹失败');
    }else {
        console.log('删除文件夹成功');
    }

});

// 同步方法
fs.rmdirSync('../data/test2');
1.8判断文件信息
let fs = require('fs');

// 异步获取文件的具体信息
fs.stat('../data/a.txt',(err,info)=>{
    if (err) {
        console.log('获取文件信息失败:',err);
    } else{
        // info.isFile() 判断是不是一个文件 返回结果为true
        console.log(info.isFile());
        // false
        console.log(info.isDirectory());
    }
});
// 同步操作获得文件信息并判断是不是文件夹
let file = fs.statSync('./1.text');

if(file.isFile()){
	console.log('这是一个文件');
}else if(file.isDirectory()){
	console.log('只是一个文件夹');
}else{
	console.log('抱歉这不是一个文件或者文件夹');
}
1.9复制大文件
var fs = require('fs');
//创建读取流
var readStream = fs.createReadStream('./1.zip');
//创建写入流
var writeStream = fs.createWriteStream('./2.zip');
//进行大文件的复制
readStream.pipe(writeStream);
2.path模块的使用

path模块用来处理路径相关内容

2.1格式化路径
let path = require('path');
let url = path.normalize('d:\\\hd///arr/indexindex.php');

// d:\hd\arr\index\index.php
console.log(url);
2.2组合多个路径
let path = require('path');
let url = path.join('d:/www','/index','/banner/index.php');
// d:\www\index\banner\index.php
console.log(url);
2.3判断路径是否为绝对路径
const path = require('path');

let url = path.isAbsolute('c:/www/baidu/public/index');//绝对路径(true)
let url_ = path.isAbsolute('www/baidu/public/index');//相对路径(false)

// true ,false
console.log(url,url_);
2.4解析并组合绝对路径
let path = require('path');

//从后往前组合,组合成第一个绝对路径就停止
//若直到要第一个参数都组合不出来绝对路径,那么就会连接上当前脚本所在结对路径,组合成一个完整的绝对路径
let url = path.resolve('c:/www','b:/res','index.php');
let url_ = path.resolve('../data/','a.txt');

// b:\res\index.php
console.log(url);
// F:\vue_project\learn_nodeJs\src\data\a.txt
console.log(url_);
2.5最后一个文件或者文件夹所在的路径
let path = require('path');
//dirname() 返回的是路径最后一个文件或者文件夹的所在路径
let url = path.dirname('F:\\vue_project\\learn_nodeJs\\src\\data\\a.txt');
// F:\vue_project\learn_nodeJs\src\data
console.log(url);
2.6最后的文件或目录名
let path = require('path');
// 返回最后一个文件名或者文件夹名
let url = path.basename('F:\\vue_project\\learn_nodeJs\\src\\data\\a.txt');
console.log(url);
2.7获取文件扩展名
let path = require('path');
// 返回最后一个文件名或者文件夹名
let url = path.extname('../data/a.txt');
console.log(url);
2.8获取当前分隔符
let path = require('path');
let separator = path.sep;
console.log(separator);
2.9删除指定文件目录下的文件和目录
let fs = require('fs');
let path = require('path');

let del  = (url)=>{
	//获得所有文件
	let arr = fs.readdirSync(url);
	//循环所有文件
	arr.forEach((x)=>{
		//组合文件路径
		let fileurl = path.resolve(url,x);
		//获得文件的详细信息
		let xinxi =  fs.statSync(fileurl);
		// 判断
		if(xinxi.isFile()){
			fs.unlinkSync(fileurl);//是文件删除
		}else if(xinxi.isDirectory()){
			del(fileurl);//是文件夹递归调用
		}
	})
	//删除文件夹(如果文件夹里面有文件删除不了)
	fs.rmdirSync(url);
}
del(path.resolve(__dirname,'public.1'));
2.10获取指定目录下的文件信息
let fs = require('fs');
let path = require('path');

let del  = (url,y=0)=>{
	
	let h = '';
	for (var i = 0; i < y; i++) {
		h += '---'
	}
	//打印目录二级以后的目录
	console.log(h + url);
	
	//获得所有文件
	let arr = fs.readdirSync(url);
	//循环所有文件
	arr.forEach((x)=>{
		//组合文件路径
		let fileurl = path.resolve(url,x);
		//获得文件的详细信息
		let info =  fs.statSync(fileurl);
		let f ='';
		for (let a=0;a<y+1;a++) {
			f += '---';
		}
		//输出的文件为再次加 ---- 个,(看上班for循环 d+1 )
		!info.isFile(fileurl) || console.log(f + fileurl);
		!info.isDirectory(fileurl) || del(fileurl,y+1);
	})

}
del(path.resolve(__dirname,'public.2'));
结果:
F:\前端学习\node\public.2
---F:\前端学习\node\public.2\06.js
---F:\前端学习\node\public.2\1.vue
---F:\前端学习\node\public.2\16.json
---F:\前端学习\node\public.2\16.vue
---F:\前端学习\node\public.2\ab
------F:\前端学习\node\public.2\ab\06.js
------F:\前端学习\node\public.2\ab\1.vue
------F:\前端学习\node\public.2\ab\16.json
------F:\前端学习\node\public.2\ab\16.vue
------F:\前端学习\node\public.2\ab\index.html
---F:\前端学习\node\public.2\index.html
3.Http模块的使用
3.1基本使用示例代码

示例一:基本的使用

// 1.引入网络相关模块
var http = require('http');

// 2.创建服务器
var server = http.createServer();

// 3.接受请求处理
server.on('request',function (request,response) {
    console.log('收到来自此IP的请求:',request.remoteAddress);
    console.log('请求URL:',request.url);
    response.setHeader('Content-Type','text/plain;charset=utf-8');
    response.write('已响应请求');
    response.end();
});

// 4.绑定并监听此端口号
server.listen(3000,function () {
    console.log('服务已启动');
});

示例二:根据路由返回不同内容


var http = require('http');
var fs = require('fs');

var server = http.createServer();

// 3.接受请求处理
server.on('request',function (request,response) {

    let url = request.url;
    console.log(url);

    // 返回一张图片
    if(url === '/image'){
        fs.readFile('../data/b.jpg',function (error,data) {
            if(error){
                console.log('读取文件失败');
            }else {
                console.log('已读取文件');
                response.setHeader('Content-Type','image/jpeg');
                response.end(data);
            }
        });
    }

});

// 4.绑定并监听此端口号
server.listen(3000,function () {
    console.log('服务已启动');
});

示例三:返回不同的Html模板

//引入http模块相当于php的apache (node.js不像php,http模块是node.js自带的)
let http = require('http');
let fs = require('fs');

//初始化server服务
var server = http.createServer();

//监听端口
server.listen(3000,()=>{
	console.log('---server服务启动,端口3000---');
})

//监听用户请求
//req : 客户端请求的相关信息和方法
//res : 客户端相应的一些方法
server.on('request',(req,res)=>{
	if(req.url == '/'){
		// 读取首页模版
		fs.readFile('./view/index.html',(err,data)=>{
			if(err){
				console.log(err);
			}else{
				res.end(data);
			}
		})
	}else if(req.url == '/list'){
		// 读取列表页模版
		fs.readFile('./view/list.html',(err,data)=>{
			if(err){
				console.log(err);
			}else{
				res.end(data);
			}
		})
	}else if(req.url == '/page'){
		// 读取内容页面模版
		fs.readFile('./view/page.html',(err,data)=>{
			if(err){
				console.log(err);
			}else{
				res.end(data);
			}
		})
	}else{
		res.end('<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>404页面没找到;</body></html>');
	}
})
3.2模块化的路由封装
var http = require('http');
var url = require('url');
var router = require('./module/router.js');

http.createServer(function(req, res) {
    if(req.url.indexOf('favicon') != -1){
        res.end();
        return false;
    }
    res.writeHead(200,{"Content-Type":"text/html;charset=utf-8"});
    var pathName=url.parse(req.url,true).pathname.replace('/','');
    // 封装的路由函数
    try {
        router[pathName](req,res);
    } catch(e) {
        router['home'](req,res);
    }
    res.end();
}).listen(8001);
//router.js
var ejs = require('ejs');
var url = require('url');

var router={
    home:function(req,res){
        res.end('首页');
    },
    login:function(req,res){
        ejs.renderFile('./views/login.html',{},function(err,data){
            res.end(data);
        })
    },
    dologin:function(req,res){
        var info=url.parse(req.url,true);
        info=JSON.parse(JSON.stringify(info));
        ejs.renderFile('./views/dologin.html',{query:info.query},function(err,data){
            res.end(data);
        })
    }
}

module.exports=router;
4.url模块的使用

引入并打印Url模块,看看都有哪些方法

cosnt url = require('url');
console.log(url);

下面是有关方法的截图

在这里插入图片描述

查阅文档发现,以下api其实官方已经弃用了[url=http://nodejs.cn/api/url.html]

在这里插入图片描述

但是在这里还是要说一说它的用法

4.1url.parse()方法
cosnt url = require('url');

/*
* url.parse方法:
* urlStr:要解析成对象的url字符串
* parseQueryString:是否解析查询参数,默认为false
* slashesDenoteHost:是否以斜线解析主机名,默认为false
* */
let urlWithStringQuery = url.parse('http://localhost:8080/test?name=xiaoming&age=20');
console.log(urlWithStringQuery);

解析后的内容是个Url对象,可以通过其中的属性访问解析的值

Url {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'localhost:8080',
  port: '8080',
  hostname: 'localhost',
  hash: null,
  search: '?name=xiaoming&age=20',
  // 如果方法的第二个参数为true,这里会解析成object
  // query: [Object: null prototype] { name: 'xiaoming', age: '20' },    
  query: 'name=xiaoming&age=20',
  pathname: '/test',
  path: '/test?name=xiaoming&age=20',
  href: 'http://localhost:8080/test?name=xiaoming&age=20'
}
4.2url.format()方法

format就是parse的返过程,把对象转换成url字符串

const url = require('url');

let urlObj =  {
    protocol: 'http:',
    slashes: true,
    auth: null,
    host: 'localhost:8080',
    port: '8080',
    hostname: 'localhost',
    hash: null,
    search: '?name=xiaoming&age=20',
    query: 'name=xiaoming&age=20',
    pathname: '/test',
    path: '/test?name=xiaoming&age=20',
    href: 'http://localhost:8080/test?name=xiaoming&age=20'
};

let parseUrl = url.format(urlObj);

// 结果:http://localhost:8080/test?name=xiaoming&age=20
console.log(parseUrl);
4.3url.resolve()方法

用于解析Url路径,有以下用法

const url = require('url');
url.resolve('/one/two/three', 'four');         // '/one/two/four'
url.resolve('http://example.com/', '/one');    // 'http://example.com/one'
url.resolve('http://example.com/one', '/two'); // 'http://example.com/two'
4.4url.search方法

这个方法用于获取网址后面的查询参数,或者给网址后面添加查询参数

// 获取查询参数
const myURL = new URL('http://localhost:8080/test?name=xiaoming&age=20');
// 打印结果:
// ?name=xiaoming&age=20
console.log(myURL.search);
// 设置查询参数
const my_url = new URL('http://localhost:8080/test');
my_url.search = 'name=xiaoming&age=20';
// 结果:http://localhost:8080/test?name=xiaoming&age=20
console.log(my_url.href);
4.5 url.searchParams()方法

URLSearchParams接口和 querystring模块有相似的目的,但是 querystring 模块的目的更加通用,因为它可以定制分隔符(=)。 但另一方面,这个 API 是专门为 URL 查询字符串而设计的

这个方法来源于URLSearchParams类,但是可以通过url对象获取到,下面是这个方法的使用示例

const myURL = new URL('http://localhost:8080/test?name=xiaoming&age=20');
console.log(myURL.searchParams.get('name'));
console.log(myURL.searchParams.get('age'));

四、模块化与Node中的模块系统

1.什么是模块化
  • 模块化是一种bai将系统分离成du独立功能部zhi分的方法,可将dao系统分割成独立的功能部分zhuan,严格定义shu模块接口、模块间具有透明性。
  • 简单来说只要遵循一定的模块标准约束,就可以形成模块化,常见的js模块化标准有AMD和CommonJS
  • 模块化可以使得文件形成文件作用域,不同文件可以引入其他模块,各个模块解耦合,减少依赖。
2.commonJS规范
2.1有关概念

Node程序由许多个模块组成,每个模块就是一个文件。Node模块采用了CommonJS规范。

  • 根据CommonJS规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的

  • CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

Node内部提供一个Module构建函数。所有模块都是Module的实例。每个模块内部,都有一个module对象,代表当前模块。它有以下属性。

属性说明
id 模块的识别符,通常是带有绝对路径的模块文件名
filename 模块的文件名,带有绝对路径
loaded 返回一个布尔值,表示模块是否已经完成加载
parent 返回一个对象,表示调用该模块的模块
children 返回一个数组,表示该模块要用到的其他模块
exports 表示模块对外输出的值

下面是一个示例文件,最后一行输出module变量。

// example.js
var jquery = require('jquery');
exports.$ = jquery;
console.log(module);
{ id: '.',
  exports: { '$': [Function] },
  parent: null,
  filename: '/path/to/example.js',
  loaded: false,
  children:
   [ { id: '/path/to/node_modules/jquery/dist/jquery.js',
       exports: [Function],
       parent: [Circular],
       filename: '/path/to/node_modules/jquery/dist/jquery.js',
       loaded: true,
       children: [],
       paths: [Object] } ],
  paths:
   [ '/home/user/deleted/node_modules',
     '/home/user/node_modules',
     '/home/node_modules',
     '/node_modules' ]
}
2.2导出成员变量
  • Node中是模块作用域,默认文件中所有的成员只在当前模块有效,对于希望可以被其他模块访问到的成员,我们需要把这些公开的成员都挂载到exports接口对象中就可以了

  • node为每一个模块提供了一个exports变量(可以说是一个对象),指向 module.exports。这相当于每个模块中都有一句这样的命令 var exports = module.exports;

这样,在对外输出时,可以在这个变量上添加方法。例如 exports.add = function ®{return Math.PI * r *r};注意:不能把exports直接指向一个值,这样就相当于切断了 exports 和module.exports 的关系。例如 exports=function(x){console.log(x)};

一个模块的对外接口,就是一个单一的值,不能使用exports输出,必须使用 module.exports输出。module.exports=function(x){console.log(x);};

总结来说:exports可以导出多个值,而modulex.exports只能导出单一的值。根据阮一峰的教程来说,两个不好区分,那就放弃 exports,只用 module.exports 就好。

例子:

使用exports导出多个值

exports.a = 123;
exports.b = function(){
    console.log('bbb')
};
exports.c = {
    foo:"bar"
};
exports.d = 'hello';

使用module.exports导出单一值

module.exports = 'hello';
// 只能导出单一值,后者会覆盖前者
module.exports = function add(x,y) {
    return x+y;
}

不过,也有办法像exports一样导出多个值,那就是导出一个对象

// 也可以通过以下方法来导出多个成员
module.exports = {
    foo = 'hello',
    add:function(){
        return x+y;
    }
};
2.3加载导出的模块

使用require关键字即可

var example = require('./example.js');

模块加载规则:

根据模块的不同,会优先加载不同类型的模块

  • 如果模块已经存在缓存中,则会优先加载缓存中的模块
  • 如果是Node.js中的核心模块,则会先去加载核心模块
  • 如果是第三方库模块(node_modules),则会去加载第三方库模块
  • 最后才会加载我们手写的模块,一般这个是否都是路径形式的模块了

根据参数的不同格式,require命令去不同路径寻找模块文件。

  • 如果参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。比如,require(’/home/marco/foo.js’)将加载/home/marco/foo.js
  • 如果参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。比如,require(’./circle’)将加载当前脚本同一目录的circle.js
  • 如果参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)
  • 如果参数字符串不以“./“或”/“开头,而且是一个路径,比如require(‘example-module/path/to/file’),则将先找到example-module的位置,然后再以它为参数,找到后续路径
  • 如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析

模块的缓存:

  • 第一次加载该模块,node会缓存该模块。再次加载,直接从缓存中取出该模块的module.exports属性。

  • 删除模块的缓存 缓存保存在require.cache中,可操作该属性进行删除

    // 删除指定模块的缓存  
    delete require.cache[moduleName];
    
    // 删除所有模块的缓存
    Object.keys(require.cache).forEach(function(key){
        delete require.cache[key];
    })
    

五、Node的其他配置

1.NPM

npm全称就是node package manage(node包管理器),用于下载各种第三方模块资源,例如通过npm命令安装jQuery包(npm install --save jquery),在安装时加上–save会主动生成说明书文件信息(将安装文件的信息添加到package.json里面)

NPM命令行工具:

  • npm是一个命令行工具,只要安装了node就已经安装了npm。

  • npm也有版本概念,可以通过npm --version来查看npm的版本

升级NPM(我升我自己)

npm install --global npm

常见的NPM命令

命令说明
npm init 生成package.json描述说明文件
npm install 一次性把dependencies选项中的依赖项全部安装
npm install [packageName] 下载依赖包
npm install --save [packageName] 下载并且保存依赖项(package.json文件中的dependencies选项)
npm uninstall [packageName] 只删除,如果有依赖项会依然保存
npm uninstall --save [packageName] 删除的同时也会把依赖信息全部删除
npm help 查看使用帮助
npm cache clear 可以清空NPM本地缓存
npm ls 查看已安装的模块,-g 查看全局已安装的模块
npm config list 查看配置
2.package.json描述文件

每一个项目都要有一个package.json文件(包描述文件,就像产品的说明书一样)

这个文件可以通过npm init自动初始化出来

{
  "name": "cls",
  "version": "1.0.0",
  "description": "这是一个测试项目",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "xiaochen",
  "license": "ISC"
}

对于目前来讲,最有用的是dependencies选项,可以用来帮助我们保存第三方包的依赖信息。

如果node_modules删除了也不用担心,只需要在控制面板中npm install就会自动把package.json中的dependencies中所有的依赖项全部都下载回来。

  • 建议每个项目的根目录下都有一个package.json文件
  • 建议执行npm install 包名的时候都加上--save选项,目的是用来保存依赖信息(新版本无需加–save参数也会自动保存了)

除此,还有一个package-lock.json这个文件,出现于Npm5之后。当你安装包的时候,npm都会生成或者更新package-lock.json这个文件

  • npm5以后的版本安装都不要加--save参数,它会自动保存依赖信息

  • 你安装包的时候,会自动创建或者更新package-lock.json文件

  • package-lock.json这个文件会包含node_modules中所有包的信息(版本,下载地址…),这样的话重新npm install的时候速度就可以提升

  • package-lock.json的另外一个作用就是锁定版本号,防止自动升级

3.__dirname和__filename成员变量

在Node模块中,除了require,exports等模块成员变量之外,还有两个特殊的成员:

  • __dirname,是一个成员,可以用来动态获取当前文件模块所属目录的绝对路径
  • __filename,可以用来动态获取当前文件的绝对路径(包含文件名)
  • __dirnamefilename是不受执行node命令所属路径影响的

在文件操作中,使用相对路径是不可靠的,因为node中文件操作的路径被设计为相对于执行node命令所处的路径。所以为了解决这个问题,只需要把相对路径变为绝对路径(绝对路径不受任何影响)就可以了。就可以使用__dirname或者__filename来帮助我们解决这个问题

打印这两个变量的值

console.log('dirname:',__dirname);
console.log('filename:',__filename);

// 输出结果
// dirname: F:\vue_project\learn_nodeJs\src\04-path模块的使用
// filename: F:\vue_project\learn_nodeJs\src\04-path模块的使用\09-filename和dirname成员变量.js

六、Express框架的使用

概念:

Express 是一个方便开发者的 web 框架,可以让开发者可以方便地处理路由,Cookie, 静态文件,上传文件, RESTFULL风格等等常见操作。

1.安装
npm install express --save
2.第一个实例入门
// 1.引入依赖
const express = require('express');

// 2.创建实例
const app = express();

// 3.监听get请求
app.get('/',(req,res)=>{
    res.send('第一个入门实例');
});

app.get('/getData',(req,res)=>{
    let obj = {
        name: '小明',
        age: '20'
    };
    // 这里返回的是格式数据
    // send方法会自动设置返回数据类型,根据传入的数据类型不同
    res.send(obj);
});

// 4.监听端口
app.listen(3000);
console.log('启动完成');
3.中间件(middleware)的使用

中间件(middleware)就是一个方法,这个方法可以用来处理http请求,但是一般情况下,中间件方法需要携带next参数。

它最大的特点就是,一个中间件处理完,再传递给下一个中间件,也就是说可以在下一次请求中访问上一次的数据

3.1使用中间件的例子:

每个中间件可以从App实例,接收三个参数,依次为request对象(代表HTTP请求)、response对象(代表HTTP回应),next回调函数(代表下一个中间件)。每个中间件都可以对HTTP请求(request对象)进行加工,并且决定是否调用next方法,将request对象再传给下一个中间件。

// use方法的使用类似all方法,都是能够接收任何请求,例如:get、post、put/delete…
// path表示请求的路径
// callback为回调函数,接收三个参数requset,response,next
// next回调函数必须被声明执行才能到达下一个中间件
app.use(path,callback);

下面是一个简单的示例:

const express = require('express');

const app = express();

// 默认路径是 "/"
app.use((req,res,next) => {
    console.log('请求经过了中间件');
    next();
});


app.get('/',(req,res,next) => {
    console.log('访问了路径/');
    res.send('访问了路径/');
});

app.get('/test',(req,res,next) => {
    console.log('访问了路径/test');
    res.send('访问了路径/test');
});

最后打印的结果是

服务启动完成...
请求经过了中间件
访问了路径/
请求经过了中间件
访问了路径/test

所以我们可以得出结论,无论是访问任何一个路径,都经过了没有声明访问路径中间件。

下面再看另外一个例子:

const express = require('express');

const app = express();

// 默认路径是 "/"
app.use((req,res,next) => {
    console.log('请求经过了中间件');
    next();
});

app.use('/request',(req,res,next) => {
    console.log('请求经过了request中间件');
    next();
});

app.use('/other',(req,res,next) => {
    console.log('请求经过了other中间件');
    next();
});

app.get('/',(req,res,next) => {
    console.log('访问了路径/');
    res.send('访问了路径/');
});

app.get('/test',(req,res,next) => {
    console.log('访问了路径/test');
    res.send('访问了路径/test');
});

app.get('/request',(req,res,next) => {
    console.log('访问了路径/request');
    res.send('访问了路径/request');
});

当浏览器地址输入http://localhost:3001/时,得到结果

请求经过了中间件
访问了路径/

当浏览器地址输入http://localhost:3001/test时,得到结果

请求经过了中间件
访问了路径/test

当浏览器地址输入http://localhost:3001/request时,得到结果

请求经过了中间件
请求经过了request中间件
访问了路径/request

由此我们又可以得出一个结论,当我们去访问某个路径时,会走没有声明path变量的中间件,也就是默认中间件,还会走声明了对应路径的中间件。

3.2利用中间件传递数据

下面是使用的一个例子

const express = require('express');

const app = express();

app.use('/getData',(req,res,next) => {
    req.student = { name:'xiaoming',age:20 };
    // 必须调用此方法以继续传递到下一个中间件 
    next();
});

app.get('/getData',(req,res) => {
    console.log(req.student);
   res.send(req.student);
});

app.listen(3000,function () {
    console.log('服务已启动');
});
3.3一个简单的应用场景
//引入express框架
const express = require('express');
//创建网站服务器
const app = express();
//网站公告  在函数中没有使用next 所以代码走到这儿就不会往下走了  这就是网站维护时  公告的使用
app.use((req, res, next) => {
    res.send('<h1>当前网站正在维护...</h1>')
});
//查询登录状态
//如果登录了的话  就使用next继续往下执行  否则输出信息  不继续往下执行
app.use('/admin', (req, res, next) => {
    // 业务操作...
    // 设置登录状态
    let isLogin = true;
    //如果用户登录  让请求继续向下执行
    if (isLogin) {
        next();
    } else {
        //如果用户没有登录  直接对客户端做出响应
        res.send('您还没有登录  不能访问/admin这个页面')
    }
})
app.get('/admin', (req, res) => {
    res.send('您已经登录  可以访问当前页面')
})
//自定义404页面
app.use((req, res, next) => {
    //为客户端响应404状态码已经提示信息
    res.status(404).send('当前访问的页面不存在404')

})
app.listen(3000);
console.log("网站服务器启动成功");
3.3错误处理

利用状态码进行错误处理

const express = require('express');
const fs = require('fs');

const app = express();

app.get('/readFile',(req,res,next) => {
    fs.readFile('../data/xx.txt',((err, data) => {
        if(err){
            next(err);
        }else {
            res.send(data.toString(),);
        }
    }));
});

app.use(function (error,req,res,next) {
    console.log(error);
    res.status(500).send(error.message);
});

app.listen(3000,function () {
    console.log('服务已启动');
});

// 给个不存在的路径,结果就会输出:no such file or directory

或者利用try catch处理

const express = require('express');
// 不引入这个模块就会报错
// const fs = require('fs');

const app = express();



app.get('/readFile',(req,res,next) => {
    try {
        fs.readFile('../data/xx.txt', ((err, data) => {
            if (err) {
                next(err);
            } else {
                res.send(data.toString(),);
            }
        }));
    } catch (e) {
        // 传递这个错误对象
        next(e);
    }
});

app.use(function (error,req,res,next) {
    console.log(error);
    res.status(500).send(error.message);
});

app.listen(3000,function () {
    console.log('服务已启动');
});

// 最后输出: fs is not defined
3.4获取get参数
const express = require('express');

const app = express();

app.get('/getStudentById',(req,res) => {
    // 获取到的结果是个对象类型:{ id: '1' }
    console.log(req.query);
    if(req?.query?.id){
        if(req.query.id === '20'){
            res.send({name:'小明',age:18,id:20})
        }else {
            res.send('未找到对应学生');
        }
    }else {
        res.send("<h3>缺乏必要参数!</h3>");
    }
});

app.listen(3000,function () {
    console.log('服务已启动');
});
3.5获取post参数

先安装body-parser模块,用于解析参数

npm install --save body-parser

代码示例,用PostMan发送x-www-form-urlencoded格式数据即可

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

// 利用中间件拦截请求
// extended:true使用第三方模块qs处理,false使用公共模块queryString进行处理
app.use(bodyParser.urlencoded({extended:false}));

app.post('/addStudent',(req,res) => {
    console.log(req.body);
    const s = JSON.stringify(req.body);
    res.send('添加成功:'+s);
});

app.listen(3000,function () {
    console.log('服务已启动');
});

页面显示结果

{"name":"小明","age":18,"id":20}
3.6路径参数
const express = require('express');

const app = express();

app.get('/index/:id/:name',(req,res)=>{
    res.send(req.params);
});

app.listen(3000,function () {
    console.log('服务已启动');
});
// 访问http://localhost:3000/index/1/xiaoming
// 网页显示结果:{"id":"1","name":"xiaoming"}

当然你还可以指定哪些参数不必传,但是至少得传递一个参数,并且传递一个参数时总会给路径最后一个参数赋值

app.get('/index?/:id?/:name',(req,res)=>{
    res.send(req.params);
});

// 例如:访问http://localhost:3000/index/xiaoming
// 结果:{"name":"xiaoming"}
3.7访问静态资源

使用express提供的static方法就好

const express = require('express');
const app = express();

// 访问静态资源
console.log(__dirname);
app.use('/data',express.static('../data/'));

app.listen(3000,function () {
    console.log('服务已启动');
});

posted on 2021-01-30 23:02  1763392456  阅读(451)  评论(1编辑  收藏  举报

导航