Vue 学习总结笔记 (九)
1. axios 介绍2. CORS(cross-origin resource sharing) 同源策略3. Vue的代理服务器 方式一3.1 方式一:配置一个代理服务器3.2 方式一:代理服务器会出现两个问题4. Vue的代理服务器 方式二4.1 方式二:配置代理服务器4.2 方式二配置的优缺点5. 对于第三方库导入使用6. 调用第三方接口呈现页面数据 案例6.1 调用Github用户接口6
文章目录
1. axios 介绍
axios是基于promise风格的。ES6语法的promise风格。
js中有两个原生发送请求的对象,分别是xhr和fetch。fetch也是基于promise风格的。但是用的最多的还是xhr。
但是目前而言,像jQuery的$get,$post,$ajax等 和 axios都是基于xhr的!
axios的安装:
针对post发送表单格式的参数,必须要使用FormData来操作。
2. CORS(cross-origin resource sharing) 同源策略
出现这个跨域资源共享的问题就说明一件事,违背了同源的原则。
原因:因为这是浏览器安全策略(同源策略),专门设计用来避免恶意的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");
- 第二种方式:jsonp格式,jsonp的本质是通过操作script的src属性,url拼接上对应后台的参数名和对应值来获取的。正因如此,所以只能支持get请求的。这种方式很少用,但是面试容易问!
//第一步,定义动态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);
-
第三种方式:代理服务器,在自己本地也配置一台代理服务器。
-
代理服务器存在前端服务器的,并且他的端口号域名都和前端服务器对应上。让代理服务器和后台服务器进行通信,这样前端服务器页面直接通过访问代理服务器来获取数据可以了,就不存在跨域问题。
-
很多人疑问代理服务器和后台服务器之前难道不会有跨域问题?
- 答:不会有跨域问题,因为代理服务器是服务器并不是在浏览器端的,代理服务器和后台服务器,是服务器与服务器之间打交道,是http协议!中间可没有什么浏览器的同源策略。
- 代理服务器既然是和前端服务器在一起的,也是nodejs环境,js写的服务器。也就是js服务器和后台服务器通过http协议进行数据通信。
- 此外,XmlHttpRequest对象是在前端浏览器中的。nodejs环境下的服务器是没有这个对象的。
3. Vue的代理服务器 方式一
3.1 方式一:配置一个代理服务器
那么对于中间这个代理服务器,我们可以通过什么来实现呢?
- 通过nginx可以实现。
- 通过vue-cli脚手架也可以实现。
一般我们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>
3.2 方式一:代理服务器会出现两个问题
第一个问题,既然代理服务器和前端服务器的域名和端口号相同,那么页面访问的时候是访问的前端服务器还是代理服务器?
- 我们访问的localhost:8080,是前端服务器的public目录下的信息。
这样就会产生一个冲突,如果我访问的路径内容,在前段服务器中有!它就不会在去找代理服务器转发到后台服务器进行通信了。
- 例如:http://localhost:8080/user/allUser , 我们通过代理服务器想要访问后台服务器的/user/allUser的接口,但是恰好前段服务器的public目录下方,也有一个/user/allUser目录。那么就直接返回前端服务器的东西了。
第二个问题,代理服务器只能配置匹配一个后台服务器不能配置多个后台服务器!
4. Vue的代理服务器 方式二
4.1 方式二:配置代理服务器
配置vue.config.js文件:
- 理解前缀,注意前缀也是会被发到后台服务器的!因此我们需要配置pathRewrite的正则表达式来去掉前缀。
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">获取学生信息1</button>
<button @click="getStudents2">获取学生信息2</button>
</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>
4.2 方式二配置的优缺点
优点:
- 可以配置多个代理服务器。
- 可以灵活的控制请求是否走代理服务器还是访问本地的前端服务器。
缺点:
- 配置略微繁琐,请求资源时必须添加前缀。
5. 对于第三方库导入使用
js文件,直接通过npm安装,直接import引入就可以。
css文件:
-
一种也是通过import引入css文件。
-
在这就是在public下面放入css文件,在index.html中引入。
6. 调用第三方接口呈现页面数据 案例
6.1 调用Github用户接口
调用github接口案例:
- https://api.github.com/search/users?q=xxx
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">
<!-- 写v-for的时候一定不要忘记写key值。 -->
<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页面:
<!DOCTYPE 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>
<!-- 我们可以在public设置好,在index中引入。 -->
<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>
6.2 数据显示模块(案例中的List) 正常开发中有四部分
对于我们平时的数据显示模块就像上面github案例上面的中的List组件, 正常开发中有四部分:
- welcome欢迎页面模块,用户未进行任何操作的页面。
- loading加载页面模块,用户查询了一些内容,中间显示一个写加载页面。
- data页面模块,用户查询到的数据显示到页面。
- error页面模块,如果出现错误应该显示的页面。
这就使用v-show加一些data判断条件就可以操作上面的内容。
- 细节问题:对于操作四个模块显示不显示的data,可以用一个统一的对象来操作,这样就方便很多。
- 四个模块的展现时机,取决于给List.vue发数据的人也就是Search.vue文件。就是展现时机也是由Search.vue决定的。
List.vue文件:
<template>
<div class="row">
<!-- 展示data页面模块 -->
<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 Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord">
<button @click="searchUsers">Search</button>
</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>
7. vue-resource 插件库
前端发送接受数据的几种方式:
- xhr对象(原始方式)。
- jQuery封装(基于xhr)。
- axios操作(基于xhr)。
- fetch(和xhr平级的一个)。
- vue-resource插件库(使用的并不是很多,但是要了解,并且他也是基于xhr的)。
安装vue-resource插件库:
main.js文件:
- 引入vue-resource插件。
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插件库:
- 它也是promise风格的。
- 使用规则和axios,fetch都差不多。
Search.vue组件
- 导入完成只需要将axios该成this.$http就可以了,都是基于promise风格很方便。
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search" v-model="keyWord">
<button @click="searchUsers">Search</button>
</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>
总结:
8. Vue的 插槽使用
8.1 默认插槽
插槽的作用:
- 让父组件可以像子组件指定位置插入html结构,也是一种组件间的通信的方式,适用于 父组件 ==> 子组件。
之前我们的操作都是操作组件标签的属性。现在我们要在组件标签体中设置相关标签,放到后台的默认插槽slot中。
App.vue文件:
<template>
<div class="container">
<!-- 我们在组件的标签体重设置内容,会默认放到slot标签中。 -->
<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="电影">
<!-- controls属性代表控制。 -->
<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>
8.2 具名插槽
具名插槽就是给插槽起名字:
- <slot name=“center”></slot>和 slot=“center” 搭配使用。
- <template v-slot:footer>也可以实现具名插槽效果,但是v-slot指令只能应用到组件标签和template标签上面。
App.vue文件:
<template>
<div class="container">
<Category title="美食">
<!-- slot=""和组件对应的solt的name对应起来。-->
<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="电影">
<!-- controls属性代表控制。 -->
<video slot="center" controls="controls" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
<!-- 想要不多添加div,可以使用template操作。注意这里v-slot只能用于template和组件标签上面。-->
<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>
8.3 作用域插槽
作用域插槽:
- 本质上就是在子组件设置的data参数数据,可以作用到父组件定义该子组件的标签上面。类似一个提升作用域的效果。
- 总结起来就是:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
- 还有一个注意点:父组件接受数据对象时一定要是template标签。
Category.vue文件:
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!--将本组件中的games数据,以对象的形式传给父组件。-->
<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="游戏">
<!-- itholmes是作用域插槽发送过来的对象数据。 -->
<template scope="itholmes">
{{itholmes}}
<ul>
<li v-for="(item,index) in itholmes.youxi" :key="index">{{item}}</li>
</ul>
</template>
</Category>
<Category title="游戏">
<!-- 可以直接从对象传过来的对象中拿到youxi参数。 -->
<template scope="{youxi}">
<ol>
<li v-for="(item,index) in youxi" :key="index">{{item}}</li>
</ol>
</template>
</Category>
<Category title="游戏">
<!-- 也可以使用slot-scope,功能和scope是一样的。 -->
<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>
8.4 总结
默认插槽:
具名插槽:
作用域插槽:
更多推荐
所有评论(0)