第一章 ajax + http
URL
url 统一资源定位符 uniformResourceLocatior
由三部分组成:通信协议 服务器名 服务器上具体存储位置
http://<host>:<port>/<path>?<searchpart>
三个步骤:请求处理响应
浏览器提供的通信过程分析工具:
F12 开发者模式 Network,选中Doc标签,在下面的具体页面点击一下,就能看到这个网页的详情。respones里面就是返回过来的内容
XHR
网页如何请求数据:
请求在服务器上的在的资源,需要用到XMLHttpRequest对象。简称xhr。属于浏览器提供的js成员
最简单用法:var xhrObj = new XMLHttpRequest()
请求资源方式:
最常见两种:get和post
AJAX
Ajax 异步js和XML .通俗理解:只要使用了XMLHttpRequest对象和服务器进行数据交互了,就是ajax。
ajax可以实现网页与服务器之间的数据传输
jQuery中的ajax
浏览器提供的xhr比较复杂,使用jQuery封装的xhr
主要适用三个方法:
$.get()
$.post()
$.ajax()
1 .get()
$.get(url,[data],[callback])
url 指要请求的地址
data 指请求资源时携带的参数
callback 指请求成功时的回调函数
//res 服务器为返回的数据
$(function () {
$('#btn1').on('click', function () {
$.get('http://www.liulongbin.top:3006/api/getbooks',function(res){
console.log(res)
})
})
})
//以下代码为带参数的get请求
$(function () {
$('#btn1').on('click',function () {
$.get('http://www.liulongbin.top:3006/api/getbooks',{id:1},function(res){
console.log(res)
})
})
})
2 .post()
$.get(url,[data],[callback])
// 123
$(function(){
$('#btnPost').on('click',function(){
$.post('http://www.liulongbin.top:3006/api/addbook',{bookname:'shuihuzhuan1',author:'shinaian',publisher: "北京图书出版社"}
,function(res){
console.log(res)
})
})
})
3 .ajax()
$.ajax({
type:’’,//请求方式
url:’’,//请求的url地址
data:{},//这次请求要带的数据
success:function(res){}//请求成功后的回调函数})
$('#btnAjax').on('click',function(){
$.ajax({
type:'GET',
url:'http://www.liulongbin.top:3006/api/getbooks',
data:{},
success:function(res){
console.log(res)
}
})
})
接口文档组成
1 接口名称
2 接口URL
3 调用方式:post get
4 参数格式
5 响应格式
6 返回示例:通过对象的形式,列举服务器返回数据的结构
2-2 图书馆列表案例
尚不存在的元素,如果需要设置js操作,需要通过代理的方式绑定操作。
$('tbody').on('click', '.deletBook', function () {
// $.post('http://www.liulongbin.top:3006/api/')
var id = $(this).attr('data-id')
console.log(id)
$.get('http://www.baidu.com:3006/api/delbook', { id: id }, function (res) {
if (res.status !== 200) return alert('delete error')
getBookList()
})
})
去除字符串的头尾空格: trim()
2-4 form表单
表单由三个基本部分组成:
表单标签
表单域
表单按钮
form标签的属性,来规定如何把采集到的数据发送到服务器。
action:值为 url地址,规定当提交表单时,向何处发送表单数据
method:值为 get 或 post ,规定以何种方式提交到action指定地址
enctype:值为
属性 | 值 | 描述 |
---|---|---|
action | URL地址 | 规定当提交表单时,向何处发送表单数据 |
method | get或post | 规定以何种方式把表单数据提交到 action URL |
enctype | application/x-www-form-urlencodedmultipart/form-datatext/plain | 规定在发送表单数据之前如何对其进行编码 |
target | _blank _self _parent _top framename | 规定在何处打开 action URL |
get形式提交数据,会显示在地址栏里面,以post形式提交数据不会显示在地址栏。
包含文件上传用post
在涉及到文件上传的操作时,必须将 enctype 编码方式的值设置为 multipart/form-data
表单同步提交:点击之后全部提交,跳转到另外的url,并且表单内容全部清空。
解决办法:表单负责采集,ajax负责提交
<form action="">
<input type="text" />
<input type="password" />
<button type="submit">tijiao</button>
</form>
jQuery中监听表单方法
$(function(){
//方式1
// $('form').submit(function(){
// alert('ohlala')
// })
//方式2
$('form').on('submit',function(){
alert('ohlala')
})
})
阻止表单默认提交行为
$(function(){
//方式1
$('form').submit(function(e){
e.prventDefault()
})
//方式2
$('form').on('submit',function(e){
e.prventDefault()
})
})
一行代码获取表单所有数据,jquery提供了serialize函数格式为:
$('#form1').serilize()
//得到的结果是:
//username=用户名&password=密码值
注意:在使用时,必须为每个表单元素添加name属性
一个提交案例:
$('#formAddCmt').submit(function (e) {
e.preventDefault()
var data = $(this).serialize()
$.post('http://www.baidu.com:3006/api/addcmt', data, function (res) {
if (res.status !== 201) {
return alert('发表评论失败!')
}
getCommentList()
$('#formAddCmt')[0].reset()//清空表单数据
})
})
art-template模板引擎
根据指定的模板结构和数据,自动生成一个html页面 。
不用手动再拼接字符串了。
art-template模板引擎,在已经提供模板和数据的条件下,自动操作dom,把数据填充在模板里
使用方法:
1 引入模板引擎 导入template-web.js ,全局中会多出一个叫做template(‘模板ID’,需要渲染的数据对象)的函数
2 定义要渲染的数据
3 定义模板 (使用script标签, type类型为 text/html)
4 调用template函数,指定要填充的数据和模板
5 在html页面中渲染数据
art-template 标准语法
art-template 提供了 {{ }} 这种语法格式,在 {{ }} 内可以进行变量输出,或循环数组等操作,这种 {{ }} 语法在 art-template 中被称为标准语法。
{{value}}
{{obj.key}}
{{obj[‘key’]}}
{{a ? b : c}}
{{a || b}}
{{a + b}}
原文输出:{{@ value}}
包含html语法则需要用原文输出
条件输出:
// /if表示if语句结束
{{if value}}按需要输出的内容{{/if}}
{{if value}}按需要输出的内容1
{{else if value2}}按需要输出的内容2
{{/if}}
循环输出:
通过each循环数组,当前循环的索引使用$index进行访问,值用$value访问。
{{each arr1}}
{{$index}} {{$value}}
{{/each}}
过滤器:
{{value | filterName}}
过滤器定义的基本语法:
template.defaults.imports.filterName = function (date) {
/*return 处理后的结果 */
}
举例:
template.defaults.imports.dateFormat = function (date) {
var y = date.getFullYear()
var m = date.getMonth() + 1
var d = date.getDate()
return y + '-' + m + '-' + d
}
<body>
<div id="container"></div>
<!-- 3. 定义模板 -->
<!-- 3.1 模板的 HTML 结构,必须定义到 script 中 -->
<script type="text/html" id="tpl-user">
<h1>{{name}} ------ {{age}}</h1>
{{@ test}}
<div>
{{if flag === 0}}
flag的值是0
{{else if flag === 1}}
flag的值是1
{{/if}}
</div>
<ul>
{{each hobby}}
<li>索引是:{{$index}},循环项是:{{$value}}</li>
{{/each}}
</ul>
<h3>{{regTime | dateFormat}}</h3>
</script>
<script>
// 定义处理时间的过滤器
template.defaults.imports.dateFormat = function (date) {
var y = date.getFullYear()
var m = date.getMonth() + 1
var d = date.getDate()
return y + '-' + m + '-' + d
}
// 2. 定义需要渲染的数据
var data = { name: 'zs', age: 20, test: '<h3>测试原文输出</h3>',
flag: 1, hobby: ['吃饭', '睡觉', '写代码'], regTime: new Date() }
// 4. 调用 template 函数
var htmlStr = template('tpl-user', data)
console.log(htmlStr)
// 5. 渲染HTML结构
$('#container').html(htmlStr)
</script>
</body>
正则与字符串操作
基本语法 exec()
RegExpObj.exec(string)
如果匹配到了,返回匹配的值,匹配失败返回null
返回值是一个数组。
示例代码:
var str ='hello'
var pattern = /o/
console.log(pattern.exec(str))
//输出结果:["0",index:4,input:"hello",groups:undefine]
正则与字符串的操作
分组
正则表达式中 ( ) 包起来的内容表示一个分组,可以通过分组来提取自己想要的内容
replace函数
字符串的replace函数
var result = '123456'.replace('123','abc')
//结果为:abc456
示例:
var str = '<div>我是{{name}}</div>'
var pattern = /{{([a-zA-Z]+)}}/
//+号表示可以匹配很多次
var patternResult = pattern.exec(str)
str = str.replace(patternResult[0], patternResult[1]) // replace 函数返回值为替换后的新字符串
// 输出的内容是:<div>我是name</div>
console.log(str)
多次替换
就多写几次,如果要用while自动执行,示例代码如下:
var str = '<div>{{name}}今年{{ age }}岁了</div>'
var pattern = /{{\s*([a-zA-Z]+)\s*}}/
var patternResult = null
while(patternResult = pattern.exec(str)) {
str = str.replace(patternResult[0], patternResult[1])
}
console.log(str) // 输出 <div>name今年age岁了</div>
replace替换真值
var data = { name: '张三', age: 20 }
var str = '<div>{{name}}今年{{ age }}岁了</div>'
var pattern = /{{\s*([a-zA-Z]+)\s*}}/
var patternResult = null
while ((patternResult = pattern.exec(str))) {
str = str.replace(patternResult[0], data[patternResult[1]])
}
console.log(str)
XHR原生使用方法
1 创建xhr对象
var xhr = new XMLHttpRequest()
2 调用open函数 规定打开方式和对应的网址
xhr.open('GET/POST','url')
3 调用send函数 发起请求
xhr.send()
4 监听onreadystatechange事件
xhr.onreadystatechange = function() {
// 4.1 监听 xhr 对象的请求状态 readyState ;与服务器响应的状态 status
if (xhr.readyState === 4 && xhr.status === 200) {
// 4.2 打印服务器响应回来的数据
console.log(xhr.responseText)
}
}
带参请求
xhr.open('GET', 'http://www.baidu.com:3006/api/getbooks?id=1')
这种在 URL 地址后面拼接的参数,叫做查询字符串。
?开头,&符号连接多个代码
URL编码解码
如果需要在url中添加其他字符,那就需要对这些字符进行编码(转义),即:用英语来表示特殊字符
编码方法:浏览器提供了URL编码与解码API
调用办法:
encodeURL('string')
decodeURL('string')
//E.G.
每三组表示一个中文字符?
xhr发起post请求
1 创建对象
2 调用xhr.open()函数
3 设置Content-Type属性(固定写法)
xhr.setRequestHeader('Content-Type','application/x-wwww-from-urlencoded')
4 调用send函数,并将数据以查询字符串的形式提交给服务器
5 监听onreadystatechange事件
json数据交换格式
json 是js对象和数组的字符串表示法,本质是字符串,用文本表示js对象或者数组的信息,不可以包含函数
json两种结构:对象与数组
对象结构:对象结构在 JSON 中表示为 { } 括起来的内容。数据结构为 { key: value, key: value, … } 的键值对结构。其中,key 必须是使用英文的双引号包裹的字符串,value 的数据类型可以是数字、字符串、布尔值、null、数组、对象6种类型。不可以是undefine和function
//对象:
{
"name": "zs",
"age": 20, //数值可以直接写
"gender": "男",
"address": null,
"hobby": ["吃饭", "睡觉", "打豆豆"]
//数组内的值也必须双引号包裹
}
//数组:
[ "java", "python", "php" ]
[ 100, 200, 300.5 ]
[ true, false, null ]
[ { "name": "zs", "age": 20}, { "name": "ls", "age": 30} ]
[ [ "苹果", "榴莲", "椰子" ], [ 4, 50, 5 ] ]
要实现从 JSON 字符串转换为 JS 对象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}')
//结果是 {a: 'Hello', b: 'World'}
要实现从 JS 对象转换为 JSON 字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'})
//结果是 '{"a": "Hello", "b": "World"}'
把数据对象转换为字符串的过程,叫做序列化,反之叫反序列化
封装自己的ajax函数
function resolveData(data) {
var arr = []
for (var k in data) {
var str = k + '=' + data[k]
arr.push(str)
}
return arr.join('&')
}
// var res = resolveData({ name: 'zs', age: 20 })
// console.log(res)
function dengdualang(options) {
var xhr = new XMLHttpRequest()
// 把外界传递过来的参数对象,转换为 查询字符串
var qs = resolveData(options.data)
if (options.method.toUpperCase() === 'GET') {
// 发起GET请求
xhr.open(options.method, options.url + '?' + qs)
xhr.send()
} else if (options.method.toUpperCase() === 'POST') {
// 发起POST请求
xhr.open(options.method, options.url)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(qs)
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var result = JSON.parse(xhr.responseText)
options.success(result)
}
}
}
XHR L2 特性
1 支持上传文件 2 支持进度查询 3 可设置http请求时限 4 可使用formdata对象管理表单数据
设定时间限制timeout
xhr.timeout = 3000
xhr.ontimeout = function () {
console.log('请求超时了')
}
FormData管理表单数据
// 获取表单元素
var form = document.querySelector('#form1')
// 监听表单元素的 submit 事件
form.addEventListener('submit', function(e) {
e.preventDefault()
// 根据 form 表单创建 FormData 对象,会自动将表单数据填充到 FormData 对象中
var fd = new FormData(form)
var xhr = new XMLHttpRequest()
xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
xhr.send(fd)
xhr.onreadystatechange = function() {}
})
上传文件
XHR L2中的上传文件,被视作一个数组。
<!-- 1 文件选择框 -->
<input type="file" id="file1" />
<!-- 2 上传文件按钮 -->
<button id="btnUpload">btnUpload</button>
<br />
<!-- 3 img标签 -->
<img src="" alt="" id="img1">
显示上传进度
// 创建 XHR 对象
var xhr = new XMLHttpRequest()
// 监听 xhr.upload 的 onprogress 事件
xhr.upload.onprogress = function(e) {
// e.lengthComputable 是一个布尔值,表示当前上传的资源是否具有可计算的长度
if (e.lengthComputable) {
// e.loaded 已传输的字节
// e.total 需传输的总字节
var percentComplete = Math.ceil((e.loaded / e.total) * 100)
}
}
基于bootstrap的进度条(空)
JQ高级用法
上传文件:
上传文件只能使用ajax指令,不能使用get()和post(),并且需要method为post
重点是 contentType 和 processData 这两个属性
$.ajax({
method: 'POST',
url: 'http://www.baidu.com:3006/api/upload/avatar',
data: fd,
// 不修改 Content-Type 属性,使用 FormData 默认的 Content-Type 值
contentType: false,
// 不对 FormData 中的数据进行 url 编码,而是将 FormData 数据原样发送到服务器
processData: false,
success: function(res) {
console.log(res)
}
})
显示loading效果
当ajsx请求启动(ajaxstart)的时候,将回调函数设置为:显示#loading这个ID对应的元素。
//监听ajaxstart函数,在callback中显示loading
效果
// 自 jQuery 版本 1.8 起,该方法只能被附加到文档
$(document).ajaxStart(function() {
$('#loading').show()
})
//结束时使用这个
// 自 jQuery 版本 1.8 起,该方法只能被附加到文档
$(document).ajaxStop(function() {
$('#loading').hide()
})
AXIOS
axios网络数据请求库,用于XMLHttpRequest,比JQ更轻量化,专注于网络数据请求
axios.get('url',{params:{/*参数*/}}).then(callback)
axios.post('url',{/*参数*/}).then(callback)
axios.ajax({
method:'请求类型',
url:,
params:{/*get请求用params*/}
data:{/*post用data*/}
}).then(callback)
同源跨域
同源
协议、域名、端口号,三个元素相同,则同源,后面的地址不用相同
同源策略:浏览器提供的一个安全功能。
本网址的资源,只能由来自本网址同源的文档或者脚本交互,其他非同源的不允许交互,保护安全。
跨域
不同源就是跨域,浏览器允许发起跨域请求,但是会拦截跨域的数据过来。
如何跨域:使用jsonp和cors。
jsonp:仅支持get,不支持post。
cors:跨域ajax请求的根本解决方案。
jsonp
jsonp:JSON with padding
padding :垫衬,废话,凑数文字
<script>标签不受同源策略的影响,可以通过src属性,来请求非同源脚本。
script标签不能发起post请求
使用jQ发起 jsonp 请求
jQuery中的ajax能发起ajax请求外还可以发起jsonp请求
$.ajax({
url:'url',
datatype:'jsonp',
//发送到服务器的参数名称,默认为callback
jsonp:'callback'
//自定义回调函数名默认值为jQueryXXX格式
jsonpCallback:'abc'
success:function(res){
console.log(res)
}
})
//jsonpcallback 可以修改回调函数名字
jQuery自动创建script标签,发起jsonp请求,发起完成后再自动删除这个标签。
淘宝搜索案例
$(function () {
// 1. 定义延时器的Id
var timer = null
// 定义全局缓存对象
var cacheObj = {}
// 2. 定义防抖的函数
function debounceSearch(kw) {
timer = setTimeout(function () {
getSuggestList(kw)
}, 500)
}
// 为输入框绑定 keyup 事件
$('#ipt').on('keyup', function () {
// 3. 清空 timer
clearTimeout(timer)
var keywords = $(this).val().trim()
if (keywords.length <= 0) {
return $('#suggest-list').empty().hide()
}
// 先判断缓存中是否有数据
if (cacheObj[keywords]) {
return renderSuggestList(cacheObj[keywords])
}
// TODO:获取搜索建议列表
// console.log(keywords)
// getSuggestList(keywords)
debounceSearch(keywords)
})
function getSuggestList(kw) {
$.ajax({
url: 'https://suggest.taobao.com/sug?q=' + kw,
dataType: 'jsonp',
success: function (res) {
// console.log(res)
renderSuggestList(res)
}
})
}
// 渲染UI结构
function renderSuggestList(res) {
if (res.result.length <= 0) {
return $('#suggest-list').empty().hide()
}
var htmlStr = template('tpl-suggestList', res)
$('#suggest-list').html(htmlStr).show()
// 1. 获取到用户输入的内容,当做键
var k = $('#ipt').val().trim()
// 2. 需要将数据作为值,进行缓存
cacheObj[k] = res
}
})
防抖节流
防抖
事件触发之后延迟n秒再执行回调,如果在n秒内又被调用那么就重新计时。
作用:避免输入一些信息时,刚开始输入就已经不停执行想要在输入完毕之后才进行的操作。
防抖(debounce):触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间
举例:就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。
节流(throttle):高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率
举例:预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。
2、区别:防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
JQ修改多个css属性
HTTP
通信:主体,内容,方式
通信协议:通信双方遵守的的规则与约定。
HTTP:超文本传输协议,采用请求响应交互模型。
请求消息
客户端发起的叫做http请求,客户端发送到服务器的消息叫做http请求消息。http请求消息又叫请求报文。
组成部分:
请求头部用于表述客户端基本信息
user-agent 客户端浏览器
content-type
请求体:存放的是要通过post方式提交到服务器的数据post才有请求体,get没有
响应消息
http响应状态码
http请求方法:get post等
http响应状态码:用来标识相应的状态
2XX: 201 请求成功 英文名称ok,201 Created 已经创建,用于post或者put请求。
3XX:需要客户端重定向。
状态码 | 状态码英文名称 | 中文描述 |
---|---|---|
301 | Moved Permanently | 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
302 | Found | 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI |
304 | Not Modified | 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源(响应消息中不包含响应体)。客户端通常会缓存访问过的资源。 |
状态码 | 状态码英文名称 | 中文描述 |
---|---|---|
400 | Bad Request | 1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。2、请求参数有误。 |
401 | Unauthorized | 当前请求需要用户验证。 |
403 | Forbidden | 服务器已经理解请求,但是拒绝执行它。 |
404 | Not Found | 服务器无法根据客户端的请求找到资源(网页)。 |
408 | Request Timeout | 请求超时。服务器等待客户端发送的请求时间过长,超时。 |
状态码 | 状态码英文名称 | 中文描述 |
---|---|---|
500 | Internal Server Error | 服务器内部错误,无法完成请求。 |
501 | Not Implemented | 服务器不支持该请求方法,无法完成请求。只有 GET 和 HEAD 请求方法是要求每个服务器必须支持的,其它请求方法在不支持的服务器上会返回501 |
503 | Service Unavailable | 由于超载或系统维护,服务器暂时的无法处理客户端的请求。 |
第二章 git 与 github
svn : 只记录不同部分
git :全部记录 直接覆盖
Git三个区:工作区,暂存区,git仓库
Git中已经被管理的文件的三个状态:
已修改modified :修改了但是没放到暂存区
已暂存staged:对当前版本已经做了标记,使之包含在下次提交的列表中
已提交committed:表示文件已经存放在本地git仓库中
初始化
安装git只用下一步就行了。
安装完毕 第一件事情是要配置自己的用户名和邮箱地址
–global 全局生效,设置一次即可。
设置用户名 git config --global user.name “XXX”
设置邮箱 git config --global user.email “XXX”
查询所有全局配置 git config --list --global
查看指定全局配置 git config user.name
创建Git仓库的两种方式
1.1 将尚未进行版本控制的本地目录转换为git仓库
1.2 从其他服务器克隆一个已存在的git仓库
1.1 转化为git仓库:在项目目录中,通过鼠标邮件打开git bansh,并执行git init命令即可.git init命令会自动创建一个.git 文件夹,此目录就是当前项目的git仓库包含了初始化的必要文件,这些文件就是git仓库的必要组成部分。
工作区中文件的四种状态
未被git管理:未追踪;已被git管理:未修改,已修改,已暂存。
查询git状态 git status。以精简方式显示文件状态:git status -s 。
-s是 --short的缩写。两个红色问号 ?? 表示未追踪。
添加追踪文件:git add + 文件名 如:git add index.html
精简方式显示状态为 绿色的A 表示放在暂存区中的文件。
使用git commit 来提交到git仓库,-m表示添加附加消息。
红色m标记表示被修改,但是没有放入暂存区。
绿色m表示已修改且已放入暂存区。
git add 命令有三个功效:1 开始跟踪新文件、2 把已跟踪且已修改的文件放入暂存区、3 把有冲突的文件标记为已解决状态
提交已暂存文件:git commit -m “提交消息”,即可将暂存区中文件提交到git仓库中。
撤销对文件修改:git checkout --index.html 将git仓库中的文件覆盖本地文件。
git add . :向暂存区中一次性添加所有新增和修改过的文件。
移除暂存区中的文件:git reset HEAD + 要移除的文件名
跳过使用暂存区: git commit -a git会自动把所有已经跟踪过的文件暂存起来一并提交。
移除文件:1 . 从git仓库和本地磁盘上同时移除 git rm -f + 文件名
2 .从git仓库移除,本地工作区保留 git rm --cached + 文件名
忽略文件:创建.gitignore 配置文件,列出要忽略文件的匹配模式
.gitignore快速入门
# #此为注释 – 将被 Git 忽略
.a # 忽略所有 .a 结尾的文件
!lib.a # 但 lib.a 除外
/TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目录下的所有文件
doc/.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
第三章 NODE
1 基于 Express 框架(http://www.expressjs.com.cn/),可以快速构建 Web 应用
2 基于 Electron 框架(https://electronjs.org/),可以构建跨平台的桌面应用
3 基于 restify 框架(http://restify.com/),可以快速构建 API 接口项目
4 读写和操作数据库、创建实用的命令行工具辅助前端开发、etc…
浏览器中的 JavaScript 学习路径:
JavaScript 基础语法 + 浏览器内置 API(DOM + BOM) + 第三方库(jQuery、art-template 等)
Node.js 的学习路径:
JavaScript 基础语法 + Node.js 内置 API 模块(fs、path、http等)+ 第三方 API 模块(express、mysql 等)
查看 node 版本号 node -v
node中执行js文件:node js文件路径和名称
cd: change directory
按住shift点鼠标右键,可以打开windows power shell
esc:快速清空当前已经输入的命令
cls:清屏
fs文件系统模块
官方提供用来操作文件的模块
使用前需要先导入
const fs = require('fs')
fs.readFile()
fs.readFile(path[,options],callback)
path:必选参数名字符串,表示文件路径
option:可选参数,编码格式
const fs = require ('fs')
fs.readFile('./files/111.txt','utf-8',function(err,dataStr){
if(err){
console.log('读取文件失败'+err.message)
return
}
// console.log(err)
console.log(dataStr)
})
fs.writeFile()
fs.writeFile(path,data[,options],callback)
const fs = require('fs')
fs.writeFile('./files/1111.txt','hello','utf-8',function(err){
if(err){
console.log(err)
}
})
//如果写入成功,则err的值等于null
//如果写入失败,err等于一个错误对象
案例 整理成绩
// 1. 导入 fs 模块
const fs = require('fs')
// 2. 调用 fs.readFile() 读取文件的内容
fs.readFile('../素材/成绩.txt', 'utf8', function(err, dataStr) {
// 3. 判断是否读取成功
if (err) {
return console.log('读取文件失败!' + err.message)
}
// console.log('读取文件成功!' + dataStr)
// 4.1 先把成绩的数据,按照空格进行分割
const arrOld = dataStr.split(' ')
// 4.2 循环分割后的数组,对每一项数据,进行字符串的替换操作
const arrNew = []
arrOld.forEach(item => {
arrNew.push(item.replace('=', ':'))
})
// 4.3 把新数组中的每一项,进行合并,得到一个新的字符串
const newStr = arrNew.join('\r\n')
// 5. 调用 fs.writeFile() 方法,把处理完毕的成绩,写入到新文件中
fs.writeFile('./files/成绩-ok.txt', newStr, function(err) {
if (err) {
return console.log('写入文件失败!' + err.message)
}
console.log('成绩写入成功!')
})
})
动态路径拼接
__dirname 表示当前文件夹的路径
path路径模块
path.join() 将多个路径片段拼接成一个完整的路径字符串path.basename() 方法从路径字符串中把文件名解析出来包含扩展名
path.extname() 获取扩展名
使用前先导入 require (‘path’)
const pathStr = path.join(__dirname,'/files/1.txt')
//输出为当前文件所处目录下的/files/1.txt
path.basename(path[,ext])
//输入ext 则截取掉文件的扩展名,只有文件名
path.extname(path)
//path需要完整包含文件名与扩展名
//扩展名包含.
时钟案例
const regScript = /<script>[\s\S]*<\/script>/
const r2 = regScript.exec(htmlStr)
// 4.3 将提取出来的内容,做进一步的处理
const newJS = r2[0].replace('<script>', '').replace('</script>', '')
exec函数返回的是一个数组,0位置是匹配到的值
fs write 可以创建新文件不能创建新路径,
再次调用,新内容会覆盖旧内容
http模块创建服务器
node中提供的http模块是用来创建服务器的模块。const server = http.createServer().
服务器相关概念:
1 点分十进制的ip
2 域名与域名服务器
3 端口号 每个电脑中可以运行多个服务器,每个服务器配置一个端口号,通过端口号访问不同服务器。一个端口对应一个web服务,不能一对多或者多队医。默认80端口,可以省略
创建基本服务器
node创建服务器
1 .导入http模块
2 .创建web服务器
3 .为服务器绑定request事件,监听客户端请求
4 .启动服务器 ,调用服务器.listen()方法,即可启动服务器实例
const http = require('http')
const server = http.createServer()
server.on('request',function(req,res){
console.log('someone visit our web server')
})
server.listen(80,function(){
console.log('server is running at http://127.0.0.1:80');
})
使用 node 服务器文件名.js 来运行js文件启动服务器。
req请求对象
只要服务器接收到了客户端的请求,就会调用通过 server.on() 为服务器绑定的 request 事件处理函数。
如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下的方式:
req是请求对象,包含了与客户端相关的数据与属性,
req.url是客户端请求的url地址,
req.method是客户端的请求类型。
control+c可以在终端里停止node服务器
const http = require('http')
const server = http.createServer()
// req 是请求对象,包含了与客户端相关的数据和属性
server.on('request', (req, res) => {
// req.url 是客户端请求的 URL 地址
const url = req.url
const url1 = req.url
// req.method 是客户端请求的 method 类型
const method = req.method
const str = `Your request url is ${url1}, and request method is ${method}`
console.log(str)
// 调用 res.end() 方法,向客户端响应一些内容
res.end(str)
})
server.listen(80, () => {
console.log('server running at http://127.0.0.1')
})
res.end ()方法 用于结束这次请求并返回一些数据
冷知识:
` 这个符号叫做反单引号,在键盘数字1 的旁边,在template模板字符串使用反引号 () 来代替普通字符串中的用双引号和单引号。
服务器中文乱码
调用res.end()方法,返回客户端的中文内容可能乱码。
解决办法设置一个响应头,指定charset=utf-8
res.setHeader('Content-Type','text/html;charset=utf-8')
res.end(str)
时钟服务器
path路径多个拼接
fpath = path.join(__dirname,'./clock',url)
第五章 模块化 包2.4
模块化
模块化规范:以什么语法引用格式,以什么语法格式向外暴露成员。
node.js中模块三大分类:内置,自定义和第三方,模块
加载模块:require()
//加载内置fs模块
const fs = require('fs')
自定义模块调用时可以不用写.js后缀。
模块作用域:某个模块内的只能在本模块内使用,别的模块导入了此模块但是此模块如果没有向外暴露依然不能访问到。
解决了变量名污染问题。
module对象:
module.exports = {} 永远以这个的最终指向为准,如果更新了,那么就覆盖了旧的。
node提供了exports对象,方便使用,最终以module.exports = {} 为准。
module.exports = {
nickname:'zhangsan',
sayHello() {//注意这里不用打function
console.log('hello!')
}
}
export使用误区:空
ComonJS规定的模块化规范
1 每个模块内部,module变量代表当前模块。
2 module变量是一个对象,它的export属性即module.exports是对外的接口。
3 加载某个模块,其实加载的是该模块的module.exports属性,加载模块用require()方法。
包 npm
node.js中第三方模块又叫做包。
npm: Nodejs下的包管理器。
npm:安装node的时候就已经安装了npm模块,可以使用 npm -v 命令查看版本号。
安装包 :在cmd中找到对应文件夹,打入 npm install ,简写为 npm i
node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包。
package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。
不要手动修改这两个文件
指定安装包的版本:
npm i moment@2.22.2
包的语义化规范:点分十进制,三位数字,第一位表示大版本,第二位表示功能版本,第三位表示bug修复版本。
-D:npm install name -save-dve 简写(npm install name -D) 自动把模块和版本号添加到devdependencies。
-S:npm install name -save 简写(npm install name -S) 自动把模块和版本号添加到dependencies。
-g : 包安装到全局环境中。
-y :yes 省略后面确认,直接安装。
导入包:
const express = require('express')
包管理配置文件
package.json:配置文件。多人协作中不需要传输包,只用传输包配置文件package.json,这一文件会指导团队成员安装哪些包。
项目开发中要把node_modules 文件夹添加到.gitignore忽略文件中去。
快速创建包管理文件,英文路径:
npm init -y
package.json中的dependencies节点记录了安装有哪些包。
执行npm i
命令,可以根据package.json自动安装所需要的包。
卸载包:npm uninstall XXX
如果一些包只在开发阶段用得到,在项目上线之后用不到,则把这些包记录到devDependencies节点中。
//简写
npm i 包名 -D
//完整写法
npm install 包名 --save-dev
在npm的官网上对应的包界面会详细介绍某个包怎么用,是否需要安装到devDependencies。
更换npm镜像服务器
//Check current server
npm config get registry
//change server
npm config set registry=https://registry.npm.taobao.org/
//nrm change mirror server
npm i nrm -g
nrm ls
nrm use taobao
nrm 快速切换服务器
npm i nrm -g
nrm ls //查看全部可用资源
nrm use taobao //切换为淘宝
包的分类
npm下载的包分为项目包与全局包。
项目包分为开发依赖包与核心依赖包,核心依赖包开发与运行都要用。
全局包为 -g ,工具性的包。
i5ting_toc 这个包可以把md文件转为html
规范包结构,必须包含三点:
1 单独目录存在
2 包含package.json 文件
3 package.jsonname version main 三个属性 名字 版本 和入口
开发属于自己的包
要求:
1 新建根目录。
2 根目录下新建三个文件:
package.json
index.js
readme.md
关于package.json
1 .npm init -y
-y 的含义:yes的意思,在init的时候省去了敲回车的步骤,生成的默认的package.json
2 .什么是Node.js的模块(Module)?在Node.js中,模块是一个库或框架,也是一个Node.js项目。
Node.js项目遵循模块化的架构,当我们创建了一个Node.js项目,意味着创建了一个模块,
这个模块的描述文件,被称为package.json。
package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。
文件夹名称不代表包名称,真正的包名称是package.json 中的name节点
{
"name": "kaka-tools",
"version": "1.1.0",
"main": "index.js",//main代表包的入口
"description": "提供了格式化时间、HTMLEscape相关的功能",
"keywords": [
"dateFormat",
"escape"
],
"license": "ISC"
}
转义与还原html
// 定义转义 HTML 字符的函数
function htmlEscape(htmlstr) {
return htmlstr.replace(/<|>|"|&/g, match => {
switch (match) {
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
// 定义还原 HTML 字符串的函数
function htmlUnEscape(str) {
return str.replace(/<|>|"|&/g, match => {
switch (match) {
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
module.exports = {
htmlEscape,
htmlUnEscape
}
拆分包
const date = require('./src/dateFormat')
const escape = require('./src/htmlEscape')
// 向外暴露需要的成员
module.exports = {
...date,
...escape
}
//ES6中的三点运算符表示展开
模块加载机制
被加载过得模块会被放入缓存,意味着被缓存的模块不会被重复执行。
内置模块最优先加载
自定义模块,加载必须以./或者…/开头
同时,在使用 require() 导入自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下的文件:
按照确切的文件名进行加载
补全 .js 扩展名进行加载
补全 .json 扩展名进行加载
补全 .node 扩展名进行加载
加载失败,终端报错
第三方模块,
如果传递给 require() 的模块标识符不是一个内置模块,也没有以 ‘./’ 或 ‘…/’ 开头,则 Node.js 会从当前模块的父目录开始,尝试从 /node_modules 文件夹中加载第三方模块。
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如,假设在 ‘C:\Users\project\foo.js’ 文件里调用了 require(‘tools’),则 Node.js 会按以下顺序查找:
C:\Users\project\node_modules\tools
C:\Users\node_modules\tools
C:\Users\node_modules\tools
C:\node_modules\tools
第六章 EXPRESS与CORS
EXPRESS是一个基于node的web开发框架,类似于内置的http模块,用于创建服务器。
前端程序员常见两种服务器:网页服务器和API接口服务器。
express均可创建。
安装:npm i express@4.17.1
通过 req.query 对象,可以得到客户端通过查询字符串的形式,发送到服务器的参数。
//1 导入express包
const express = require('express')
//2 调用express函数,创建web服务器实例app
const app = express()
//监听get方法,监听客户端发来的get请求
app.get('/user', (req, res) => {
res.send({ name: 'zs', age: '17' })
})
app.post('/users', (req, res) => {
res.send('post success!')
})
//3 调用app.listen,启动服务器
app.listen(80, () => {
console.log('express server is running at http://127.0.0.1');
})
req.qery 获取url的参数
req.params 获取通过 : 来匹配的动态参数动态参数
app.get('/user/:id/:name', (req, res) => {
console.log(req.params)
res.send(req.params)
})
id这个名字能随意更换。
后面可以添加多个
静态托管资源
通过这个可以方便地创建静态服务器
aapp.use(express.static('public'))
这条代码表示,创建静态服务器aapp,并可以访问public文件夹下的全部内容。piblic这层路径不会出现在网址中
挂载路径前缀
aapp.use('/abc',express.static('public'))
再访问public下的文件的时候,就需要先打上abc了
127.0.0.1/abc/index.html
nodemon 自动重启本地服务器
命令:nodemon app.js
express路由
广义上来讲,路由就是映射关系。
在 Express 中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express 中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数,格式如下:
app.METHOD(URL,HANDELER)
路由模块化
单独创建一个路由文件
//1 创建路由模块对应的 .js 文件
var express = require('express')
//2 调用 express.Router() 函数创建路由对象
var router = express.Router()
//3 向路由对象上挂载具体的路由
router.get('/user', (req, res) => {
res.send({ name: 'zs', age: '17' })
})
router.post('/users', (req, res) => {
res.send('post success!')
})
//4 使用 module.exports 向外共享路由对象
module.exports = router
//5 使用 app.use() 函数注册路由模块
使用前要先在主程序require导入然后是use注册
app.use(router)
app.use()这个命令用来注册中间件
为router添加前缀
app.use(’/api’,router)
中间件
中间件(Middleware ),特指业务流程的中间处理环节。
形参中必须包含next函数。next函数是实现中间件连续调用的关键,表示把流转关系转给下个中间件或者路由。
客户端发起的任何请求到达服务器后都会触发的中间件,叫做全局中间件。通过调用app.use()即可定义一个全局生效中间件。中间件调用顺序就是书写中间件的先后顺序。
程序先后怎么写,服务器运行中先后怎么调用。
案例:记录到服务器每个请求的时间 const time = Date.now()
局部生效中间件
写在具体请求中的中间件函数,叫做局部生效中间件。
app.get('/',mw1,mw2,(req,res)=>{res.send('home page')})
//或者写成
app.get('/',[mw1,mw2],(req,res)=>{res.send('home page')})
中间件五个注意事项
1 .一定要在路由之前注册中间件
2 .客户端发送过来的请求,可以连续调用多个中间件进行处理
3 .执行完中间件的业务代码之后,不要忘记调用 next() 函数
4 .为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码
5 .连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象
中间件的分类
应用级别中间件 ,绑定在服务器实例上
路由级别中间件 ,绑定在路由上
错误级别:用于捕获项目中发生的异常错误,避免项目崩溃。
格式:四个形参,(err,req,res,next)
app.get('/',function(req,res){
//抛出一个自定义错误
throw new Error('internal error!')
res.send('home page')
})
//错误级别中间件
app.use(function(err,req,res,next){
//服务器内部打印错误信息
console.log('an error occured'+err.message)
//向客户端响应错误信息。
res.send('error!' +err.message)
})
注意:错误级别中间件必须注册在所有路由之后。
内置中间件:
1 express.static 快速托管静态资源的内置中间件
2 express.json 解析json格式的请求体数据
3 express.urlencoded 解析url-encoded格式的请求体数据
除了错误级别中间件之外,其他中间件都需要放在路由前进行配置。
const app = express()
app.use(express.json())
app.post('/user',(req,res)=>{
console.log(req.body)
res.send('ok')
})