node学习1:系统模块、第三方模块、gulp、http、异步编程
文章目录
一、node入门
服务端要做的事:
1.实现网站的业务逻辑
2.数据的增删改查
node是一个基于Chrome V8引擎的JavaScript代码运行环境
node.js运行环境安装:
官网:https://nodejs.org/en/
环境变量:如果想要在命令行中运行node,那就要确认node.exe 的path已经存储在环境变量中
基本使用:
可以使用cmd也可以用powershell
在文件目录下按shift+右键可以打开powershell
按clear可以清空内容
二、Node.js模块化开发
模块化开发的优点:可以抽离而不影响他人
例如:
a.js
const add = (n1, n2) => n1 + n2;
exports.add = add; //或者module.exports.add = add;
b.js
const a = require("./a.js");
console.log(a.add(10, 20)); //30
注意:exports
是module.exports
的别名,地址引用关系相同,当地址引用发生改变时,以module.exports为准
三、系统模块
node运行环境提供的API,因为这些API都是以模块化的方式进行开发的,所以我们又称node运行环境提供的API为系统模块
文件模块(fs):读取文件api、写入文件api、创建文件夹api
1. 文件操作
const fs = require('fs');
读取文件内容:fs.readFile('文件路径/文件名称' [, '文件编码'], callback);
//1. 通过模块的名字fs对模块进行引入
const fs = require('fs');
//2. 通过readFile读取文件
fs.readFile('./a.js', 'utf-8', (err, doc) => {
console.log(err); //错误信息,成功读取则为null
console.log(doc); //文件内容
});
写入文件内容:fs.writeFile('文件路径/文件名称' , '数据', callback);
会把原来的内容覆盖掉,没有文件则创建文件
const fs = require('fs');
fs.writeFile('./a.js', '写入的内容', err => {
if (err != null) {
console.log(err);
return;
}
console.log('文件写入成功');
})
2. 路径操作
const path = require('path');
路径拼接:path.join('路径', '路径', ...)
// public/uploads/avater
const path = require('path');
const finalPath = path.join('public', 'uploads', 'avater');
console.log(finalPath); //public\uploads\avater
可以使用__dirname
获取当前文件所在的绝对路径
const absolutePath = path.join(__dirname, 'a.js');
四、第三方模块
别人写好的、具有特定功能的、我们能直接使用的模块就是第三方模块
有两种形式:
1.以js文件的形式存在,提供实现项目具体功能的api接口
2.以命令行工具形式存在,辅助项目开发
npm:node的第三方模块管理工具
下载:npm install 模块名称
卸载:npm uninstall 模块名称
命令行工具:全局安装
库文件:本地安装
1. 第三方模块nodemon
有了这个第三方模块,我们就可以一边更新代码一边看到输出,而不用每次都重新执行
按ctrl + c 退出
2. 第三方模块nrm
npm install nrm -g
下载nrm
nrm ls
查看可用下载地址
nrm use taobao
换到阿里提供的下载地址
另外:nrm ls报错:
修改:
参考:nrm ls报错解决方案
3. 第三方模块gulp
基于node平台开发的前端构建工具
将机械化的操作编写成任务,只需要一个命令行命令任务就能自动执行了,能够提高开发效率
譬如:
(1)gulp的基本使用
gulp中提供的方法:
gulp.task(任务名字, 回调函数)
:建立gulp任务
gulp.src()
:获取任务要处理的文件
gulp.dest()
:输出文件
gulp.watch()
:监控文件的变化
gulpfile.js文件
// 引用gulp模块
const gulp = require('gulp');
// 使用gulp.task建立任务
gulp.task('first', done => {
console.log('第一个gulp任务');
//1. 使用gulp.src获取要处理的文件
gulp.src('./src/css/normalize.css').pipe(gulp.dest('dist/css')); //仅仅完成了复制功能
done();
});
运行(powershell):
npm install gulp-cli -g
(安装gulp命令行命令)
gulp first
(执行任务)
报错:gulp: Did you forget to signal async completion报错问题处理方案
(2)gulp插件
常用插件:
gulp-htmlmin
:html文件压缩
gulp-csso
:压缩css
gulp-babel
:es6转换成es5
gulp-less
:less转换成css
gulp-uglify
:压缩混淆JavaScript
gulp-file-include
:公共文件包含
browsersync
:浏览器实时同步
文档:https://www.npmjs.com/
使用:下载插件;在gulpfile.js中引入插件;调用插件
例子:
a. html任务:抽取公共代码+压缩操作
注意:我们在写html时,可以将公共的部分抽取到common.html文件中,然后在需要用到的地方通过@@include('./common.html')
引回来
//html任务
const htmlmin = require('gulp-htmlmin'); //引入插件
const fileinclude = require('gulp-file-include');
gulp.task('htmlmin', done => {
gulp.src('./src/*.html')
//抽取公共代码
.pipe(fileinclude())
//压缩代码
.pipe(htmlmin({ collapseWhitespace: true }))
//输出到目标位置
.pipe(gulp.dest('dist'));
done();
});
运行:npm install gulp-htmlmin
、npm install gulp-file-include
、gulp htmlmin
结果:在目标位置生成了一个压缩的代码文件(已经把公共部分合并了)
b. css任务:less代码转换+css代码压缩
//css任务 less语法转换 css代码压缩
const less = require('gulp-less'); //引入
const csso = require('gulp-csso');
gulp.task('cssmin', done => {
//选择css目录下的所有less和css文件
gulp.src(['./src/css/*.less', './src/css/*.css'])
//less转换为css
.pipe(less())
//css代码压缩
.pipe(csso())
//将处理的结果输出
.pipe(gulp.dest('dist/css'))
done();
})
运行:npm install gulp-less
、 npm install gulp-csso
、 gulp cssmin
结果:在目标位置生成压缩的css文件,less代码也相应被转化
c. js任务:es6转换成es5+js代码压缩
//js任务 es6转换 代码压缩
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
gulp.task('jsmin', done => {
gulp.src('./src/js/*.js')
//es6转换成es5
.pipe(babel({
// 判断当前代码的运行环境,将代码转换为当前环境所支持的代码
presets: ['@babel/env']
}))
// js代码压缩
.pipe(uglify())
.pipe(gulp.dest('dist/js'));
done();
})
运行:npm install gulp-babel @babel/core @babel/preset-env
、npm install gulp-uglify
、gulp-jsmin
结果:es6语法都被转成es5,并且的带压缩的js文件
d. 复制剩余文件夹
//复制文件夹
gulp.task('copy', done => {
gulp.src('./src/images/*')
.pipe(gulp.dest('dist/images'));
gulp.src('./src/upload/*')
.pipe(gulp.dest('dist/upload'));
done();
})
运行:gulp copy
e. 总的构建
//构建任务
// gulp.task('default', ['htmlmin', 'cssmin', 'jsmin', 'copy']); //报错 要放到gulp.series()里
gulp.task('default', gulp.series(['htmlmin', 'cssmin', 'jsmin', 'copy']));
运行:gulp default
/ gulp
能够依次执行列出的的任务
五、Node.js中模块的加载机制
六、package.json文件
package.json可以解决上述两个问题。
1. 方便第三方模块的下载
在根目录下,使用npm init -y
来生成package.json文件,-y表示直接使用默认值
当我们下载了一些第三方模块时,package.json文件都有记录
package.json:
{
"name": "description",
"version": "1.0.0",
"description": "",
"main": "index.js", //项目的主入口文件
"scripts": { //命令的别名
//我们可以使用npm run test 来简化命令
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [], //描述当前项目的关键字
"author": "",
"license": "ISC", //项目遵循的协议 ISC开放源代码协议
"dependencies": { //依赖的第三方模块 项目依赖
"formidable": "^1.2.2", //通过npm install formidable 下载时会添加进来
"mime": "^2.5.2"
},
"devDependencies": { //开发依赖
"gulp": "^4.0.2" //通过npm install gulp --save-dev 下载时会添加进来
}
}
项目依赖与开发依赖
项目依赖是运行项目必须的,开发依赖只是在开发阶段的辅助工具,不是必须的
当别人拿到只有package.json 没有node_modules 文件夹的时候,可以在命令行输入
1.npm install
:下载所有的依赖,包括项目依赖和开发依赖,用于开发环境中
2.npm install --production
:只下载项目依赖,用于生产环境(线上/服务器运营环境)
2. 解决模块与模块之间依赖的问题
七、请求响应原理及http协议
1. 创建web服务器
app.js:
//创建网站服务器模块
const http = require('http');
//app对象:网站服务器对象
const app = http.createServer();
//当客户端有请求的时候 req请求对象 res结果对象
app.on('request', (req, res) => {
res.end('<h2>hello vivian</h2>');
});
//监听端口
app.listen(3000);
console.log('网站服务器启动成功');
运行,启动成功
访问:在浏览器地址栏输入:http://localhost:3000/
2. http协议
http:超文本传输协议,规定了如何从网站服务器传输超文本到本地浏览器,两者请求与应答的标准。
报文:请求和响应过程中携带的数据块
获取请求方式req.method
//获取请求方式 req.method
if (req.method == 'POST') {
res.end('post');
} else if (req.method == 'GET') {
res.end('get');
}
当直接在浏览器输入localhost:3000
时,是get
请求
当往服务器提交表单时,是post请求
<form action="http://localhost:3000" method="post">
<input type="submit">
</form>
点击提交按钮后显示
获取请求地址req.url
if (req.url == '/index' || req.url == '/') {
res.end('welcome to homepage');
} else {
res.end('not found');
}
获取请求头部信息req.header
可以通过req.headers[‘accept’]获取具体信息。
设置响应状态码和返回内容类型
//设置响应的状态码
res.writeHead(200, {
//纯文本,不能解析html
//'content-type': 'text/plain',
//可以解析html
//'content-type': 'text/html',
//中文不会乱码
'content-type': 'text/html;charset=utf8'
});
get请求参数
浏览器中输入:
http://localhost:3000/index?name=vivian&age=21
服务器接收解析参数:
const url = require('url');
引入url模块
url.parse(req.url, true)
//解析url true表示查询参数query解析成对象形式
//console.log(url.parse(req.url, true));
let { query, pathname } = url.parse(req.url, true);
console.log(query);
console.log(pathname);
//路径判断(路由)
if (pathname == '/index' || pathname == '/') {
res.end('<h2>welcome 到 homepage</h2>');
} else {
res.end('not found');
}
post请求参数
表单输入:
<form action="http://localhost:3000" method="post">
<input type="text" name="username" id="">
<input type="password" name="password" id="">
<input type="submit">
</form>
服务器接收处理:
const querystring = require('querystring');
引入querystring内置模块
app.on('request', (req, res) => {
let postParams = '';
//当请求参数传递时发出data事件
req.on('data', params => {
postParams += params;
});
//当参数传递完成时发出end事件
req.on('end', () => {
console.log(querystring.parse(postParams));
});
res.end('ok');
});
路由
就是请求什么响应什么,例如请求首页就响应首页,这个过程就是路由,需要一系列的判断代码
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, true).pathname;
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
});
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('服务器启动成功');
静态资源
不需要处理可以直接返回的资源,如css、js、image文件
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');
const app = http.createServer();
app.on('request', (req, res) => {
//获取用户的请求路径
let pathname = url.parse(req.url).pathname;
pathname = pathname == '/' ? '/index.html' : pathname;
//将用户的请求路径转换为实际服务器硬盘路径
let realPath = path.join(__dirname, 'public' + pathname);
//获取路径对应资源的类型
let type = mime.getType(realPath)
//读取文件
fs.readFile(realPath, (error, result) => {
//如果文件读取失败
if (error != null) {
res.writeHead(404, {
'content-type': 'text/html;charset=utf8'
});
res.end('文件读取失败');
return;
}
//指定返回资源的类型
res.writeHead(200, {
'content-type': type
})
res.end(result);
})
});
app.listen(3000);
console.log('服务器启动完成');
动态资源
相同的请求地址可以请求不同的响应资源
八、Node.js异步编程
1. 同步API
- 只有当前API执行完成之后,才能继续执行下一个API
- 可以从返回值中得到结果
2. 异步API
- 当前API的执行不会阻塞后续代码的执行,执行的顺序看事件循环过程
- 通过回调函数获得结果
3. 回调地狱
需求:依次读取1,2,3三个文件
const fs = require('fs');
fs.readFile('./1.txt', 'utf8', (err, result1) => {
console.log(result1);
fs.readFile('./2.txt', 'utf8', (err, result2) => {
console.log(result2);
fs.readFile('./3.txt', 'utf8', (err, result3) => {
console.log(result3);
})
})
});
4. promise
解决回调地狱问题
const fs = require('fs');
function p1() {
return new Promise(resolve => {
fs.readFile('./1.txt', 'utf8', (err, result) => {
resolve(result);
})
})
};
function p2() {
return new Promise(resolve => {
fs.readFile('./2.txt', 'utf8', (err, result) => {
resolve(result);
})
})
};
function p3() {
return new Promise(resolve => {
fs.readFile('./3.txt', 'utf8', (err, result) => {
resolve(result);
})
})
};
p1().then((r1) => {
console.log(r1);
return p2();
}).then((r2) => {
console.log(r2);
return p3();
}).then((r3) => {
console.log(r3);
})
5. 异步函数
异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。
const fn = async () => {};
async function fn() {};
- 异步函数的默认返回值是promise对象,使用return返回promise对象,相当于resolve方法
- 使用throw抛出错误
- await promise 可以暂停异步函数的执行,等待promise对象返回结果后再向下执行
- 可以调用then、catch获取结果
const fs = require('fs');
async function p1() {
fs.readFile('./1.txt', 'utf8', (err, result) => {
console.log(result);
})
}
async function p2() {
fs.readFile('./2.txt', 'utf8', (err, result) => {
console.log(result);
})
}
async function p3() {
fs.readFile('./3.txt', 'utf8', (err, result) => {
console.log(result);
})
}
async function run() {
await p1();
await p2();
await p3();
}
run();
const fs = require('fs');
//promisify方法中,传入一个异步函数,返回一个promise对象
const promisify = require('util').promisify;
const readFile = promisify(fs.readFile);
async function run() {
let r1 = await readFile('./1.txt', 'utf8');
let r2 = await readFile('./2.txt', 'utf8');
let r3 = await readFile('./3.txt', 'utf8');
console.log(r1, r2, r3);
}
run();
九、node.js 全局对象global
在浏览器中全局对象是window,在node中全局对象是global
global有以下方法,其中global可以省略
console.log()
setTimeout()
clearTimeout()
setInterval()
clearInterval()