简介AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML。通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。【原生】AJAX请求准备工作1、前往 Node.js 官方网站 并下载适合你操作系统的 Node.js 安装程序。2、前往并下载 Express 框架。安装Node.jsnode -v //检查Node.js版本npm -v // 检查Npm包版本npm install -g npm // 更细Npm安装Expressnpm init --yesnpm i express //mac用户前置sudo命令HTTP协议请求HTTP(hypertext transport protocol)协议『超文本传输协议』,协议详细规定了浏览器和万维网服务器之间互相通信的规则。请求报文格式参数请求行POST /form.html HTTP/1.1请求头Host: www.example.comUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)Content-Type: application/x-www-form-urlencodedContent-Length: 27空行请求体/username=admin&password=admin响应报文格式参数状态行HTTP/1.1 200 OK响应头Date: Wed, 21 Oct 2015 10:55:34 GMTContent-Type: text/html; charset=UTF-8Content-Length: 1354Content-encoding: gzip空行响应体<html> <head> </head> <body> <h1>hello ,Ajax !</h1> </body> </html>状态码状态码类别描述100信息性状态码Continue:继续执行。101信息性状态码Switching Protocols:切换协议。200成功状态码OK:请求成功。201成功状态码Created:请求成功并且服务器创建了新的资源。202成功状态码Accepted:请求已接受,但处理尚未完成。204成功状态码No Content:请求成功,但响应不包含内容。301重定向状态码Moved Permanently:请求的资源已被永久移动到新位置。302重定向状态码Found:请求的资源临时从不同的URI响应。304重定向状态码Not Modified:资源未修改,可以使用客户端缓存的版本。400客户端错误状态码Bad Request:请求无效或无法被服务器理解。401客户端错误状态码Unauthorized:请求需要用户认证。403客户端错误状态码Forbidden:服务器拒绝请求。404客户端错误状态码Not Found:请求的资源在服务器上不存在。500服务器错误状态码Internal Server Error:服务器遇到未知错误。501服务器错误状态码Not Implemented:服务器不支持请求的功能。502服务器错误状态码Bad Gateway:网关或代理服务器从上游服务器收到无效响应。Express规则// 引入express const express = require('express'); // 创建应用对象 const app = express(); // 创建路由规则 // request是对请求报文的封装 // response是对响应报文的封装 app.get('/server',(request,response)=>{ // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin','*') // 设置响应体 response.send('Hello AJAX!') }) app.post('/server', (request, response) => { // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*') // 设置响应体 response.send('Hello AJAX!') }) // 监听端口,启动服务 app.listen(8000, ()=>{ console.log('服务器已启动,8000端口监听中...') })AJAX基本原理XMLHttpRequest(XHR)对象用于与服务器交互。通过XMLhttpRequest可以再不刷新页面的情况下,请求特定URL获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest在AJAX编程中被大量使用。使用XMLHttpRequest 创建XMLHttpRequest对象const xhr = new XMLHttpRequest();配置请求方法和请求url地址xhr.open('请求方法','请求url地址')监听loadend时间,接收响应结果xhr.addEventListener('loadend',()=>{ //响应结果 console.log(xhr.response) })发送请求xhr.send()<script> const btn = document.getElementsByTagName('button')[0]; const result = document.getElementById('result'); btn.onclick = function() { // 1. 创建对象 const xhr = new XMLHttpRequest(); // 2. 初始化 设置请求方法和url xhr.open('GET','http://127.0.0.1:8000/server'); // 3. 发送 xhr.send(); // 4. 事件绑定 处理服务端返回的结果 // on when 当....时候 // readystate 是xhr对象中的属性,表示状态 0-4 // 0:xhr对象创建完成 // 1:open方法调用完成 // 2:send方法调用完成 // 3:服务端返回部分结果 // 4:服务端返回所有结果,可以在该阶段获取返回结果 // change 改变 xhr.onreadystatechange = function() { // 判断服务端返回所有结果 if (xhr.readyState === 4) { // 判断响应状态码 200 404 403 401 500 // 2xx 都为成功 if (xhr.status >= 200 && xhr.status < 300) { // 处理结果 行 头 空行 体 // 1. 响应行 // console.log(xhr.status); // 状态码 // console.log(xhr.statusText); // 状态字符串 // console.log(xhr.getAllResponseHeaders()); // 所有响应头 // console.log(xhr.response); // 响应体 // 设置result的文本 result.innerText = xhr.response; } } } } </script>POST请求设置/* 当鼠标移入#result,则发送Post请求... */ <div id="result" class="w-48 h-48 border solid black"></div><script> const result = document.getElementById('result'); result.addEventListener('mouseover', function () { //1.创建对象 const xhr = new XMLHttpRequest(); //2.初始化 xhr.open('POST', 'http://127.0.0.1:8000/server'); xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); // 设置请求头信息(预定义) xhr.setRequestHeader('name','parlo'); //可以设置自定义请求头 //3.发送 xhr.send('a=100&b=200'); //请求体也可以为'number';'a:100&b:100..' //4.事件绑定 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { result.innerText = xhr.response; } } } }); </script>当设定为自定义的请求头时,则需要在后端添加新的请求类型(一般不需要前端去操作)// server.js // 可以接受任意类型的请求 app.all('/server', (request, response) => { // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*') response.setHeader('Access-Control-Allow-Headers', '*') // 设置响应体 response.send('Hello AJAX!') })服务端响应JSON数据// server.js // 可以接受任意类型的请求 app.all('/json-server', (request, response) => { // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*') response.setHeader('Access-Control-Allow-Headers', '*') // 响应一个数据 const data = { name: 'parlo', } // 对象转换为字符串 let str = JSON.stringify(data) // 设置响应体 response.send(str) })/* 当键盘在浏览器视口按下,则发送请求 */ <div id="result" class="w-48 h-48 border solid black"></div>手动转换数据<script> //当键盘在浏览器视口下,按下键盘则发送POST请求 const result = document.getElementById('result') window.onkeydown = function () { // 发送请求 const xhr = new XMLHttpRequest(); // 初始化 xhr.open('get', 'http://127.0.0.1:8000/json-server'); // 发送 xhr.send(); // 事件绑定 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { // 手动处理服务器返回的结果 let data = JSON.parse(xhr.response) console.log(data) result.innerText = data.name } } } } </script>自动转换数据<script> //当键盘在浏览器视口下,按下键盘则发送POST请求 const result = document.getElementById('result') window.onkeydown = function () { // 发送请求 const xhr = new XMLHttpRequest(); // 设置响应体类型 xhr.responseType = 'json' // 初始化 xhr.open('get', 'http://127.0.0.1:8000/json-server'); // 发送 xhr.send(); // 事件绑定 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { // 自动处理服务器返回的结果 console.log(xhr.response) result.innerText = xhr.response.name } } } } </script>Nodemon工具安装Nodemonnpm install -g nodemon运行服务端nodemon server.jsIE缓存问题解决解决全世界最好用的IE浏览器的缓存解决问题!需要注意的是,现代浏览器几乎不需要调整。<button>点我发送请求</button> <div id="result" class="w-48 h-48 border solid black"></div>// server.js app.get('/ie', (request, response) => { // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*'); // 设置响应体 response.send('HELLO IE! 2~~~'); });<script> const btn = document.getElementsByTagName('button')[0]; const result = document.getElementById('result'); btn.addEventListener('click', () => { const xhr = new XMLHttpRequest(); // 初始化 xhr.open('GET', 'http://localhost:8000/ie?t=' + Date.now()); //为IE浏览器添加参数 + 时间戳 // 发送 xhr.send(); // 绑定事件 xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { result.innerText = xhr.response; } } }; }); </script>超时与网络异常增加友好的判定来告知AJAX请求的网络情况。<button>点我发送请求</button> <div id="result" class="w-48 h-48 border solid black"></div>// server.js app.get('/delay', (request, response) => { // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*'); // 设置响应体 setTimeout(() => { response.send('延时响应'); }, 3000); });<script> const btn = document.getElementsByTagName('button')[0]; const result = document.getElementById('result'); btn.addEventListener('click', () => { // 创建对象 const xhr = new XMLHttpRequest(); // 设置超时时间 xhr.timeout = 2000; // 设置超时回调 xhr.ontimeout = function () { alert('网络超时,请稍后重试!'); }; // 设置网络异常回调 xhr.onerror = function () { alert('网络异常,请稍后重试!'); }; // 初始化 xhr.open('GET', 'http://localhost:8000/delay'); // 发送 xhr.send(); // 事件绑定 xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { result.innerText = xhr.response; } } }; }); </script>取消请求取消正在发送的AJAX请求。<button>点击发送请求</button> <button>点击取消请求</button><script> const btn = document.querySelectorAll('button'); let xhr = null; // 作用域需放在外层 btn[0].onclick = function () { xhr = new XMLHttpRequest(); xhr.open('GET', 'http://localhost:8000/delay'); xhr.send(); }; btn[1].onclick = function () { xhr.abort(); //取消请求 }; </script>重复请求问题当多次发送AJAX请求时,取消之前的请求。这样可以减少服务器的性能冲突。<script> const btn = document.querySelectorAll('button'); let xhr = null; //标识变量 let isSending = false; // 是否正在发送AJAX请求 btn[0].onclick = function () { if (isSending) xhr.abort();//判断!如果正在发送,则取消本次发送 xhr = new XMLHttpRequest(); //修改 标识变量的值 isSending = true; xhr.open('GET', 'http://localhost:8000/delay'); xhr.send(); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { //修改表示变量 isSending = false; } }; }; btn[1].onclick = function () { xhr.abort(); // 取消请求 }; </script>jQuery方法<button>GET请求</button> <button>POST请求</button> <button>通用AJAX请求</button>// jquery app.all('/jquery-server', (request, response) => { // 设置响应头,设置允许跨域 response.setHeader('Access-Control-Allow-Origin', '*'); // 设置响应体 //response.send('HELLO jquery AJAX'); const data = {name:'parlo'}; response.send(JSON.stringify(data)); });GET请求<script> $('button').eq(0).click(function () { $.get('http://127.0.0.1:8000/jquery-server', {name: 'parlor', age: 18}, function (data) { console.log(data); }, 'json'); }); </script>POST请求<script> $('button').eq(1).click(function () { $.post('http://127.0.0.1:8000/jquery-server', {name: 'parlor', age: 18}, function (data) { console.log(data); }, 'json'); }); </script>通用方法$('button').eq(2).click(function () { $.ajax({ // url url: 'http://127.0.0.1:8000/jquery-server', // 参数 data: {name: 'parlo', age: 18}, // 请求类型 type: 'GET', // 响应体结果 dataType: 'json', // 成功的回调 success: function (data) { console.log(data); }, // 超时时间 timeout: 2000, // 失败的回调 error: function () { console.log('出错了'); }, headers: { a: 300, b: 400 } }); });Axiso方法基本语法axuis({ url:`目标资源地址` }).then(result=>{ //对服务器是返回的数据做后续处理 })参数查询axuis({ url:`目标资源地址` params:{ 参数名:值 } }).then((result)=>{ //对服务器是返回的数据做后续处理 })<p></p> <script> axios({ url: 'https://hmajax.itheima.net/api/city', params: { pname: '山东省' } }).then(result => { console.log(result.data.list); document.querySelector('p').innerHTML = result.data.list.join('<br>'); }); </script><form> <label>省份名字 <input name="province" placeholder="请输入省份名字" type="text" value="北京"> </label> <label>城市名字 <input name="city" placeholder="请输入城市名字" type="text" value="北京市"> </label> </form> <button type="button">查询</button> <p>地区列表</p> <ul> <li></li> </ul> <script> <!--创建绑定事件--> document.querySelector('button').addEventListener('click', function () { // 获取用户输入的省份值 let pname = document.querySelectorAll('input')[0].value; let cname = document.querySelectorAll('input')[1].value; console.log(pname, cname); // 使用axios发送请求 axios({ url: 'https://hmajax.itheima.net/api/area', params: { pname, cname } }).then(result => { // 打印并检查数据 console.log(result.data.list); let list = result.data.list; // ele.map()方法是根据当前数组生成一个新数组 let theLi = list.map(ele => `<li>${ele}</li>`).join(''); console.log(theLi); // 将生成的li标签添加到ul中 document.querySelector('ul').innerHTML = theLi; }); }); </script>请求配置axuis({ url:`目标资源地址`, method:'请求方法', data:{ 参数名:值 } params:{ 参数名:值 } }).then(result=>{ //对服务器是返回的数据做后续处理 })错误处理axuis({ //请求选项 }).then(result=>{ //处理数据 }).catch(error=>{ //处理错误 })<button id="btn">点击注册用户</button> <script> document.getElementById('btn').addEventListener('click', () => { axios({ url: 'https://hmajax.itheima.net/api/register', method: 'post', data: { username: 'parlo0532', password: '12345678' } }).then(result => { console.log(result); }).catch(error => { console.log(error); alert(error.response.data.message); }); }); </script>Form-serialize(插件)引入插件 Form-serialize<script src="path/to/form-serialize.js"></script>使用serialize函数快速手机表单元素的值参数详情获取表单的数据元素表单设置name属性,值会作为对象的属性名配置对象hash , empty<script> document.querySelector('.btn').addEventListener('click',(){ const form = document.querySelector('.example-form') /* hash:设置获取对象的结构 - true:JS对象(推介)一般请求体里提交给服务器 - false:查询字符串 empty:设置是否获取空值 - true:获取空值(推介)一般请求体里提交给服务器 - false:不获取空值 */ const data = serialize(form,{hash:true,empty:true}) console.log(data) }) </script>案例一:登录表单<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta content="width=device-width, initial-scale=1.0" name="viewport"> <title>案例_登录</title> <!-- 引入bootstrap.css --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- 公共 --> <style> html, body { background-color: #EDF0F5; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } .container { width: 520px; height: 540px; background-color: #fff; padding: 60px; box-sizing: border-box; } .container h3 { font-weight: 900; } </style> <!-- 表单容器和内容 --> <style> .form_wrap { color: #8B929D !important; } .form-text { color: #8B929D !important; } </style> <!-- 提示框样式 --> <style> .alert { transition: .5s; opacity: 0; } .alert.show { opacity: 1; } </style> <script src="../node_modules/axios/dist/axios.js"></script> <script src="form-serialize.js"></script> </head> <body> <div class="container"> <h3>欢迎-登录</h3> <!-- 登录结果-提示框 --> <div class="alert alert-success" role="alert"> 提示消息 </div> <!-- 表单 --> <div class="form_wrap"> <form class="login-form"> <div class="mb-3"> <label class="form-label" for="username">账号名</label> <input autocomplete="current-username" class="form-control username" name="username" type="text"> </div> <div class="mb-3"> <label class="form-label" for="password">密码</label> <input autocomplete="current-password" class="form-control password" name="password" type="password"> </div> <button class="btn btn-primary btn-login" type="button"> 登 录</button> </form> </div> </div> <script> //目标2:将错误或登录信息放在前端显示 const message = document.querySelector('.alert') function alertFn(msg, isSuccess) { //显示提示框 message.classList.add('show') //放入提示消息 message.innerText = msg //设置样式 const className = isSuccess ? 'alert-success' : 'alert-danger' message.classList.add(className) //过两秒隐藏 setTimeout(() => { message.classList.remove('show') message.classList.remove(className) }, 2000) } // 目标1:点击登录时,用户名和密码长度判断,并提交数据和服务器通信 document.querySelector('.btn-login').addEventListener('click', () => { // 使用serialize函数收集表单里用户名和密码 const form = document.querySelector('.login-form') const data = serialize(form, {hash: true, empty: true}) console.log(data); const {username, password} = data; console.log(username, password) // const username = document.querySelector('.username').value // const password = document.querySelector('.password').value if (username.length < 6) { console.log('用户名长度不能小于6位'); alertFn('用户名长度不能小于6位', false) return } if (password.length < 8) { console.log('密码长度不能小于8位') alertFn('密码长度不能小于8位', false) return } axios({ url: 'https://hmajax.itheima.net/api/login', method: 'post', data: { username, password, }, }).then(result => { console.log(result); console.log(result.data.message); alertFn(result.data.message, true) }).catch(error => { console.log(error); console.log(error.response.data.message); alertFn(error.response.data.message, false) }); }); </script> </body> </html>案例二:图书管理01>使用属性控制弹窗Bootstrap含义data-bs-toggle="modal"用于触发模态框(Modal)的显示。data-bs-target="#myModal"查找具有相应NAME模态框,并显示或操作该模态框。data-bs-dismiss="modal"关闭对应的模态框<!-- 使用属性控制弹窗显示 1、引入bootstrap.css和bootstrap.js 2、准备弹窗标签,确认结构 3、通过自定义属性,控制弹窗的显示和隐藏 data-bs-toggle="modal" 用于触发模态框(Modal)的显示。 data-bs-target="#myModal" 查找具有相应NAME模态框,并显示或操作该模态框。 data-bs-dismiss="modal" 关闭对应的模态框 --> <button class="btn btn-primary" data-bs-target=".my-modal" data-bs-toggle="modal" type="button">显示弹窗 </button> <div class="modal my-modal" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Modal title</h5> <button aria-label="Close" class="btn-close" data-bs-dismiss="modal" type="button"></button> </div> <div class="modal-body"> <p>Modal body text goes here.</p> </div> <div class="modal-footer"> <button class="btn btn-secondary" data-bs-dismiss="modal" type="button">Close</button> <button class="btn btn-primary" type="button">Save changes</button> </div> </div> </div> </div>02>使用JS控制弹窗Bootstrap方法创建弹窗对象const modaDom = document.querySelector('.modal') const modal = new bootstrap.Modal(modaDom)调用弹窗对象内置方法.show() 显示弹窗 .hide() 隐藏弹窗<!-- 目标:使用JS控制弹窗,显示和隐藏 1. 创建弹窗对象 const modaDom = document.querySelector('.modal') const modal = new bootstrap.Modal(modaDom) 2. 调用弹窗对象内置方法 .show() 显示弹窗 .hide() 隐藏弹窗 --> <button class="btn btn-primary edit-btn" type="button"> 编辑姓名 </button> <div class="modal name-box" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">请输入姓名</h5> <button aria-label="Close" class="btn-close" data-bs-dismiss="modal" type="button"></button> </div> <div class="modal-body"> <form action=""> <span>姓名:</span> <input class="username" type="text"> </form> </div> <div class="modal-footer"> <button class="btn btn-secondary" data-bs-dismiss="modal" type="button">取消</button> <button class="btn btn-primary save-btn" type="button">保存</button> </div> </div> </div> </div> <!-- 引入bootstrap.js --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script> <script> // 1. 创建弹窗对象 const modaDom = document.querySelector('.name-box') const modal = new bootstrap.Modal(modaDom) // 编辑姓名->点击->弹窗显示 document.querySelector('.edit-btn').addEventListener('click', () => { document.querySelector('.username').value = '张三' modal.show() }) // 保存按钮->点击->获取姓名打印->弹窗隐藏 document.querySelector('.save-btn').addEventListener('click', () => { const username = document.querySelector('.username').value console.log('模拟把姓名保存到服务器上', username) modal.hide() }) </script>03>完整代码<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta content="width=device-width, initial-scale=1.0" name="viewport"> <title>案例-图书管理</title> <!-- 字体图标 --> <link href="https://at.alicdn.com/t/c/font_3736758_vxpb728fcyh.css" rel="stylesheet"> <!-- 引入bootstrap.css --> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet"> <!-- 核心样式 --> <link href="CSS/index.css" rel="stylesheet"> </head> <body> <!-- 主体区域 --> <div class="container"> <!-- 头部标题和添加按钮 --> <div class="top"> <h3>图书管理</h3> <button class="btn btn-primary plus-btn" data-bs-target=".add-modal" data-bs-toggle="modal" type="button"> + 添加 </button> </div> <!-- 数据列表 --> <table class="table"> <thead class="table-light"> <tr> <th style="width: 150px;">序号</th> <th>书名</th> <th>作者</th> <th>出版社</th> <th style="width: 180px;">操作</th> </tr> </thead> <tbody class="list"> <tr> <td>1</td> <td>JavaScript程序设计</td> <td>马特·弗里斯比</td> <td>人民邮电出版社</td> <td> <span class="del">删除</span> <span class="edit">编辑</span> </td> </tr> </tbody> </table> </div> <!-- 新增-弹出框 --> <div class="modal fade add-modal"> <!-- 中间白色区域 --> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header top"> <span>添加图书</span> <button aria-label="Close" class="btn-close" data-bs-dismiss="modal" type="button"></button> </div> <div class="modal-body form-wrap"> <!-- 新增表单 --> <form class="add-form"> <div class="mb-3"> <label class="form-label" for="bookname">书名</label> <input class="form-control bookname" name="bookname" placeholder="请输入书籍名称" type="text"> </div> <div class="mb-3"> <label class="form-label" for="author">作者</label> <input class="form-control author" name="author" placeholder="请输入作者名称" type="text"> </div> <div class="mb-3"> <label class="form-label" for="publisher">出版社</label> <input class="form-control publisher" name="publisher" placeholder="请输入出版社名称" type="text"> </div> </form> </div> <div class="modal-footer btn-group"> <button class="btn btn-primary" data-bs-dismiss="modal" type="button"> 取消</button> <button class="btn btn-primary add-btn" type="button"> 保存</button> </div> </div> </div> </div> <!-- 编辑-弹出框 --> <div class="modal fade edit-modal"> <!-- 中间白色区域 --> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header top"> <span>编辑图书</span> <button aria-label="Close" class="btn-close" data-bs-dismiss="modal" type="button"></button> </div> <div class="modal-body form-wrap"> <!-- 编辑表单 --> <form class="edit-form"> <input class="id" name="id" type="hidden"> <div class="mb-3"> <label class="form-label" for="bookname">书名</label> <input class="form-control bookname" name="bookname" placeholder="请输入书籍名称" type="text"> </div> <div class="mb-3"> <label class="form-label" for="author">作者</label> <input class="form-control author" name="author" placeholder="请输入作者名称" type="text"> </div> <div class="mb-3"> <label class="form-label" for="publisher">出版社</label> <input class="form-control publisher" name="publisher" placeholder="请输入出版社名称" type="text"> </div> </form> </div> <div class="modal-footer btn-group"> <button class="btn btn-primary" data-bs-dismiss="modal" type="button"> 取消</button> <button class="btn btn-primary edit-btn" type="button"> 修改</button> </div> </div> </div> </div> <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.2.0/axios.min.js"></script> <script src="./lib/form-serialize.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/js/bootstrap.min.js"></script> <!-- 核心逻辑 --> <script src="JS/index.js"></script> </body> </html><script> /** * 目标1:渲染图书列表 * 1.1 获取数据 * 1.2 渲染数据 */ const creator = '张三'; function getBookList() { // 1.1 获取数据 axios({ url: 'https://hmajax.itheima.net/api/books', params: { creator, }, }).then(result => { console.log(result) const bookList = result.data.data; console.log(bookList) // 1.2 渲染数据 const htmlStr = bookList.map((item, index) => { return `<tr> <td>${index + 1}</td> <td>${item.bookname}</td> <td>${item.author}</td> <td>${item.publisher}</td> <td data-id="${item.id}"> <span class="del">删除</span> <span class="edit">编辑</span> </td> </tr>` }).join('') console.log(htmlStr) document.querySelector('.list').innerHTML = htmlStr }) } //网页加载运行时,获取并渲染列表一次 getBookList(); /** * 目标2:新增图书 * 2.1 新增弹窗-->显示和隐藏 * 2.2 收集表单数据并提交到服务器保存 * 2.3 刷新图书列表 */ // 2.1 新增弹窗-->显示和隐藏 const addModalDom = document.querySelector('.add-modal') const addModal = new bootstrap.Modal(addModalDom) // 保存按钮-->点击->隐藏弹窗 document.querySelector('.add-btn').addEventListener('click', () => { // 2.2 收集表单数据 const addForm = document.querySelector('.add-form') const bookObj = serialize(addForm, {hash: true, empty: true}) console.log(bookObj) //提交到服务器保存 axios({ url: 'https://hmajax.itheima.net/api/books', method: 'post', data: { ...bookObj, creator, }, }).then(result => { console.log(result) // 2.3 添加成功后,重新请求并渲染图书列表 getBookList(); // 重置表单 addForm.reset() // 隐藏弹窗 addModal.hide() }) }) /** * 目标3:删除图书 * 3.1 删除元素绑定点击事件->获取图书ID * 3.2 调用删除接口 * 3.3 刷新图书列表 */ // 3.1 删除元素->(事件委托) document.querySelector('.list').addEventListener('click', event => { // console.log(event.target) if (event.target.classList.contains('del')) { // console.log('删除') const theID = event.target.parentNode.dataset.id console.log(theID) // 3.2 删除元素 axios({ url: `https://hmajax.itheima.net/api/books/${theID}`, method: 'delete', }).then(() => { // 3.3 刷新图书列表 getBookList() }) } }) /** * 目标4:编辑图书 * 4.1 编辑弹窗->显示和隐藏 * 4.2 获取当前编辑图书数据->回显到编辑表单中 * 4.3 提交保存修改,并刷新列表 */ // 4.1 编辑弹窗->显示和隐藏 const editDom = document.querySelector('.edit-modal') const editModal = new bootstrap.Modal(editDom) // 编辑元素->点击->显示弹窗 document.querySelector('.list').addEventListener('click', event => { // 判断点击的是否为编辑元素 if (event.target.classList.contains('edit')) { // console.log('编辑') // 4.2 获取当前编辑图书数据->回显到编辑表单中 const theID = event.target.parentNode.dataset.id console.log(theID) axios({ url: `https://hmajax.itheima.net/api/books/${theID}`, }).then(result => { // 查看数据 console.log(result) const bookObj = result.data.data // 遍历数据对象,使用属性去获取对应的标签 const keys = Object.keys(bookObj) keys.forEach(key => { document.querySelector(`.edit-form .${key}`).value = bookObj[key] }) }) editModal.show() } }) // 修改按钮->点击->隐藏弹窗 document.querySelector('.edit-btn').addEventListener('click', () => { // 4.3 提交保存修改,并刷新列表 const editForm = document.querySelector('.edit-form') const {id, bookname, author, publisher} = serialize(editForm, {hash: true, empty: true}) axios({ url: `https://hmajax.itheima.net/api/books/${id}`, method: 'put', data: { bookname, author, publisher, creator, }, }).then(() => { getBookList() editModal.hide() }) }) </script>图片上传使用 FormData 携带图片文件const fd = new FormData() fd.append(参数名,值)<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport"> <meta content="ie=edge" http-equiv="X-UA-Compatible"> <title>图片上传</title> <script src="../dist/http_cdn.bootcdn.net_ajax_libs_axios_1.2.0_axios.js"></script> </head> <body> <input class="upload" type="file"> <img alt="" class="my-img" src=""> <script> /** * 目标: 图片上传,显示在网页上 * 1. 获取图片文件 * 2. 使用 FormData 将图片文件传递给后端 * 3. 提交到服务器,获取图片url网址并使用 */ // 文件选择元素 -> change改变事件 document.querySelector('.upload').addEventListener('change', e => { // 获取文件对象 console.log(e.target.files[0]) // 创建FormData对象 const fd = new FormData() // 将文件对象添加到FormData中 fd.append('img', e.target.files[0]) // 发送请求 axios({ url: 'https://hmajax.itheima.net/api/uploadimg', method: 'post', data: fd, }).then(result => { console.log(result) // 将图片地址显示在网页上 const imgUrl = result.data.data.url document.querySelector('.my-img').src = imgUrl }) }) </script> </body> </html>案例三:更换背景<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport"> <meta content="ie=edge" http-equiv="X-UA-Compatible"> <title>Document</title> <style> .upload { display: none; } .bq { width: fit-content; padding: 10px; background: #000; color: #fff; border-radius: 10px; } </style> <script src="../dist/http_cdn.bootcdn.net_ajax_libs_axios_1.2.0_axios.js"></script> </head> <body> <div class="bq"> <label for="upload">更换背景</label> <input class="upload" id="upload" type="file"> </div> <script> document.querySelector('.upload').addEventListener('change', e => { // 选择图片上传,设置body背景 const formData = new FormData() formData.append('img', e.target.files[0]) axios({ url: 'https://hmajax.itheima.net/api/uploadimg', method: 'post', data: formData, }).then(result => { // 获取上传服务器后的返回地址 const imgUrl = result.data.data.url // 设置body背景 document.body.style.backgroundImage = `url(${imgUrl})` // 上传成功时保存在本地 localStorage.setItem('bg', imgUrl) }) }) // 页面加载时,如果本地有背景,则设置body背景 const bgUrl = localStorage.getItem('bg') document.body.style.backgroundImage = `url(${bgUrl})` </script> </body> </html>案例四:个人信息设置<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta content="width=device-width, initial-scale=1.0" name="viewport"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- 核心样式 --> <link href="./css/index.css" rel="stylesheet"> <title>个人信息设置</title> </head> <body> <!-- toast 提示框 --> <div class="toast my-toast" data-bs-delay="1500"> <div class="toast-body"> <div class="alert alert-success info-box"> 操作成功 </div> </div> </div> <!-- 核心内容区域 --> <div class="container"> <ul class="my-nav"> <li class="active">基本设置</li> <li>安全设置</li> <li>账号绑定</li> <li>新消息通知</li> </ul> <div class="content"> <div class="info-wrap"> <h3 class="title">基本设置</h3> <form action="javascript:;" class="user-form"> <div class="form-item"> <label for="email">邮箱</label> <input autocomplete="off" class="email" id="email" name="email" placeholder="请输入邮箱" type="text"> </div> <div class="form-item"> <label for="nickname">昵称</label> <input autocomplete="off" class="nickname" id="nickname" name="nickname" placeholder="请输入昵称" type="text"> </div> <div class="form-item"> <label>性别</label> <label class="male-label"><input class="gender" name="gender" type="radio" value="0">男</label> <label class="male-label"><input class="gender" name="gender" type="radio" value="1">女</label> </div> <div class="form-item"> <label for="desc">个人简介</label> <textarea autocomplete="off" class="desc" cols="20" id="desc" name="desc" placeholder="请输入个人简介" rows="10"></textarea> </div> <button class="submit">提交</button> </form> </div> <div class="avatar-box"> <h4 class="avatar-title">头像</h4> <img alt="" class="prew" src="./img/头像.png"> <label for="upload">更换头像</label> <input class="upload" id="upload" type="file"> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js"></script> <script src="./lib/form-serialize.js"></script> <!-- 核心逻辑 --> <script src="./js/index.js"></script> </body> </html>01>信息渲染/** * 目标1:信息渲染 * 1.1 获取用户的数据 * 1.2 回显数据到标签上 * */ const creator = 'parlocc' axios({ url: 'https://hmajax.itheima.net/api/settings', params: { creator, }, }).then(result => { const userObj = result.data.data console.log(userObj) Object.keys(userObj).forEach(key => { if (key === 'avatar') { document.querySelector('.prew').src = userObj[key]; } else if (key === 'gender') { const gRadio = document.querySelectorAll('.gender') const gNum = userObj[key] console.log(userObj[key]) gRadio[gNum].checked = true } else { document.querySelector(`.${key}`).value = userObj[key] } }) }) 02>修改头像/** * 目标2:修改头像 * 2.1 获取头像文件 * 2.2 提交服务器并更新头像 * */ document.querySelector('.upload').addEventListener('change', e => { console.log(e.target.files[0]) const fd = new FormData() fd.append('avatar', e.target.files[0]) fd.append('creator', creator) axios({ url: 'https://hmajax.itheima.net/api/avatar', method: 'put', data: fd, }).then(result => { console.log(result) const imgUrl = result.data.data.avatar document.querySelector('.prew').url = imgUrl }) })03>提交表单/** * 目标3:提交表单 * 3.1 收集表单信息 * 3.2 提交服务器保存 * */ document.querySelector('.submit').addEventListener('click', () => { const userFrom = document.querySelector('.user-form') const userObj = serialize(userFrom, {hash: true, empty: true}) userObj.creator = creator // 性别数字字符串,转换成数字类型 userObj.gender = +userObj.gender console.log(userObj) axios({ url: 'https://hmajax.itheima.net/api/settings', method: 'put', data: userObj, }).then(result => { const toastDom = document.querySelector('.my-toast') const toast = new bootstrap.Toast(toastDom) toast.show() }) })Fetch函数<button>AJAX</button><script> const btn = document.querySelector('button'); btn.onclick = function () { fetch('http://127.0.0.1:8000/fetch-server?parlo=31', { //请求方法 method: 'POST', //请求头 headers: { name: 'parlo' }, //请求体 body: 'username=admin&password=123456' }).then(response => { return response.json(); }).then(response => { console.log(response); }); }; </script>跨域同源策略同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略。同源: 协议、域名、端口号 必须完全相同。而违背同源策略就是跨域。JSONPJSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持 get 请求。用户名:<input id="username" type="text"/> <p></p>// 用户名检测是否存在 app.all('/check-username', (request, response) => { const data = { exist: 1, msg: '用户名已经存在' }; let str = JSON.stringify(data); response.send(`handle(${str})`); });<script> //获取input元素 const input = document.querySelector('input'); const p = document.querySelector('p'); // 声明handle函数 function handle(data) { input.style.border = '1px solid red'; //修改p标签的提示文本 p.innerHTML = data.msg; } //绑定事件 input.onblur = function () { //获取用户输入的值 let username = this.value; //向服务器发送请求 检测用户名是否存在 //1、创建script标签 const script = document.createElement('script'); //2、设置script标签的src属性 script.src = 'http://127.0.0.1:8000/check-username'; //3、将script标签添加到页面中 document.body.appendChild(script); }; </script>jQuery发送JSONPapp.all('/jquery-jsonp-server', (request, response) => { // response.send('HELLO IE! 2~~~'); const data = { name: '中国', city: ['北京', '上海', '青岛'] }; let str = JSON.stringify(data); // 接受callback参数 let cb = request.query.callback; // 拼接数据 response.send(`${cb}(${str})`); });<button>点击发送jsonp请求</button> <div id="result"></div><script> $('button').eq(0).click(function () { $.getJSON('http://127.0.0.1:8000/jquery-jsonp-server?callback=?', function (data) { // console.log(data); $('#result').html( ` 名称:${data.name}<br> 城市: ${data.city} ` ); }); }); </script>CORSCORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些 源站通过浏览器有权限访问哪些资源。<button>点击发送jsonp请求</button> <div id="result"></div> javas//cors app.all('/cors-server', (request, response) => { //设置响应头 response.setHeader('Access-Control-Allow-Origin', '*'); response.setHeader('Access-Control-Allow-Headers', '*'); response.setHeader('Access-Control-Allow-Method', '*'); response.send('hello cors'); });<script> const btn = document.querySelector('button'); btn.onclick = function () { //1. 创建对象 const xhr = new XMLHttpRequest(); //2. 设置请求方法和url xhr.open('GET', 'http://127.0.0.1:8000/cors-server'); //3. 发送 xhr.send(); //4. 监听事件 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { console.log(xhr.response); } } }; }; </script>