Axios介绍

1、Axios是什么?

  • Axios是一个基于promise的HTTP库,类似于jQuery的ajax,用于http请求。可以应用于浏览器端和node.js,既可以用于客户端,也可以用于node.js编写的服务端。

2、Axios特性

  • (1)支持Promise API

  • (2)拦截请求与响应,比如:在请求前添加授权和响应前做一些事情。

  • (3)转换请求数据和响应数据,比如:进行请求加密或者响应数据加密。

  • (4)取消请求

  • (5)自动转换JSON数据

  • (6)客户端支持防御XSRF

3、浏览器支持情况

  • Firefox、Chrome、Safari、Opera、Edge、IE8+

Axios使用

vue创建项目 vue create axios-vue,选择自定义配置manually select features,终端显示如下:上下键切换,空格键选择对应配置。

而后,我们一次选择一下配置:

运行终端指令 cd axios-vue 、npm run serve启动项目后,运行终端指令npm add axios安装axios,至此,项目创建完成,axios安装完成。

在项目的public文件夹下新建data.json文件,用于模拟数据。在组建中引入axios,并发起请求。

{"title":"vue project","create_time":"2019-01-01"
}

在组件中中,引入axios并使用。

import axios from "axios"axios.get("/data.json").then(res=>{console.log(res)
}).catch(err=>{console.log(err)
})

此时,已经可以请求掉数据了,效果如下:

axios常用的请求方法

方法列举:get, post, put, patch, delete

  • get:一般用户获取数据
  • post:一般用于表单提交与文件上传
  • patch:更新数据(只将修改的数据推送到后端)
  • put:更新数据(所有数据推送到服务端)
  • delete:删除数据

备注:post一般用于新建数据,put一般用于更新数据,patch一般用于数据量较大的时候的数据更新。

get方法

  • 方式一
    如果不带有参数,代码如下:
    axios.get("/data.json").then(res => {console.log(res);}).catch(err => {console.log(err);});

如果带有参数,代码如下:

 axios.get("/data.json", {params: {id: 12}}).then(res => {console.log(res);}).catch(err => {console.log(err);});

此时表示,参数为id=12,最终的请求路径Request URL: http://localhost:8080/data.json?id=12

  • 方式二
    如果不带参数,代码如下:
      axios({method:'get',url:'/data.json'}).then(res=>{console.log(res)})

如果带有参数,代码如下:

 axios({method: "get",url: "/data.json",params:{id:12}}).then(res => {console.log(res);});

此时表示,参数为id=12,最终的请求路径Request URL:http://localhost:8080/data.json?id=12

浏览器控制台相关信息介绍:

  • Request URL:请求URL

  • Request Method:请求方式

post方法

post请求常用的数据请求格式有两种:

form-data(常用于表单提交(图片上传、文件上传))

 let data = {id: 12};let formData = new FormData();for(let key in data){formData.append(key,data[key])}console.log(formData)axios.post('/data.json',formData).then(res=>{console.log(res,'formData')})

注意:请求地址Request URL: http://192.168.1.106:8080/data.json,

请求头中Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydgohzXGsZdFwS16Y

参数形式:id:12

application/json(常用)
  • 方式一
  let data = {id: 12};axios.post("/data.json", data).then(res=>{console.log(res, 'post')});
  • 方式二
 let data = {id: 12};axios({method:'post',url:'/data.json',data:data}).then(res=>{console.log(res)})

注意:请求地址Request URL: http://192.168.1.106:8080/data.json,

请求头中Content-Type: application/json;charset=UTF-8

参数形式:{id:12}

put方法

let data = {id: 12
};
axios.put("/data.json", data).then(res=>{console.log(res, 'put')
});

patch方法

let data = {id: 12
};
axios.patch("/data.json", data).then(res=>{console.log(res, 'patch')
});

备注:put与patch与post方法只有method不同,其他相同。

delete方法

  • 方式一:params
axios.delete("/data.json", {params: {id: 12}}).then(res => {console.log(res, "delete");});let params = {id: 12
};
axios({method:'delete',url:'/data.json',params:params
}).then(res=>{console.log(res)
})
  • 方式二:data
axios.delete("/data.json", {data: {id: 12}}).then(res => {console.log(res, "delete");});let data = {id: 12
};
axios({method:'delete',url:'/data.json',data:data
}).then(res=>{console.log(res)
})

注意:params方式会将请求参数拼接在URL上面,Request URL: http://192.168.1.106:8080/data.json?id=12

参数形式:id:12

Content-Type: text/html; charset=utf-8

data方式不会讲参数拼接,是直接放置在请求体中的,Request URL:http://192.168.1.106:8080/data.json

参数形式:{id:12}

Content-Type: application/json;charset=UTF-8

总结:上述方法中均对应两种写法:(1)使用别名:形如axios.get();(2)不使用别名形如axios();

并发请求

并发请求,就是同时进行多个请求,并统一处理返回值。

在例子中,我们使用axios.all,对data.json/city.json同时进行请求,使用axios.spread,对返回的结果分别进行处理。代码如下:

// 并发请求
axios.all([axios.get("/data.json"), axios.get("/city.json")]).then(axios.spread((dataRes, cityRes) => {console.log(dataRes, cityRes);})
);

注意:axios.all的参数是请求函数的数组,在对应的回调then中,调用axios.spead对返回值进行处理,即可。

并发请求的应用场景:需要同时进行多个请求,并且需要同时处理接口调用的返回值的时候,我们可以使用并发请求。

axios实例的创建

比如:后端接口地址有多个(www.test.com、www.example.com),并且超时时长不同(1000ms、2000ms),这个时候,我们可以创建实例。

思路如下:创建多个实例,配置不同的超时时长,用不同的实例去请求不同的接口。使用axios.acreate来创建实例,配置相关信息,进行网络请求。代码如下:

// 实例1
let instance = axios.create({baseURL:'http://loacalhost:8080',timeout:1000
})
instance.get('/data.json').then(res=>{console.log(res)
})
//实例2
let instance2 = axios.create({baseURL: "http://loacalhost:8081",timeout: 2000
});
instance2.get("/city.json").then(res => {console.log(res);
});

备注:此时我们就可以访问http://loacalhost:8080与http://loacalhost:8081两个不同域名的接口,并且使用不同的配置。

axios实例的相关配置

(1)配置列表

  • baseURL:请求的域名(基本地址)。

  • timeout:请求的超时时长,超出后后端返回401。

  • 备注:一般由后端定义,后端的接口需要的处理时长较长的时候,如果请求的时间过长,后端处理不过来,就会阻塞,给服务器造成较大的压力。设置后,可以及时释放掉。

  • url:请求路径。

  • method:请求方法。如:get、post、put、patch、delete等。

  • headers:请求头。

  • params:将请求参数拼接到url上

  • data:将请求参数放置到请求体里

    axios.create({baseURL:'', //请求的域名(基本地址)timeout:2000, //请求的超时时长,单位毫秒,默认。url:'/data.json', //请求路径method:'get', //请求方法headers:{token:''}, //设置请求头params:{},//将请求参数拼接到url上data:{}, //将请求参数放置到请求体里});

三种配置方式:

  • axios全局配置
    axios.defaults.baseURL = 'http://localhost:8080'axios.defaults.timeout = 2000
  • axios实例配置
    let instance = axios.create();instance.defaults.timeout = 3000
  • axios请求配置
    instance.get('/data.json',{timeout:5000})

优先级:axios全局配置 < axios实例配置 < axios请求配置

常用参数配置的使用方法

  • 举例1:
    let instance1 = axios.create({baseURL:'http://localhost:9090',timeout:1000})instance1.get("/contactList",{params:{id:10}}).then(res=>{console.log(res)})

分析:配置的参数为baseURL:‘http://localhost:9090’,timeout:1000,method:‘get’,params:{ id:10},url:’/contactList’

  • 举例2:
    let instance2 = axios.create({baseURL:'http://localhost:9091',timeout:3000})instance2.get("/contactList",{timeout:5000}).then(res=>{console.log(res)})

分析:配置的参数为baseURL:‘http://localhost:9091’,timeout:5000,method:‘get’,url:’/contactList’

注意:最终的有效配置是由优先级高的覆盖优先级低的。

拦截器

什么拦截器?

在请求前或响应被处理前拦截他们,分为两种:请求拦截器与响应拦截器

拦截器的使用方法

  • 请求拦截器
    //   请求拦截器axios.interceptors.request.use(config => {// 在发送请求前做些什么return config;}, err=>{// 在请求错误的时候的逻辑处理return Promise.reject(err)});
  • 响应拦截器
    // 响应拦截器axios.interceptors.response.use(res => {// 在请求成功后的数据处理return res;}, err=>{// 在响应错误的时候的逻辑处理return Promise.reject(err)});
  • 取消拦截器
 let inter = axios.interceptors.request.use(config=>{config.header={auth:true}return config})axios.interceptors.request.eject(inter)

实用举例A:登录权限

需要token的接口实例
    // 需要token的接口let instance = axios.create({});instance.interceptors.request.use(config=>{config.headers.token = '';return config})
不需要token的接口实例
    // 不需要token接口let newInstance = axios.create({});

实用举例B:移动端开发数据加载loading动画

    // 请求的加载动画loadinglet instance_phone = axios.create({});instance_phone.interceptors.request.use(config=>{$('#loading').show();return config})instance_phone.interceptors.response.use(res=>{$('#loading').hide();return res})

备注:实现的效果是请求数据的时候显示loading动画,数据响应后隐藏loading动画。

错误处理

结合请求拦截器与响应拦截器来说,不管是请求错误还是响应错误,都会执行catch方法。

     //  请求拦截器axios.interceptors.request.use(config => {// 在发送请求前做些什么return config;},err => {// 在请求错误的时候的逻辑处理return Promise.reject(err);});// 响应拦截器axios.interceptors.response.use(res => {// 在请求成功后的数据处理return res;},err => {// 在响应错误的时候的逻辑处理return Promise.reject(err);}); axios.get("data.json").then(res => {console.log(res);}).catch(err => {console.log(res);});

错误处理举例

在实际开发中,不会再每次网络请求的时候,都使用catch方法,可以添加统一的错误处理方法。代码如下:

    //   请求错误处理let instance = axios.create({});instance.interceptors.request.use(config => {return config;},err => {// 请求错误的常见状态码:4XX  401-请求超时  404-mot found$("#error").show();setTimeout(()=>{$("#error").hide(); }, 2000)return Promise.reject(err);});// 响应错误处理instance.interceptors.response.use(res => {return res;},err => {// 响应错误的常见状态码 5XX 500-服务器错误 502-服务器重启$("#error").show();setTimeout(()=>{$("#error").hide(); }, 2000)return Promise.reject(err);});instance.get("/data.json").then(res=>{console.log(res,'请求成功')}).catch(err=>{console.log(err,'除了拦截器设置的处理之外的其他处理')})

思路分析:首先创建实例,给实例设置请求拦截器与响应拦截器。

  • (1)请求错误的常见状态码以4开头,如401-请求超时、404-接口未找到;

  • (2)响应错误的常见状态码以5开头,如500-服务器错误、502-服务器重启等。

  • (3)处理设置请求拦截器与响应拦截器的操作外,如果还要其他操作,我们可以在请求的时候,在使用catch方法。

取消请求(不常用)

  • 代码示例
let source = axios.CancelToken.source();axios.get("/data.json", {cancelToken: source.token}).then(res => {console.log(res);}).catch(err=>{console.log(err)})//   取消请求(参数msg)source.cancel('自定的的字符串可选')
  • 应用场景
    在查询数据的时候,很长时间(3-5s)仍未获取数据,这个时候需要取消请求。

代理

vue cil2
代理分为旧版本 也就是 vue cil2  这个版本需要在config/index.js下配置
dev: {// PathsassetsSubDirectory: 'static',assetsPublicPath: '/',// 后端请求地址代理,配置后testIp再之后的页面调用时就直接指代 http://197.82.15.15:8088proxyTable: {'/testIp': {target: 'http://197.82.15.15:8088',changeOrigin: true,pathRewrite: { '^/testIp': ''}},'/elseIp': {target: 'http://182.83.19.15:8080',changeOrigin: true,pathRewrite: { '^/esleIp': ''}},},
-----------------------------------------------------------vue cil 3在 vue.config.js文件下devServer: {overlay: { // 让浏览器 overlay 同时显示警告和错误warnings: true,errors: true},host: "localhost",port: 8080, // 端口号https: false, // https:{type:Boolean}open: false, //配置后自动启动浏览器hotOnly: true, // 热更新// proxy: 'http://localhost:8080'   // 配置跨域处理,只有一个代理proxy: { //配置多个代理"/testIp": {target: "http://197.0.0.1:8088",changeOrigin: true,ws: true,//websocket支持secure: false,pathRewrite: {"^/testIp": "/"}},"/elseIp": {target: "http://197.0.0.2:8088",changeOrigin: true,//ws: true,//websocket支持secure: false,pathRewrite: {"^/elseIp": "/"}},}}
当项目有多个后台的时候,可以在api文件夹下 新建一个elseApi.js  ,书写当前ip下的接口请求。方法同上,只是 `let resquest = "/elseIp/request/"` 调用的时候把端口更改一下。

request.js

在项目src目录下新建utils文件夹,然后在其中新建 request.js文件,这个文件是主要书写axios的封装过程。
/****   request.js   ****/
// 导入axios
import axios from 'axios'
// 使用element-ui Message做消息提醒
import { Message} from 'element-ui';
//1. 创建新的axios实例,
const service = axios.create({// 公共接口--webpack中的全局变量process.env.BASE_API//为了适应多个后台或者开发的时候的api地址和发布的时候的api地址不一样这种情况baseURL: process.env.BASE_API,// 超时时间 单位是ms,这里设置了3s的超时时间timeout: 3 * 1000
})
// 2.请求拦截器
service.interceptors.request.use(config => {//发请求前做的一些处理,数据转化,配置请求头,设置token,设置loading等,根据需求去添加***** 下面详解config.data = JSON.stringify(config.data); //数据转化,也可以使用qs转换config.headers = {'Content-Type':'application/x-www-form-urlencoded' //配置请求头}//注意使用token的时候需要引入cookie方法或者用本地localStorage等方法,推荐js-cookieconst token = getCookie('名称');//这里取token之前,你肯定需要先拿到token,存一下if(token){config.params = {'token':token} //如果要求携带在参数中config.headers.token= token; //如果要求携带在请求头中}*****return config
}, error => {Promise.reject(error)
})// 3.响应拦截器
service.interceptors.response.use(response => {//接收到响应数据并成功后的一些共有的处理,关闭loading等return response
}, error => {/***** 接收到异常响应的处理开始 *****/if (error && error.response) {// 1.公共错误处理// 2.根据响应码具体处理switch (error.response.status) {case 400:error.message = '错误请求'break;case 401:error.message = '未授权,请重新登录'break;case 403:error.message = '拒绝访问'break;case 404:error.message = '请求错误,未找到该资源'window.location.href = "/NotFound"break;case 405:error.message = '请求方法未允许'break;case 408:error.message = '请求超时'break;case 500:error.message = '服务器端出错'break;case 501:error.message = '网络未实现'break;case 502:error.message = '网络错误'break;case 503:error.message = '服务不可用'break;case 504:error.message = '网络超时'break;case 505:error.message = 'http版本不支持该请求'break;default:error.message = `连接错误${error.response.status}`}} else {// 超时处理if (JSON.stringify(error).includes('timeout')) {Message.error('服务器响应超时,请刷新当前页')}error.message('连接服务器失败')}Message.error(error.message)/***** 处理结束 *****///如果不需要错误处理,以上的处理过程都可省略return Promise.resolve(error.response)
})
//4.导入文件
export default service0

数据转换说明

config.data = JSON.stringify(config.data);
config.headers = { 'Content-Type':'application/x-www-form-urlencoded'  }
const token = getCookie('名称')
if(token){ config.params = {'token':token} ; config.headers.token= token;
}上述的代码都是请求的配置项,非必须,也是分情况的,data/headers/params 这种本身的参数都有多种,和后台沟通,需要什么就配什么!
config.data = JSON.stringify(config.data);为什么不用qs.stringify,因为我的后台想要的只是json类型的传参,而qs转换会转换成为键值对拼接的字符串形式。当然你们后台需要传递字符串类型参数,那就换成qs或者其他格式方式。
const token = getCookie('名称')这是token的取值,在取之前你肯定需要发请求拿到token,然后setCookie存起来,而名称就是你存的token的名称,每个人的不一样;
config.headers = { 'Content-Type':'application/x-www-form-urlencoded' }请求头内容的配置,也是不同的,application/x-www-form-urlencoded :form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式),你可以根据实际情况去配置自己需要的;
以上
我已经举了很清晰的例子,写代码的过程是自己动脑去搭建工程的,希望能看到我文章的各位,善于搜索,善于思考,善于总结;
当然我很喜欢帮大家解决问题,但是相关的基础问题,还是建议自己去学习掌握。

Axios中的http.js

在项目src目录下的utils文件夹中新建 http.js文件,这个文件是主要书写几种请求的封装过程。
/****   http.js   ****/
// 导入封装好的axios实例
import request from './request'const http ={/*** methods: 请求* @param url 请求地址 * @param params 请求参数*/get(url,params){const config = {method: 'get',url:url}if(params) config.params = paramsreturn request(config)},post(url,params){const config = {method: 'post',url:url}if(params) config.data = paramsreturn request(config)},put(url,params){const config = {method: 'put',url:url}if(params) config.params = paramsreturn request(config)},delete(url,params){const config = {method: 'delete',url:url}if(params) config.params = paramsreturn request(config)}
}
//导出
export default http

Axios中的api.js

在项目src目录下新建api文件夹,然后在其中新建 api.js文件,这个文件是主要书写API的封装过程。 当一个项目中后台请求不是同一个IP地址,是多个IP地址的时候,可以在api文件夹下创建多个js文件,用来调用请求api.js有两种导出方式,分类导出和全部导出分类导出
import http from '../utils/http'
//
/***  @params resquest 请求地址 例如:http://197.82.15.15:8088/request/...*  @param '/testIp'代表vue-cil中config,index.js中配置的代理*/
let resquest = "/testIp/request/"// get请求
export function getListAPI(params){return http.get(`${resquest}/getList.json`,params)
}
// post请求
export function postFormAPI(params){return http.post(`${resquest}/postForm.json`,params)
}
// put 请求
export function putSomeAPI(params){return http.put(`${resquest}/putSome.json`,params)
}
// delete 请求
export function deleteListAPI(params){return http.delete(`${resquest}/deleteList.json`,params)
}---------------------------------------------------
全部导出
import http from '../utils/http'
//
/***  @params resquest 请求地址 例如:http://197.82.15.15:8088/request/...*  @param '/testIp'代表vue-cil中config,index.js中配置的代理*/
let resquest = "/testIp/request/"// get请求
export default{getListAPI(params){return http.get(`${resquest}/getList.json`,params)},postFormAPI(params){return http.post(`${resquest}/postForm.json`,params)}
}

———————————————————————————————————————————————————
以上封装完毕接下来我们看如何调用

在Vue中调用接口

用到哪个api 就调用哪个接口——适用于上文接口分类导出;
import {getListAPI,postFormAPI, putSomeAPI, deleteListAPI} from '@/api/api'methods: {//promise调用,链式调用, getList()括号内只接受参数;//   get不传参getList() {getListAPI().then(res => console.log(res)).catch(err => console.log(err))},//post传参postForm(formData) {let data = formDatapostFormAPI(data).then(res => console.log(res)).catch(err => console.log(err))},//async await同步调用async postForm(formData) {const postRes = await postFormAPI(formData)const putRes = await putSomeAPI({data: 'putTest'})const deleteRes = await deleteListAPI(formData.name)// 数据处理console.log(postRes);console.log(putRes);console.log(deleteRes);},}
-----------------------------------------------把api全部导入,然后用哪个调用哪个api——适用于全部导出import api from '@/api/api'methods: {getList() {api.getListAPI(data).then(res => {//数据处理}).catch(err => console.log(err))}}

Axios(万字详细教程)相关推荐

  1. 实现五子棋的简单人人对战和人机对战的万字详细教程

    创建一个窗体来承载五子棋 如何创建一个窗体,以及如何在窗体上添加必要的按钮组件等操作,可以查看我的第一篇文章,里面有详细的讲解,这里就不在重新累述.具体的代码如下: public class Draw ...

  2. java使用web3j,部署智能合约在测试链上,并调用(万字详细教程)

    目录 前言 一.准备工作 二.智能合约的编写 三.用java完成web3j调用 (一)定义为生成对应合约的bin 以及 abi 文件 (二).利用abi,bin文件生成solidity的java代码 ...

  3. ❤️ 万字Python MySQL从入门到精通详细教程❤️ 再也不用担心学不会数据库了❤️

    文章目录 前言 ⭐集合三万字基础教程⭐ 一.SQL详细教程 二.mysql入门详细教程 ⭐转python mysql⭐ 三.Python MySQL入门连接 3.1基本环境准备 3.2连接 四.Pyt ...

  4. 【宝藏级】全网最全的Matplotlib详细教程-数据分析必备手册(4.5万字总结)

    [宝藏级]全网最全的Matplotlib详细教程(4.5万字总结) 1. 数据分析中常用图 折线图: 柱状图: 直方图: 散点图: 饼状图: 箱线图: 更多参考: 2. Matplotlib库 安装: ...

  5. 【宝藏级】全网最全的Pandas详细教程(2万字总结)

    [回炉重造]Python之Pandas详细教程 前言 为什么要学习Pandas? 什么是Pandas? 1. Pandas的索引操作 1. Series和DataFrame中的索引都是Index对象 ...

  6. 【宝藏级】全网最全的Seaborn详细教程-数据分析必备手册(2万字总结)

    数据分析必备手册-Seaborn详细教程 seaborn库 安装: 官方文档: 关系绘图 relplot 1. 基本使用: 2. 添加hue参数: 3. 添加col和row参数: 4. 指定具体的列: ...

  7. SpringBoot+vue3对接支付宝支付详细教程

    SpringBoot+vue3对接支付宝支付详细教程 本人也是第一次做这个,是一个刚刚学习自学支付的萌新,目的是在于学习,只是为了记录自己的学习过程,怕以后会忘记,因为我没有企业账号,所以用的是自己的 ...

  8. 手把手带你入门前端工程化——超详细教程(高级前端必备)

    本文将分成以下 7 个小节: 技术选型 统一规范 测试 部署 监控 性能优化 重构 部分小节提供了非常详细的实战教程,让大家动手实践. 另外我还写了一个前端工程化 demo 放在 github 上.这 ...

  9. 手把手带你入门前端工程化——超详细教程

    授权自@谭光志 链接:https://segmentfault.com/a/1190000037752931,也可点击阅读原文 本文将分成以下 7 个小节: 技术选型 统一规范 测试 部署 监控 性能 ...

最新文章

  1. li前面的原点或者方的样式修改html中列表项li所显示的圆点的颜色?,以及相关样式的设定...
  2. 联机日志损坏的解决办法
  3. ESP-TOUCH编码规则及解码
  4. 清理linux内存cache
  5. Spring Security入门(3-4)Spring Security 异常处理、异常传递和异常获取
  6. datax oracle mysql_从 MySQL 到 Lindorm时序引擎 的数据迁移
  7. win7服务器未能登入,Win7提示profile服务未能登录,无法在系统中创建怎么办
  8. 【官方方法】ROS源
  9. 针对数据泵导出 (expdp) 和导入 (impdp)工具性能降低问题的检查表 (文档 ID 1549185.1)...
  10. 0Day发布Confluence 2.1.4 破解,所见即所得的编辑界面终于亮相
  11. android 微信小程序 本地包,Android 7 以上版本微信小程序抓包方法
  12. 封电脑机器码怎么解决_如何通过修改机器码解决游戏封号问题
  13. 你好2019,我是全新的CPDA数据分析师课程
  14. LiveQing视频点播RTMP直播服务一步一步搭建视频直播RTMP推流流媒体服务
  15. matlab单回路和串级控制回路,串级控制回路PID参数如何整定?
  16. 代码评审(CR)实践指南
  17. cross product or vector product - 叉积或向量积
  18. Centos6 yum源设置
  19. 图片占位符生成器holder.js的简单使用
  20. 计算机中mpeg代表什么,mpg和mpeg格式的区别

热门文章

  1. ext2与ext3的区别 .
  2. 遗传算法-matlab实现
  3. 1-性能测试方案与流程剖析
  4. 一小时就做好的python游戏实战:打牌小游戏,好玩归好玩,注意别玩上瘾了哟~~
  5. python怎样安装词云_在python中怎样安装词云-女性时尚流行美容健康娱乐mv-ida网...
  6. Problem AB: 开宝箱 1/2 (最沙雕的做法)(未用指针做) 改:附上一种指针做法...
  7. 【Matlab】正态分布常用函数normpdf_normcdf_norminv_normrnd_normfit
  8. css 动画 重播,javascript – Animate.CSS重播?
  9. http://www.runoob.com
  10. tailwindcss 一览表