一、概念
1.什么是nodejs
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
,可以用来动态获取当前文件的绝对路径(包含文件名) -
__dirname
和filename
是不受执行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('服务已启动');
});