一、服务器的基本概念与初识Ajax
视频地址:
https://www.bilibili.com/video/BV11N411o7wU?p=1
目标:
- 能够知道和服务器相关的基本概念
- 能够知道客户端和服务器通信的过程
- 能够知道数据也是一种资源
- 能够说出什么是Ajax以及应用场景
- 能够使用jQuery中的Ajax函数请求数据
- 能够知道接口和接口文档的概念
目录:
客户端与服务器
URL地址
分析网页的打开过程
服务器对外提供了哪些资源
了解Ajax
jQuery中的Ajax接口
案例–图书管理
案例-聊天机器人
1.客户端与服务器
1.1 上网的目的
上网的本质目的:通过互联网的形式来获取和消费资源
1.2 服务器
上网过程中,负责存放和对外提供资源的电脑,叫做服务器。
1.3 客户端
上网过程中,负责获取和消费资源的电脑,叫做客户端。
2.URL地址
2.1 URL地址的概念
URL(全称是UniformResourceLocator)中文叫统一资源定位符,用于标识互联网上每个资源的唯一存放位置。浏览器只有通过URL地址,才能正确定位资源的存放位置,从而成功访问到对应的资源。
常见的URL举例:
http://www.baidu.com
http://www.taobao.com
http://www.cnblogs.com/liulongbipblogs/p/11649393.html
2.2 URL地址的组成部分
URL地址一般由三部分组成:
①客户端与服务器之间的通信协议
②存有该资源的服务器名称
③资源在服务器上具体的存放位置
3.分析网页的打开过程
3.1 图解客户端与服务器的通信过程
注意;
客户端与服务器之间的通信过程,分为请求-处理-响应三个步骤。
网页中的每一个资源,都是通过请求-处理-响应的方式从服务器获取回来的。
3.2 基于浏览器的开发者工具分析通信过程
Doc 代表网页资源
Resource可以看到服务器响应回来的数据。
4.服务器对外提供了哪些资源
4.1 例举网页中常见的资源
4.2 数据也是资源
网页中的数据,也是服务器对外提供的一种资源。例如股票数据、各行业排行榜等。
4.3 数据是网页的灵魂
骨架、颜值、行为皆为数据服务数据,数据在网页中无处不在。
4.4 网页中如何请求数据
数据,也是服务器对外提供的一种资源。只要是资源,必然要通过请求–处理–响应的方式进行获取。
如果要在网页中请求服务器上的数据资源,则需要用到XMLHttpRequest对象。
XMLHttpRequest(简称xhr)是浏览器提供的js 成员,通过它,可以请求服务器上的数据资源。
最简单的用法var xhrObj = new XMLHttpRequest()
4.5 资源的请求方式
客户端请求服务器时,请求的方式有很多种,最常见的两种请求方式分别为get和 post请求。
- get 请求通常用于获取服务端资源(向服务器要资源)
- 例如:根据URL地址,从服务器获取HTML文件、css文件、js文件、图片文件、数据资源等
- post 请求通常用于向服务器提交数据(往服务器发送资源)
- 例如:登录时向服务器提交的登录信息、注册时向服务器提交的注册信息、添加用户时向服务器提交的用户信息等各种数据提交操作
5.了解Ajax
5.1什么是Ajax
Ajax的全称是 Asynchronous Javascript And XML(异步JavaScript和XML)。
通俗的理解:在网页中利用XMLHttpRequest对象和服务器进行数据交互的方式,就是Ajax。
5.2 为什么要学Ajax
之前所学的技术,只能把网页做的更美观漂亮,或添加一些动画效果。
但是,Ajax能让我们轻松实现网页与服务器之间的数据交互。
5.3 Ajax的典型应用场景
用户名检测:注册用户时,通过ajax的形式,动态检测用户名是否被占用。
搜索提示:当输入搜索关键字时,通过ajax的形式,动态加载搜索提示列表。
数据分页显示:当点击页码值的时候,通过 ajax的形式,根据页码值动态刷新表格的数据。
数据的增删改查:数据的添加、删除、修改、查询操作,都需要通过ajax的形式,来实现数据的交互
6.jQuery中的Ajax
6.1 了解jQuery中的Ajax
浏览器中提供的XMLHttpRequest 用去比较复杂,所以jQuery对 XMLHttpRequest进行了封装,提供了一系列Ajax相关的函数,极大地降低了Ajax的使用难度。
jQuery中发起 Ajax请求最常用的三个方法如下:
- $.get()
- $.post()
- $.ajax()
6.2 $.get()函数的语法
jQuery 中 $.get(函数的功能单一,专门用来发起get请求,从而将服务器上的资源请求到客户端来进行使用。
$.get()函数的语法如下:
$.get(url[, data][, callback])
其中,三个参数各自代表的含义如下:
参数名 | 参数类型 | 是否必选 | 说明 |
---|---|---|---|
url | string | 是 | 要请求的资源地址 |
data | object | 否 | 请求资源期间要携带的参数 |
callback | function | 否 | 请求成功时的回调函数 |
6.2 $.get()发起不带参数的请求
使用$.get()函数发起不带参数的请求时,直接提供请求的URL地址和请求成功之后的回调函数即可,示例代码如下:
$.get('http: //www.liulongbin.top:3006/api/getbooks', function(res){
console.log (res) //这里的 res 是服务器返回的数据
})
6.3 $.post()向服务器提交数据
6.4 $.ajax()函数的语法
相比于$.get()
和$.post()
函数,jQuery中提供的$.ajax()
函数,是一个功能比较综合的函数,它允许我们对Ajax 请求进行更详细的配置。
$.ajax()函数的基本语法如下:
$.ajax({
type: '', //请求的方式,例如GET或POST.默认值: "GET"
url: '', //请求的URL地址
data:{ }, //这次请求要携带的数据
success: function(res) {} //请求成功之后的回调函数
})
6.4.1 使用$.ajax()发起GET请求
使用$.ajax()
发起 GET 请求时,只需要将type属性的值设置为’GET’即可:
6.4.2 使用$.ajax()发起POST请求
使用$.ajax()发起 POST 请求时,只需要将type 属性的值设置为’POST’即可:
7.接口
使用Ajax请求数据时,被请求的URL地址,就叫做数据接口(简称接口)。同时,每个接口必须有请求方式。
例如:
http://www.liulongbin.top:3006/api/getbooks 获取图书列表的接口(GET请求)
http://www.liulongbin.top:3006/api/addbook 添加图书的接口(POST请求)
7.1 分析接口的请求过程
7.1.1 通过GET方式请求接口的过程
7.1.2 通过POST方式请求接口的过程
7.2 接口测试工具
7.2.1 什么是接口测试工具
为了验证接口能否被正常被访问,我们常常需要使用接口测试工具,来对数据接口进行检测。
好处:接口测试工具能让我们在不写任何代码的情况下,对接口进行调用和测试。
7.2.2下载并安装PostMan
访问PostMan的官方下载网址 https://www.getpostman.com/downloads/,下载所需的安装程序后,直接安装即可。
7.2.3 了解PostMan界面的组成部分
7.3 使用PostMan测试GET接口
GET接口的参数,只能写在Params面板
7.4 使用PostMan测试POST接口
8.案例–图书管理
8.1 渲染UI结构
8.2案例用到的库和插件
用到的css 库 bootstrap.css
用到的 javascript库 jquery.js
用到的 vs code插件 Bootstrap 3 Snippets
在visual studio中输入快捷指令bs3
,可以快速写入很多的代码片段。
8.3渲染图书列表(核心代码)
http://www.liulongbin.top:3006/api/getbooks
visual studio 小技巧:
Ctrl+B 关闭资源管理器
点击右上角的书本形状的“向右拆分编辑器”按钮,可以同时查看该文件的UI部分和JavaScript部分。
9.案例-聊天机器人
待补
二、form表单与模板引擎
目标
能够说出form表单的常用属性
能够知道如何阻止表单的默认提交行为
能够知道如何使用jQuery快速获取表单数据
能够知道如何安装和使用模板引擎
能够知道模板引擎的实现原理目录
form表单的基本使用
通过Ajax提交表单数据
案例–评论列表
模板引擎的基本概念
art-template模板引擎
模板引擎的实现原理
1 form表单的基本使用
1.1 什么是表单
表单在网页中主要负责数据采集功能。HTML中的<form>
标签,就是用于采集用户输入的信息,并通过<form>
标签的提交操作,把采集到的信息提交到服务器端进行处理。
1.2表单的组成部分
表单域∶包含了文本框、密码框、隐藏域、多行文本框、复选框、单选框、下拉选择框和文件上传框等。
1.3 <form>
标签的属性
<form>
标签用来采集数据,<form>
标签的属性则是用来规定如何把采集到的数据发送到服务器。
1.action
action 属性用来规定当提交表单时,向何处发送表单数据。
action属性的值应该是后端提供的一个URL地址,这个URL地址专门负责接收表单提交过来的数据。
当<form>
表单在未指定 action属性值的情况下,action 的默认值为当前页面的URL地址。
注意:当提交表单后,页面会立即跳转到action 属性指定的 URL地址
2.target
target属性用来规定在何处打开action URL。
它的可选值有5个,默认情况下,target的值是_self,表示在相同的框架中打开 action URL。
3.method
method属性用来规定以何种方式把表单数据提交到 action URL。它的可选值有两个,分别是get和post。
默认情况下,method 的值为 get,表示通过URL地址的形式,把表单数据提交到action URL。
注意:
get方式适合用来提交少量的、简单的数据。
post方式适合用来提交大量的、复杂的、或包含文件上传的数据。
在实际开发中,<form>
表单的post提交方式用的最多,很少用get。例如登录、注册、添加数据等表单操作,都需要使用post方式来提交表单。
4.enctype
注意:
在涉及到文件上传的操作时,必须将enctype 的值设置为multipart/form-data
如果表单的提交不涉及到文件上传操作,则直接将enctype的值设置为application/x-www-form-urlencoded即可!
1.4表单的同步提交及缺点
1.什么是表单的同步提交
通过点击 submit 按钮,触发表单提交的操作,从而使页面跳转到 action URL的行为,叫做表单的同步提交。
⒉表单同步提交的缺点
<form>
表单同步提交后,整个页面会发生跳转,跳转到action URL所指向的地址,用户体验很差。
<form>
表单同步提交后,页面之前的状态和数据会丢失。
3.如何解决表单同步提交的缺点
如果使用表单提交数据,则会导致以下两个问题:
页面会发生跳转
页面之前的状态和数据会丢失
解决方案:表单只负责采集数据,Ajax负责将数据提交到服务器。
2 通过Ajax提交表单数据
2.1监听表单提交事件
在jQuery 中,可以使用如下两种方式,监听到表单的提交事件:
$('#form1').submit(function(e) {
alert('监听到了表单的提交事件')
})
$('#form1').on('submit', function (e) {
alert('监听到了表单的提交事件')
)}
2.2阻止表单默认提交行为
当监听到表单的提交事件以后,可以调用事件对象的event.preventDefault()
函数,来阻止表单的提交和页面的跳转,示例代码如下:
3 案例–评论列表
4 模板引擎的基本概念
4.1渲染Ul结构时遇到的问题
上述代码是通过字符串拼接的形式,来渲染UI结构。
如果UI结构比较复杂,则拼接字符串的时候需要格外注意引号之前的嵌套。且一旦需求发生变化,修改起来也非常麻烦。
4.2什么是模板引擎
模板引擎,顾名思义,它可以根据程序员指定的模板结构和数据,自动生成一个完整的HTML页面。
4.3模板引擎的好处
减少了字符串的拼接操作使代码结构更清晰
使代码更易于阅读与维护
5 art-template模板引擎
5.1 art-template简介
art-template 是一个简约、超快的模板引擎。中文官网首页为http://aui.github.io/art-template/zh-cn/index.htm
5.2 art-template的安装
在浏览器中访问http://aui.github.io/art-template/zh-cn/docs/installation.html页面,找到下载链接后,鼠标右键,选择“链接另存为”,将art-template 下载到本地,然后,通过<script>
标签加载到网页上进行使用。
5.3 art-template模板引擎的基本使用
1.使用传统方式渲染UI结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/jquery.js"></script>
</head>
<body>
<div id="title"></div>
<div>姓名:<span id="name"></span></div>
<div>年龄:<span id="age"></span></div>
<div>会员:<span id="isVIP"></span></div>
<div>注册时间:<span id="regTime"></span></div>
<div>爱好:
<ul id="hobby">
<li>爱好1</li>
<li>爱好2</li>
</ul>
</div>
<script>
var data = {
title: '<h3>用户信息</h3>',
name : 'zs',
age : 20,
isVIP: true,
regTime: new Date() ,
hobby: ['吃饭', '睡觉', '打豆豆']
}
$(function() {
$('#title').html(data.title)
$('#name').html(data.name)
$('#age').html(data.age)
$('#isVIP').html(data.isVIP)
$('#regTime').html(data.regTime)
$('#regTime').html(data.regTime)
var rows = []
$.each(data.hobby,function(i, item) {
rows.push('<li>'+ item + '</li>')
})
$('#hobby').html(rows.join(''))
})
</script>
</body>
</html>
2.art-template的使用步骤
- 导入art-template
- 定义数据
- 定义模板
- 调用template函数
- 渲染HTML结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 1.导入模板引擎 -->
<!-- 导入后,在windows全局,多一个函数,叫做template(‘模板的ID’,需要渲染的数据对象) -->
<script src="./lib/template-web.js"></script>
<!-- 使用jQuery进行DOM操作 -->
<script src="./lib/jquery.js"></script>
</head>
<body>
<div id="container"></div>
<!-- 3.定义模板 -->
<!-- 3.1 模板的HTML结构,一定要定义到script中 -->
<!-- 3.2 设置type为“text/html”-->
<!-- 3.3 占位符 -->
<script type="text/html" id="tpl-user">
<h1>{{name}}</h1>
</script>
<script>
// 2.定义需要渲染的数据
var data = {
name: 'zs'
}
// 4.调用template函数
// 返回的是一个 渲染好HTML结构的字符串
var htmlStr = template('tpl-user',data)
console.log(htmlStr);
// 5.渲染HTML结构
$('#container').html(htmlStr)
</script>
</body>
</html>
5.4 art-template标准语法
1.什么是标准语法
art-template 提供了{{}}这种语法格式,在{{}}内可以进行变量输出,或循环数组等操作,这种{{}}语法在art-template中被称为标准语法。
2.标准语法–输出
{{value}}
{(obj. key}}
{{obj [‘key’] }}
{{a ? b : c}}
{{a ll b}
{{a + b}}
在{{}}语法中,可以进行变量的输出、对象属性的输出、三元表达式输出、逻辑或输出、加减乘除等表达式输出。
3.标准语法–原文输出
{{@ value )
如果要输出的 value值中,包含了HTML标签结构,则需要使用原文输出语法,才能保证HTML标签被正常渲染。
4.标准语法-条件输出
如果要实现条件输出,则可以在{{}}中使用if … else if … /if 的方式,进行按需输出。
5.标准语法-循环输出
如果要实现循环输出,则可以在{{}}内,通过each语法循环数组,当前循环的索引使用$index
进行访问,当前的循环项使用$value
进行访问。
6.标准语法–过滤器
过滤器的本质,就是一个function处理函数。
过滤器的使用语法:
{{value | filterName}}
过滤器语法类似管道操作符,它的上一个输出作为下一个输入。
定义过滤器的基本语法如下:
template.defaults.imports.filterName = function(value){ /*return处理的结果*/}
其中filterName
是自己定义的。
举例:
<div>注册时间:{{regTime | dateFormat} }</div>
定义一个格式化时间的过滤器dateFormat 如下:
template.defaults.imports.dateFormat = function (date) {
var y = date.getFullYear ()
var m = date.getMonth() + 1
var d = date.getDate()
return y + '-' + m + '-' + d //注意,过滤器最后一定要return一个值
}
5.5 案例-新闻列表
1.实现步骤
①获取新闻数据
②定义template模板
③编译模板
④定义时间过滤器
⑤定义补零函数
news.html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./assets/news.css">
<script src="./lib/template-web.js"></script>
<script src="./lib/jquery.js"></script>
<script src="./js/news.js"></script>
</head>
<body>
<div id="new-list">
</div>
<script type="text/html" id="tpl">
<!-- 这个data可以被访问到 -->
<!-- 可能是由于js文件中设置了res,res是个包含data的对象,故可以访问data -->
{{each data}}
<div class="news-item">
<!-- 标准语法设置标签的属性 -->
<img src="{{'http://www.liulongbin.top:3006' + $value.img}}" alt="" class="thumb">
<div class="right-box">
<h1 class="title">{{$value.title}}</h1>
<div class="tags">
{{each $value.tags.split(',')}}
<span>{{$value}}</span>
{{/each}}
</div>
<div class="footer">
<div>
<span>{{$value.source}}</span>
<span>{{$value.time | dateFormat}}</span>
</div>
<span>评论数:{{$value.cmtcount}}</span>
</div>
</div>
</div>
{{/each}}
</script>
</body>
</html>
news.js代码如下:
$(function() {
// 给时间补零的函数
function padZero(n) {
if(n < 10) {
return '0' + n
}else {
return n
}
}
// 定义格式化事件的过滤器
template.defaults.imports.dateFormat = function(dtStr) {
var dt = new Date(dtStr)
console.log(dt);
var y = dt.getFullYear(dt)
var m = padZero(dt.getMonth(dt) + 1)
var d = padZero(dt.getDate(dt))
var hh = padZero(dt.getHours(dt))
var mm = padZero(dt.getMinutes(dt))
var ss = padZero(dt.getSeconds(dt))
// return 'yyyy-mm-dd hh:mm:ss'
return y + '-' + m + '-' + d + ' ' + hh + ':' + mm + ':' + ss
}
$.get('http://www.liulongbin.top:3006/api/news', function(res) {
console.log(res);
if(res.status !== 200) {
return alert (res.msg + ',获取新闻列表数据失败!')
}
var htmlStr = template('tpl', res)
$('#new-list').html(htmlStr)
})
})
news.css代码如下:
#new-list .news-item {
width: 1000px;
height: 215px;
border: 1px solid #000;
}
#new-list .news-item img {
float: left;
height: 195px;
margin-top: 10px
}
#new-list .news-item .right-box {
position: relative;
float: left;
width: 600px;
height: 215px;
/* width: 700px; */
padding-left: 20px;
/* padding-bottom: 10px; */
}
#new-list .news-item .right-box title {
height: 200px;
}
.tags {
position: absolute;
top: 130px;
}
.tags span {
background-color: #eee;
border-radius: 10px;
padding: 0 5px;
}
.footer {
position: absolute;
bottom: 10px;
width: 600px;
}
.footer div {
float: left;
}
.footer>span {
float: left;
position: absolute;
right: 10px;
}
6 模板引擎的实现原理
6.1正则与字符串操作
1.基本语法
exec()函数用于检索字符串中的正则表达式的匹配。
如果字符串中有匹配的值,则返回该匹配值,否则返回null。
RegExpObject.exec(string)
示例代码如下:
var str = 'hello'var pattern = /o/// 输出的结果["o", index: 4, input: "hello", groups: undefined]console.log(pattern.exec(str))
2.分组
正则表达式中()包起来的内容表示一个分组,可以通过分组来提取自己想要的内容,示例代码如下;
var str = '<div>我是{{name}}</div>'var pattern = /{{([a-zA-Z]+)}}/var patternResult = pattern.exec(str)console.log(patternResult)// 得到name 相关的分组信息// ["{{name}} ","name ",index: 7,input: "<div>我是({name } )</div>",groups: undefined]
待补 P74
三、Ajax加强
目标:
能够知道如何使用XMLHttpRequest发起Ajax请求
能够知道如何封装自己的Ajax函数
能够使用XMLHttpRequest Level2中提供的新特性
能够知道jQuery中如何实现文件上传与loading效果
能够知道如何使用axios发起Ajax请求目录:
XMLHttpRequest的基本使用
数据交换格式
封装自己的Ajax函数
XMLHttpRequest Level2的新特性
jQuery高级用法
axios
待补
四、跨域与JSONP
目标:
能够知道什么是同源策略和跨域
能够知道什么是JSONP
能够说出JSONP的实现原理
能够知道防抖和节流的概念
1.了解同源策略和跨域
1.1同源策略
1.什么是同源
如果两个页面的协议,域名和端口都相同,则两个页面具有相同的源。
例如,下表给出了相对于http://www.test.com/index.html 页面的同源检测:
2.什么是同源策略
同源策略(英文全称Same origin policy)是浏览器提供的一个安全功能。
MDN官方给定的概念:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
通俗的理解:浏览器规定,A网站的JavaScript,不允许和非同源的网站C之间,进行资源的交互,例如:
①无法读取非同源网页的Cookie、LocalStorage和IndexedDB
②无法接触非同源网页的DOM
③无法向非同源地址发送 Ajax请求
1.2跨域
1.什么是跨域
同源指的是两个URL的协议、域名、端口一致,反之,则是跨域。
出现跨域的根本原因:浏览器的同源策略不允许非同源的URL之间进行资源的交互。
网页: http://www.test.com/index.html
接口: http://www.api.com/userlist
2.浏览器对跨域请求的拦截
注意:浏览器允许发起跨域请求,但是,跨域请求回来的数据,会被浏览器拦截,无法被页面获取到!
3.如何实现跨域数据请求
现如今,实现跨域数据请求,最主要的两种解决方案,分别是JSONP和CORS。
JSONP:出现的早,兼容性好(兼容低版本IE)。是前端程序员为了解决跨域问题,被迫想出来的一种临时解决方案。缺点是只支持GET请求,不支持POST 请求。
CORS:出现的较晚,它是W3C标准,属于跨域Ajiax请求的根本解决方案。支持GET和 POST请求。缺点是不兼容某些低版本的浏览器。
2.JSONP
2.1 什么是JSONP
JSONP (SON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
2.2 JSONP的实现原理
由于浏览器同源策略的限制,网页中无法通过 Ajax请求非同源的接口数据。但是<script>
标签不受浏览器同源策略的影响,可以通过 src属性,请求非同源的js脚本。
因此,JSONP的实现原理,就是通过<script>
标签的src属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据。
3.案例-淘宝搜索
3.5输入框的防抖
1.什么是防抖
防抖策略(debounce)是当事件被触发后,延迟n秒后再执行回调,如果在这 n秒内事件又被触发,则重新计时。
2.防抖的应用场景
用户在输入框中连续输入一串字符时,可以通过防抖策略、只在输入完后,才执行查询的请求,这样可以有效减少请求次数,节约请求资源;
3.实现输入框的防抖
var timer = null // 1.防抖动的 timerfunction debounceSearch (keywords) { // 2.定义防抖的函数 timer = setTimeout (function() { //发起JSONP请求 getSuggestList (keywords) }, 500) $('#ipt').on( 'keyup', function(){ // 3.在触发keyup事件时,立即清空 timer clearTimeout (timer) // ...省略其他代码 debounceSearch (keywords) })
4.防抖和节流
五、HTTP加强
能够说出什么是HTTP协议
能够知道HTTP请求消息的组成部分
能够知道HTTP响应消息的组成部分
能够说出常见的请求方法
能够说出常见的响应状态码
目录:
1.HTTP协议简介
1.1什么是通信
通信,就是信息的传递和交换。
通信三要素:
通信的主体
通信的内容
通信的方式
1.现实生活中的通信
案例:张三要把自己考上传智专修学院的好消息写信告诉自己的好朋友李四。
其中:
通信的主体是张三和李四;
通信的内容是考上传智专修学院;
通信的方式是写信;
2.互联网中的通信
案例:服务器把传智专修学院的简介通过响应的方式发送给客户端浏览器。
其中,
通信的主体是服务器和客户端浏览器;
通信的内容是传智专修学院的简介;
通信的方式是响应;
1.2什么是通信协议
通信协议(Communication Protocol)是指通信的双方完成通信所必须遵守的规则和约定。
通俗的理解:通信双方采用约定好的格式来发送和接收消息,这种事先约定好的通信格式,就叫做通信协议。
1.现实生活中的通信协议
张三与李四采用写信的方式进行通信,在填写信封时,写信的双方需要遵守固定的规则。信封的填写规则就是一种通信协议。
2.互联网中的通信协议
客户端与服务器之间要实现网页内容的传输,则通信的双方必须遵守网页内容的传输协议。
网页内容又叫做超文本,因此网页内容的传输协议又叫做超文本传输协议(HyperText Transfer Protocol),简称HTTP协议。
1.3 HTTP
1.什么是HTTP协议
HTTP协议即超文本传送协议(HyperText Transfer Protocol),它规定了客户端与服务器之间进行网页内容传输时,所必须遵守的传输格式。
例如:
客户端要以HTTP协议要求的格式把数据提交到服务器
服务器要以HTTP协议要求的格式把内容响应给客户端
2.HTTP协议的交互模型
HTTP协议采用了请求/响应的交互模型。
2.HTTP请求消息
2.1什么是HTTP请求消息
由于HTTP协议属于客户端浏览器和服务器之间的通信协议。因此,客户端发起的请求叫做HTTP请求,客户端发送到服务器的消息,叫做HTTP请求消息。
注意:HTTP请求消息又叫做HTTP请求报文。
2.2 HTTP请求消息的组成部分
HTTP请求消息由请求行(request line)、请求头部( header)、空行和请求体4个部分组成。
1.请求行
请求行由请求方式、URL和HTTP协议版本3个部分组成,他们之间使用空格隔开。
2.请求头部
请求头部用来描述客户端的基本信息,从而把客户端相关的信息告知服务器。
比如: User-Agent
用来说明当前是什么类型的浏览器;Content-Type
用来描述发送到服务器的数据格式;Accept
用来描述客户端能够接收什么类型的返同内容:Accept-Language
用来描述客户端期望接收哪种人类语言的文本内容。
请求头部由多行键/值对组成,每行的键和值之间用英文的冒号分隔。
2.请求头部–常见的请求头字段
3.空行
最后一个请求头字段的后面是一个空行,通知服务器请求头部至此结束。
请求消息中的空行,用来分隔请求头部与请求体。
4.请求体
请求体中存放的,是要通过POST方式提交到服务器的数据。
**注意:**只有POST请求才有请求体,GET请求没有请求体!
3.HTTP响应消息
3.1什么是HTTP响应消息
响应消息就是服务器响应给客户端的消息内容,也叫作响应报文。
3.2 HTTP响应消息的组成部分
HTTP响应消息由状态行、响应头部、空行和响应体4个部分组成,如下图所示:
1.状态行
状态行由HTTP协议版本、状态码和状态码的描述文本3个部分组成,他们之间使用空格隔开。
2.响应头部
响应头部用来描述服务器的基本信息。响应头部由多行键/值对组成,每行的键和值之间用英文的冒号分隔。
2.响应头部-常见的响应头字段
关于更多响应头字段的描述,可以查看MDN官方文档: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers
3.空行
在最后一个响应头部字段结束之后,会紧跟一个空行,用来通知客户端响应头部至此结束。
响应消息中的空行,用来分隔响应头部与响应体。
4.响应体
响应体中存放的,是服务器响应给客户端的资源内容。
4.HTTP请求方法
4.1什么是HTTP请求方法
HTTR请求方法,属于HTTP协议中的一部分,请求方法的作用是:用来表明要对服务器上的资源执行的操作。
最常用的请求方法是 GET和POST.
4.2 HTTP的请求方法
5.HTTP响应状态代码
5.1什么是HTTP响应状态码
HTTP响应状态码(HTTP Status Code),也属于HTTP协议的一部分,用来标识响应的状态。
响应状态码会随着响应消息一起被发送至客户端浏览器,浏览器根据服务器返回的响应状态码,就能知道这次HTTP请求的结果是成功还是失败了。
5.2 HTTP响应状态码的组成及分类
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字用来对状态码进行细分。
HTTP状态码共分为5种类型:
5.3常见的HTTP响应状态码
1.2**成功相关
的响应状态码
2**
范围的状态码,表示服务器已成功接收到请求并进行处理。常见的2**类型的状态码如下:
2.3**重定向相关
的响应状态码
3**范围的状态码,表示表示服务器要求客户端重定向,需要客户端进一步的操作以完成资源的请求。常见的3**类型的状态码如下:
3.4**客户端错误相关
的响应状态码
4**
范围的状态码,表示客户端的请求有非法内容,从而导致这次请求失败。常见的4**类型的状态码如下;
4.5**
服务端错误相关的响应状态码
5**
范围的状态码,表示服务器未能正常处理客户端的请求而出现意外错误。常见的5**类型的状态码如下;
六、Git
能够掌握Git基本命令的使用
能够使用Github创建和维护远程仓库
能够掌握Git分支的基本使用
1.起步
起步-关于版本控制
1.文件的版本
操作麻烦
每次都需要复制→粘贴→重命名
命名不规范
无法通过文件名知道具体做了哪些修改
容易丢失
如果硬盘故障或不小心删除,文件很容易丢失
协作困难
需要手动合并每个人对项目文件的修改,合并时极易出错
2.版本控制软件
3.使用版本控制软件的好处
操作简便
只需识记几组简单的终端命令,即可快速上手常见的版本控制软件
易于对比
基于版本控制软件提供的功能,能够方便地比较文件的变化细节,从而查找出导致问题的原因
易于回溯
可以将选定的文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态
不易丢失
在版本控制软件中,被用户误删除的文件,可以轻松的恢复回来
协作方便
基于版本控制软件提供的分支功能,可以轻松实现多人协作开发时的代码合并操作
4.版本控制系统的分类
4.1本地版本控制系统
特点:
使用软件来记录文件的不同版本,提高了工作效率,降低了手动维护版本的出错率
缺点:
①单机运行,不支持多人协作开发
②版本数据库故障后,所有历史更新记录会丢失
4.2集中化的版本控制系统
特点:基于服务器、客户端的运行模式
①服务器保存文件的所有更新记录
②客户端只保留最新的文件版本
优点:联网运行,支持多人协作开发
缺点:
①不支持离线提交版本更新
②中心服务器崩溃后,所有人无法正常工作
③版本数据库故障后,所有历史更新记录会丢失
4.3 分布式版本控制系统
特点:基于服务器、客户端的运行模式
服务器保存文件的所有更新版本
客户端是服务器的完整备份,并不是只保留文件的最新版本
优点:
①联网运行,支持多人协作开发
②客户端断网后支持离线本地提交版本更新
③服务器故障或损坏后,可使用任何一个客户端的备份进行恢复
典型代表:Git
起步-Git基础概念
1.什么是Git
Git是一个开源的分布式版本控制系统,是目前世界上最先进、最流行的版本控制系统。可以快速高效地处理从很小到非常大的项目版本管理。
特点:项目越大越复杂,协同开发者越多,越能体现出Git的高性能和高可用性!
2.Git 的特性
Git之所以快速和高效,主要依赖于它的如下两个特性:
①直接记录快照,而非差异比较
②近乎所有操作都是本地执行
2.1 SVN的差异比较
传统的版本控制系统(例如SVN)是基于差异的版本控制,它们存储的是一组基本文件和每个文件随时间逐步累积的差异。
好处:节省磁盘空间
缺点:耗时、效率低
在每次切换版本的时候,都需要在基本文件的基础上,应用每个差异,从而生成目标版本对应的文件。
2.2 Git的记录快照
Git快照是在原有文件版本的基础上重新生成一份新的文件,类似于备份。为了效率,如果文件没有修改,Git不再重新存储该文件,而是只保留一个链接指向之前存储的文件。
缺点:占用磁盘空间较大
优点:版本切换时非常快,因为每个版本都是完整的文件快照,切换版本时直接恢复目标版本的快照即可。
特点:空间换时间
2.3近乎所有操作都是本地执行
在Git中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。
特性:
①断网后依旧可以在本地对项目进行版本管理
②联网后,把本地修改的记录同步到云端服务器即可
3.Git中的三个区域
使用Git管理的项目,拥有三个区域,分别是工作区、暂存区、Git仓库。
4.Git中的三种状态
注意:
- 工作区的文件被修改了,但还没有放到暂存区,就是已修改状态。
- 如果文件已修改并放入暂存区,就属于已暂存状态。
- 如果Git仓库中保存着特定版本的文件,就属于已提交状态。
5.基本的Git工作流程
基本的Git工作流程如下:
① 在工作区中修改文件
② 将你想要下次提交的更改进行暂存
③ 提交更新,找到暂存区的文件,将快照永久性
存储到Git仓库
2.Git基础
Git基础–安装并配置Git
1.在 Windows中下载并安装Git
在开始使用Git管理项目的版本之前,需要将它安装到计算机上。可以使用浏览器访问如下的网址,根据自己的操作系统,选择下载对应的Git安装包:
https://git-scm.com/downloads
查看是否安装成功:
鼠标右键弹出的选项框中有“Git GUI Here” 和“Git Bash Here”,则安装成功。
2.配置用户信息
安装完Git之后,要做的第一件事就是设置自己的用户名和邮件地址。因为通过Git对项目进行版本管理的时候,Git需要使用这些基本信息,来记录是谁对项目进行了操作:
(观察:git命令后面的双杠接的是单词,单杠接的是缩写)
git config --global user.name "itheima"git config --global user.email "itheima@itcast.cn"
注意:如果使用了--global
选项,那么该命令只需要运行一次,即可永久生效。
3.Git的全局配置文件
通过git config --global user.name
和git config --global user.email
配置的用户名和邮箱地址,会被写入到C:/Users/用户名文件夹/.gitconfig
文件中。这个文件是Git的全局配置文件,配置一次即可永久生效。
可以使用记事本打开此文件,从而查看自己曾经对Git做了哪些全局性的配置。
4.检查配置信息
除了使用记事本查看全局的配置信息之外,还可以运行如下的终端命令,快速的查看Git的全局配置信息:
# 查看所有的全局配置项
git config --list --global
# 查看指定的全局配置项
git config user.name
git config user.email
5.获取帮助信息
可以使用git help <verb>
命令,无需联网即可在浏览器中打开帮助手册,例如:
# 要想打开git config命令的帮助手册
git help config
如果不想查看完整的手册,那么可以用-h
选项获得更简明的“help”输出:
# 想要获取git config命令的快速参考
2 git config -h
Git基础- Git的基本操作
1.获取Git仓库的两种方式
①将尚未进行版本控制的本地目录转换为Git 仓库
②从其它服务器克隆一个已存在的Git仓库
以上两种方式都能够在自己的电脑上得到一个可用的Git仓库
2.在现有目录中初始化仓库
如果自己有一个尚未进行版本控制的项目目录,想要用Git 来控制它,需要执行如下两个步骤:
①在项目目录中,通过鼠标右键打开“Git Bash"
②执行git init
命令将当前的目录转化为Git仓库
git init
命令会创建一个名为.git
的隐藏目录,这个.git
目录就是当前项目的Git仓库,里面包含了初始的必要文件,这些文件是Git仓库的必要组成部分。
3.工作区中文件的4种状态
工作区中的每一个文件可能有4种状态,这四种状态共分为两大类,如图所示:
Git操作的终极结果:让工作区中的文件都处于“未修改”的状态。
4.检查文件的状态
可以使用git status命令查看文件处于什么状态,例如:
在状态报告中可以看到新建的index.html文件出现在Untracked files(未跟踪的文件)下面。
未跟踪的文件意味着Git在之前的快照(提交)中没有这些文件;Git 不会自动将之纳入跟踪范围,除非明确地告诉它“我需要使用Git 跟踪管理该文件”。
5.以精简的方式显示文件状态
使用git status
输出的状态报告很详细,但有些繁琐。如果希望以精简的方式显示文件的状态,可以使用如下两条完全等价的命令,其中-s
是--short
的简写形式:
# 以精简的方式显示文件状态git status -sgit status --short
未跟踪文件前面有红色的??标记,例如:
6.跟踪新文件
使用命令git add
开始跟踪一个文件。所以,要跟踪index.html 文件,运行如下的命令即可:
git add index.html
此时再运行git status 命令,会看到 index.html文件在Changes to be committed这行的下面,说明已被跟踪,并处于暂存状态:
以精简的方式显示文件的状态:
新添加到暂存区中的文件前面有绿色的A标记
7.提交更新
现在暂存区中有一个index.html文件等待被提交到Git仓库中进行保存。可以执行git commit
命令进行提交,其中-m
选项后面是本次的提交消息,用来对提交的内容做进一步的描述:
git commit -m“新建了index.html文件"
提交成功之后,会显示如下的信息:
提交成功之后,再次检查文件的状态,得到提示如下:
证明工作区中所有的文件都处于“未修改”的状态,没有任何文件需要被提交。
8.对已提交的文件进行修改
目前,index.html文件已经被Git跟踪,并且工作区和Git仓库中的index.html文件内容保持一致。当我们修改了工作区中 index.html的内容之后,再次运行git status
和git status -s
命令,会看到如下的内容;
文件index.html出现在Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。
注意:修改过的、没有放入暂存区的文件前面有红色的M标记。
9.暂存已修改的文件
目前,工作区中的 index.html文件已被修改,如果要暂存这次修改,需要再次运行git add
命令,这个命令是个多功能的命令,主要有如下3个功效:
①可以用它开始跟踪新文件
②把已跟踪的、且已修改的文件放到暂存区
③把有冲突的文件标记为已解决状态
10.提交已暂存的文件
再次运行git commit -m "提交消息"命令,即可将暂存区中记录的index.html的快照,提交到Git仓库中进行保存:
11.撤销对文件的修改
撤销对文件的修改指的是:把对工作区中对应文件的修改,还原成Git仓库中所保存的版本。
操作的结果:所有的修改会丢失,且无法恢复!危险性比较高,请慎重操作!
P208待补
3.Github
4.Git分支
大事件项目
1.如何使用github管理项目
git init
在现有目录中初始化仓库
git add .
开始跟踪项目中的文件,处于暂存状态
git commit -m"init project"
提交并保存到本地的Git仓库中
github上新建仓库“new repository“
在项目的当前目录下执行跳转页面提示的命令
# 对本地仓库和github仓库进行关联
git remote add origin https://github.com/jingw1214/web_bigevent.git
git branch -M main
# 将本地仓库上传至github
git push -u origin main
刷新页面后,可以查看到上传至该项目中的文件。
2.注册登录功能实现
git checkout -b login
创建并切换至login分支
git branch
可以查看项目的分支情况,此时有 * 的是当前分支。
使用Live Server插件
因为“View in Browser”查看网页,是file://协议
,不便于开发;使用LIve Server插件,是http协议,更加符合开发需要。
启用 Live Server :页面右键
点击关闭 Live Server
使用layui
网址:layui.com
绘制文本框前的小图标
为表单验证添加校验规则
1.导入js文件
在body标签底部,导入自己的页面的js文件的语句之前,导入layui的js文件。(前后顺序要保证,自己的js中可能用到layui.js文件中的对象或方法。)
2.layui官网中,页面元素→表单→相关动态→表单验证,查看layui_lay-verify
属性的用法
自定义校验规则
1.查看是否有对应的官方的校验规则
2.没有的话,自定义,调用form.verify()方法
,以键值对的形式自定义校验规则
具体:layui官网中,页面元素→表单→相关动态→表单验证,查看。
// 从layui 中获取form对象
// 只要导入了layui.js,就可以使用layui的对象了
var form = layui.form
// 通过form.verify()函数自定义校验规则
form.verify({
// [\S]表示不为空的字符
pwd:[/^[\S](6,12)$/,'密码必须6到12位,且不能出现空格']
})
3.在表单项的lay-verify属性
中添加上pwd
值
// 原来的lay-verify属性
lay-verify="required"
// 添加上`pwd`值,用 `|` 分隔
lay-verify="required|pwd"
密码一致校验规则
函数的形式 添加校验规则
// 从layui 中获取form对象
// 只要导入了layui.js,就可以使用layui的对象了
var form = layui.form
// 通过form.verify()函数自定义校验规则
form.verify({
// 两次密码一致的校验
repwd: function(value, item){ //value:表单的值、item:表单的DOM对象
// 拿到密码框中的内容
// 判断密码框中的内容是否等于确认密码框中的内容
// 如果不一致,则return 提示消息
// jQuery val() 设置或返回表单字段的值,获得输入字段的值:
var pwd1 = $('.reg-box [name="password"]').val()
if(value !== pwd1) {
return '两次密码不一致';
}
}
注册功能实现
API说明文档的链接:showdoc.cc/escook?page_id=3707158761215217
项目的请求根路径为 http://api-breakingnews-web.itheima.net
// 监听注册表单的提交时间
$('#form_reg').on('submit',function(e) {
// 阻止该事件的默认提交行为。
// 当提交表单后,页面会立即跳转到action 属性指定的 URL地址
e.preventDefault()
// 获取用户输入的用户名和密码,存储到data对象中
var data = {username:$('#form_reg [name=username]').val(),password:$('#form_reg [name=password]').val()}
// 发起ajax的post请求
$.post('http://api-breakingnews-web.itheima.net/api/reguser',data, function(res) {
if(res.status !== 0) {
return console.log(res.message)
}
console.log('注册成功');
})
})
使用内置模块–弹出层layer
的msg
方法提示用户
var layer = layui.layer layer.msg(res.message) // 弹出提示消息,并在3秒后消失
以header请求头的形式发送token
将登录返回的结果中的token值存储到localStorage中
// 将登录成功得到的token字符串,保存到localStorage中localStorage.setItem('token',res.token)
点击F12打开检查,查看Application下的Storage里的该网页,点击清空按钮;
进行登录操作后,该处会出现一条数据,存储到localStorage中的token。
代码优化:自动将把请求根路径和url拼接起来
1.新建一个baseAPI.js文件
<!-- 导入自己封装的baseAPI.js文件--><!-- 放到jQuery 的js文件之后,自己的js文件之前 --><script src="assets/js/baseAPI.js"></script>
2.$.ajaxPrefilter()
函数拦截每一次的ajax请求(即,在请求之前会调用该函数),并获取配置对象。
// 每次调用$.get()或$.post()或$.ajax()的时候,// 会先调用ajaxPrefilter这个函数// 在这个函数中,可以拿到我们给ajax提供的配置对象$.ajaxPrefilter(function(options) { console.log(options.url); // 在发起ajax请求之前,统一拼接请求的根路径 options.url = 'http://api-breakingnews-web.itheima.net' + options.url})
这样,在根路径发生变化时,就不用一个个地去修改了。
提交login分支的代码到github
1.git status
查看文件的状态
2.git branch
查看当前所处的分支
如图所示,处于本地的login分支。
3.将代码提交到本地
git add .
将所有的已修改的文件添加到暂存区
git commit -m"XXXX"
提交
4.将本地的login分支推送到云端的origin仓库中,也命名为login分支
git push -u origin login
如果报错10054,可以多尝试几遍就OK了。
4.将login的代码合并到master
git branch
查看当前所处的分支
git checkout master
切换至master分支
git merge login
本地的master代码是最新的
git push
本地代码push到云端
3.index实现
查阅layui的文档,左侧菜单中选择页面元素下的布局→右边的悬浮菜单中选择后台布局。可以查看layui提供的布局效果和代码,复制粘贴至index文件。
鼠标停留在头像上时,会弹出下拉菜单 – JavaScript功能,需要导入js文件
// 在body标签结束之前导入
<script src="./assets/lib/layui/layui.js"></script>
修改侧边栏结构
细节修改–默认折叠
观察可知,有一个li是默认折叠的,有一个li是默认展开的,对比两者区别,去掉类名layui-item-itemed
即可默认折叠。
为菜单项添加图标
使用第三方提供的图标
阿里巴巴适量图标库中,收藏→创建项目→添加图标到项目中→下载→导入iconfront.css样式表→挑选相应图标并获取类名,应用于页面:
<span class="iconfont icon-xxx"></span>
使用layui提供的图标
<i class="layui-icon layui-icon-face-smile"></i>
iframe的使用
1.内容主题区域中加入iframe标签,并填写name属性值
<div class="layui-body"> <!-- 内容主体区域 --> <iframe name="fm" src="./home/dashboard.html" frameborder="0"></iframe> </div>
2.菜单项中的a标签中的href设置为页面的html文件路径;target值设置为iframe标签的name属性值。从而设置iframe标签显示的位置
<li class="layui-nav-item"> <a href="./home/dashboard.html" target="fm"> <span class="iconfont icon-wxbzhuye"></span> 首页 </a></li>
3.由于iframe标签默认设置了宽高,所以要手动设置宽高以适应要求。
iframe { width: 100%; height: 100%;}
解决3个小问题
- 打开页面时,iframe默认显示首页的内容
– 强制指定iframe标签的src值为首页的html文件路径
<div class="layui-body">
<!-- 内容主体区域 -->
<iframe name="fm" src="./home/dashboard.html" frameborder="0"></iframe>
</div>
- 导航栏菜单项的默认选中设置
– 查看官网文档,导航栏菜单项的class设置为layui-this
即可
渲染文字头像
实现退出功能
1.给退出按钮所在的a链接的href属性设置为:javascript:;
,从而阻止a链接的默认跳转行为
2.给a链接绑定一个点击事件处理函数
3.点击事件处理函数中,弹出确认框
4.弹出框点击确定后,// 1.清空本地存储中的 token;// 2.重新跳转到login页面
// 1.清空本地存储中的 tokenlocalStorage.removeItem('token')// 2.重新跳转到login页面location.href = '/login.html'
问题:直接输入首页链接,不应该能看到首页,而应该是回车后强制跳回到login页面。
// 无论成功还是失败,最终都会调用 complete 函数
complete: function(res) {
console.log('执行了 complete 回调');
console.log(res);
// 在 complete 函数中,可以使用res.responseJSON拿到服务器响应回来的数据
if(res.responseJSON.status === 1 && res.responseJSON.message === '身份认证失败!') {
// 1.强制清空token
localStorage.removeItem('token')
// 2.强制跳转到登录页面
location.href = './login.html'
}
}
优化
在全局所有的ajax请求统一挂载complete回调函数.
在$.ajaxPrefilter(function(options){}
中加入以下代码:
// 无论成功还是失败,最终都会调用 complete 函数
options.complete = function(res) {
console.log('执行了 complete 回调');
console.log(res);
// 在 complete 函数中,可以使用res.responseJSON拿到服务器响应回来的数据
if(res.responseJSON.status === 1 && res.responseJSON.message === '身份认证失败!') {
// 1.强制清空token
localStorage.removeItem('token')
// 2.强制跳转到登录页面
location.href = './login.html'
}
}
提交代码
git status
查看文件的状态
git branch
检查所处的分支
git add .
所有的文件添加到暂存区
git commit -m"完成了主页功能的开发"
提交到本地
git push -u origin index
提交到github,将本地的index分支推送到了云端
切换到master,检查所在的分支
git merge index
本地的master代码是最新的
git push
将index的代码和master代码push到云端
4.个人中心
git checkout -b user
创建新的user分支
卡片面板的使用
初始化用户信息
// 初始化用户的基本信息
function initUserInfo() {
$.ajax({
method:'GET',
url: '/my/userinfo',
success: function(res) {
if(res.status !== 0) {
return layer.msg('获取用户信息失败')
}
console.log(res);
// $('.layui-form-item name[username]').html(res.data.username)
// 调用form.val()方法快速为表单赋值
form.val("formUser", res.data)
}
})
}
实现表单的重置效果–数据还原
实现图片上传的功能
初识node.js与内置模块
视频地址:
https://www.bilibili.com/video/BV11N411o7wU?p=342
目标:
能够知道什么是Node.js
能够知道Node.js可以做什么
能够说出Node.js 中的JavaScript的组成部分
能够使用fs模块读写操作文件
能够使用path模块处理路径
能够使用http模块写一个基本的web 服务器
1.初识 Node.js
1.1 Node.js 简介
1.1.1 什么是Node.js
Node.js is a JavaScript ru ntime built on Chrome’s V8 JavaScript engine.
Node.js
是一个基于Chrome V8 引擎的JavaScript运行环境。
Node.js 的官网地址: https://nodejs.org/zh-cn/
1.1.2 Node.js 中的JavaScript运行环境
注意:
- 浏览器是JavaScript的前端运行环境。
- Node.js是JavaScript的后端运行环境。
- Node.js 中无法调用DOM和BOM等浏览器内置API。
Node.js 特性:
- event-driven 事件驱动
- non-blocking I/O model 非阻塞IO模型(异步)
- lightweight and efficient 轻量高效
1.1.3.Node.js 可以做什么
- Web服务器后台
- 命令行工具
- npm(node)
- git(c语言)
- hexo(node)
- 等等
- 对于前端开发军程师来讲,接触 node最多的是它的命令行工具。自己写的很少,主要是使用别人第三方的
- webpack
- gulpo npm
1.1.4 Node.js 怎么学
浏览器中的JavaScript学习路径:
JavaScript基础语法+浏览器内置API(DOM + BOM) + 第三方库(jQuery、art-template等)
Node.js 的学习路径:
JavaScript 基础语法 + Node.,js 内置API模块(fs、path、http等)+第三方API模块(express、mysql等)
1.1.5一些资源
- 《深入浅出Node.js》
- 《Node.,js权威指南》
- JavaScript标准参考教程(alpha) : http://javascript.ruanyifeng.com/
- Node 入门:http://www.nodebeginner.org/index-zh-cn.html
- 官方API文档: https://nodejs.org/dist/latest-v6.x/docslapil
- 中文文档(版本比较旧,凑合看):http://www.nodeclass.com/api/node.html
业 - CNODE社区:http:/lcnodejs.org
- CNODE-新手入门: http://cnodejs.org/getstart
1.2 Node.js 环境安装
如果希望通过 Node.js 来运行Javascript 代码,则必须在计算机上安装 Node.js 环境才行。
1.下载安装包,双击安装
安装包可以从Node.js的官网首页直接下载,进入到Node.js 的官网首页(https://nodejs.org/en/),点击绿色的按钮,下载所需的版本后,双击直接安装即可。
如果已经安装过,重新安装,会进行覆盖升级。
2.这个地方不建议修改安装目录
3.其余页面,默认下一步即可。
4.确认Node 环境 是否安装成功
打开终端,在终端输入命令 node -v
后,按下回车键,可查看已安装的 Node.js的版本号,则安装成功。
1.3 在 Node.js 环境中执行 JavaScript 代码
步骤:
- 打开终端
- 进入代码文件所在的文件目录
- 输入 node + 空格 + 要执行的js文件的相对路径
小技巧:在 Visual Studio Code 软件中,使用快捷键 Ctrl + ~,可以在软件中打开终端窗口。
浏览器中 的 JavaScript 是由 JS 核心语法 和 WebAPI 组成的。
浏览器中有 JavaScript 解析引擎,可以解析并执行 JavaScript 代码。
JavaScript 操作 DOM 和BOM 的原理:
每个浏览器都内置了DOM、BOM这样的API函数,因此,浏览器中的JavaScript才可以调用它们。
浏览器中的 JavaScript 运行环境
☆ 运行环境是指代码正常运行所需的必要环境。
1. V8 引擎负责解析和执行 JavaScript 代码
- 内置 API 是由运行环境提供的特殊接口,只能在所属的运行环境中被调用。
2. fs 文件系统模块
fs文件系统模块
是Node.js官方提供的、用来操作文件的模块。它提供了一系列的方法和属性,用来满足用户对文件的操作需求。
例如:
fs.readFile()方法
,用来读取指定文件中的内容fs.writeFile()方法
,用来向指定的文件中写入内容
2.1 导入 fs 文件系统模块
如果要在JavaScript 代码中,使用fs模块来操作文件,则需要使用require函数
先导入它:
const fs = require('fs')
2.2 读取指定文件中的内容
fs.readFile()
方法,读取指定文件中的内容
2.2.1.语法格式
fs.readFile(path[, options], callback)
参数解读:
- 参数1:必选参数,字符串,表示文件的路径。
- 参数2:可选参数,字符串,表示以什么编码格式来读取文件。
- 参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。
2.2.2 示例代码
以utf8的编码格式,读取指定文件的内容,并打印err 和dataStr的值:
// 导入 fs 文件系统模块const fs = require('fs');fs.readFile('1.js', 'utf8', function (err, dataStr) { // 如果读取成功,则 error 的值为 null,datastr就是读取的数据 // 如果读失败,则 error 的值为错误对象,dataStr的值为 undefined console.log(err); console.log(‘-----’); console.log(dataStr);// 打印成功的结果})
2.2.3 判断文件是否读取成功
可以判断 err 对象是否为 null ,从而知晓文件读取的结果
const fs = require('fs');fs.readFile('3.js', 'utf8', function (err, dataStr) { if (err) { return console.log('文件读取失败!' + err.message); } console.log('文件读取成功,内容是:' + dataStr); })
2.3 向指定的文件中写入内容
fs.writeFile()
方法,向指定的文件中写入内容
2.3.1 语法格式
fs.writeFile(file,data[, options], callback)
参数解读:
- 参数1:必选参数,需要制定一个文件路径的字符串,表示文件的存放路径
- 参数2:必选参数,表示要写入的内容
- 参数3:可选参数,表示以什么格式写入文件内容,默认是 utf8
- 参数4:必选参数,文件写入完成后的回调函数
注意:
- 覆盖写入,而不是追加
- 如果文件不存在,则会新建该文件。(写f盘,且该盘符不存在的情况下报错)
2.3.2 示例代码:
const fs = require('fs');fs.writeFile('5.js', 'console.log("hello js~~~")', function(err){ console.log(err)})
2.3.3 判断文件是否写入成功
const fs = require('fs');fs.writeFile('1.js', 'console.log("hello js~~~")', function(err){ if (err) { return console.log('文件写入失败:' + err.message); } console.log('输入成功!');})
2.4 文件路径动态拼接的问题
2.4.1 使用相对路径
在使用fs模块操作文件时,操作路径是以 ./
或 ../
开头的相对路径。
缺点:很容易出现路径动态拼接错误的问题
(原因:代码在运行的时候,会以执行node命令时所处的目录,动态拼接出被操作文件的完整路径。)
2.4.2 使用绝对路径
在使用fs模块操作文件时,直接提供完整的路径,从而防止路径动态拼接的问题。
缺点:完整的路径–移植性非常差,不利于维护
// 文件路径字符串中的“\”为转移符号,得写2个才代表一个。fs.write('D:\\projects\\Node\\01-文件读取.js', 'console.log("hello js~~~")', function(err){ //}
2.4.3 使用 __dirname(推荐使用)
__dirname
表示当前文件所处的目录
// “\\1.js” 也可以写作‘/1.js’fs.readFile(__dirname + '\\1.js', 'utf-8', function(err){ if (err) { return console.log('文件读取失败:' + err.message); } console.log('输入成功!');})}
3 path 路径模块
path模块
是Node,js官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理需求。
例如:
path.join()
方法,用来将多个路径片段拼接成一个完整的路径字符串path.basename()
方法,用来从路径字符串中,将文件名解析出来
3.1 导入 path 模块
如果要在JavaScript 代码中,使用path模块来处理路径,则需要先导入它
const path = require('path')
3.2 路径拼接
使用path.join()
方法,将多个路径片段拼接起来。
语法格式:
path.join([…paths])
参数解读:
...paths
: <string>路径片段的序列- 返回值:<string>
const path = require('path')// ../ 会把前面的一层路径抵消掉const pathStr = path.join( '/a", "/b/c', '../', './d','e')console.log(pathStr) //输出\a\b\d\econst pathStr2 = path.join(__dirname,'./files/1.txt')console. log(pathStr2)//输出当前文件所处目录\files\1.txt
注意:今后凡是涉及到路径拼接的操作,都要使用path.join()
方法进行处理,不要直接使用 +
进行字符串的拼接。(path.join()方法可以屏蔽路径中多写的“.”)
3.3 获取路径中的文件名
使用path.basename()
方法,可以获取路径中的最后一部分,经常通过这个方法获取路径中的文件名.
语法格式:
path.basename(path[, ext])
参数解读:
- path 必选参数,表示一个路径的字符串
- ext 可选参数,表示文件扩展名–不希望返回扩展名时填写。
返回:
- 表示路径中的最后一部分
代码示例:
const path = require('path')const fpath = '/a/b/c/index.html' //文件的存放路径var fullName = path.basename(fpath)console.log(fullName)//输出index.html// 注意:文件的扩展名一定要写正确,否则返回的还是带扩展名的文件名。var namewithoutExt = path.basename(fpath, '.html')console.log(namewithoutExt)//输出index
3.4 获取路径中的文件扩展名
语法格式:
path.extname(path)
参数:
path 必选参数,表示一个路径的字符串
返回:
返回得到的扩展名字符串
示例代码:
const fpath = " /a/b/c/index.html'1/路径字符串const fext = path.extname(fpath)console.log(fext)1/输出.html
3.5 综合案例 - 时钟案例
372集–待补
4. http 模块
在网络节点中,负责消费资源的电脑,叫做客户端;负责对外提供网络资源的电脑,叫做服务器。
http模块是Node,js官方提供的、用来创建 web服务器的模块。通过 http模块提供的 http.createServer()方法,就能方便的把一台普通的电脑,变成一台Web服务器。从而对外提供Web资源服务。
4.1 http 模块的使用
最简单的 http 服务:
// 加载 http 核心模块const http = require('http');// 使用 `http.createServer()`方法创建一个 Web 服务器,返回一个 Server 实例var server = http.createSever();// 当服务器收到客户端的请求后,自动触发服务器的 request 请求事件,然后执行第二个参数,回调处理server.on('request', function () { console.log('收到客户端的请求了');})// 绑定端口号,启动服务器成功会调用函数。server.listen(3000, function () { console.log('服务器启动成功, 可以通过http://127.0.0.1:3000/来进行访问。')})
4.2 请求对象和响应对象
request 请求事件的处理函数,需要接受两个参数:
- request 请求对象:请求对象可以用来获取客户端的一些请求信息,例如请求路径
- response 相应对象: 用来给客户端发送响应信息
// 当服务器收到客户端的请求后,自动触发服务器的 request 请求事件,然后执行第二个参数,回调处理server.on('request', function (request, response) { console.log('收到客户端的请求了,请求路径是:' + request.url); // response对象有一个方法: write可以用来给客户端发送响应数据 // write可以使用多次,但是最后一定要使用end来结束响应,否则客户端会一直等待 response.write('hello'); // 告诉客户端相应对象传递完整,可以呈现给用户 response.end();})
以上的http服务,无论是什么请求,响应的数据都相同。(/
、/index、/login)
4.3 根据不同的请求,响应不同的内容
模块化
视频地址:
https://www.bilibili.com/video/BV11N411o7wU?p=396
目标:
能够说出模块化的好处
能够知道CommonJS规定了哪些内容
能够说出Node.js 中模块的三大分类各自是什么
能够使用npm 管理包
能够了解什么是规范的包结构
能够了解模块的加载机制
1 模块化的基本概念
1.1 什么是模块化
模块化是指,解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块是可组合、分解和更换的单元。
编程领域中的模块化
编程领域中的模块化,就是遵守固定的规则,把一个大文件拆成独立并互相依赖的多个小模块。
把代码进行模块化拆分的好处:
提高了代码的复用性
提高了代码的可维护性
可以实现按需加载
1.2 模块化规范
模块化规范就是,对代码进行模块化的拆分与组合时,需要遵守的那些规则。
例如:
使用什么样的语法格式来引用模块
在模块中使用什么样的语法格式向外暴露成员
模块化规范的好处:大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用,利人利己。
2 Node.js 中模块化
2.1 Node.js 中模块的分类
Node.js 中根据模块来源的不同,将模块分为了3大类,分别是:
- 内置模块 (内置模块是由Node.js官方提供的,例如fs、path、http 等)
- 自定义模块(用户创建的每个.js 文件,都是自定义模块)
- 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)
2.2 加载模块
使用强大的require()方法
,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。
例如;
// 1.加载内置的fs模块const fs = require('fs') // 2.加载用户的自定义模块const custom = require( './custom.js')// 3.加载第三方模块(关于第三方模块的下载和使用,会在后面的课程中进行专门的讲解)const moment = require('moment')
注意:
- 使用require()方法加载其它模块时,会执行被加载模块中的代码。
- 在使用require()加载用户自定义模块期间,可以省略.js 的后缀名,node 会自动补全。
2.3 node.js 中的模块作用域
和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。
好处:防止了全局变量污染的问题
2.4 向外共享模块作用域中的成员
2.4.1 module对象
在每个.js 自定义模块中都有一个module 对象,它里面存储了和当前模块有关的信息。
代码:
console.log(module);
输出的结果如下:
2.4.2 module.exports 对象
在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用。
外界用require()方法导入自定义模块时,得到的就是 module.exports所指向的对象。
在一个自定义模块中,默认情况下,module.exports 是个空对象。
// 向module. exports 对象上挂载 username属性module.exports. username = 'zs'// 向module . exports 对象上挂载 sayHello方法module.exports.sayHello = function() { console.log( 'Hello! ')}
2.4.3 exports对象
由于module.exports单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了exports 对象。
默认情况下,exports 和module.exports 指向同一个对象。
最终共享的结果,还是以module.exports 指向的对象为准。
// 1.向 module.exports 对象上挂载属usernameexports.username = 'zs'// 2.向 module. exports对象上挂载方法sayHelloexports.sayHello = function() { console.log( 'Hello!')}// 3.让 module.exports指向一个全新的对象// 外界导入该模块时,module.exports指向的就是这个新的对象。exports = ( nickname: "小黑",sayti() { console.log('Hi!') }}
2.4.4 共享成员时的注意点
使用require()方法导入模块时,导入的结果,永远以module.exports指向的对象为准。
// 1.向 module.exports 对象上挂载属usernamemodule.exports.username = 'zs'// 2.向 module. exports对象上挂载方法sayHellomodule.exports.sayHello = function() { console.log( 'Hello!')}// 3.让module.exports指向一个全新的对象// 外界导入该模块时,module.exports指向的就是这个新的对象。module.exports = ( nickname: "小黑",sayti() { console.log('Hi!') }}
2.4.5 exports 和module.exports的使用误区
时刻谨记,require()模块时,得到的永远是module.exports指向的对象:
2.5 Node.js 中的模块化规范
Node.,js遵循了CommonJS模块化规范,CommonJS规定了模块的特性和各模块之间如何相互依赖。
CommonJS 规定:
- 每个模块内部,module 变量代表当前模块。
- module变量是一个对象,它的 exports属性(即 module.exports)是对外的接口。
- 加载某个模块,其实是加载该模块的 module.exports 属性。require()方法用于加载模块。
3 npm 与包
3.1包
1.什么是包
Node.js 中的第三方模块又叫做包。
就像电脑和计算机指的是相同的东西,第三方模块和包指的是同一个概念,只不过叫法不同。
2.包的来源
不同于Node.js 中的内置模块与自定义模块,包是由第三方个人或团队开发出来的,免费供所有人使用。
注意:Node.js 中的包都是免费且开源的,不需要付费即可免费下载使用。
3.为什么需要包
由于Node.js 的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发的时,效率很低。
包是基于内置模块封装出来的,提供了更高级、更方便的 API,极大的提高了开发效率。
包和内置模块之间的关系,类似于 jQuery和浏览器内置API之间的关系。
4.从哪里下载包
国外有一家IT公司,叫做npm, Inc.这家公司旗下有一个非常著名的网站: https://www.npmjs.com/,它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包,只要你有足够的耐心!
到目前位置,全球约1100多万的开发人员,通过这个包共享平台,开发并共享了超过120多万个包供我们使用。
npm, Inc.公司提供了一个地址为https://registry.npmjs.org/的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包。
注意:
- 从https://www.npmjs.com/网站上搜索自己所需要的包
- 从https://registry.npmjs.org/服务器上下载自己需要的包
3.2 npm 初体验
1.格式化时间的传统做法
- 创建格式化时间的自定义模块
- 定义格式化时间的方法
- 创建补零函数
- 从自定义模块中导出格式化时间的函数
- 导入格式化时间的自定义模块
- 调用格式化时间的函数
⒉格式化时间的高级做法
- 使用npm包管理工具,在项目中安装格式化时间的包moment
- 使用require(导入格式化时间的包
- 参考 moment的官方API文档对时间进行格式化
3.在项目中安装包的命令
如果想在项目中安装指定名称的包,需要运行如下的命令:
npm install 包的完整名称
上述的装包命令,可以简写成如下格式:
npm i 完整的包名称
4.初次装包后多了哪些文件
初次装包完成后,在项目文件夹下多一个叫做node_modules 的文件夹和package-lock.json 的配置文件。
node_modules文件夹用来存放所有已安装到项目中的包。require()导入第三方包时,就是从这个目录中查找并加载包。package-lockjson配置文件用来记录node_modules目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。
注意:程序员不要手动修改node modules 或package-lockjson文件中的任何代码,npm包管理工具会自动维护它们。
5.安装指定版本的包
默认情况下,使用npm install命令安装包的时候,会自动安装最新版本的包。如果需要安装指定版本的包,可以在包名之后,通过**@符号**指定具体的版本,例如:
nmp i moment@2.22.2
6.包的语义化版本规范
包的版本号是以“点分十进制”形式进行定义的,总共有三位数字,例如2.24.0其中每一位数字所代表的的含义如下:
第1位数字:大版本 底层重构
第2位数字:功能版本
第3位数字:Bug修复版本
版本号提升的规则:只要前面的版本号增长了,则后面的版本号归零。
3.3.3 包管理配置文件
npm规定,在项目根目录中,必须提供一个叫做package.json 的包管理配置文件。用来记录与项目有关的一些配置信息。例如:
- 项目的名称、版本号、描述等
- 项目中都用到了哪些包
- 哪些包只在开发期间会用到
- 那些包在开发和部署时都需要用到
- 1.多人协作的问题
遇到的问题:第三方包的体积过大,不方便团队成员之间共亨项目源代码。
解决方案:共享时剔除 node_modules
2.如何记录项目中安装了哪些包
在项目根目录中,创建一个叫做 package.json 的配置文件,即可用来记录项目中安装了哪些包。从而方便剔除node_modules目录之后,在团队成员之间共亨项目的源代码。
注意;今后在项目开发中,一定要把node modules文件夹,添加到 .gitignore忽略文件中。
3.快速创建package.json
npm包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快速创建 package.json 这个包管理配置文件:
作用:在执行命令所处的目录中,快速新建package.json文件
npm init -y
注意:
- 上述命令只能在英文的目录下成功运行!所以,项目文件夹的名称一定要使用英文命名,不要使用中文,不能出现空格。
- 运行npm install 命令安装包的时候,npm包管理工具会自动把包的名称和版本号,记录到package.json中。
4.dependencies节点
package.json文件中,有一个dependencies节点,专门用来记录您使用npm install命令安装了哪些包。
5.一次性安装所有包
当我们拿到一个剔除了 node_modules 的项目之后,需要先把所有的包下载到项目中,才能将项目运行起来。
否则会报类似于下面的错误:
- Error: Cannot find module " moment"
可以运行npm install命令(或npm i)一次性安装所有的依赖包:
//执行npm install 命令时,npm 包管理工具会先读取package.json 中的dependencies节点, //读取到记录的所有依赖包名称和版本号之后,npm包管理工具会把这些包一次性下载到项目中 npm install
6.卸载包
可以运行npm uninstall命令,来卸载指定的包:
//使用npm uninstall具体的包名来卸载包npm uninstall moment
注意: npm uninstall 命令执行成功后,会把卸载的包,自动从 package.json 的dependencies 中移除掉。
7.devDependencies节点
如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到 devDependencies节点中。
与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies 节点中。
您可以使用如下的命令,将包记录到devDependencies节点中:
// 安装指定的包,并记录到 devDependencies 节点中npm i 包名 -D// 注意:上述命令是简写形式,等价于下面完整的写法:npm install 包名 --save-dev
☆ 判断是否放在devDependencies节点中,可以通过npm网站中的介绍,如下图,有红框标记部分的语句,则要放在devDependencies节点中。
3.3.4 解决下包速度慢的问题
1.下包速度慢的原因
在使用npm 下包的时候,默认从国外的 https://registry.npmjs.org/服务器进行下载,此时,网络数据的传输需要经过漫长的海底光缆,因此下包速度会很慢。
2.淘宝NPM镜像服务器
淘宝在国内搭建了一个服务器,专门把国外官方服务器上的包同步到国内的服务器,然后在国内提供下包的服务。从而极大的提高了下包的速度。
扩展:
镜像(Mirroring)是一种文件存储形式,一个磁盘上的数据在另一个磁捂上存在一个完全相同的副本即为镜像。
3.切换npm的下包镜像源
下包的镜像源,指的就是下包的服务器地址
#查看当前的下包镜像源npm config get registry#将下包的镜像源切换为淘宝镜像源npm config set registry=https://registry.npm.taobao.org/#检查镜像源是否下载成功npm config get registry
4.nrm
为了更方便的切换下包的镜像源,我们可以安装 nrm 这个小工具,利用 nrm提供的终端命令,可以快速查看和切换下包的镜像源。
#通过npm包管理器,将nrm安装为全局可用的工具npm i nrm -g#查看所有可用的镜像源nrm ls#将下包的镜像源切换为taobao镜像nrm use taobao
3.3.5 包的分类
1.项目包
那些被安装到项目的 node modules 目录中的包,都是项目包。
项目包又分为两类,分别是:
- 开发依赖包 (被记录到devDependencies节点中的包,只在开发期间会用到)
- 核心依赖包(被记录到dependencies 节点中的包,在开发期间和项目上线之后都会用到)
npm i 包名-D #开发依赖包(会被记录到devDependencies节点下)
npm i 包名 #核心依赖包(会被记录到 dependencies节点下)
2.全局包
在执行npm install命令时,如果提供了 -g 参数,则会把包安装为全局包。
全局包会被安装到C:\Users\用户目录\AppData\Roaming\npm\node_modules目录下。
npm i 包名 -g #全局安装指定的包
npm uninstall 包名 -g #卸载全局安装的包
注意:
- 只有工具性质的包,才有全局安装的必要性。因为它们提供了好用的终端命令。
- 判断某个包是否需要全局安装后才能使用,可以参考官方提供的使用说明即可。
3.i5ting_toc
i5ting_toc是一个可以把 md文档转为 html 页面的小工具,使用步骤如下:
#将i5ting_ toc安装为全局包npm install -g i5ting_toc#调用i5ting_toc,轻松实现md转 html的功能i5ting_toc -f 要转换的md文件路径+文件名 -o // -o代表转换完成后自动打开转换后的html页面
3.3.6 规范的包结构
在清楚了包的概念、以及如何下载和使用包之后,接下来,我们深入了解一下包的内部结构。
一个规范的包,它的组成结构,必须符合以下3点要求:
- 包必须以单独的目录而存在
- 包的顶级目录下要必须包含 package.json这个包管理配置文件
- package.json 中必须包含name、version、main这三个属性,分别代表包的名字、版本号、包的入口。
注意:以上3点要求是一个规范的包结构必须遵守的格式,关于更多的约束,可以参考如下网址:
https://yarnpkg.com/zh-Hans/docs/package-json
3.3.7 开发属于自己的包
439集待补
3.3.8 发布包
1.注册npm账号
- 访问https://www.npmjs.com/网站,点击 sign up 按钮,进入注册用户界面
- 填写账号相关的信息:Full Name、Public Email、Username、Password
- 点击Create an Account按钮,注册账号
- 登录邮箱,点击验证链接,进行账号的验证
4 模块的加载机制
4.1优先从缓存中加载
模块在第一次加载后会被缓存。这也意味着多次调用require()
不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块、还是第三方横块,它们都会优先从缓存中加载,从而提高模块的加载效率。
代码:
1.js:
console.log('OK')
2.js
require('./1.js')require('./1.js')require('./1.js')
执行结果:只会输出一遍OK
4.2 内置模块的加载机制
内置模块是由Node.js官方提供的模块,内置模块的加载优先级最高。
例如,require(‘fs’)始终返回内置的 fs 模块,即使在node_modules目录下有名字相同的包也叫做fs。
4.3 自定义模块的加载机制
使用require()
加载自定义模块时,必须指定以 ./
或 ../
开头的路径标识符。在加载自定义模块时,如果没有指定./
或../
这样的路径标识符,则node 会把它当作内置模块或第三方模块进行加载。
同时,在使用require()
导入自定义模块时,如果省略了文件的扩展名,则Node.js 会按顺序分别尝试加载以下的文件:
- 按照确切的文件名进行加载补全
- .js扩展名进行加载补全
- .json扩展名进行加载
- 补全.node 扩展名进行加载
- 加载失败,终端报错
4.4 第三方模块的加载机制
如果传递给 require()
的模块标识符不是一个内置模块,也没有以 ./’
或 ../
开头,则 Node.js 会从当前模块的父目录开始,尝试从./node_modules
文件夹中加载第三方模块。
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如,假设在’C:\Users\itheima\project\foo.js’文件里调用了require('tools')
,则 Node,js 会按以下顺序查找:
1. C:\Users\itheima\project\node_modules\tools2. C:\Users\itheima\node_modules\itools3. C:\Users\node_modules\itools4. C:\node_modules\itools
4.5 目录作为模块
当把目录作为模块标识符,传递给require()
进行加载的时候,有三种加载方式:
- 在被加载的目录下查找一个叫做
package.json
的文件,并寻找 main属性,作为require()
加载的入口 - 如果目录里没有package.ison文件,或者main入口不存在或无法解析,则 Node.is 将会试图加载目录下的
index.js
文件 - 如果以上两步都失败了,则Node,js 会在终端打印错误消息,报告模块的缺失:
Error: Cannot find module 'xxx'
Express
视频地址:
https://www.bilibili.com/video/BV11N411o7wU?p=457
目标:
能够使用 express.static() 快速托管静态资源
能够使用 express 路由精简项目结构
能够使用常见的 express 中间件
能够使用 express 创建API接口
能够在 express 中启用cors 跨域资源共享
1 初识 Express
1.1 Express 简介
官方给出的概念:Express
是基于Node.js 平台,快速、开放、极简的 Web 开发框架。
通俗的理解:Express 的作用和 Node.js 内置的 http 模块类似,是专门用来创建Web 服务器的。
Express的本质:就是一个npm上的第三方包,提供了快速创建Web服务器的便捷方法。
Express 的中文官网:http://www.expressjs.com.cn/
3.Express 的作用
使用 Express,我们可以方便、快速的创建 Web 网站的服务器或API 接口的服务器。
对于前端程序员来说,最常见的两种服务器,分别是:
- Web网站服务器:专门对外提供 Web 网页资源的服务器。
- API接口服务器:专门对外提供API接口的服务器。
1.2 Express 的基本使用
1.2.1 安装
在项目所处的目录中,运行如下的终端命令,即可将express安装到项目中使用:
npm i express@4.17.1
1.2.2 创建基本的 Web 服务器
// 1,导入expressconst express = require( 'express')// 2,创建Web服务器const app = express()// 3.调用app.listen(端口号,启动成功后的回调函数),启动服务器app.listen(80,() => {console.log('express server running at http:/ /127.0.0.1')
启动服务器后,可以按Ctrl + c
停掉服务器。
1.2.3 监听 GET 请求
通过app.get()
方法,可以监听客户端的GET请求,具体的语法格式如下:
app.get(URL, function(req,res) { /*处理函数*/})
参数1:客户端请求的URL地址
参数2∶请求对应的处理函数
req:请求对象(包含了与请求相关的属性与方法)
res:响应对象(包含了与响应相关的属性与方法)
☆ 注意127.0.0.1:80 可能被之前安装的软件给占用了(比如:禅道),可以先在浏览器输入服务器+url+端口号,试试是否可以正常使用。
4.监听 POST 请求
通过app.post()方法,可以监听客户端的POST请求,具体的语法格式如下:
app.post( ‘请求URL’ , function(req,res){/处理函数/})
参数1:客户端请求的URL地址
参数2∶请求对应的处理函数
req:请求对象(包含了与请求相关的属性与方法)
res:响应对象(包含了与响应相关的属性与方法)
5.把内容响应给客户端
通过 res.send() 方法
,可以把处理好的内容,发送给客户端:
app.get('/user', (req, res) => { //向客户端发送JSON对象 res.send({ name : 'zs', age: 20, gender: '男' })})app.post( '/user', (req, res) => { //向客户端发送文本内容 res.send( '请求成功")})
☆ 可以通过 postman 进行验证:
6.获取URL中携带的查询参数
通过req.query方法
,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:
app.get('/', (req, res) => { // req. query 默认(URL没有携带查询参数时)是一个空对象 //客户端使用 ?name=zs&age=20 这种查询字符串形式,发送到服务器的参数, //可以通过req.query对象访问到,例如: //req.query.name req.query.age console.log(req.query) res.send(req.query)})
注意:默认情况下,req.query是一个空对象.
7.获取 URL 中的动态参数
通过 req.params 对象
,可以访问到URL中,通过:
匹配到的动态参数:
// URL地址中,可以通过:参数名的形式,匹配动态参数值// 注意:这里的:id 是一个动态的参数// 匹配2个动态参数时,URL的写法:'user/:id/:name'app.get('/user/:id', (req,res) => { // req.params 是动态匹配到的 URL 中的动态参数, 默认是一个空对象 //里面存放着通过:动态匹配到的参数值 console.log(req.params) res.send(req.params)})
1.3 托管静态资源
1.3.1 express.static()
通过express.static()
,可以非常方便地创建一个静态资源服务器。
例如,通过如下代码就可以将 public目录下的图片、CSS文件 、JavaScript 文件对外开放访问了:
app.use(express.static('public'))
这样,就可以访问public目录中的所有文件了:
http://localhost:3000/images/bg.jpg
http://localhost.3000/css/style.css
http://localhost:3000fjs/login.js
注意:Express在指定的静态目录中查找文件,并对外提供资源的访问路径。因此,存放静态文件的目录名不会出现在URL中。
const express = require('express')const app = new express()// 调用 express.static() 方法,快速的对外提供静态资源// 可以通过http://127.0.0.1/1.html 可以访问本文件同级目录staticRes下的文件app.use(express.static('./staticRes'))app.listen(80,(req, res) => { console.log('express server running at http://127.0.0.1');})
1.3.2 托管多个静态资源目录
如果要托管多个静态资源目录,请多次调用express.static()
函数:
app.use(express.static('public'))app.use(express.static( 'files'))
访问静态资源文件时,express.static()函数会根据目录的添加顺序查找所需的文件。在public文件夹中找到了的话,就不会再去files文件夹中找了。
1.3.3 挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:
app.use('/public', express.static('public'))
现在,你就可以通过带有/public前缀地址来访问public目录中的文件了:
http://localhost:3000/public/images/kitten.jpg
http://localhost:3000/public/css/style.css
http://localhost:3000/public/js/app.js
1.4 nodemon
1.nodemon的作用
在编写调试 Node.js 项目的时候,如果修改了项目的代码,则需要手动关掉再重新启动服务器,非常繁琐。
现在,我们可以使用nodemon这个工具,它能够监听项目文件的变动,当代码被修改后,nodemon 会自动帮我们重启项目,极大方便了开发和调试。
2.安装 nodemon
在终端中,运行如下命令,即可将nodemon安装为全局可用的工具:
npm install -g nodemon
3.使用nodemon
当基于 Node.js 编写了一个网站应用的时候,传统的方式,是运行node appjs 命令,来启动项目。
这样做的坏处是:代码被修改之后,需要手动重启项目。
现在,我们可以将node 命令替换为 nodemon 命令,使用nodemon app.js 来启动项目。这样做的好处是:代码被修改之后,会被nodemon监听到,从而实现自动重启项目的效果。
node app.js
#将上面的终端命令,替换为下面的终端命令,即可实现自动重启项目的效果
nodemon app.js
2 Express 路由
2.1 路由的概念
1.什么是路由
广义上来讲,路由就是映射关系。
2.现实生活中的路由
3.Express 中的路由
在 Express 中,路由指的是请求的类型、客户端的请求与服务器处理函数之间的映射关系。
Express 中的路由分3部分组成,分别是请求的类型、请求的URL地址、处理函数,格式如下:
METHOD 代表请求的类型,值可以是get、post
app.METHOD(PATH, HANDLER)
4.Express中的路由的例子
// 匹配GET请求,且请求URL为/app.get('/', function (req,res) { res.send('Heilo world!')})// 匹配POST请求,且请求URL为/app. post('/', function (req,res) { res.send('Got a POST request')})
5.路由的匹配过程
每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则 Express会将这次请求,转交给对应的function函数进行处理。
路由匹配的注意点:
1. 按照定义的**先后顺序**进行匹配2. **请求类型**和**请求的URL**同时匹配成功,才会调用对应的处理函数
2.2 路由的使用
2.2.1.最简单的用法
在Express 中使用路由最简单的方式,就是把路由挂载到app 上,示例代码如下:
const express = require('express')// 创建web 服务器,命名为 appconst app = express()// 挂载路由app.get('/', (req, res) => { res.send( 'Hello world.') })app.post('/', (req, res) => { res.send( 'Post Request. ') })// 启动 web服务器app.listen(80,(req, res) => { console.log('express server running at http://127.0.0.1');})
今后用的不多,因为随着代码的增多,该文件的体积会变大,不方便管理。
2.2.2.模块化路由
为了方便对路由进行模块化的管理,Express 不建议将路由直接挂载到app 上,而是推荐将路由抽离为单独的模块。
将路由抽离为单独模块的步骤如下:
1. 创建路由模块对应的.js文件 2. 调用`express.Router()`函数创建路由对象3. 向路由对象上挂载具体的路由4. 使用`module.exports`向外共享路由对象5. 使用`app.use()`函数注册路由模块
// 这是路由模块// 1.导入 express const express = require('express');// 2.创建路由对象const router = express.Router();// 3.挂载具体的路由router.get('/user/list',(req, res) => { res.send('Get user list');})router.post('/user/add',(req, res) => { res.send('Add new user');})// 4.向外导出路由对象module.exports = router
2.2.3.注册路由模块
const express = require('express');const app = new express();// 1.导入路由模块const router = require('./10router');console.log(router)// 2.注册路由模块app.use(router);app.listen(80,() => { console.log("express service running at http://127.0.0.1");})
其中,app.use() 函数的作用,就是来注册全局中间件
2.2.4.为路由模块添加前缀
类似于托管静态资源时,为静态资源统—挂载访问前缀一样,路由模块添加前缀的方式也非常简单:
// 1.导入路由模块const userRouter = require('./router/user.js')// 2.使用app.use()注册路由模块,并添加统一的访问前缀 /apiapp.use('/api', userRouter)
3 Express 中间件
3.1 中间件的概念
1.什么是中间件
中间件(Middleware ) ,特指业务流程的中间处理环节。
2.现实生活中的例子
在处理污水的时候,一般都要经过三个处理环节,从而保证处理过后的废水,达到排放标准。
处理污水的这三个中间处理环节,就可以叫做中间件。
3.Express中间件的调用流程
当一个请求到达Express的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
4.Express中间件的格式
Express 的中间件,本质上就是一个function处理函数,Express 中间件的格式如下:
注意:中间件函数的形参列表中,必须包含next参数。而路由处理函数中只包含req和res.
5.next 函数的作用
next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
3.2 Express 中间件初体验
1.定义中间件函数
// 定义一个最简单的中间件函数const mw = function (req, res, next) { console.log('这是最简单的中间件函数') // 把流转关系,转交给下一个中间件或路由 next()}
2.全局生效的中间件
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
通过调用app.use(中间件函数),即可定义一个全局生效的中间件,示例代码如下:
// 常量mw所指向的,就是一个中间件函数const mw =function (req,res, next) { console. log('这是一个最简单的中间件函数") next()}// 全局生效的中间件app.use(mw)
3.定义全局中间件的简化形式
// 全局生效的中间件app.use(function (req,res, next) { console.log('这是一个最简单的中间件函数") next()})
4.中间件的作用
多个中间件之间,共享同一份req和res。基于这样的特性,我们可以在上游的中间件中,统一为req或res 对象添加自定义的属性或方法,供下游的中间件或路由进行使用。
5.定义多个全局中间件
可以使用app.use()连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用,示例代码如下:
app.use(function(req,res,next) { // 第1个全局中间件 console.log( '调用了第1个全局中间件') next()})app.use(function(req,res,next) { // 第2个全局中间件 console.log( '调用了第2个全局中间件') next()})app.get('/user', (req, res) => { // 请求这个路由,会依次触发上述两个全局中间件 res.send( 'Home page.')})
6.局部生效的中间件
不使用app.use()定义的中间件,叫做同部生效的中间件,示例代码如下:
// 定义中间件函数mw1const mw1 = function(req, res, next) { console.log( '这是中间件函数') next()}// mw1这个中间件只在"当前路由中生效",这种用法属于"局部生效的中间件"app.get('/', mw1, function(req,res) { res.send( 'Home page.')})// mw1这个中间件不会影响下面这个路由↓↓↓app.get('/user', function(req,res) { res.send( 'User page.')})
7.定义多个局部中间件
可以在路由中,通过如下两种等价的方式,使用多个局部中间件:
// 以下两种写法是""完全等价"的,可根据自己的喜好,选择任意一种方式进行使用// 都是从左至右依次调用中间件app.get('/', mw1, mw2, (req,res)=> { res.send('Home page.')}app.get('/', [mw1, w2], (req,res) => { res.send( ' Home page.') }
8.了解中间件的5个使用注意事项
1. 一定要在**路由之前**注册中间件 2. 客户端发送过来的请求,**可以连续调用**多个中间件进行处理3. 执行完中间件的业务代码之后,**不要忘记调用next()函数**4. 为了**防止代码逻辑混乱**,调用next(函数后不要再写额外的代码5. 连续调用多个中间件时。多个中间件之间,**共享**req和res 对象
3.3 中间件的分类
为了方便大家理解和记忆中间件的使用,Express官方把常见的中间件用法,分成了5大类,分别是:
1. 应用级别的中间件2. 路由级别的中间件3. 错误级别的中间件4. Express内置的中间件5. 第三方的中间件
1.应用级别的中间件
通过 app.use()或app.get()或 app.post(),绑定到app实例上的中间件,叫做应用级别的中间件,代码示例如下:
// 应用级别的中间件(全局中间件)app.use(req, res, next) => { next())// 应用级别的中间件(局部中间件)app.get('/', mw1, (req, res) => { res.send('Home page.')}
2.路由级别的中间件
绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上,代码示例如下:
var app = express()var router = express.Router() // 路由级别的中间件router.use(function (req, res, next) { console.log( 'Time:', Date.now() next()})app.use('/', router)
3.错误级别的中间件
错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
**格式:**错误级别中间件的 function 处理函数中,必须有4个形参,形参顺序从前到后,分别是(err, rea, res, next).
app.get('/', function (req,res) { // 1.路由 throw new Error('服务器内部发生了错误! ') //1.1 抛出一个自定义的错误 res.send( ' Home Page. ')})app.use(function (err,req, res, next) { // 2.错误级别的中间件 console.log( '发生了错误:' + err.message)// 2.1在服务器打印错误消息 res.send( 'Error!' + err.message)// 2.2向客户端响应错误相关的内容})
注意:错误级别的中间件,必须注册在所有路由之后!
3.3.4 Express内置的中间件
自Express 4.16.0版本开始,Express 内置了3个常用的中间件,极大的提高了Express项目的开发效率和体验:
1. **express.static**快速托管静态资源的内置中间件,例如: HTML文件、图片、CSS样式等(无兼容性)2. **express.json**解析JSON格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)3. **express.urlencoded** 解析 URL-encoded 格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
// 配置解析application/json格式数据的内置中间件app.use(express.json())// 配置解析 application/x-www-form-urlencoded 格式数据的内置中间件app.use(express.urlencoded( { extended: false }))
express.json 中间件的使用:
默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefined.
// 除了错误级别的中间件,其他的中间件,必须在路由之前进行配置// 通过 express.json 中间件,解析表单中的json格式的数据app.use(express.json())app.post('/user',(req, res) => { // 在服务器,可以使用req.body 这个属性,来接收客户端发送过来的请求体数据 // 默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefined console.log(req.body) res.send("OK")})
用postman测试的填写如下图:
运行结果如下图:
express.urlencoded中间件的使用:
// 通过 express.urlencode 中间件,解析表单中的url-encoded 格式的数据,挂载到req.body 属性上app.use(express.urlencoded( { extended: false }))app.post('/book', (req, res) => { // 在服务器端,可用过req.body来获取 JSON 格式的表单数据和 url-encoded 格式的数据 console.log(req.body); res.send('OK')})
用postman测试的填写如下图:
运行结果如下图:
3.3.5 第三方的中间件
非Express 官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率。
例如:在 express@4.16.0之前的版本中,经常使用body-parser这个第三方中间件,来解析请求体数据。便用步骤如下:
1. 运行 `npm install body-parser` 安装中间件2. 便用 `require()` 导入中间件3. 调用`app.use()`注册并使用中间件
具体使用方法:
// 1.导入解析表单数据的中间件 body-parserconst parser = require('body-parser')// 2.使用 app.use() 注册全局中间件app.use(parser.urlencoded({extended: false}))app.post('/user', (req, res) => { console.log(req.body); res.send('OK')})
注意:Express内置的express.urlencoded 中间件,就是基于body-parser这个第三方中间件进一步封装出来的。
3.4 自定义中间件
1.需求描述与实现步骤
自己手动模拟一个类似于express.urlencoded 这样的中间件,来解析POST提交到服务器的表单数据。
实现步骤;
1. 定义中间件2. 监听req,的data事件3. 监听req的end事件4. 使用querystring模块解析请求体数据5. 将解析出来的数据对象挂载为req.body6. 将自定义中间件封装为模块
2.定义中间件
使用app.use(来定义全局生效的中间件。代码如下:
app.use(function(req, res,next) { //中间件的业务逻辑})
3.监听req 的data 事件
在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据。
如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器。所以data事件可能会触发多次,每一次触发data事件时,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接。
P495