1.Node 开发概述
1.1 为什么学习服务器开发基础
能够和后端程序员更加紧密的配合
网站业务逻辑前置,学习前端技术需要后端技术支撑(Ajax)
拓宽知识视野,能够站在更高的角度审视整个项目
成为真正的全栈工程师
1.2服务器端开发要做的事情
实现网站的业务逻辑
数据的增删改查
1.3为什么要选择Node
使js语法开发后端应用
一些公司要求前端工程师掌握NOde开发
生态系统活跃,有大量开源库可以使用
前端开发工具大多基于NOde开发
1.4 Node是什么
Node 是一个基于 Chrome v8引擎的javaScript代码运行环境
运行环境:
- 浏览器(软件)能够运行JavaScript代码,浏览器就是JavaScript的运行环境
- Node(软件)能够运行jJavaScript代码,Node就是JavaScript的运行环境
2.NOde.js快速入门
2.1Node.js组成
JavaScript由三部分组成,ECMAScript,Dom,Bom
Node.js是由ECMAScript及Node环境提供的一些附加API组成的,包括文件,网络,路径等等一些更强大的API
2.2Node.js基础语法
- 所有ECMAScript语法在Node环境都可以使用。
- 在Node环境下执行代码,使用Node命令执行后缀为.js的文件即可
2.3Node.js全局对象global
- 在浏览器中全局对象是window,在Node中全局对象是global。
- Node中全局对象有以下方法,可以在任何地方使用,global可以省略
3.Node.js模块化开发
3.1JavaScript开发弊端
JavaScript在使用时存在两大问题,文件依赖和命名冲突
3.2软件中的模块化开发
一个功能就是一个模块,多个模块可以组成完整应用,抽离一个模块不会影响其他功能的运行
3.3 Node.js中模块化开发规范
Node.js规定一个Javascript文件就是一个模块,模块内部定义的变量和函数默认情况下在外部是无法得到的
模块内部可以使用exports对象成员导出,使用require方法导入其他模块
3.4模块成员导出
a.js //文件名 let version = 1.0; //在模块内部定义变量 const sayHi = name =>`您好,${name}`; //在模块内部定义方法 //向外导出数据 exports.version = version; exports.sayHi = sayHi;
3.5模块成员导入
b.js //文件名
let a = require('./b.js');
console.log(a.version);//输出b模块中的version变量
console.log(a.sayHi('您好 时间'));//调用b模块中的sayHi方法,并输出其返回值
导入模块时后缀可以省略 在当前文件中导入的实际就是b模块中的exports对象
3.6模块成员导出的另一种方式
module.export.version=version;
module.exports.sayHi=sayHi;
exports是module.exports的别名(地址引用关系),导出对象最终以module.exports为准
3.7模块导出两种方式的联系与区别
exports.version = version;
module.exports.version = version;
module.exports ={
name:'zhangshan',
}
4.系统模块
4.1什么是系统模块
Node运行环境提供的API,因为这些API都是以模块化的方式进行的,所以又称为Node运行环境提供的API为系统模块
4.2系统模块 fs文件操作
f:file文件,s:system系统,文件操作系统
const fs = require('fs');
读取文件内容
fs.readFile('文件路径/文件名称',[,'文件编码'],callback)
fs.readFile('../index.html','utf8',(err,data)=>{
if (err!=null){
console.log(err);
return;
}
console.log(data);
})
写入文件内容
fs.writeFile('文件路径/文件名称','数据',callback)
const content = '<h3>正在写入文件</h3>';
fs.writeFile('../index.html',content,err=>{
if (err!=null){
console.log(err);
return;
}
console.log('文件写入成功');
})
4.3系统模块path路径操作
为什么要进行路径拼接?
- 不同操作系统的路径分隔符不统一
- /public/uploads/avatar
- Windows上是\ /
- Linux上是/
4.4路径拼接语法
path.join('路径','路径',...)
const path = require('path');//导入path模块
let finialPath = path.join('itcast','a','b','c.css');//路径拼接
console.log(finialPath);//输出结果itacast/a/b/c.css
4.5系统模块
相对路径vs绝对路径
- 大多数情况下使用绝对路径,因为相对路径有时候相对的是命令行工具的当前工作目录
- 在读取文件或者设置文件路径时都会选择绝对路径
- 使用__dirname 获取当前当前文件的绝对路径
5.第三方模块
5.1什么是第三方模块
别人写好的,具有特定功能的,我们能直接使用的模块就是第三方模块,由于第三方模块通常都是由多个文件组成并且被放置在一个文件夹中,所以又名包
第三方模块的两种存在形式
- 以js文件的形式存在,提供实现项目具体功能的API接口
- 以命令行工具形式存在,辅助项目开发
5.2获取地三方模块
npm第三方存储和分发参考
npm(node package manager):node的第三方模块管理工具
下载:mpm install 模块名称
卸载 npm uninstall package 模块名称
全局安装与本地安装
- 命令行工具 :全局安装 -g
- 库文件:本地安装
5.3第三方模块nodemon
nodemon是一个命令行工具,用以辅助项目开发
在Node.js中,每次修改文件都要在命令行工具中重新执行改文件,非常繁琐
使用步骤
- 使用npm install nodemon -g下载它
- 在命令行工具用nodemon命令工具 替代node命令执行文件
3.如果nodemon安装后使用的时候报错
4. win+X打开powershell 窗口 选择管理身份运行 输入set-executionpolicy remotesigned 回车 输入Y回车
5.4第三方模块nrm
nrm(npm registry manager):npm 下载地址切换工具
npm默认的下载地址再国外,下载速度非常慢
使用步骤
- 使用npm install nrm -g下载它
- 查询可用下载地址列表 nrm ls
- 切换 npm下载地址nrm use下载地址名称
5.5第三方模块Gulp
基于node平台开发的前端构建工具
将机械化操作编写任务,想要执行机械化操作时执行下一个命命令,任务就自动执行了
用机器代替手工,提高开发效率
5.5.1Gulp能做什么
- 项目上线,HTML,CSS,JS文件压缩合并
- 语法转换(es6,less'....)
- 公关文件抽离
- 修改文件浏览器自动刷新
3.5.2Gulp使用
- 使用npm install gulp下载gulp库文件
- 在项目根目录下建立gulpfile.js文件
- 重构项目的文件夹结构src目录放置项目源代码文件dist目录放置构建后文件
- 在gulpfile.js文件中编写任务
- 在命令行工具执行gulp任务
5.5.3Gulp中提供的方法
- gulp.src():获取任务要处理的文件
- gulp.dest():输出文件
- gulp.task():建立gulp任务
- gulp.watch():监控文件的变化
const gulp = require('gulp');
const csso = require('gulp-csso');
// 使用gulp.task()方法建立任务
// 参数1:任务的名字
// 参数2:任务的回调函数
gulp.task('first', () => {
// 获取要处理的文件
return gulp.src('./src/css/*.css')
// .pipe(less())
.pipe(csso())
// 将处理后的文件输出到dist目录
.pipe(gulp.dest('./dist/css'));
});
gulp代码的执行:
提供了同名的命令行工具,我们需要下载它
- npm install gulp-cli-g 全局安装
- 执行命令 gulp first ,gulp+命令名称
5.5.4 Gulp
- gulp-htmlnin:html文件压缩
- gulp-csso : 压缩css
- gulp-babel : JavaScript语法转化
- gulp-uglify : 压缩混淆JavaScript
- gulp-file-include : 公共文件包含
- browsersync : 浏览器实时同步
5.5.5 插件的使用步骤
- 通过npm 命令下载插件
- 在gulpfile.js文件引入插件
- 调用插件
任务1:html任务
1.压缩html文件
const gulp = require('gulp');
const htmlmin = require('gulp-htmlmin');
const fileinclude = require('gulp-file-include');
const { listeners } = require('gulp');
gulp.task('minify', () => {
return gulp.src('src/*.html')
.pipe(fileinclude())//抽离公共部分代码
.pipe(htmlmin({ collapseWhitespace: true }))
.pipe(gulp.dest('dist'));
});
6.package.json文件
6.1 node_modules文件夹的问题
- 文件夹以及文件过多过碎,当我们将项目整体拷贝给别人的时候,,传输速度会很慢很慢.
- 复杂的模块依赖关系需要被记录,确保模块的版本和当前保持一致,否则会导致当前项目运行报错
6.2 package.json文件的作用
项目描述文件,记录了当前项目信息,例如项目名称、版本、作者、github地址、当前项目依赖了哪些第三方模块等。
使用npm init -y命令生成。
命令别名问题:
1.修改package.json文件scripts添加命令别名 "build":"nodemon app.js"
2.命令行中执行命令npm run build
6.3 项目依赖
- 在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖
- 使用npm install 包名命令下载的文件会默认被添加到 package.json 文件的 dependencies 字段中
6.4 开发依赖
- 在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖
- 使用npm install 包名 --save-dev命令将包添加到package.json文件的devDependencies字段中
{
"dependencies": {
"jquery": "^3.3.1“
}
}
6.5 package-lock.json文件的作用
- 锁定包的版本,确保再次下载时不会因为包版本不同而产生问题
- 加快下载速度,因为该文件中已经记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装时只需下载即可,不需要做额外的工作
{
"devDependencies": {
"gulp": "^3.9.1“
}
}
7.Node.js中模块加载机制
7.1 模块查找规则-当模块拥有路径但没有后缀时
require('./find.js');
require('./find');
- require方法根据模块路径查找模块,如果是完整路径,直接引入模块。
- 如果模块后缀省略,先找同名JS文件再找同名JS文件夹
- 如果找到了同名文件夹,找文件夹中的index.js
- 如果文件夹中没有index.js就会去当前文件夹中的package.json文件中查找main选项中的入口文件
- 如果找指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到
7.2 模块查找规则-当模块没有路径且没有后缀时
require('find');
Node.js会假设它是系统模块
Node.js会去node_modules文件夹中
首先看是否有该名字的JS文件
再看是否有该名字的文件夹
如果是文件夹看里面是否有index.js
如果没有index.js查看该文件夹中的package.json中的main选项确定模块入口文件
否则找不到报错
8.服务器端基础概念
8.1 网站的组成
网站应用程序主要分为两大部分:客户端和服务器端。
客户端:在浏览器中运行的部分,就是用户看到并与之交互的界面程序。使用HTML、CSS、JavaScript构建。
服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑。
8.2 服务器端基础概念
能够提供网站访问服务的机器就是网站服务器,它能够接收客户端的请求,能够对请求做出响应。
8.3 IP地址
互联网中设备的唯一标识。
IP是Internet Protocol Address的简写,代表互联网协议地址.
8.4域名
由于IP地址难于记忆,所以产生了域名的概念,所谓域名就是平时上网所使用的网址。
http://www.baidu.com => http://220.181.38.150:443
虽然在地址栏中输入的是网址, 但是最终还是会将域名转换为ip才能访问到指定的网站服务器。
8.5 端口
端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供的不同的服务。
8.6 URL
统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL。
URL的组成
传输协议://服务器IP或域名:端口/资源所在位置标识
http://www.baidu.com/a/b/1.html
http:超文本传输协议,提供了一种发布和接收HTML页面的方法。
8.7 开发过程中客户端和服务器端说明
在开发阶段,客户端和服务器端使用同一台电脑,即开发人员电脑
客户端(浏览器) <=>服务器端(Node)
本地域名:localhost
本地IP:127.0.0.1
9.创建web服务器
// 引用系统模块
const http = require('http');
// 创建web服务器
const app = http.createServer();
// 当客户端发送请求的时候
app.on('request', (req, res) => {
// 响应
res.end('<h1>hello world</h1>');
});
// 监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问 localhost:3000')
10.HTTP协议
10.1 HTTP协议的概念
超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。
10.2报文
在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。
10.3请求报文
1. 请求方式 (Request Method)
●GET 请求数据
●POST 发送数据
2 . 请求地址(Request URL)
app.on('request', (req, res) => {
req.headers // 获取请求报文
req.url // 获取请求地址
req.method // 获取请求方法
});
10.4响应报文
1 . HTTP状态码
●200 请求成功
●400 客户端请求有语法错误
●403 forbidden 禁止访问
●404 not found 请求的资源没有找到
●500 客户端请求有语法错误
2 . 内容类型
●text/html
●text/css
●application/javascript
●image/jpeg
●application/json
app.on('request', (req, res) => {
// 设置响应报文
res.writeHead(200, {'Content-Type': 'text/html;charset=utf8'});
});
11.HTTP请求与响应处理
11.1 请求参数
客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。
11.2 GET请求参数
●参数被放置在浏览器地址栏中,例如:http://localhost:3000/? name=zhangsan&age=20
●参数获取需要借助系统模块url,url模块用来处理url地址
const http = require('http');
// 导入url系统模块 用于处理url地址
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
// 将url路径的各个部分解析出来并返回对象
// true 代表将参数解析为对象格式
let {pathname,query} = url.parse(req.url, true);
console.log(query);
});
app.listen(3000);
11.3 POST请求参数
●参数被放置在请求体中进行传输
●获取POST参数需要使用data事件和end事件
●使用querystring系统模块将参数转换为对象格式
11.4路由
http://localhost:3000/index
http://localhost:3000/login
路由是指客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。
// 当客户端发来请求的时候
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('抱歉, 您访问的页面出游了');
}
});
11.5 静态资源
服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件。
http://www.baidu.com/logo.png
托管静态资源express.static()
express提供了一个非常好用的函数,叫做express.static(),通过它,我们可以非常方便的创建一个静态资源服务器
例如通过以下代码就可以将public目录下的图片,css文件,javascript文件对外开放访问了
app.use(express.static('文件夹名'))
挂载路径前缀
如果希望在托管的静态页面之前,挂载路径前缀,则可以使用如下方法
app.ues('/文件夹名' express.static('文件夹名'))
const express = require('express');
const app = express();
//在这里 调用express.static()方法,快速的对外提供静态资源
app.use('/files', express.static('./files'));
app.use(express.static('./countdown'));
// 托管多个静态资源 ,就多次调用 express.static()方法
app.listen(80, () => {
console.log('正在访问服务器 http://localhost:80')
})
// 1.引入系统模块 http
const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
const mime = require('mime'); // 通过 mime.getType();获取文件的类型
// 2.创建服务器 返回实例对象
const app = http.createServer();
// 3.绑定请求事件,用于处理用户的响应 req = request 请求 ,res = response 响应
app.on('request',(req,res)=>{
const mimetype = mime.getType(req.url);
console.log(mimetype);
res.writeHead(200,{
'content-type': mimetype //根据响应的文件的类型 响应
})
let {pathname,query} = url.parse(req.url,true);
pathname = pathname == '/'?'index.html':pathname;
let method = req.method.toLowerCase(); // 小写
if(method == 'get'){
const filename = path.join(__dirname,'public',pathname);
console.log(filename);
fs.readFile(filename,(err,data)=>{
if(err != null){
res.end('请求失败了');
return ;
}
res.end(data);
});
}else if(method == 'post'){
res.end('<h1>hello post</h1>');
}
})
// 4.监听端口
app.listen(3000);
console.log('服务器已经启动,请访问:http://localhost:3000');
11.6动态资源
相同的请求地址不同的响应资源,这种资源就是动态资源。
http://localhost?gid=1;
http://localhost?gid=2;
11.7客户端请求途径
1 . GET
a . 浏览器地址栏
b . link标签的href属性
c . script标签的src属性
d . img标签的src属性
e . Form表单提交
2 . POST
a . Form表单
12. Node.js异步编程
12.1 同步API,异步API
// 路径拼接
const public = path.join(__dirname, 'public');
// 请求地址解析
const urlObj = url.parse(req.url);
// 读取文件
fs.readFile('./demo.txt', 'utf8', (err, result) => {
console.log(result);
});
同步API:只有当前API执行完成后,才能继续执行下一个API
console.log('before');
console.log('after');
步API:当前API的执行不会阻塞后续代码的执行
console.log('before');
setTimeout(
() => { console.log('last');
}, 2000);
console.log('after');
12.2 同步API, 异步API的区别( 获取返回值 )
同步API可以从返回值中拿到API执行的结果, 但是异步API是不可以的
// 同步
function sum (n1, n2) {
return n1 + n2;
}
const result = sum (10, 20);
// 异步
function getMsg () {
setTimeout(function () {
return { msg: 'Hello Node.js' }
}, 2000);
}
const msg = getMsg ();
12.3 回调函数
自己定义函数让别人去调用。
// getData函数定义
function getData (callback) {}
// getData函数调用
getData (() => {});
12.4 使用回调函数获取异步API执行结果
function getMsg (callback) {
setTimeout(function () {
callback ({ msg: 'Hello Node.js' })
}, 2000);
}
getMsg (function (msg) {
console.log(msg);
});
12.5 同步API,异步API的区别(代码执行顺序)
同步API从上到下依次执行,前面代码会阻塞后面代码的执行
for (var i = 0; i < 100000; i++) {
console.log(i);
}
console.log('for循环后面的代码');
异步API不会等待API执行完成后再向下执行代码,不会阻塞后面代码
console.log('代码开始执行');
setTimeout(() => { console.log('2秒后执行的代码')}, 2000);
setTimeout(() => { console.log('"0秒"后执行的代码')}, 0);
console.log('代码结束执行');
12.6 代码执行顺序分析
console.log('代码开始执行');
setTimeout(() => {
console.log('2秒后执行的代码');
}, 2000);
setTimeout(() => {
console.log('"0秒"后执行的代码');
}, 0);
console.log('代码结束执行');
12.7 Node.js中的异步API
fs.readFile('./demo.txt', (err, result) => {});
var server = http.createServer();
server.on('request', (req, res) => {});
如果异步API后面代码的执行依赖当前异步API的执行结果,但实际上后续代码在执行的时候异步API还没有返回结果,这个问题要怎么解决呢?
fs.readFile('./demo.txt', (err, result) => {});
console.log('文件读取结果');
需求:依次读取A文件、B文件、C文件
回调地域
12.8 Promise
Promise出现的目的是解决Node.js异步编程中回调地域的问题。
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (true) {
resolve({name: '张三'})
}else {
reject('失败了')
}
}, 2000);
});
promise.then(result => console.log(result); // {name: '张三'})
.catch(error => console.log(error); // 失败了)
异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。
const fn = async () => {};
async function fn () {}
async关键字
1. 普通函数定义前加async关键字 普通函数变成异步函数
2. 异步函数默认返回promise对象
3. 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
4. 在异步函数内部使用throw关键字抛出程序异常
5. 调用异步函数再链式调用then方法获取异步函数执行结果
6. 调用异步函数再链式调用catch方法获取异步函数执行的错误信息
await关键字
1 . await关键字只能出现在异步函数中
2 . await promise await后面只能写promise对象 写其他类型的API是不不可以的
3 . await关键字可是暂停异步函数向下执行 直到promise返回结果
const fs = require('fs');
const promisify = require('util').promisify;
const readfile = promisify(fs.readFile);
async function fn(){
let r1 = await readfile('./1.txt','utf8');
let r2 = await readfile('./2.txt','utf8');
let r3 = await readfile('./3.txt','utf8');
console.log(r1);
console.log(r2);
console.log(r3);
}
fn();