NodeJs学习记录
1.node简介
Node是一个基于Chrome V8引擎的JavaScript代码运行环境。
2.Node运行环境安装
3.快速入门
在控制台 进入当前目录
1
2
3 // 语法
node ***.jsnpm切换源
获取原本镜像地址
1
2 npm get registry
1设为淘宝镜像
1
2
3 npm config set registry http://registry.npm.taobao.org/
yarn config set registry http://registry.npm.taobao.org/
2.global
4.模块化开发
概述
JavaScript在使用时存在两大问题,文件依赖和命名冲突。
开发规范
导出
方式1
方式2
但是
// 当exports 对象 和 moudle.exports 对象指向的不是同一个对象时 以module.exports为准
1
2
3
4
5
6
7
8
9
10
11
12 const greeting = name => `hello ${name}`
const x = 100;
exports.x = x;
module.exports.greeting = greeting;
// 当exports 对象 和 moudle.exports 对象指向的不是同一个对象时 以module.exports为准
module.exports ={
name:'zhangsan'
}
exports = {
age:20
}
1
2
3 const a = require('./04.node.js');
// console.log(a.greeting('zhangsan'));
console.log(a); // {name:'zhangsan'}
导入
案例
1 | // a.js |
1 | // b.js |
系统模块
概述
Node运行环境提供的API.因为这些API都是以模块化的方式进行开发的所以我们又称Node运行环境提供的API为系统模块
fs 文件读取操作
1 | // 1. 通过模块的名字 fs 对模块进行引用 |
fs 文件写入操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 // 1. 通过模块的名字 fs 对模块进行引用
const fs = require('fs');
// 2. 通过模块内部的 readFile 读取文件内容
fs.readFile('./hellow.js','utf-8',(err,doc)=>{
// 如果文件读取出错 err 是一个对象 包含错误信息
// 如果文件读取正确 err 是 null
// doc是文件读取结果
console.log(err);
console.log(doc);
if(err==null) {
// 如果读取没出错 就调用 写入 api 把文件内容写到 中
fs.writeFile('./05.write.js',doc,(err)=>{
if (err != null) { // 写入出错时调用
console.log('写入出错');
} else { // 写入成功时调用
console.log('写入成功');
}
})
}
})成功后 看文件中是否有内容
如果 写入文件不存在会自动创建文件 成功
1 fs.writeFile('./err.txt',doc,(err)=>{}
path 路径操作
语法
1
2
3
4
5 // public/uploads/avatar
const path = require('path');
var pat = path.join('publisc','uploads','avatar')
console.log(pat);
相对路径与绝对路径
__dirname是获取绝对路径
1 | const fs = require('fs'); |
第三方模块
1 | node init --yes |
概述
存在形式
获取第三方模块
1 | npm install formidable //安装 |
本地安装 全局安装
第三方模块 nodemon
nodemon是一个命令行工具,用以辅助项目开发。
在Node.js中,每次修改文件都要在命令行工具中重新执行该文件,非常繁琐。
- 使用npm install nodemon
-g
下载-g
全局安装- 在命令行工具中使用 nodemon命令替代 node命令执行文件
nodemon运行 提示错误:无法加载文件 C:\Users\gxf\AppData\Roaming\npm\nodemon.ps1,因为在此系统上禁止运行脚本。
这是你笔记本禁止运行脚本,解决办法
1.管理员身份打开powerShell
2.输入set-ExecutionPolicy RemoteSigned
3 选择Y 或者A ,就好了
第三方模块nrm
nrm ( npm registry manager ): npm下载地址切换工具npm默认的下载地址在国外,国内下载速度慢
- 使用 npm install nrm -g 下载
- 查询可用下载地址列表 nrm ls
- 切换 npm 下载地址 nrm use 下载地址名称
5.Gulp
1.概述
2.初始化
1 | cnpm install gulp |
Gulp提供的方法
基本使用
1 | // 引用gulp模块 |
发现dist 有了 css
3.Gulp插件
概述
使用
html压缩操作 下载
1 npm install gulp-htmlmin引用
1 const htmlmin = require('gulp-htmlmin');调用 压缩html代码
1
2
3
4
5
6
7
8
9
10 // html任务
// 1. html文件中代码压缩操作
// 2. 抽取html文件中的公共代码
gulp.task('htmlmin', () => {
gulp.src('./src/*.html')
// 压缩html文件中的代码
.pipe(htmlmin({collapseWhitespace: true})) // 是否压缩空格
.pipe(gulp.dest('dist')) // 输出到 dist 下
})抽离 html 公共代码
第一步 下载插件
1
2 // 下载
cnpm install gulp-file-include第二步 引用
1
2 // 调用
const fileinclude = require('gulp-file-include');第三步 把html公共样式 抽离出来 写在 common文件夹下
在html 公共部分文件中写入
1 @@include('./common/header.html')第五步 使用 htmlmin方法
1// html任务
// 1. html文件中代码压缩操作
// 2. 抽取html文件中的公共代码
gulp.task(‘htmlmin’, () => {
gulp.src(‘./src/*.html’).pipe(fileinclude()) // 这一步是 使用公共部分 // 压缩html文件中的代码 .pipe(htmlmin({collapseWhitespace: true})) // 是否压缩空格 .pipe(gulp.dest('dist')) // 输出到 dist 下
})
1
2
3
4 3. css代码操作 less转换以及 css代码压缩
- 第一步 安装插件cnpm install gulp-csso // 压缩 css 的
cnpm install gulp-less // less 转换为 css
1
2
3
- 第二步 引用
const csso = require(‘gulp-csso’);
const less = require(‘gulp-less’);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 第三步 调用
```js
// css 任务
// 1.将less 转换为 css
// 2. 将css 代码压缩
gulp.task('cssmin',()=>{
// 利用数组的形式选择多个路径 选择 css 路径下 所有的 less 文件以及 css 文件
gulp.src(['./src/css/*.less','./src/css/*.css'])
// less 转换为 css
.pipe(less())
// css 代码压缩
.pipe(csso())
.pipe(gulp.dest('./dist/css'))
})
js 代码转换es5操作 与 压缩操作
1
2
3 安装
cnpm install gulp-babel @babel/core @babel/preset-env 这是转换插件
cnpm install gulp-uglify 这是压缩插件
1
2
3 引用
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
1
2
3
4
5
6
7
8
9
10
11
12
13 调用
// js任务
// 1.es6 代码转化
// 2.代码压缩
gulp.task('jsmin',()=>{
gulp.src('./src/js/*.js')
.pipe(babel({
// 它可以判断当前代码运行环境 将代码转换为当前运行环境所支持的代码
presets: ['@babel/env']
})) // uglify压缩
.pipe(uglify())
.pipe(gulp.dest('dist/js'))
})进行了转换和压缩
构建任务
1
2 // 构建任务
gulp.task('default', gulp.series(gulp.parallel('htmlmin','cssmin','jsmin','copy')));
4.node_modules文件夹
1.package.json文件
项目描述文件,记录了当前项目信息,例如项目名称、版本、作者、github地址、当前项目依赖了哪些第三方模块等。使用
npm init -y
命令生成。使用 npm安装后 package,json会记录你安装过的 依赖
把 node_modules删除后 在文件根目录打开命令行
1 npm install就会下载记录的 依赖
别名的用法
1 调用时 输入 npm run bulid 就可以了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 {
"name": "description",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
// 在这里设置 "别名":"命令" 调用时 输入 npm run bulid 就可以了
"test": "echo \"Error: no test specified\" && exit 1",
"build": "nodemon app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"formidable": "^1.2.2",
"gulp": "^4.0.2",
"mime": "^2.5.0"
}
}
2.项目依赖 以及 开发依赖
项目依赖
1
2开发完成后 用户只想安装项目依赖
npm install --production开发依赖
1
npm install --save-dev gulp 安装依赖时设为开发依赖
package-lock.json作用
5.nodejs中模块的加载机制
案例1
案例2
6.服务器相关
1.Node网站服务器
域名
由于IP地址难于记忆,所以产生了域名的概念,所谓域名就是平时上网所使用的网址。http://www.itheima.com => http://124.165.219.100/
虽然在地址栏中输入的是网址,但是最终还是会将域名转换为ip才能访问到指定的网站服务器。
端口
端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供的不同的服务。
2.URL
统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL。
url组成
3.开发过程中客户端和服务器端说明
4.创建网站服务器
1 | // 引用系统模块 |
启动 app.js 在浏览器输入 localhost:3000 就能访问
5.HTTP协议
**
超文本传输协议
(英文:HyperText Transfer Protocol,缩写:HTTP
**规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。
6.报文
在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式
请求报文
1.请求方式 (Request Method)
- GET 请求数据
- POST 发送数据
2.请求地址 (Request URL)
1
2
3
4
5
6 app.on('request', (req, res) => {
req.headers // 获取请求报文
req.url // 获取请求地址
req.method // 获取请求方法
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 // 用于创建网站服务器的模块
const http = require('http');
// app对象就是网站服务器对象
const app = http.createServer();
// 当客服端有请求来的时候
app.on('request', (req, res) => {
// req请求对象 res响应
// console.log(req);
// 获取请求方式 req.method
// 获取请求地址
// req.url
// 获取请求报文信息
// req.headers
console.log(req.headers);
console.log(req.url);
if (req.url == '/index' || req.url == '/') {
res.end('welcom to homepage')
} else if (req.url == '/list') {
res.end('welcom to list')
} else {
res.end('not found')
}
console.log(req.url);
console.log(req.method);
// if (req.method == 'POST') {
// res.end('post')
// } else if (req.method == 'GET') {
// res.end('GET')
// }
});
app.listen(3000); // 监听端口
console.log('网站服务器启动成功');
响应报文
HTTP状态码
200 请求成功
404 请求的资源没有被找到
500 服务器端错误
400 客户端请求有语法错误
1
2
3
4
5
6 res.writeHead(500) // 设置状态码
res.writeHead(200,{
'content-type':'text/html;charset=utf8' // 默认存文本
})
设置状态码以及返回类型 与 字符编码
内容类型
text/plain 纯文本
text/html
text/css
application/javascript
image/jpeg
application/json
7.请求参数
概述
分类
GET请求参数
参数被放置在浏览器地址栏中,例如:http://localhost:3000/`?name=zhangsan&age=20`
参数获取需要借助系统模块url,url模块用来处理url地址
node提供了内置模块
1
2// 用于处理 url 地址
const httpUrl = require('url');使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18console.log(req.url);
// 1) req.url 要解析的 url地址
// 2) true 是否保存为对象 query即为url对象里面的 url解析
// 利用解构 let query = httpUrl.parse(req.url,true).query
// let pathname = httpUrl.parse(req.url,true).pathname
let {query,pathname} = httpUrl.parse(req.url,true);
console.log(query.name);
console.log(query.age);
// 这里进行了修改 利用 pathname 进行判断
if (pathname == '/index' || pathname == '/') {
res.end('<h2>welcom to啦啦啦啦 homepage</h2>')
} else if (pathname == '/list') {
res.end('welcom to list')
} else {
res.end('not found')
}POST请求参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31// 用于创建网站服务器的模块
const http = require('http');
// 用于处理 url 地址
const url = require('url')
// app对象就是网站服务器对象
const app = http.createServer();
// 处理请求参数模块
const queryString = require('querystring');
// 当客服端有请求来的时候
app.on('request', (req, res) => {
// post 参数是通过事件的方式接收的
// data 当请求参数传递的时候 触发data事件
// end 当参数传递完成的时候 触发end事件
let postParams ='';
req.on('data',(params)=>{
postParams += params // 这里之索引进行字符串拼接是因为 请求数据不是一次性发送完毕的
});
req.on('end',()=>{
console.log(queryString.parse(postParams));
})
res.end('ok')
});
app.listen(3000); // 监听端口
console.log('网站服务器启动成功');1
2
3
4
5
6
7
8
9
10
11
12// 导入系统模块querystring 用于将HTTP参数转换为对象格式
const querystring = require('querystring');
app.on('request', (req, res) => {
let postData = '';
// 监听参数传输事件
req.on('data', (chunk) => postData += chunk;);
// 监听参数传输完毕事件
req.on('end', () => {
console.log(querystring.parse(postData));
});
});
8.路由
路由是指客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。
1
2
3
4
5
6
7
8
9
10
11
12
13 // 当客户端发来请求的时候
app.on('request', (req, res) => {
// 获取客户端的请求路径
let { pathname } = url.parse(req.url);
if (pathname == '/' || pathname == '/index') {
res.end('欢迎来到首页');
} else if (pathname == '/list') {
res.end('欢迎来到列表页页');
} else {
res.end('抱歉, 您访问的页面出游了');
}
});
案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 // 1.引入系统模块 http
// 2.创建网站服务器
// 3.为网站服务器对象添加请求事件
// 4.实现路由功能
// 1) 获取客户端的请求方式
// 2) 获取客户端的请求地址
const http = require('http');
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
// 获取请求方式
const method = req.method.toLowerCase(); // 转换为小写
// 获取请求地址
const pathname = url.parse(req.url).pathname;
res.writeHead(200,{
'content-type':'text/html;charset=utf-8',
})
if (method === 'get') {
if (pathname === '/' || pathname === '/index') {
res.end('欢迎来到首页')
} else if (pathname === '/list') {
res.end('欢迎来到列表页')
} else {
res.end('您访问的页面不存在')
}
} else if (method === 'post') {
}
})
app.listen(3000)
console.log('服务器启动成功');
9.静态资源
服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件
1 | http://www.itcast.cn/images/logo.png |
10.动态资源
相同的请求地址不同的响应资源,这种资源就是动态资源。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 // 1. 引入系统模块 http
// 2. 创建网站服务器对象
// 3. 为服务器对象添加请求事件
// 4. 实现路由功能
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const app = http.createServer();
app.on('request', (req, res) => {
// 1.用户的请求路径
let pathname = url.parse(req.url).pathname; // 无参数的 获取请求路径
console.log(pathname);
// __dirname 获取文件所在的绝对路径
let lujing = path.join(__dirname,'public',pathname)
// console.log(lujing);
// 读取文件
fs.readFile(lujing,(err,result)=>{
// 如果文件读取失败
if(err != null) {
res.writeHead(404,{
'Content-Type':'text/html;charset=utf-8',
})
res.end('文件读取失败');
return;
}
res.end(result)
})
})
app.listen(3000);
console.log('启动成功');
11.静态资源访问案例
这里引入了一个新模块 mime
1 npm install mime
1 | // 1. 引入系统模块 http |
12.客户端请求途径
13.同步Api,异步api
1 | // 路径拼接 |
同步API
同步API:只有当前API执行完成后,才能继续执行下一个API
1
2
3console.log('before');
console.log('after');异步API
异步API:当前API的执行不会阻塞后续代码的执行
1
2
3
4
5
6console.log('before');
setTimeout(
() => { console.log('last');
}, 2000);
console.log('after');
14.同步Api,异步api的区别
同步API可以从返回值中拿到API执行的结果, 但是异步API是不可以的
1 | // 同步 |
1 | // 异步 |
15.回调函数
自己定义函数让别人去调用。
1
2
3
4
5 // getData函数定义
function getData (callback) {}
// getData函数调用
getData (() => {});
1
2
3
4
5
6
7
8
9
10
11
12
13 // 异步
function getMsg(callback) {
setTimeout(function () {
callback(
{ msg: 'Hello Node.js' }
);
}, 2000);
}
getMsg(function (data) {
console.log(data);
});
16.同步API, 异步API的区别(代码执行顺序)
同步API从上到下依次执行,前面代码会阻塞后面代码的执行
1
2
3
4
5 for (var i = 0; i < 100000; i++) {
console.log(i);
}
console.log('for循环后面的代码');异步API不会等待API执行完成后再向下执行代码
1
2
3
4
5
6
7
8 console.log('代码开始执行');
setTimeout(() => { console.log('2秒后执行的代码')}, 2000);
setTimeout(() => { console.log('"0秒"后执行的代码')}, 0);
console.log('代码结束执行');
// 代码开始执行
// 代码结束执行
// "0秒"后执行的代码
// 2秒后执行的代码
17.Node.js中的异步API
1 fs.readFile('./demo.txt', (err, result) => {});
1
2
3 var server = http.createServer();
server.on('request', (req, res) => {});
如果异步API后面代码的执行依赖当前异步API的执行结果,但实际上后续代码在执行的时候异步API还没有返回结果,这个问题要怎么解决呢?
1 | fs.readFile('./demo.txt', (err, result) => {}); |
需求:依次读取A文件、B文件、C文件
回调地狱版
1
2
3
4
5
6
7
8
9
10
11
12
13
14 const fs = require('fs');
fs.readFile('./1.txt', 'utf8', (err, data1) => {
console.log(data1);
fs.readFile('./2.txt', 'utf8', (err, data2) => {
console.log(data2);
fs.readFile('./3.txt', 'utf8', (err, data3) => {
console.log(data3);
})
})
})
18.promise
Promise出现的目的是解决Node.js异步编程中回调地狱的问题。
基础语法 实现
异步api执行以及错误的处理
进行了分离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 const fs = require('fs');
let promise = new Promise((resolve, reject) => {
fs.readFile('./100.txt', 'utf8', (err, data) => {
if (err != null) {
reject(err)
} else {
resolve(data)
}
})
})
promise.then((result) => {
console.log(result);
})
.catch((err) => {
console.log(err);
})
promise
解决回调地狱
1 | const fs = require('fs'); |
19.异步函数
异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。
语法
1
2 const fn = async () => {};
1
2 async function fn () {}async总结
1
2
3
4
5
6
7 1. 普通函数定义前加async关键字 普通函数变成异步函数
2. 异步函数默认返回promise对象
3. 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
4. 在异步函数内部使用throw关键字抛出程序异常
5. 调用异步函数再链式调用then方法获取异步函数执行结果
6. 调用异步函数再链式调用catch方法获取异步函数执行的错误信息await关键字
1
2
3
4 1. await关键字只能出现在异步函数中
2. await promise await后面只能写promise对象 写其他类型的API是不不可以的
3. await关键字可是暂停异步函数向下执行 直到promise返回结果案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 // 1. 在普通函数定义的前面加上 async 关键字 普通函数就变成了异步函数
// 2. 异步函数默认返回promise对象
// 3. 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
// 4. 在异步函数内部使用throw关键字抛出程序异常
// await 关键字
// 1.他只能出现在 异步函数 中
// 2. await promise 他可以暂停异步函数的执行 等待promise对象返回结果后再向下执行函数
// async function fn() {
// // throw
// throw '发生了一些错误'
// // return 替代 resolve
// return 123
// }
// // console.log(fn());
// fn().then((result) => {
// console.log(result);
// }).catch((err) => {
// console.log(err);
// })
async function p1() {
return 'p1'
}
async function p2() {
return 'p2'
}
async function p3() {
return 'p3'
}
async function run() {
let r1 = await p1();
let r2 = await p2();
let r3 = await p3();
console.log(r1);
console.log(r2);
console.log(r3);
}
run();
20.改造读取文件
1 | const fs = require("fs"); |