Ajax 基础
Ajax 定义
官网:https://www.axios-http.cn/
定义:AJAX 是异步的JavaScript和XML(Asynchronous JavaScript And XML)。简单点说,就是使用XMLHttpRequest 对象与服务器通信。它可以使用JSON,XML,HTML和text文本等格式发送和接收数据。AJAX最吸引人的就是它的“异步”特性,也就是说它可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面。
概念:Ajax是浏览器和服务器进行数据通信的技术
Ajax 使用
axios
- 语法
1 2 3 4 5 6
| axios({ url: '目标资源地址' }).then((result) => { })
|
URL
定义
统一资源定位符,俗称网页地址,简称网址,是因特网上标准的资源的地址
组成
URL由协议,域名和资源路径组成
- 协议
http协议:超文本传输协议,规定浏览器和服务器之间传输数据的格式
- 域名
域名:标记服务器在互联网中方位
- 资源路径
资源路径:标记资源在服务器下的具体位置
查询参数
定义:浏览器提供给服务器的额外信息,让服务器返回浏览器想要的数据
语法:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2
axios 查询参数
语法:使用axios提供的params选项
注意:axios在运行时把参数名和值,会拼接到url?参数名=值
1 2 3 4 5 6 7 8
| axios({ url: '目标资源地址', params: { 参数名: 值 } }).then(result => { })
|
常用请求方法
| 请求方法 |
操作 |
| GET |
获取数据 |
| POST |
提交数据 |
| PUT |
修改数据(全部) |
| DELETE |
删除数据 |
| PATCH |
修改数据(部分) |
axios 请求配置
url:请求的 URL 网址
method:请求的方法,GET可以省略(不区分大小写)
data:提交数据
1 2 3 4 5 6 7 8 9
| axios({ url: '目标资源地址', method: '请求方法', data: { 参数名: 值 } }).then(result => { })
|
axios 处理错误信息
1 2 3 4 5 6 7 8 9 10 11
| axios({ url: '目标资源地址', method: '请求方法', data: { 参数名: 值 } }).then(result => { }).catch(error => { })
|
HTTP 协议 - 请求报文
HTTP协议:规定了浏览器发送及服务器返回内容的格式
请求报文:浏览器按照HTTP协议要求的格式,发送给服务器的内容
请求报文的组成部分有:
- 请求行:请求方法,URL,协议
- 请求头:以键值对的格式携带的附加信息,比如:Content-Type
- 空行:分隔请求头,空行之后的是发送给服务器的资源
- 请求体:发送的资源
HTTP 协议 - 响应报文
HTTP协议:规定了浏览器发送及服务器返回内容的格式
响应报文:服务器按照HTTP协议要求的格式,返回给浏览器的内容
- 响应行(状态行):协议、HTTP响应状态码、状态信息
- 响应头:以键值对的格式携带的附加信息,比如:Content-Type
- 空行:分隔响应头,空行之后的是服务器返回的资源
- 响应体:返回的资源
HTTP 响应状态码
HTTP 响应状态码:用来表明请求是否成功完成
比如:404(服务器找不到资源)
| 状态码 |
说明 |
| 1xx |
信息 |
| 2xx |
成功 |
| 3xx |
重定向消息 |
| 4xx |
客服端错误 |
| 5xx |
服务端错误 |
接口文档
接口文档:描述接口的文章(后端工程师)
接口:使用Ajax和服务器通讯时,使用的 URL,请求方法,以及参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <!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>13.form-serialize插件使用</title> </head>
<body> <form action="javascript:;" class="example-form"> <input type="text" name="username"> <br> <input type="text" name="password"> <br> <input type="button" class="btn" value="提交"> </form>
<script src="./lib/form-serialize.js"></script> <script> document.querySelector('.btn').addEventListener('click', () => {
const form = document.querySelector('.example-form') const data = serialize(form, {hash: true, empty: true}) console.log(data) }) </script> </body>
</html>
|
图片上传
- 获取图片文件对象
- 使用 FormData 携带图片文件
1 2
| const fd = new FormData() fd.append(参数名, 值)
|
- 提交表单数据到服务器,使用图片url网址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <!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>图片上传</title> </head>
<body> <input type="file" class="upload"> <img src="" alt="" class="my-img">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script>
document.querySelector('.upload').addEventListener('change', e => { console.log(e.target.files[0]) const fd = new FormData() fd.append('img', e.target.files[0]) axios({ url: 'http://hmajax.itheima.net/api/uploadimg', method: 'post', data: fd }).then(result => { console.log(result.data.data.url) const imgUrl = result.data.data.url document.querySelector('.my-img').src = imgUrl }) }) </script> </body>
</html>
|
Ajax 原理
XMLHttpRequest(XHR)
- 定义:XMLHttpRequest(XHR)对象用于与服务器交互。通过XMLHttpRequest可以在不刷新页面的情况下请求特定URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest在AJAX 编程中被大量使用。
- 关系:axios内部采用XMLHttpRequest与服务器交互
- 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址')
xhr.addEventListener('loadend', () => { console.log(xhr.reponse) })
xhr.send()
|
- 查询参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const xhr = new XMLHttpRequest()
xhr.open('请求方法', '请求url网址?参数1=值1&参数2=值2')
xhr.addEventListener('loadend', () => { console.log(xhr.reponse) })
xhr.send()
|
- URLSearchParams 对象
1 2 3 4 5 6 7 8 9
| const paramsObj = new URLSearchParams({ 参数名1: 值1, 参数名2: 值2 })
const queryString = paramsObj.toString()
|
Promise
- 定义:Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值
- 语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const p = new Promise((resolve, reject) => {
})
p.then(result => {
}).catch(error => {
})
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <!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>认识Promise</title> </head>
<body> <script>
const p = new Promise((resolve, reject) => { setTimeout(() => { resolve('模拟Ajax成功-成功结果') reject(new Error('模拟Ajax失败-失败结果')) }, 2000) })
p.then(result => { alert(result) }).catch(result => { alert(result) }) </script> </body>
</html>
|
- 三种状态
概念:一个Promise对象,必然处于以下几种状态之一
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝
- 已兑现(fulfilled):意味着,操作成功完成
- 已拒绝(rejected):意味着,操作失败
注意:Promise对象一旦被兑现/拒绝就是已经敲定了,状态无法在被改变
封装简易Axios函数
- 封装不包含查询参数和请求体的Axios函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <!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>封装_简易axios函数_获取省份列表</title> </head>
<body> <p class="my-p"></p> <script>
function myAxios(config) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open(config.method || 'GET', config.url) xhr.addEventListener('loadend', () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(JSON.parse(xhr.response)) } else { reject(new Error(xhr.response)) } }) xhr.send() }) }
myAxios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => { document.querySelector('.my-p').innerHTML = result.list.join('<br>') }).catch(error => { console.dir(error) document.querySelector('.my-p').innerHTML = error.message }) </script> </body>
</html>
|
- 封装不包含请求体的Axios函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <!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>封装_简易axios函数_获取地区列表</title> </head>
<body> <p class="my-p"></p> <script> /** * 目标:封装_简易axios函数_获取地区列表 * 1. 判断有params选项,携带查询参数 * 2. 使用URLSearchParams转换,并携带到url上 * 3. 使用myAxios函数,获取地区列表 */ function myAxios(config) { return new Promise((resolve, reject) => { // 如果请求参数存在,则通过URLSearchParams对象将对象参数转为字符串类型 if (config.params) { const paramsObj = new URLSearchParams(config.params) const queryString = paramsObj.toString() config.url += `?${queryString}` } const xhr = new XMLHttpRequest() xhr.open(config.method || 'GET', config.url) xhr.addEventListener('loadend', () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(JSON.parse(xhr.response)) } else { reject(new Error(xhr.response)) } }) xhr.send() }) }
myAxios({ url: 'http://hmajax.itheima.net/api/area', params: { pname: '四川省', cname: '南充市' } }).then(result => { console.log(result) }).catch(error => { console.dir(error) }) </script> </body>
</html>
|
- 封装带有核心功能的Axios函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <!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>封装_简易axios函数_注册用户</title> </head>
<body> <button class="reg-btn">注册用户</button> <script>
function myAxios(config) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest()
if (config.params) { const paramsObj = new URLSearchParams(config.params) const queryString = paramsObj.toString() config.url += `?${queryString}` } xhr.open(config.method || 'GET', config.url)
xhr.addEventListener('loadend', () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(JSON.parse(xhr.response)) } else { reject(new Error(xhr.response)) } })
if (config.data) { const dataJson = JSON.stringify(config.data) xhr.setRequestHeader('content-type', 'application/json') xhr.send(dataJson) } else { xhr.send() } }) }
document.querySelector('.reg-btn').addEventListener('click', () => { myAxios({ url: 'http://hmajax.itheima.net/api/register', method: 'post', data: { username: 'aoshudingli', password: '123456' } }).then(result => { console.log(result) }).catch(error => { console.dir(error) }) }) </script> </body>
</html>
|
Ajax 进阶
Axios 实例
当项目中会有多个请求,并且每个请求的基地址等等基础配置不同,那么可以通过 axios 实例来区分不同的请求
1 2 3 4 5
| const instance = axios.create({ baseURL: 'https://some-domain.com/api/', timeout: 1000, headers: {'X-Custom-Header': 'foobar'} });
|
同步代码和异步代码
同步代码:逐行执行,需原地等待结果后,才继续向下执行
异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数
回调函数地狱
概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
缺点:可读性差,异常无法捕获,耦合性严重,牵一发动全身
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <!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>回调地狱</title> </head>
<body> <form> <span>省份:</span> <select> <option class="province"></option> </select> <span>城市:</span> <select> <option class="city"></option> </select> <span>地区:</span> <select> <option class="area"></option> </select> </form> <script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script>
axios({ url: 'http://hmajax.itheima.net/api/province'}).then(result => { const pname = result.data.list[0] document.querySelector('.province').innerHTML = pname axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname }}).then(result =>{ const cname = result.data.list[0] document.querySelector('.city').innerHTML = cname axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }}).then(result => { const areaName = result.data.list[0] document.querySelector('.area').innerHTML = areaName }) }) }).catch(error => { console.dir(error) }) </script> </body>
</html>
|
Promise - 链式调用
概念:依靠then()方法会返回一个新生成的Promise对象特性,继续串联下一环任务,直到结束
细节:then()回调函数中的返回值,会影响新生成的Promise对象最终状态和结果
好处:通过链式调用,解决回调函数嵌套问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!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>Promise_链式调用</title> </head>
<body> <script>
const p = new Promise((resolve, reject) => { setTimeout(() => { resolve('北京市') }, 2000) })
const p2 = p.then(result => { console.log(result) return new Promise((resolve, reject) => { setTimeout(() => { resolve('东城区') }) }) })
p2.then(result => { console.log(result) })
</script> </body>
</html>
|
使用Promise的链式调用解决回调函数地狱问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <!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>Promise链式调用_解决回调地狱</title> </head>
<body> <form> <span>省份:</span> <select> <option class="province"></option> </select> <span>城市:</span> <select> <option class="city"></option> </select> <span>地区:</span> <select> <option class="area"></option> </select> </form> <script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script>
let pname axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => { pname = result.data.list[0] document.querySelector('.province').innerHTML = pname return axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname }}) }).then(result => { const cname = result.data.list[0] document.querySelector('.city').innerHTML = cname return axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }}) }).then(result => { const areaName = result.data.list[0] document.querySelector('.area').innerHTML = areaName }) </script> </body>
</html>
|
Promise.all 静态方法
概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <!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>Promise的all方法</title> </head>
<body> <ul class="my-ul"></ul> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script>
const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100'} }) const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100'} }) const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100'} }) const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300'} })
const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise]) p.then(result => { console.log(result) const weatherStr = result.map(item => `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`).join('') document.querySelector('.my-ul').innerHTML = weatherStr }).catch(error => { console.dir(error) }) </script> </body>
</html>
|
async函数和await
- 定义
async函数是使用async关键字声明的函数。async函数是 AsyncFunction构造函数的实例,并且其中允许使用 await 关键字。async 和 await关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise。
- 使用async函数和await关键字解决回调函数地狱的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <!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>async函数和await_解决回调函数地狱</title> </head>
<body> <form> <span>省份:</span> <select> <option class="province"></option> </select> <span>城市:</span> <select> <option class="city"></option> </select> <span>地区:</span> <select> <option class="area"></option> </select> </form> <script src="https://fastly.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script>
async function getData() { const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' }) const pname = pObj.data.list[0] const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } }) const cname = cObj.data.list[0] const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } }) const areaName = aObj.data.list[0]
document.querySelector('.province').innerHTML = pname document.querySelector('.city').innerHTML = cname document.querySelector('.area').innerHTML = areaName } getData() </script> </body>
</html>
|
- 使用try….catch语句块获取异常信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <!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>async函数和await_错误捕获</title> </head>
<body> <form> <span>省份:</span> <select> <option class="province"></option> </select> <span>城市:</span> <select> <option class="city"></option> </select> <span>地区:</span> <select> <option class="area"></option> </select> </form> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script>
async function getData() { try { const pObj = await axios({ url: 'http://hmajax.itheima.net/api/province' }) const pname = pObj.data.list[0] const cObj = await axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } }) const cname = cObj.data.list[0] const aObj = await axios({ url: 'http://hmajax.itheima.net/api/area1', params: { pname, cname } }) const areaName = aObj.data.list[0]
document.querySelector('.province').innerHTML = pname document.querySelector('.city').innerHTML = cname document.querySelector('.area').innerHTML = areaName } catch (error) { console.dir(error) } }
getData() </script> </body>
</html>
|
事件循环
- 概念:JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。这个模型与其它语言中的模型截然不同,比如C和Java。
- 原因:JavaScript 是单线程语言(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型
- JavaScript 内代码执行流程
- 执行同步代码,遇到异步代码交给宿主浏览器环境执行
- 异步有了结果后,把回到函数放入任务队列排队
- 当调用栈空闲后,反复调用任务队列里的回调函数
JavaScript 内代码执行流程视频演示:https://www.bilibili.com/video/BV1MN411y7pw?spm_id_from=333.788.player.switch&vd_source=4ad0a2f2ad6c40766c4bfa7c008aaa3f&p=53
宏任务和微任务
ES6 之后引入了 Promise 对象,让JS引擎也可以发起异步任务
- 宏任务:由浏览器环境执行的异步代码
- 微任务:由JS引擎环境执行的异步代码
| 任务(代码) |
执行所在环境 |
| JS脚本执行事件(script) |
浏览器 |
| setTimeout/setInterval |
浏览器 |
| Ajax请求完成事件 |
浏览器 |
| 用户交互事件等 |
浏览器 |
| Promise对象.then() |
JS 引擎 |
- 执行顺序:
- 执行栈中先执行同步代码和Promise中的同步代码(Promise对象中只有then和catch函数中的回调函数才是异步代码)
- 遇到宏任务/微任务 交给宿主环境,有结果回调函数则放入相应队列中
- 当执行栈空闲时,先执行微任务队列,后执行宏任务队列
Axios - 配置默认参数
配置默认请求前缀
1
| axios.defaults.baseURL = '默认请求前缀'
|
Axios 请求拦截器
axios 请求拦截器: 发起请求之前,触发的配置函数,对请求参数进行额外配置
1 2 3 4 5 6 7 8
| axios.interceptors.request.use(function (config) { return config; }, function (error) { return Promise.reject(error); });
|
Axios 响应拦截器
axios 响应拦截器:响应回到then/catch之前,触发的拦截函数,对响应结果统一处理
例如:身份验证失败,统一判断并做处理
1 2 3 4 5 6 7 8 9 10
| axios.interceptors.response.use(function (response) { return response; }, function (error) { return Promise.reject(error); });
|
Axios 优化响应结果
在响应拦截器中的修改成功时的返回结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| axios. interceptors.response.use(function (response) { const result = response.data return result; }, function (error) { if (error ?. response ?. status === 401) { alert('登录状态过期,请重新登录') localStorage.clear () location.href =' .. /login/index.html' } return Promise.reject(error); });
|
wangeditor - 富文本编辑器
富文本:带样式,多格式的文本,在前端一般使用标签配合内联样式实现
富文本编辑器:用于编写富文本内容的容器
官方网址:https://www.wangeditor.com/