Ajax-day02
表单相关操作
表单应用场景
Web2.0典型特征:网站需要有交互(互联网中看到的内容由上网者产生)
通过表单,用户可以向服务器提交数据信息,比如注册账号、登录账号、发微博…..
用户与网站的交互方式:基于表单提交数据给服务器
表单基本结构
- form标签本身具有提交行为,默认提交方式是get
 - 表单输入域
 - 提交按钮
 
<!-- 传统的表单提交后服务器返回的结果会覆盖当前页面(整页刷新) --><!-- action表示提交表单数据目标url地址 --><!-- method表示提交方式 --><form action="http://www.liulongbin.top:3006/api/addbook" method="post"><div>图书名称:<input type="text" name="bookname"></div><div>图书作者:<input type="text" name="author"></div><div>图书出版社:<input type="text" name="publisher"></div><div><!-- submit类型默认有提交表单行为 --><!-- <input type="submit" value="提交"> --><!-- button类型没有提交表单行为 --><!-- <input type="button" value="提交"> --><!-- 如下的按钮也默认有提交行为 --><button>提交</button></div></form>
提交表单:输入域的name属性必须提供并且要与后台要求的名称匹配
form标签的核心属性
- action 表示提交的后台url地址
 method 表示提交的方式
- get 默认提交方式
 - post
 
总结:
- 表单输入域的name属性值由后端接口决定
 - 按钮的默认提交行为
 
Form标签属性
- action 提交到后端的地址,如果不写,默认表示当前页面
 - method 请求方式,支持get和post,默认是get方式
 target 打开action地址的方式(服务器返回的结果在哪里显示)
- _blank 表示打开一个新的页面
 - _self 表示在当前页面刷新(默认值)
 
enctype 发送数据的编码方式(设置前端提交给服务器的数据格式)
默认值 application/x-www-form-urlencoded
- bookname=abc&author=lisi&publisher=hello
 
- 用于文件上传 multipart/form-data
 

总结
enctype的值类型
- application/x-www-form-urlencoded (默认值,用于普通表单提交)
 - multipart/form-data (用于文件上传)
 
form常用类型标签
- 普通文本框 text
 - 文件上传按钮 file
 - 密码框 password
 - 下拉选框 select
 - 多行文本框 textarea
 - 单选框 radio
 - 多选框 checkbox
 - ……
 
传统表单提交的问题与解决方法
- 表单页面在提交后会发生跳转,如何不跳转页面?
 - 解决办法:使用Ajax方式提交表单
 - 第一种方式:Ajax提交表单
 
<body><!-- 传统的表单提交后服务器返回的结果会覆盖当前页面(整页刷新) --><!-- action表示提交表单数据目标url地址 --><!-- method表示提交方式 --><form><div>图书名称:<input type="text" name="bookname"></div><div>图书作者:<input type="text" name="author"></div><div>图书出版社:<input type="text" name="publisher"></div><div><!-- <button id="btn">提交</button> --><input id="btn" type="button" value="提交"></div></form><script src="lib/jquery.js"></script><script>// 基于Ajax提交表单$('#btn').click(function () {$.post('http://www.liulongbin.top:3006/api/addbook', {bookname: $('input[name=bookname]').val(),author: $('input[name=author]').val(),publisher: $('input[name=publisher]').val()}, function (ret) {console.log(ret)})})</script></body>
- 第二种方式:基于Ajax提交表单
 
<form id="myform">
// 基于Ajax提交表单$('#myform').submit(function (e) {// 需要手动阻止表单的默认行为e.preventDefault()$.post('http://www.liulongbin.top:3006/api/addbook', {bookname: $('input[name=bookname]').val(),author: $('input[name=author]').val(),publisher: $('input[name=publisher]').val()}, function (ret) {console.log(ret)})})
总结:基于Ajax提交表单的方式
- 使用click事件,该事件需要绑定到按钮上,此时推荐使用 type=’button’的按钮
 - 使用submit事件,该事件需要绑定到form标签上,此时推荐使用button标签或者type是submit的按钮,此时需要阻止表单的默认提交行为。
 
关于表单数据的处理
关于表单数据的获取方式
// jQuery针对表单单独提供了一个方法serialize,用于获取表单的所有的数据// 表单输入域必须有name属性,并且name的值需要按照接口文档的要求来设置const formData = $(this).serialize()
注意:必须使用form标签的jQuery实例对象调用serialize方法
发表评论案例
发表评论案例图示如下
- 新闻或者博客文章底部一般都有评论功能
 - 提供表单发表评论,评论列表展示
 

案例基本布局
- 顶部表单区
 - 底部列表区
 
<!-- 评论面板 --><div class="panel panel-primary"><div class="panel-heading"><h3 class="panel-title">发表评论</h3></div><form class="panel-body" id="formAddCmt"><div>评论人:</div><input type="text" class="form-control" name="username" autocomplete="off" /><div>评论内容:</div><textarea class="form-control" name="content"></textarea><button type="submit" class="btn btn-primary">发表评论</button></form></div><!-- 评论列表 --><ul class="list-group" id="cmt-list"><li class="list-group-item"><span class="badge" style="background-color: #F0AD4E;">评论时间:</span><span class="badge" style="background-color: #5BC0DE;">评论人:</span>Item 1</li></ul>
获取评论列表数据
- 页面加载时调用接口获取评论列表数据
 
// 加载评论列表数据function loadCommentList () {$.get('http://www.liulongbin.top:3006/api/cmtlist', function (res) {if (res.status === 200) {// 获取数据成功const list = res.data} else {alert(res.message)}})}loadCommentList()
总结:页面打开时,直接调用接口获取评论列表数据。
渲染评论列表
- 解析数据拼接字符串(ES6的模板字符串拼接数据)
 
// 获取评论列表数据if (ret.status === 200) {const list = ret.datalet rowTag = ''$.each(list, function (index, item) {rowTag += `<li class="list-group-item"><span class="badge" style="background-color: #F0AD4E;">评论时间:${item.time}</span><span class="badge" style="background-color: #5BC0DE;">评论人:${item.username}</span>${item.content}</li>`})$('#cmt-list').html(rowTag)} else {alert(ret.msg)}
总结:把数据填充到页面的过程称之为渲染,前端渲染。
实现发表评论功能
绑定事件
- form表单元素绑定submit事件
 - 按钮元素绑定click事件
 
获取表单数据提交到后台
$(this).serialize()- 重置表单方法reset
 
// 功能2:发表评论// 1、绑定按钮的点击事件$('#formAddCmt').submit(function (e) {e.preventDefault()// 2、获取表单数据const formData = $(this).serialize()// 3、提交表单$.post('http://www.liulongbin.top:3006/api/addcmt', formData, function (ret) {// 4、处理返回结果if (ret.status === 201) {// 发表成功,刷新列表loadCommnetList()// 清空表单:原生DOMAPI中有一个方法reset可以重置表单$('#formAddCmt').get(0).reset()} else {alert(ret.msg)}})})
原生Ajax
Ajax Asynchronous Javascript and Xml 异步的JavaScript和Xml
原生Ajax基本用法
浏览器已经实现了Ajax发送请求的技术XMLHttpRequest(2005之后)
基本使用步骤get post 模板
- 创建xhr对象
 - 调用xhr.open()方法准备参数
 - 调用xhr.send()方法执行发送动作
 - 监听xhr.onreadystatechange事件,用于获取服务器返回数据
 
// 1、创建xhr实例对象var xhr = new XMLHttpRequest()// 2、准备发送请求的相关参数xhr.open('get', 'http://www.liulongbin.top:3006/api/getbooks')// 3、执行发送请求的动作xhr.send(null)// 4、指定回调函数,用于处理服务器的返回xhr.onreadystatechange = function () {// 该函数是服务器返回数据后触发,该方法不仅仅触发一次if (xhr.readyState === 4 && xhr.status === 200) {// 如果上述两个条件都满足,就可以获取服务器返回的正常数据// ret 是普通字符串var ret = xhr.responseTextconsole.log(ret)console.log(typeof ret)}}
总结:使用原生Ajax发送请求很繁琐。
xhr对象详解-请求参数
get请求方式传参
- url地址查询字符串方式传递参数
 
xhr.open('get', 'http://www.liulongbin.top:3006/api/getbooks?id=1&author=吴承恩')var param = '?id=1&author=吴承恩'// params = encodeURI(params)xhr.open('get', 'http://www.liulongbin.top:3006/api/getbooks' + param)
url编码(url如果传递参数本身是不支持中文,所以get传参时需要编码,如果不进行编码,那么后端可能得不到正确的数据)
- encodeURI() 编码函数
 - decodeURI() 解码函数
 
var str = '黑马程序员'var str1 = encodeURI(str)console.log(str1)var str2 = decodeURI('%E9%BB%91%E9%A9%AC')console.log(str2)
post请求方式传参
- 必须设置请求头
 - 必须在send方法调用前设置请求头
 
// 设置 Content-Type 请求头(告诉服务器前端传递过去的数据格式)xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')// 调用 send 函数xhr.send('bookname=水浒传&author=施耐庵&publisher=上海图书出版社')
xhr对象详解-响应状态
readyState
- 如果该状态值为4表示服务器已经完全把数据返回了,此时可以获取后台的数据
 - 仅仅表示数据已经得到,但是正确与否不确定
 

status
- http协议的状态:如果status是200表示数据正常,否则表示数据不正常
 
// readyState状态值是4表示服务器数据已经完全返回// status的值是200表示服务器返回的数据是正常的// if (xhr.readyState === 4 && xhr.status === 200) {// // 如果这两个条件都成立,就可以获取服务器数据了// var res = xhr.responseText// console.log(typeof res)// } else {// alert(xhr.responseText)// }// ---------------------------------------------------// 考虑异常情况,需要用这种写法if (xhr.readyState === 4) {if (xhr.status === 200) {// 成功获取服务器数据var res = xhr.responseText} else {alert('服务器发生错误')}}
总结
- xhr.readyState===4表示后端已经把数据完整返回,但是正常与否不确定
 - xhr.status===200表示服务器返回的数据是正常的,否则是异常的
 
xhr对象详解-响应数据
数据格式分析
xml (类似于html格式,标签包裹数据)
- 缺点1:冗余数据比较多,不方便网络传输
 - 缺点2:不方便前端解析
 
json(本质上是字符串,但是有规则)
- 解决XML的两个问题
 
JSON基本规则(JSON是字符串的一种格式)
- 数据结构为键值对 uname: ‘lisi’
 - key 必须是使用英文的双引号包裹的字符串 “uname”:”lisi”
 - 字符串类型的值必须使用双引号包裹 “uname”:”lisi”
 - JSON 中不能写注释
 - JSON 的最外层必须是对象或数组格式
 - 不能使用 undefined 或函数作为 JSON 的值
 - value 的数据类型可以是数字、字符串、布尔值、null、数组、对象6种类型
 
JSON格式转换(JSON和对象之间的关系)
- 对象转字符串 JSON.stringify()
 - 字符串转对象 JSON.parse()
 
var jsonStr = '{"a": "Hello", "b": "world"}'// 把JSON字符串转成对象var obj = JSON.parse(jsonStr)console.log(obj)// 把对象转成JSON字符串var obj2 = { a: 'hello', b: 'world', c: false }var str = JSON.stringify(obj2)console.log(str)
Ajax返回的结果处理
- xhr.responseText本身是字符串
 
var obj = JSON.parse(xhr.responseText)
总结:
- JSON格式的具体规范
 - JSON字符串与对象之间的转换
 
模仿$.ajax封装函数
- 基本结构分析:回调函数参数如何得到
 - 参数处理:支持get和post
 - 响应数据处理:转换为对象
 
function myAjax (options) {// console.log(options.success)// 这里的函数调用调用的是谁?其实就是回调函数success// 通过原生Ajax发送请求并且获取服务器返回的数据// 请求参数参数处理/*{id: 1,author: 'tom'}把上述数据转换为如下的格式id=1&author=tom*/var params = ''for (var key in options.data) {// 每次遍历拼接一个属性params += key + '=' + options.data[key] + '&'}// id=1&author=tom&if (params) {// 去掉最后一个字符params = params.substring(0, params.length - 1)}var xhr = new XMLHttpRequest()// 处理get请求参数if (options.type === 'get') {options.url = options.url + '?' + encodeURI(params)}xhr.open(options.type, options.url)if (options.type === 'post') {// 处理post请求传参xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')xhr.send(params)} else {// 执行get请求动作xhr.send(null)}xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status === 200) {// 服务端返回的原始数据var res = xhr.responseTextres = JSON.parse(res)options.success(res)}}}}
总结
表单相关操作
- 表单的作用:与后端交互
 - 表单的基本结构
 - 表单本身就有提交数据功能,会自动刷新网页
 可以通过Ajax方式提交表单,可以不刷新网页
- 基于click事件提交
 - 基于submit事件提交
 
发表评论案例
- 动态渲染评论列表
 - 点击发表评论
 - 获取所有表单数据serialize()
 - 重置表单 reset()
 
原生Ajax基本使用
基本使用步骤
- 创建xhr实例对象
 - 准备请求参数 xhr.open
 - 执行发送动作 xhr.send
 - 监听服务器返回 xhr.onreadystatechange = function(){}
 
细节分析
- get请求传参(通过url地址传递),需要处理中文的编码
 - post请求传参(通过send传递),需要设置请求头
 关于响应状态
- readyState===4表示服务器已经完全返回了数据
 - status===200表示返回的数据是正常的
 
关于响应的结果
- 了解XML数据格式
 - 熟悉JSON格式的具体规则
 - JSON字符串和对象之间的转换
 
原生Ajax升级版
XHR2.0介绍
旧版的缺点
- 仅仅支持文本数据传输,无法上传文件(早期文件上传基于Flash)
 - 传送数据没有进度提示
 
新版功能
- 可以使用FormData传递表单数据
 - 可以上传文件
 - 传输数据时进行提示
 
FormData基本用法
- FormData是标准的js构造函数(WebAPI)
 - FormData作用是啥?传递post请求参数(一般数据来源于表单)
 - 如何知道FormData实例对象有几个方法?看官网
 FormData怎么用来传参?两种用法
- 构造函数不传参使用append方法添加参数
 - 构造函数传递form元素作为参数
 
用法一:
- 造函数不传参使用append方法添加参数
 - 不需要设置请求头
 
var xhr = new XMLHttpRequest()xhr.open('post', 'http://www.liulongbin.top:3006/api/formdata')var fd = new FormData()fd.append('bookname', '水浒传')fd.append('author', '施耐庵')fd.append('publisher', '出版社')// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')xhr.send(fd)xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {var res = xhr.responseTextconsole.log(res)}}
用法二:
- 这种方式,表单的输入域必须提供name属性
 
// 通过 DOM 操作,获取到 form 表单元素var form = document.querySelector('#form1')// 创建 FormData,快速获取到 form 表单中的数据var fd = new FormData(form)// 发送请求xhr.send(fd)
FormData相关方法
- fd.append
 - fd.get
 - fd.has
 - fd.delete
 
var fd = new FormData()fd.append('msg', 'nihao')// var v = fd.get('msg')// fd.delete('msg')var f = fd.has('msg')console.log(f)
FormData文件上传
文件上传的本质:把本地的文件传递到服务器并进行存储,然后返回文件的URL
- UI布局需要用到file标签
 - 判断是否选择文件
 - 向FormData中追加文件
 - 使用xhr发起上传文件请求
 - 监听onreadystatechange事件
 
<!-- 1. 文件选择框 --><input type="file" id="file1" /><!-- 2. 上传文件的按钮 --><button id="btnUpload">上传文件</button>
// 点击提交按钮进行文件上传var btn = document.getElementById('btn')btn.onclick = function (e) {e.preventDefault()var xhr = new XMLHttpRequest()xhr.open('post', 'http://www.liulongbin.top:3006/api/upload/avatar')// 如何得到选中的文件?// file标签DOM元素的files属性表示选中的所有文件// files不是数组,而是伪数组var files = document.getElementById('myfile').filesconsole.log(files)// 把文件添加到FormData中(avatar由后端决定)var fd = new FormData()fd.append('avatar', files[0])xhr.send(fd)xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {var res = xhr.responseText// 得到上传成功后图片的路径信息res = JSON.parse(res)var img = document.getElementById('myimg')// 把图片显示出来img.setAttribute('src', 'http://www.liulongbin.top:3006' + res.url)}}}
- 直接基于Form表单也可以进行文件上传
 
<form><input type="file" name="avatar" id="myfile"><button id="btn">点击</button></form>
// 通过FormData上传文件var form = document.querySelector('form')var fd = new FormData(form)
上传文件进度提示
上传大一点儿的文件需要一些时间,这段时间最好给一个提示,提升用户体验
xhr.upload.onprogress = function (e) {}
- 监听文件上传进度
 
xhr.upload.onload = function () {}
- 监听文件上传完成事件
 
<progress id="pg" max="100" value="0"></progress><!-- bootstrap 中的进度条 --><div class="progress" style="width: 500px; margin: 15px 10px;"><div class="progress-bar progress-bar-striped active" style="width: 0%" id="percent">0%</div></div>
// 获取到用户选择的文件列表var files = document.querySelector('#file1').filesif (files.length <= 0) {return alert('请选择要上传的文件!')}var fd = new FormData()// 将用户选择的文件,添加到 FormData 中fd.append('avatar', files[0])var xhr = new XMLHttpRequest()// 监听文件上传的进度xhr.upload.onprogress = function (e) {if (e.lengthComputable) {// 判断文件是否正在上传// 已经上传的大小/文件的总大小 = 进度var rate = Math.ceil((e.loaded / e.total) * 100)// 把进度数据应用到进度条上var bar = document.getElementById('percent')// 修改进度条的进度bar.style = 'width: '+ rate +'%'// 进度条显示的文字进度bar.innerHTML = rate + '%'}}// 监听文件上传完成的动作xhr.upload.onload = function () {// 文件上传完成后触发,修改进度条颜色var bar = document.getElementById('percent')// DOM元素的classList可以操作类名// 删除已有的类名bar.classList.remove('progress-bar-striped')// 添加一个新的类名bar.classList.add('progress-bar-success')}xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar')xhr.send(fd)
axios
axios介绍
axios 是一个专门用于调用后台接口的js库,后续Vue和React环境都可以使用它
- 支持客户端发送Ajax请求
 - 支持服务端Node.js发送请求
 - 支持Promise相关用法(解决回调地狱问题)
 - 支持请求和响应的拦截器功能(登录时会用到)
 - 可以手动取消请求
 - 自动转换JSON数据(JSON.parse(res))
 - 可以更加安全的处理请求
 
基本用法
- 发送请求通过axios.get
 获取结果通过then方法
- 对于返回的结果来说,axios会在后台返回的数据外面包裹一层data属性
 - 假如后台返回的数据是 {status: 200, msg: ‘返回成功’,data: [{}]}
 - 那么axios实际得到的数据是 {data: {status: 200, msg: ‘返回成功’,data: [{}]}}
 
axios.get('http://www.liulongbin.top:3006/api/getbooks').then(function (res) {// 这里用于获取服务端返回的结果// res.data中的data是由axios规定的名称,表示从后台得到的数据console.log(res.data)})

axios的get用法
get 请求数据并传递参数
get方法第二个参数是配置对象 axios(url, [, config])
- params 用于传递get参数,它的值为对象,对象中用于传递键值对形式参数
 - 发送请求时,params中的参数会自动拼接到url中进行发送
 - params 是专门用于get请求
 
// 上述传参方式不太方便axios.get('http://www.liulongbin.top:3006/api/getbooks', {// params名称是否固定?是的,是axios的规定params: {id: 1,bookname: 'nihao'}}).then(function (res) {// 这里用于获取服务端返回的结果console.log(res)})
// api/get 接口专门测试get请求,请求参数是什么,就返回什么axios.get('http://www.liulongbin.top:3006/api/get', {// params名称是否固定?是的,是axios的规定params: {id: 1,bookname: 'nihao',publisher: 'abc'}}).then(function (res) {// 这里用于获取服务端返回的结果console.log(res)})
axios发送post请求
post请求数据传递
传统的表单提交的数据格式 content-type: application/x-www.form-urlencoded
- 后端一般默认支持这种提交的参数格式
 
post请求参数如果是对象,默认发送的是json形式的参数(content-type: application/json)
- 这种方式的请求参数也需要后台提供支持
 
// axios发送post请求 post(url[, data[, config]])// post 参数一:请求地址// post 参数二:表示请求参数,可选的// post 参数三:表示配置对象,可选的// 传统的表单提交的数据格式 content-type: application/x-www.form-urnencoded// 如下的请求方式,默认发送的是json形式的参数(content-type: application/json)// 这种方式的请求参数也需要后台提供支持axios.post('http://www.liulongbin.top:3006/api/post', {uname: 'lisi',pwd: '123'}).then (function (res) {console.log(res)})
- 强制采用content-type: application/x-www.form-urnencoded 请求参数格式
 
// axios发送post请求 post(url[, data[, config]])// post 参数一:请求地址// post 参数二:表示请求参数,可选的// post 参数三:表示配置对象,可选的// 强制post发送这种格式请求参数 content-type: application/x-www.form-urlencoded// 类似于FormData的用法,专门用于参数传递// URLSearchParams 是一个标准的构造函数,类似于FormData,属于新的APIvar fd = new URLSearchParams()fd.append('uname', 'lisi')fd.append('pwd', '123')// 发送请求时会转换为如下格式// uname=lisi&pwd=123// 这种格式的请求参数,服务器一般默认都支持axios.post('http://www.liulongbin.top:3006/api/post', fd).then (function (res) {console.log(res)})
另一种用法
axios作为方法使用
- 支持各种请求方式
 - 支持各种方式传参
 
// axios更加通用的调用接口方式// axios({// // 表示请求方式// method: 'get',// url: 'http://www.liulongbin.top:3006/api/get',// params: {// id: 1,// uname: 'lisi'// }// }).then(function (res) {// console.log(res)// })// 发送请求参数 json格式// axios({// // 表示请求方式// method: 'post',// url: 'http://www.liulongbin.top:3006/api/post',// data: {// id: 123,// uname: 'lisi',// age: 12// }// }).then(function (res) {// console.log(res)// })// 发送请求参数 form格式// var fd = new URLSearchParams()// fd.append('uname', 'lisi')// fd.append('pwd', '123')// axios({// // 表示请求方式// method: 'post',// url: 'http://www.liulongbin.top:3006/api/post',// data: fd// }).then(function (res) {// console.log(res)// })
全局默认配置
配置基准URL地址
- 做项目时,基准一般配置一次即可,发送请求时检修url后面的路径即可
 
axios.defaults.baseURL = 'http://www.liulongbin.top:3006/'axios.get('api/get?id=1').then(function (res) {console.log(res)})
