async javascripts and XML
原生方式
- 创建jax核心对象XMLHttpRequest
let xhr = new XMLHttpRequest()
- 注册回调函数
xhr.onreadystatechange = function () {
console.log(xhr.readyState)//会被调用4次
}
xhr.onreadystatechange = function () {
console.log(xhr.readyState)
// readyState 1-2-3-4
if (this.readyState == 4) {
//表示响应结束了
console.log("响应结束了")
//响应结束会有一个HTTP状态码200 - 300表示成功 是HTTP协议的一部分
console.log(this.status)
if (this.status == 404) {
alert('访问的页面不存在')
}
else if (this.status >= 200 && this.status <= 300) {
alert('响应成功')
document.querySelector('mydiv').innerHTML = this.responseText//从服务器传出来的数据用来渲染div盒子
}
}
}
- 开启通道
XMLHttpRequest.open(method,url,async,user,psw)
//method :GET/POST
//url请求的路径
//async:异步与否
//user:用户名
//pwd:密码
- 通过 ajax 提交数据
xhr.open("GET","url?name=value&name=value...",true)
- post请求提交数据:
xhr.open("POST","url",true)
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
//模拟form表单数据提交,这里一定要先open才能设置请求头
let password = document.querySelector('password').value
xhr.send("username="+username+"&password="+password)
- 发送请求
- send()用于get请求, send(string)用于POST请求
xhr.send()
完整代码
<!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>
</head>
<body>
<script type="text/javascript">
window.onload = function () {
document.getElementById("btn").onclick = function () {
//创建XMLHttprequest核心对象
let xhr = new XMLHttpRequest()
//注册回调函数
xhr.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
//通过XMLHttpRequest对象的response属性可以获取到服务器的内容
document.querySelector('.myspan').innerHTML = this.responseText
} else {
alert(this.status)
}
}
}
//开通道
xhr.open('GET', "/ajax/??", true)
//发送请求
xhr.send()
}
}
</script>
<button id='btn'>发送ajax get请求</button>
<span id="myspan"></span>
</body>
</html>
fetch
他是区别于XML的ajax请求接口,可以看作升级版的XMLHttprequest 值得注意的是:fetch 不会发送跨域的cookie
Promise<Response> fetch(input[, init]);
input
• 一个 USVString 字符串,包含要获取资源的 URL。一些浏览器会接受 blob: 和 data: 作为 schemes.
init
- 一个
Request对象。init 可选一个配置项对象,包括所有对请求的设置。可选的参数有: method: 请求使用的方法,如GET、POST。headers: 请求的头信息,形式为Headers的对象或包含ByteString值的对象字面量。body: 请求的 body 信息:可能是一个Blob、BufferSource、FormData、URLSearchParams或者USVString对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。mode: 请求的模式,如cors、no-cors或者same-origin。credentials: 请求的 credentials,如omit、same-origin或者include。为了在当前域名内自动发送 cookie,必须提供这个选项,从 Chrome 50 开始,这个属性也可以接受[FederatedCredential(en-US)](developer.mozilla.org/en-US/docs/…) 实例或是一个[PasswordCredential(en-US)](developer.mozilla.org/en-US/docs/…) 实例。cache: 请求的 cache 模式:default、no-store、reload、no-cache、force-cache或者only-if-cached。redirect: 可用的 redirect 模式:follow(自动重定向),error(如果产生重定向将自动终止并且抛出一个错误),或者manual(手动处理重定向)。在 Chrome 中默认使用follow(Chrome 47 之前的默认值是manual)。referrer: 一个USVString可以是no-referrer、client或一个 URL。默认是client。referrerPolicy: 指定了 HTTP 头部 referer 字段的值。可能为以下值之一:no-referrer、no-referrer-when-downgrade、origin、origin-when-cross-origin、unsafe-url。integrity: 包括请求的 subresource integrity 值(例如:sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=)。
返回值
一个 Promise,resolve 时回传 Response 对象。
使用实例
fetch('
autolinkhttp://example.com/movies.jsonautolink
')
.then(response => response.json())
.then(data => console.log(data));
fetch返回一个Promise对象,但是不是JSON,而是HTTP响应,用response.json()获得JSON的Promise对象,然后再次用then方法来获取到JSON格式的data
是javascript的全局对象,可以直接调用
btn[2].onclick = () => {
fetch('
autolinkhttp://127.0.0.1:8000/serverautolink
',
{
method: "POST",
headers: {
name: "chenzikai"
},
body: 'username=admin&password=admin'
}
).then(response => {
return response.json()
}).then(response => {
console.log(response.name)
})
}
跨域问题
从一个域名的网页去请求另一个域名的资源
通过超链接,form表单,window.location.href/document.location.href执行的跨域提交或者访问操作是没有问题的
如果尝试用ajax去访问其他站点的资源,默认情况下会报错:ajax访问被同源策略(CORS)阻止,只有同源的情况下XMLHttpRequest.responseText才可以共享
同源策略是浏览器的策略: 所谓同源就是协议一致,域名一致,端口号一致,就是同源,任何一个不同就不算同源。同源策略是一种安全策略(因为ajax是可以共享数据的)
解决方案
设置响应头
b站点允许ajax跨域访问
response.setHeader("Access-Control-Allow-Origin","
" ) *response.setHeader("Access-Control-Allow-Headers","*
")
response.setHeader("Access-Control-Allow-Method","*")
这是对所有网站开放
response.setHeader("Access-Control-Allow-Origin","autolinkhttp://localhost:8080autolink")
这是对localhost:8080端口开放
jsonp 方式
json with padding (带填充的json) 不是一个真正的ajax请求,jsonp是一种类似ajax请求的机制, 可以完成局部刷新的效果和跨域
利用script的src属性可以是xxx.js文件,让sevelet作为src也可以
//在后端中
@WebServelet("jsonp1")
//在doGet方法中返回JavaScript指令,这个指令会传到前端,由前端浏览器来解析执行
out.print("sayHello()")//这个函数是前端定义的
out.print("sayHello(\"这里写json字符串\")")
//动态获取函数名
String fun = request.getParameter("fun")
out.print(fun + "(JSON对象字符串)")
//在前端中
<script type="text/javascript">
function sayHello (
alert("hello," + data.name) {}
</script>
<script type="text/javascript" src="后端servelet路径 localhost:8080/b/jsonp1 ?fun=sayHello">
=</script>//把函数名传到后端让后端动态读取函数名
只能是get请求
上面的版本是打开的时候就会执行,下面实现点击的局部刷新
<body>
<script type="text/javascript">
function sayHello(data) {
document.getElementById("mydiv").innerHTML = data.username
}
window.onload = () => {//等待页面加载
document.getElementBy('btn').onclick = () => {//绑定点击事件
const htmlScirptElement = document.createElement("script")//创建script对象
htmlScirptElement.type = "text/javascript"
htmlScirptElement.src = "http:/localhost:8081/b/jsonp2?fun=sayHello"//设置script属性
document.getElementsByName("body")[0].appendChild(htmlScirptElement)//将script标签添加到body中,就是加载javascript
}
}
</script>
<button id="btn">jsonp 解决跨域问题,达到ajax局部刷新效果</button>
<div id="mydiv"></div>
</body>
jquery 库
已经有现成的jquery库,对jsonp进行了高度封装,底层原理完全相同
<!-- //引入jQuery库 -->
<script type="text/javascript" src="/a/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$function () {
$
("#btn").click(function(){
$.ajax({
type :"GET" ,
url :"<http://localhost:8081/b/json3>",
//这里jQuery会自动生成一个callback函数并加到url后面,在后端中只需要直接用callback()就行
datatype : "jsonp",
success : function(data){//data是json对象
$
("#mydiv").html("欢迎你:" + data.useranme)
}
})
})
}
</script>
不采用默认函数名和回调函数可以这样写
$.ajax({
type :"GET" ,
url :"
autolinkhttp://localhost:8081/b/json3autolink
",
datatype : "jsonp",
jsonp:"fun",//指定函数名字
jsonpCallback : "sayHello"//不设置的的话会自动生成一个调用success的callbakc函数,名字就叫callback
使用 反向代理
用java代码去跨域访问,ajax就不用跨域
react vite proxy 配置 反向代理
npm install vite axios- 主要是配置
vite.config.js文件
proxy: {
"/api": {//接口可匹配多个域名,为/api时走这个域名
// 当遇到 /api 路径时,将其转换成 target 的值
target: "
autolinkhttps://study:8888autolink
",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, "/api"),// 将 /api 重写
},
"/api2": {
target: "
autolinkhttps://lianghj.top:8888autolink
",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
- 配置好代理之后,向对应的路径请求的时候要注意不能写完整的url否则会导致404
- 他会拼上target然后再请求,所以我们直接写/api开始的路径就行了
creat-react-app 配置 反向代理
好像是用webpack,不清楚这方面还得学
npm install createProxyMiddleware- 在src文件目录下创建一个
setupProxy.js必须是这个名字
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
'/ajax', //以ajax开头的代理
createProxyMiddleware({
target: '
autolinkhttps://i.maoyan.comautolink
',//目标域名
changeOrigin: true,
})
)
}
这样看好像是node.js的语法,配置了一个express中间件,不知其中的原理如何
AJAX异常和取消
异常取消
btn.onclick = function () {
//创建对象1
const xhr = new XMLHttpRequest()
//打开通道并发送请求23
xhr.open("GET", "
autolinkhttp://127.0.0.1:8000/serverautolink
")
xhr.send()
//注册回调函数4 只需要在创建之后再注册就行
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
//响应成功,处理结果
// console.log(xhr.status)
// console.log(xhr.statusText)
// console.log(xhr.getAllResponseHeaders())
// console.log(xhr.response)
document.getElementById('result').innerHTML = xhr.response
}
}
}
}
在以上的代码基础上,添加一个超时和网络异常处理
xhr.timeout=2000;//2s
xhr.ontimeout = () => {
alert("请求超时,请稍后重试")
}
xhr.onerror = () => {
alert("你的网络好像出现了一些问题")
}
主动取消请求
let x = null; //注意要放在函数外部,因为发送请求和取消请求的按钮应该是不同的函数体
btn.onclick = () => {
x.abort()
}
防止重复请求
创建标识符
let isSending = false
//在发送请求的时候就表示在发送
isSending = true
//在x.readyState === 4 就是响应结束的时候判断为false
if ( x.readyState === 4 ) isSending = flase
在点击发送的时候先判断有没有正在发送的同一种请求,如果有就取消重新发
let isSending = false
btn.onclick = function () {
//创建对象1
if ( isSending) xhr.abort
const xhr = new XMLHttpRequest()
isSending = true
//打开通道并发送请求23
xhr.open("GET", "
autolinkhttp://127.0.0.1:8000/serverautolink
")
xhr.send()
//注册回调函数4 只需要在创建之后再注册就行
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
isSending = false
if (xhr.status >= 200 && xhr.status < 300) {
//响应成功,处理结果
// console.log(xhr.status)
// console.log(xhr.statusText)
// console.log(xhr.getAllResponseHeaders())
// console.log(xhr.response)
document.getElementById('result').innerHTML = xhr.response
}
}
}
}
手撕Jquery
重复代码封装工具类,可以把他当作一个JS的库,所以这里和ajax其实没什么关系,换句话说就是手写jQuery
初步封装:
<script type="text/javascript">
// 封装一个函数,通过这个函数可以找到html中的页面的结点
function jQuery(selector) {
if (typeof selector == "string") {
if (selector.charAt(0) == "#") {//这是个id选择器
let element = document.getElementById(selector.substring(1))//截掉字符串
return element
}
}
if (typeof selector == "function") {
window.onload = selector
}
}
$ = jQuery
$(function () {//这里jQuery先得到一个回调函数,所以会执行window.onload = selector
$
("#btn").onclick = function () {
$("#div1").innerHTML = "<font color='red'> 用户名不可用 ~~~~</font>"//下面这几个就是把jquery作为对选择器的封装来用了
}
})
</script>
进一步封装:
function jQuery(selector) {
if (typeof selector == "string") {
if (selector.charAt(0) == "#") {//这是个id选择器
domObj = document.getElementById(selector.substring(1))//让他是一个全局变量
return new jQuery()//返回的是一个对象,这样就可以再对这个对象封装函数
}
}
if (typeof selector == "function") {
window.onload = selector
}
this.html = function (htmlStr) {
domObj.innnerHTML = htmlStr
}
this.click = function (fun) {
domObj.onclick = fun
}
//还可以继续封装方法
this.focus = function(fun) {
domObj.onfocus = fun
}
}
$ = jQuery
$(function () {//这里jQuery先得到一个回调函数,所以会执行window.onload = selector
$
("#btn").click(function () {//这里将jquery当作一个对象,使用它的实例方法
$("#div1").innerHTML = "<font color='red'> 用户名不可用 ~~~~</font>"//下面这个就是把jquery作为对选择器的封装来用了
})
})
封装彻底之后:
用()函数如果传入的是一个字符串,就会当作一个选择器用,用完之后返回一个jQuery对象,可以直接用jQuery封装的函数
如果(这里面写下一步代码)就能实现window.onload = function() {同上一个花括号内容}的效果,封装完之后,整个代码变得非常简洁
其实这里手撕jQuery是为了理解它的源码,实际开发的时候我我们直接用别人包装好的方法就行了
最重要的是这个想法:
在把jquery当作选择器来用之后返回一个对象,而且将选择到的元素声明为全局变量,然后给jquery对象写静态方法,最后用$ = jQuery 实现彻底的封装
如果对 ajax 的四步进行封装
下面这一个封装从元素的获取到发送ajax请求到渲染页面的函数可以当作手写的jQuery v1.0.0
function jQuery(selector) {
if (typeof selector == "string") {
if (selector.charAt(0) == "#") {//这是个id选择器
domObj = document.getElementById(selector.substring(1))//让他是一个全局变量
return new jQuery()//返回的是一个对象,这样就可以再对这个对象封装函数
}
}
if (typeof selector == "function") {
window.onload = selector
}
this.html = function (htmlStr) {
domObj.innnerHTML = htmlStr
}
this.click = function (fun) {
domObj.onclick = fun
}
//还可以继续封装方法
this.focus = function (fun) {
domObj.onfocus = fun
}
//静态方法
//要保证灵活性:所以要传参(用一个json对象) :
// 请求方式type
// url
// 提交的数据data
// 异步还是同步
jQuery.ajax = function (jsonArgs) {
//1
const xhr = new XMLHttpRequest()
//2
xhr.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
const jsonObj = JSON.parse(this.responseText)
jsonArgs.success(jsonObj)
}
}
if (jsonArgs.type.toUpperCase() == "POST") {
// 3
xhr.open()("POST", jsonArgs.url, jsonArgs.async)
// 4
xhr.setRequestHeader("")
xhr.send(jsonArgs.data)
}
if (jsonArgs.type.toUpperCase() == "GET") {
// 3
xhr.open()("GET", jsonArgs.url+"?"+jsonArgs.data, jsonArgs.async)
// 4
xhr.send()
}
}
}
}
$ = jQuery
new jQuery //这里必须new一下后面才能用jQuery里面的静态方法
⚠️要注意的是,这里一定要先new一下,才能用里面的ajax等静态方法
对于封装好的工具,我们只需要引入,然后像下面这样引用就可
$$(function(){
$$("btn1").click(function(){
$.ajax({
type: "POST",
url: "",
data: "username" + $
("#username").val(),
async: true,
success: function (Obj) {
// 里面写回调函数要执行的内容
$("#div1").html(json.uname)
}
}
)
}
}
)
$.get方法
$$(() => {
$$('button').eq(0).click(() => {
$.get("
autolinkhttp://127.0.0.1:8000/server-jQueryautolink
", { a: 100, b: 200 }, function (data) {
console.log(data)
})
})
}
)