一、概念

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('服务已启动');
});

NodeJS入门学习教程的更多相关文章

  1. MyBatis入门学习教程-使用MyBatis对表执行CRUD操作

    上一篇MyBatis学习总结(一)--MyBatis快速入门中我们讲了如何使用Mybatis查询users表中的数据,算是对MyBatis有一个初步的入门了,今天讲解一下如何使用MyBatis对use ...

  2. Linux入门学习教程:虚拟机体验之KVM篇

    本文中可以学习到的命令: 1. aptitude 是apt-get 不会产生垃圾的版本 2.       dpkg -L virtualbox 显示属于该包的文件 lsmod | grep kvmfi ...

  3. Nginx 入门学习教程

    昨天听一个前同事说他们公司老大让他去研究下关于Nginx 方面的知识,我想了下Nginx 在如今的开发技术栈中应该会很大可能会用到,所以写篇博文记录总结下官网学习教程吧. 1. 什么是Nginx? 我 ...

  4. PHP 入门学习教程及进阶(源于知乎网友的智慧)

    思过崖历程: 自学的动机.自学的技巧.自学的目标三个方面描述学习PHP的经历 一.自学的动机: 一定要有浓厚的兴趣,兴趣是最后的老师,可以在你迷茫的时候不断地支撑着你走下去. 自学不是为了工作,不是为 ...

  5. gulp入门学习教程(入门学习记录)

    前言 最近在通过教学视频学习angularjs,其中有gulp的教学部分,对其的介绍为可以对文件进行合并,压缩,格式化,监听,测试,检查等操作时,看到前三种功能我的心理思想是,网上有很多在线压缩,在线 ...

  6. Julia 入门学习教程

    有一门语言,它看起来像 Python ,感觉起来像 Lisp ,运行起来又像 C 一样快速,他就是Julia. 近年来,Julia 语言已然成为编程界的新宠,尤其在科学计算和人工智能领域炙手可热. 据 ...

  7. NodeJs入门学习(一)

    NodeJs是针对前端工程师向web后端深入理解的一门很好的语言. 首先,记录NodeJS几大特性,后续补充: 一.Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. ...

  8. 屌丝逆袭--Asp.net快速入门学习教程 第1晚

    本人屌丝一名,因工作原因,不能白天学习编程,所以只能做夜猫子学习编程,期待一天能逆袭成一名高帅富的技术大牛(靠,都想到流口水了........囧). 本教程记录本屌丝学习Asp.net的过程,大牛就飞 ...

  9. Python入门学习教程:数据库操作,连接MySql数据库

    各位志同道合的同仁可以点击上方关注↑↑↑↑↑↑ 本教程致力于程序员快速掌握Python语言编程. 本文章内容是基于上次课程Python教程:Python教程:连接数据库,对数据进行增删改查操作 和py ...

随机推荐

  1. Mybatis-Plus的Service方法使用 之 泛型方法default <V> List<V> listObjs(Function<? super Object, V> mapper)

    首先 我们先看到的这个方法入参是:Function<? super Object , V> mapper ,这是jdk1.8为了统一简化书写格式引进的函数式接口 . 简单 解释一下我对Fu ...

  2. Q227 Basic Calculator II

    /* 看的答案,设置一个符号变量记录这个数前边的符号是什么,不同的符号进行不同的操作.这点自己想到了. 没想到的是由于乘除相当于一个优先级高的线程,所以要先处理,还有存取前一个乘数或者分子,应该怎么办 ...

  3. 在搜索引擎中输入汉字就可以解析到对应的域名,请问如何用LoadRunner进行测试。

    建立测试计划,确定测试标准和测试范围 设计典型场景的测试用例,覆盖常用业务流程和不常用的业务流程等 根据测试用例,开发自动测试脚本和场景: 录制测试脚本:新建一个脚本(Web/HTML协议):点 ...

  4. 自家公司关于git commit 的规范

    代码提交的commit info提个建议,fix的issue是哪个issue?都要有明确的链接.推荐方式:1.建立issue,说明问题的背景和原因.http://git.startdt.net/pay ...

  5. 详细介绍如何自研一款"博客搬家"功能

    前言 现在的技术博客(社区)越来越多,比如:imooc.spring4All.csdn.cnblogs或者iteye等,有很多朋友可能在这些网站上都发表过博文,当有一天我们想自己搞一个博客网站时就会发 ...

  6. Vs2017编译器提示:不能将“const char *”类型的值分配到“char *”类型的实体

    在项目属性中将语言符合模式改成否即可

  7. Thread中run和start方法的模板设计模式

    创建一个Thread需要继承Thread重写run方法或者实现Runnable接口中的run方法,其实两者都是一样因为Thread也继承了Runnable接口. 实现了run方法,但是启动确实用sta ...

  8. 对象存储 COS 全新集成媒体处理功能

    根据<2020年中国网络视听发展研究报告>,截至2020年6月,我国网络视听用户规模达9.01亿,网民使用率95.8%.这表明视频行业已经成为新的流量洼地,而抖音.快手等视频平台的崛起也让 ...

  9. 2020安徽程序设计省赛 G序列游戏

    2020安徽程序设计省赛 G序列游戏 有一个序列w,初始为空.再给出一个长度为m 单调递增的序列a.你需要对序列w 作如下n 次操作: (1)操作0,在序列尾部添加数字0. (2)操作1,在序列尾部添 ...

  10. ssh 免密设置

    在master中生成dsa: ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa cat ~/.ssh/id_dsa.pub >> ~/.ssh/author ...