[TOC]

1 URL

URL(全称是UniformResourceLocator)中文叫统一资源定位符,用于标识互联网上每个资源的唯一存放位置。
浏览器只有通过URL地址,才能正确定位资源的存放位置,从而成功访问到对应的资源。

URL地址一般由三部组成:
① 客户端与服务器之间的通信协议
② 存有该资源的服务器名称
③ 资源在服务器上具体的存放位置
image.png

2 请求数据

XMLHttpRequest(简称 xhr)是浏览器提供的 js 成员,通过它,可以请求服务器上的数据资源。

var xhrObj = new XMLHttpRequest()

2.1 资源的请求方式

get 请求通常用于获取服务端资源(向服务器要资源)
post 请求通常用于向服务器提交数据(往服务器发送资源)

2.2 Ajax

Ajax 的全称是 Asynchronous Javascript And XML(异步 JavaScript 和 XML)。
通俗的理解:在网页中利用 XMLHttpRequest 对象和服务器进行数据交互的方式,就是Ajax。

3 jQuery中的Ajax

jQuery 对 XMLHttpRequest 进行了封装

3.1 get请求

$.get(url, [data], [callback])

$.get('http://www.liulongbin.top:3006/api/getbooks', function(res) {
  console.log(res) // 这里的 res 是服务器返回的数据
})

$.get('http://www.liulongbin.top:3006/api/getbooks', { id: 1 }, function(res) {
  console.log(res)
})

3.2 post请求

$.post(url, [data], [callback])

$.post(
  'http://www.liulongbin.top:3006/api/addbook', // 请求的URL地址
  { bookname: '水浒传', author: '施耐庵', publisher: '上海图书出版社' }, // 提交的数据
  function(res) { // 回调函数
    console.log(res)
  }
)

3.3 ajax

$.ajax({
  type: '', // 请求的方式,例如 GET 或 POST
  url: '', // 请求的 URL 地址
  data: { },// 这次请求要携带的数据
  success: function(res) { } // 请求成功之后的回调函数
})

get

$.ajax({
  type: 'GET', // 请求的方式
  url: 'http://www.liulongbin.top:3006/api/getbooks', // 请求的 URL 地址
  data: { id: 1 },// 这次请求要携带的数据
  success: function(res) { // 请求成功之后的回调函数
    console.log(res)
  }
})

post

$.ajax({
  type: 'POST', // 请求的方式
  url: 'http://www.liulongbin.top:3006/api/addbook', // 请求的 URL 地址
  data: { // 要提交给服务器的数据
    bookname: '水浒传',
    author: '施耐庵',
    publisher: '上海图书出版社'
  },
  success: function(res) { // 请求成功之后的回调函数
    console.log(res)
  }
})

4 接口

使用 Ajax 请求数据时,被请求的 URL 地址,就叫做数据接口(简称接口)。

5 表单

5.1 上传数据编码

enctype 属性用来规定在发送表单数据之前如何对数据进行编码。

  • application/x-www-form-urlencoded 在发送前编码所有字符(默认)
  • multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
  • text/plain 空格转换为 “+” 加号,但不对特殊字符编码。(很少用)

5.2 阻止表单默认提交

$('#form1').on('submit', function(e) {
  // 阻止表单的提交和页面的跳转
  e.preventDefault()
})

5.3 jQuery快速获取表单数据

$(selector).serialize()

serialize() 函数的好处:可以一次性获取到表单中的所有的数据。
使用 serialize() 函数快速获取表单数据时,必须为每个表单元素添加 name 属性!

<form id="form1">
  <input type="text" name="username" value="hello"/>
  <input type="password" name="password"  value="12345"/>
  <button type="submit">提交</button>
</form>
<script>
  console.log($('#form1').serialize());  // username=hello&password=12345
</script>

6 XMLHttpRequest

XMLHttpRequest(简称 xhr)是浏览器提供的 Javascript 对象,通过它,可以请求服务器上的数据资源。
image.png

6.1 get请求

// 1. 创建 XHR 对象
var xhr = new XMLHttpRequest()
// 2. 调用 open 函数,指定 请求方式 与 URL地址
xhr.open('GET', 'http://www.liulongbin.top:3006/api/getbooks')
// 3. 调用 send 函数,发起 Ajax 请求
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)
  }
}

6.2 xhr对象的readyState属性

image.png

6.3 查询字符串

定义:查询字符串(URL 参数)是指在 URL 的末尾加上用于向服务器发送信息的字符串(变量)。
格式:将英文的 ? 放在URL 的末尾,然后再加上 参数=值 ,想加上多个参数的话,使用 & 符号进行分隔。以 这个形式,可以将想要发送给服务器的数据添加到 URL 中。

http://www.liulongbin.top:3006/api/getbooks?id=1&bookname=西游记

6.4 URL编码与解码

URL编码原则的通俗理解:使用英文字符去表示非英文字符。

http://www.liulongbin.top:3006/api/getbooks?id=1&bookname=西游记
// 经过 URL 编码之后,URL地址变成了如下格式:
http://www.liulongbin.top:3006/api/getbooks?id=1&bookname=%E8%A5%BF%E6%B8%B8%E8%AE%B0

encodeURI() 编码的函数 
decodeURI() 解码的函数

console.log(encodeURI('中国')); // %E4%B8%AD%E5%9B%BD
console.log(decodeURI('%E4%B8%AD%E5%9B%BD'));  // 中国

6.2 post请求

// 1. 创建 xhr 对象
var xhr = new XMLHttpRequest()
// 2. 调用 open()
xhr.open('POST', 'http://www.liulongbin.top:3006/api/addbook')
// 3. 设置 Content-Type 属性(固定写法)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 4. 调用 send(),同时将数据以查询字符串的形式,提交给服务器
xhr.send('bookname=水浒传&author=施耐庵&publisher=天津图书出版社')
// 5. 监听 onreadystatechange 事件
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText)
  }
}

7 JSON

JSON 的英文全称是 JavaScript Object Notation
JSON 就是 Javascript 对象和数组的字符串表示法
它使用文本表示一个 JS 对象或数组的信息,因此, JSON 的本质是字符串。
JSON 中包含对象和数组两种结构。

对象结构

{
  "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 ] ]

7.1 JSON语法

注意事项:
① 属性名必须使用双引号包裹
② 字符串类型的值必须使用双引号包裹
③ JSON 中不允许使用单引号表示字符串
④ JSON 中不能写注释
⑤ JSON 的最外层必须是对象或数组格式
⑥ 不能使用 undefined 或函数作为 JSON 的值
JSON 的作用:在计算机与网络之间存储和传输数据。
JSON 的本质:用字符串来表示 Javascript 对象数据或数组数据

7.2 JSON转换

要实现从 JSON 字符串转换为 JS 对象,使用 JSON.parse() 方法
要实现从 JS 对象转换为 JSON 字符串,使用 JSON.stringify() 方法

var obj = {
  username: 'xixi',
  age: 18,
  sex: 'boy'
}
var str = JSON.stringify(obj) 
console.log(str);  // {"username":"xixi","age":18,"sex":"boy"}
console.log(JSON.parse(str)); // {username: "xixi", age: 18, sex: "boy"}

把数据对象转换为字符串的过程,叫做序列化,例如:调用 JSON.stringify() 函数的操作,叫做 JSON 序列化
把字符串转换为数据对象的过程,叫做反序列化,例如:调用 JSON.parse() 函数的操作,叫做 JSON 反序列化。

8 封装Ajax

/**
* 处理请求参数data
* @param {data} 需要发送到服务器的数据,是个对象
* @returns {string} 返回拼接好的查询字符串 name=zs&age=10
*/
function resolveData(data) {
  var arr = [];
  for (let k in data) {
    arr.push(k + '=' + data[k]);
  }
  return arr.join('&')
}

// options是一个对象
function ajax_func(options) {
  var xhr = new XMLHttpRequest()
  // 拼接查询字符串
  var qs = resolveData(options.data)

  // 判断请求
  if (options.type.toUpperCase() === 'GET') {
    xhr.open('get', options.url + '?' + qs)
    xhr.send()
  } else if (options.type.toUpperCase() === 'POST') {
    xhr.open('post', 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)
    }
  }
}


// 调用
ajax_func({
  type: 'GET',
  url: 'http://www.liulongbin.top:3006/api/getbooks',
  data: {
    id: 1
  },
  success: function (res) {
    console.log(res)
  }
})
ajax_func({
  type: 'post',
  url: 'http://www.liulongbin.top:3006/api/addbook',
  data: {
    bookname: '你麻痹',
    author: '施耐庵',
    publisher: '北京图书出版社'
  },
  success: function (res) {
    console.log(res)
  }
});

9 XMLHttpRequest Level2

旧版XMLHttpRequest的缺点:
① 只支持文本数据的传输,无法用来读取和上传文件
② 传送和接收数据时,没有进度信息,只能提示有没有完成

XMLHttpRequest Level2的新功能:
① 可以设置 HTTP 请求的时限
② 可以使用 FormData 对象管理表单数据
③ 可以上传文件
④ 可以获得数据传输的进度信息

9.1 设置HTTP请求时限

xhr.timeout = 3000 // 将最长等待时间设为 3000 毫秒
// 与之配套的还有一个timeout 事件,
xhr.ontimeout = function(event){
  alert('请求超时!')
}

9.2 FormData对象管理表单数据

HTML5 新增了一个 FormData 对象,可以模拟表单操作

// 1. 新建 FormData 对象
var fd = new FormData()
// 2. 为 FormData 添加表单项
fd.append('uname', 'zs')
fd.append('upwd', '123456')
// 3. 创建 XHR 对象
var xhr = new XMLHttpRequest()
// 4. 指定请求类型与URL地址
xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
// 5. 直接提交 FormData 对象,这与提交网页表单的效果,完全一样
xhr.send(fd)

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() {
    if (xhr.readyState === 4 && xhr.status === 200) {
      // 响应数据, 字符串
      console.log(xhr.responseText);
    }
  }
})

9.3 上传文件

新版 XMLHttpRequest 对象,不仅可以发送文本信息,还可以上传文件

<input type="file" id="file1" />
<button id="btnUpload">上传</button>
<br />
<img src="" alt="" id="img" width="800" />
<script>
  var btn = document.querySelector('#btnUpload');
  btn.addEventListener('click', e => {
    // 获取文件列表, 数组形式
    var files = document.querySelector('#file1').files;
    console.log(files);
    // 验证是否选择了文件
    if (files.length <= 0)
      return alert('上传文件为空');

    // 用来添加文件
    var fd = new FormData();
    fd.append('avatar', files[0]);

    var xhr = new XMLHttpRequest();
    xhr.open('post', 'http://www.liulongbin.top:3006/api/upload/avatar')
    xhr.send(fd)
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4 && xhr.status === 200) {
        var res = JSON.parse(xhr.responseText);
        if (res.status === 200) {
          document.querySelector('#img').src = 'http://www.liulongbin.top:3006' + res.url
        } else {
          alert(res.message)
        }
      }
    }
  })
</script>

image.png

9.4 显示文件上传进度

新版本的 XMLHttpRequest 对象中,可以通过监听 xhr.upload.onprogress 事件,来获取到文件的上传进度。

// 创建 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)
    }
}

完整:

var btn = document.querySelector('#btnUpload');
btn.addEventListener('click', e => {
  // 获取文件列表, 数组形式
  var files = document.querySelector('#file1').files;
  console.log(files);

  if (files.length <= 0)
    return alert('上传文件为空');

  // 用来添加文件
  var fd = new FormData();
  fd.append('avatar', files[0]);

  // 上传文件
  var xhr = new XMLHttpRequest();

  // 监听上传进度
  xhr.upload.onprogress = e => {
    // e.lengthComputable 是一个布尔值,表示当前上传的资源是否具有可计算的长度 
    if (e.lengthComputable) {
      // e.loaded 已传输的字节   e.total 需传输的总字节
      var percentComputable = Math.ceil((e.loaded / e.total) * 100);
      $('#percent').attr('style', 'width:' + percentComputable + '%').html(percentComputable + '%');
    }
  }

  // 监听上传完成事件
  xhr.upload.onload = () => {
    $('#percent').removeClass().addClass('progress-bar progress-bar-success')
  }

  xhr.open('post', 'http://www.liulongbin.top:3006/api/upload/avatar')
  xhr.send(fd)
  xhr.onreadystatechange = () => {
    if (xhr.readyState === 4 && xhr.status === 200) {
      var res = JSON.parse(xhr.responseText);
      if (res.status === 200) {
        document.querySelector('#img').src = 'http://www.liulongbin.top:3006' + res.url
      } else {
        alert(res.message)
      }
    }
  }
})

9.5 jQuery上传

注意:
$(document).ajaxStart() 函数会监听当前文档内所有的 Ajax 请求。
Ajax 请求结束时,执行 ajaxStop 函数。可以在 ajaxStop 的 callback 中隐藏 loading 效果,

// 会监听当前文档内所有的 Ajax 请求
$(document).ajaxStart(() => {
  $('#img').show()
})
// Ajax 请求结束时,执行 ajaxStop 函数
$(document).ajaxStop( ()=>{
  $('#img').hide()
})

$('#btnUpload').on('click', function(){
  // 转换成dom对象
  var files = $('#file1')[0].files
  if (files.length <= 0) {
    return alert('请选择图片上传')
  }
  var fd = new FormData()
  fd.append('avatar', files[0])

  // 上传
  $.ajax({
    type: 'post',
    url: 'http://www.liulongbin.top:3006/api/upload/avatar',
    data: fd,
    // 不修改 Content-Type 属性,使用 FormData 默认的 Content-Type 值
    contentType: false,
    // 不对 FormData 中的数据进行 url 编码,而是将 FormData 数据原样发送到服务器
    processData: false,
    success: res => {
      console.log(res);
    }
  })
})

10 axios

Axios 是专注于网络数据请求的库。
相比于原生的 XMLHttpRequest 对象,axios 简单易用
相比于 jQuery,axios 更加轻量化,只专注于网络数据请求。

10.1 axios发起GET请求

axios.get('url', { params: { /*参数*/ } }).then(callback)

var url = 'http://www.liulongbin.top:3006/api/get'
// 请求的参数对象
var paramsObj = { name: 'zs', age: 20 }
// 调用 axios.get() 发起 GET 请求
axios.get(url, { params: paramsObj }).then(function(res) {
  // res.data 是服务器返回的数据
  var result = res.data
  console.log(res)
})

10.2 axios发起POST请求

axios.post('url', { /*参数*/ }).then(callback)

// 请求的 URL 地址
var url = 'http://www.liulongbin.top:3006/api/post'
// 要提交到服务器的数据
var dataObj = { location: '北京', address: '顺义' }
// 调用 axios.post() 发起 POST 请求
axios.post(url, dataObj).then(function(res) {
  // res.data 是服务器返回的数据
  var result = res.data
  console.log(result)
})

10.3 综合

axios({
  method: '请求类型',
  url: '请求的URL地址',
  data: { /* POST数据 */ },
  params: { /* GET参数 */ }
}) .then(callback)


axios({
  method: 'GET',
  url: 'http://www.liulongbin.top:3006/api/get',
  params: { // GET 参数要通过 params 属性提供
    name: 'zs',
    age: 20
  }
}).then(function(res) {
  console.log(res.data)
})

11 跨域

在express章节中 more

11.1 同源

如果两个页面的协议,域名和端口都相同,则两个页面具有相同的源。
image.png

11.2 同源策略

同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能。
MDN 官方给定的概念:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
通俗的理解:浏览器规定,A 网站的 JavaScript,不允许和非同源的网站 C 之间,进行资源的交互,例如:
① 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
② 无法接触非同源网页的 DOM
③ 无法向非同源地址发送 Ajax 请求

11.3 跨域

出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。
网页:http://www.test.com/index.html
接口:http://www.api.com/userlist
image.png

11.4 解决方式JSONP

出现的早,兼容性好(兼容低版本IE)。缺点是只支持 GET 请求,不支持 POST 请求。

由于浏览器同源策略的限制,网页中无法通过 Ajax 请求非同源的接口数据。但是 标签不受浏览器同源策略的影响,可以通过 src 属性,请求非同源的 js 脚本。

因此,JSONP 的实现原理,就是通过 标签的 src 属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域接口响应回来的数据。

JSONP 和 Ajax 之间没有任何关系,不能把 JSONP 请求数据的方式叫做 Ajax,因为 JSONP 没有用到 XMLHttpRequest 这个对象。
image.png
通过src获取的是abc函数的调用。而同理对于远端的可以用请求参数获取指定的方法调用返回

11.5 jQuery中的JSONP

$.ajax({
  url: 'http://www.liulongbin.top:3006/api/jsonp?name=zs&age=20',
  // 如果要使用 $.ajax() 发起 JSONP 请求,必须指定 datatype 为 jsonp
  dataType: 'jsonp',
  success: function(res) {
    console.log(res)
  }
})

自定义参数及回调函数名称

$.ajax({
  url: 'http://www.liulongbin.top:3006/api/jsonp?name=zs&age=20',
  // 代表我们要发起JSONP的数据请求
  dataType: 'jsonp',
  jsonp: 'callback',    // 发送到服务端的参数名称,默认值为 callback
  jsonpCallback: 'abc',  // 自定义的回调函数名称,默认值为 jQueryxxx 格式
  success: function (res) {
    console.log(res)
  }
})

jQuery 中的 JSONP,也是通过 标签的 src 属性实现跨域数据访问的,只不过,jQuery 采用的是动态创建和移除