axios是基于promise风格的。ES6语法的promise风格。
js中有两个原生发送请求的对象,分别是xhr和fetch。fetch也是基于promise风格的。但是用的最多的还是xhr。
但是目前而言,像jQuery的$get,$post,$ajax等 和 axios都是基于xhr的!
出现这个跨域资源共享的问题就说明一件事,违背了同源的原则。
原因:因为这是浏览器安全策略(同源策略),专门设计用来避免恶意的Javascript访问你的web页面和用户的cookie。所以呢,如果你的要访问的数据跟你的不是在同一个服务器下,使用XMLHTTPRequest是会被拦截住的。
解决CORS跨域的几种方式:
//设置跨域问题。
//特定的方法或者ip名,自己定义。
HttpServletResponse resp = (HttpServletResponse)response;
resp.addHeader("Access-Control-Allow-Origin", "*");
resp.addHeader("Access-Control-Allow-Headers", "*");
resp.addHeader("Access-Control-Allow-Methods", "*");
resp.addHeader("Access-Control-Max-Age", "3600");
resp.addHeader("Access-Control-Allow-Credentials", "false");
//第一步,定义动态script
var script = document.createElement("script");
//这里写了url路径和参数参数值格式,最后添加了一个callback来定义方法名。
script.src = "url(路径)?参数=" + 参数值 + "&callback=fun";
script.type = "text/javascript";
//第二步,指定的回调函数
window["fun"] = function(data){
console.log(data);
};
//第三部,将script放到head元素下面。
var head = document.querySelector("head");
head.appendChild(script);
第三种方式:代理服务器,在自己本地也配置一台代理服务器。
代理服务器存在前端服务器的,并且他的端口号域名都和前端服务器对应上。让代理服务器和后台服务器进行通信,这样前端服务器页面直接通过访问代理服务器来获取数据可以了,就不存在跨域问题。
很多人疑问代理服务器和后台服务器之前难道不会有跨域问题?
那么对于中间这个代理服务器,我们可以通过什么来实现呢?
一般我们vue开发就肯定用vue脚手架来配置代理服务器。
并且代理服务器也不需要我们自己创建,只需要更改vue脚手架的配置就可以了。
配置代理服务器和演示效果:
第一步:配置vue.config.js核心配置文件,设置为:
module.exports = {
//关闭语法检查
lintOnSave:false,
//开启代理服务器,这台代理服务器默认就是和被代理的服务器的端口域名相同。
//注意:重写了config文件就必须重新启动vue脚手架项目
devServer: {
//这里配置需要访问的后台服务器
proxy: 'http://39.103.163.156:8081/'
}
}
第二步:检验和使用代理服务器,接受数据。
App.vue文件:
<template>
<div id="root">
<button @click="getStudents">获取学生信息button>
div>
template>
<script>
import axios from 'axios'
export default{
name:'App',
methods:{
getStudents(){
//这里直接访问代理服务器域名和端口(和脚手架服务器域名端口一样),并且要加上后台路径。
axios.get('http://localhost:8080/marry/user/allUser').then(
response => {
//取值
console.log("请求成功:",response.data)
},
error =>{
//取错误信息
console.log("请求失败了")
console.log(error)
}
)
}
}
}
script>
第一个问题,既然代理服务器和前端服务器的域名和端口号相同,那么页面访问的时候是访问的前端服务器还是代理服务器?
这样就会产生一个冲突,如果我访问的路径内容,在前段服务器中有!它就不会在去找代理服务器转发到后台服务器进行通信了。
第二个问题,代理服务器只能配置匹配一个后台服务器不能配置多个后台服务器!
配置vue.config.js文件:
module.exports = {
lintOnSave:false,
//开启代理服务器,这台代理服务器默认就是和被代理的服务器的端口域名相同。
devServer: {
proxy: {
//请求前缀。就是我们每次想要请求代理服务器的内容,可以在路径前面加一个前缀,方便识别。
//这样可以灵活的控制,访问前端服务器还是走代理服务器。
'/itholmes': {
target: 'http://39.103.163.156:8081/',
//如果不配置pathRewrite,那么也会将前缀带到后台服务其上面的。要注意!
//这里是个正则表达式。
pathRewrite:{'^/itholmes':''},
//这里的ws是websocket,用于支持websocket的。
ws: true,
/*
changeOrigin:
false的情况下:实话实说,直接告诉后台服务器我本地的ip和地址,我来自哪里。
true的情况下:撒谎,给后台服务器发送的请求头中的host值是后台服务器的ip和地址,相当于欺骗后台服务器了。
一般我们都设置为true,因为服务器可能会限制如果不是当前ip端口的拒绝请求。这样我们也可以通过撒谎访问后台服务器。
总而言是,它就是用来控制请求头中host的值。
*/
changeOrigin: true
},
//配置多个代理就继续像下面添加就可以了。
'/abc':{
target:'http://39.103.163.156:8080/',
pathRewrite:{'^/abc':''},
},
}
}
}
App.vue组件:
<template>
<div id="root">
<button @click="getStudents">获取学生信息1button>
<button @click="getStudents2">获取学生信息2button>
div>
template>
<script>
import axios from 'axios'
export default{
name:'App',
methods:{
getStudents(){
//这里直接访问代理服务器域名和端口(和脚手架服务器域名端口一样),并且要加上后台路径。
axios.get('http://localhost:8080/itholmes/marry/user/allUser').then(
response => {
//取值
console.log("请求成功:",response.data)
},
error =>{
//取错误信息
console.log("请求失败了")
console.log(error)
}
)
},
getStudents2(){
//这里直接访问代理服务器域名和端口(和脚手架服务器域名端口一样),并且要加上后台路径。
axios.get('http://localhost:8080/abc/marry/user/allUser').then(
response => {
//取值
console.log("请求成功:",response.data)
},
error =>{
//取错误信息
console.log("请求失败了")
console.log(error)
}
)
}
}
}
script>
优点:
缺点:
js文件,直接通过npm安装,直接import引入就可以。
css文件:
调用github接口案例:
main.js文件:
import Vue from "vue"
import App from "./App.vue"
Vue.config.productionTip = false;
new Vue({
el:'#app',
render:h=>h(App),
//安装全局事件总线
beforeCreate() {
Vue.prototype.$bus = this;
}
})
App.vue文件:
<template>
<div class="container">
<Search/>
<List/>
div>
template>
<script>
import List from './components/List.vue'
import Search from './components/Search.vue'
export default{
name:'App',
components:{
List,
Search
}
}
script>
List.vue文件:
<template>
<div class="row">
<div class="card" v-for="user in users" :key="user.login">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style="width: 100px;">
a>
<p class="card-text">{{user.login}}p>
div>
div>
template>
<script>
export default{
name:'List',
data(){
return {
users:[]
}
},
mounted() {
this.$bus.$on('getUsers',(data)=>{
// console.log("我是list,收到",data)
this.users = data
})
}
}
script>
<style>
.album{
min-height: 50rem;
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card{
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img{
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text{
font-size: 85%;
}
style>
index.html页面:
<html lang="">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %>title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.strong>
noscript>
<div id="app">div>
body>
html>
对于我们平时的数据显示模块就像上面github案例上面的中的List组件, 正常开发中有四部分:
这就使用v-show加一些data判断条件就可以操作上面的内容。
List.vue文件:
<template>
<div class="row">
<div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style="width: 100px;">
a>
<p class="card-text">{{user.login}}p>
div>
<h1 v-show="info.isFirst">欢迎使用!h1>
<h1 v-show="info.isLoading">加载中...h1>
<h1 v-show="info.errMsg">错误信息为:{{info.errMsg}}h1>
div>
template>
<script>
export default{
name:'List',
data(){
return {
info:{
isFirst:true,
isLoading:false,
errMsg:'',
users:[]
}
}
},
mounted() {
//我们不接受一堆上面的东西,isFirst,isLoading,errMsg,users一下子四个参数。这样可读性太差。
//我们传入一个对象过来就可以了dataObj
this.$bus.$on('updateListData',(dataObj)=>{
console.log(dataObj)
//我们直接使用info来统一管理这些模块的显示就可以了!
// 这里不能直接等于容易丢失对象
// this.info = dataObj
// 我们可以通过字面量的合并这两个对象
this.info = {...this.info,...dataObj}
})
}
}
script>
<style>
.album{
min-height: 50rem;
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
}
.card{
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
}
.card > img{
margin-bottom: .75rem;
border-radius: 100px;
}
.card-text{
font-size: 85%;
}
style>
Search.vue文件:
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Usersh3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord">
<button @click="searchUsers">Searchbutton>
div>
section>
template>
<script>
import axios from 'axios'
export default{
name:'Search',
data(){
return {
keyWord:''
}
},
methods:{
searchUsers(){
//请求前更新List对应模块的数据
console.log("请求前")
// List中四个模块的状态
//与前面对应,不传入单个值,传入一个对象让函数接受是最好的!! 这样子语义化更好!!! 格式问题!!
this.$bus.$emit('updateListData',{isFirst:false,isLoading:true,errMsg:'',users:[]})
//使用es6的模板字符串, ` 参数用${}表示 ` 。
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
//请求成功后更新List对应模块的数据
console.log("请求成功了")
// List中四个模块的状态
this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items})
}
).catch(
error => {
//请求失败后更新List对应模块的数据
console.log("发生错误了")
// List中四个模块的状态
this.$bus.$emit('updateListData',{isLoading:false,errMsg:error,users:[]})
}
)
}
}
}
script>
前端发送接受数据的几种方式:
import Vue from "vue"
import App from "./App.vue"
//导入vue-resource插件库
import vueResource from 'vue-resource'
Vue.config.productionTip = false;
//Vue使用插件
Vue.use(vueResource)
new Vue({
el:'#app',
render:h=>h(App),
//安装全局事件总线
beforeCreate() {
Vue.prototype.$bus = this;
}
})
vue-resource插件库:
Search.vue组件
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Usersh3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord">
<button @click="searchUsers">Searchbutton>
div>
section>
template>
<script>
export default{
name:'Search',
data(){
return {
keyWord:''
}
},
methods:{
searchUsers(){
console.log(this)
//请求前更新List对应模块的数据
console.log("请求前")
// List中四个模块的状态
//与前面对应,不传入单个值,传入一个对象让函数接受是最好的!! 这样子语义化更好!!! 格式问题!!
this.$bus.$emit('updateListData',{isFirst:false,isLoading:true,errMsg:'',users:[]})
//使用es6的模板字符串, ` 参数用${}表示 ` 。
this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
//请求成功后更新List对应模块的数据
console.log("请求成功了")
// List中四个模块的状态
this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items})
}
).catch(
error => {
//请求失败后更新List对应模块的数据
console.log("发生错误了")
// List中四个模块的状态
this.$bus.$emit('updateListData',{isLoading:false,errMsg:error,users:[]})
}
)
}
}
}
script>
插槽的作用:
之前我们的操作都是操作组件标签的属性。现在我们要在组件标签体中设置相关标签,放到后台的默认插槽slot中。
App.vue文件:
<template>
<div class="container">
<Category title="美食">
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
Category>
<Category title="游戏">
<ul>
<li v-for="(item,index) in games" :key="index">{{item}}li>
ul>
Category>
<Category title="电影">
<video controls="controls" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4">video>
Category>
div>
template>
<script>
import Category from './components/Category.vue'
export default{
name:'App',
data(){
return {
foods:['火锅','烧烤','小龙虾'],
games:['红色警戒','穿越火线','英雄联盟'],
films:['abc','efg','功夫熊猫']
}
},
components:{
Category
}
}
script>
<style lang="css">
.container{
display: flex;
justify-content: space-around;
}
img{
width: 100%;
}
video{
width: 100%;
}
style>
Category.vue文件:
<template>
<div class="category">
<h3>{{title}}分类h3>
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现。slot>
div>
template>
<script>
export default{
name:'Category',
props:['listData','title']
}
script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
style>
具名插槽就是给插槽起名字:
App.vue文件:
<template>
<div class="container">
<Category title="美食">
<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
<a slot="footer" href="">更多美食a>
Category>
<Category title="游戏">
<ul slot="center">
<li v-for="(item,index) in games" :key="index">{{item}}li>
ul>
<div slot="footer" class="foot">
<a href="">单机游戏a>
<a href="">网络游戏a>
div>
Category>
<Category title="电影">
<video slot="center" controls="controls" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4">video>
<template v-slot:footer>
<div class="foot">
<a href="">经典a>
<a href="">热门a>
<a href="">推荐a>
div>
<h4>欢迎前来观影h4>
template>
Category>
div>
template>
<script>
import Category from './components/Category.vue'
export default{
name:'App',
data(){
return {
foods:['火锅','烧烤','小龙虾'],
games:['红色警戒','穿越火线','英雄联盟'],
films:['abc','efg','功夫熊猫']
}
},
components:{
Category
}
}
script>
<style lang="css">
.container,.foot{
display: flex;
justify-content: space-around;
}
img{
width: 100%;
}
video{
width: 100%;
}
h4{
text-align: center;
}
style>
Category.vue文件:
<template>
<div class="category">
<h3>{{title}}分类h3>
<slot name="center">slot1:我是一些默认值,当使用者没有传递具体结构时,我会出现。slot>
<br>
<slot name="footer">slot2:我是一些默认值,当使用者没有传递具体结构时,我会出现。slot>
div>
template>
<script>
export default{
name:'Category',
props:['listData','title']
}
script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
style>
作用域插槽:
Category.vue文件:
<template>
<div class="category">
<h3>{{title}}分类h3>
<slot :youxi="games">
我是的插槽默认的一些内容。
slot>
div>
template>
<script>
export default{
name:'Category',
props:['title'],
data(){
return {
games:['红色警戒','穿越火线','英雄联盟'],
}
},
}
script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
style>
App.vue文件:
<template>
<div class="container">
<Category title="游戏">
<template scope="itholmes">
{{itholmes}}
<ul>
<li v-for="(item,index) in itholmes.youxi" :key="index">{{item}}li>
ul>
template>
Category>
<Category title="游戏">
<template scope="{youxi}">
<ol>
<li v-for="(item,index) in youxi" :key="index">{{item}}li>
ol>
template>
Category>
<Category title="游戏">
<template slot-scope="{youxi}">
<h4 v-for="(item,index) in youxi" :key="index">{{item}}h4>
template>
Category>
div>
template>
<script>
import Category from './components/Category.vue'
export default{
name:'App',
components:{
Category
}
}
script>
<style lang="css">
.container,.foot{
display: flex;
justify-content: space-around;
}
img{
width: 100%;
}
video{
width: 100%;
}
h4{
text-align: center;
}
style>