Vue笔记04-Vue中的Ajax

解决开发环境中Ajax跨域问题

在使用jQuery的时候,前后端交互通常使用$.ajax,但是会存在跨域问题。在jQuery中,有大量操作DOM的方法,但是在Vue中,我们不会主动操作DOM,所以配合Vue使用的时候,大多使用axios,使用npm i axios进行安装。
首先使用命令node server1.jsnode server2.js启动两个本地服务。
在axios中,使用代理的方式解决了跨域问题。通过Vue-CLI的devServer.proxy配置可以看到两种配置方式。需要手动创建vue.config.js配置文件,在这里添加代理服务器的配置。

module.exports = {
    pages: {
        index: {
            // 入口
            entry: 'src/main.js',
        }
    },
    // 关闭语法检查
    lintOnSave:false,
    // 开启代理服务器
    devServer: {
        proxy: 'http://localhost:5000'
    }
}

在App.vue中使用axios请求数据。

<template>
	<div>
		<button @click="getData">获取数据</button>
	</div>
</template>
<script>
	import axios from 'axios'
	export default {
		name:'App',
		methods:{
			getData(){
				// 下一行会提示跨域问题,跨域的时候,请求是发送到服务器了,服务器也返回了,但是被浏览器拦截了
				// axios.get('http://localhost:5000/students').then(
				axios.get('http://localhost:8080/students').then(
					response => {
						console.log('请求成功', response.data);
					},
					error => {
						console.log('请求失败', error.message);
					}
				);
			}
		}
	}
</script>

整个流程是这样的:点击按钮后,通过axios向本机的代理服务器发送/students请求,代理服务器接收到/students请求后,先检查自身public文件夹下,是否有students,如果有,直接返回,如果没有,向proxy配置的地址转发/students请求获取数据,在localhost:5000上有一个/students的服务,可以拿到students的数据。
上面这种配置方式有局限性:不能配置多个代理,不能控制具体请求是否走代理。
下面介绍第二种代理配置方式。

module.exports = {
    pages: {
        index: {
            // 入口
            entry: 'src/main.js',
        }
    },
    // 关闭语法检查
    lintOnSave:false,
    // 开启代理服务器
    // devServer: {
    //     proxy: 'http://localhost:5000'
    // }
    devServer: {
        proxy: {
            '/proxy1': {
                target: 'http://localhost:5000',
                pathRewrite:{'^/proxy1': ''},// 正则替换,去掉代理前缀
                ws: true,// 支持WebSocket,默认为true,可忽略
                changeOrigin: true// 控制请求头host的值,默认为true,可忽略
            },
            'proxy2': {
                target: 'http://localhost:5001',
                pathRewrite:{'^/proxy2': ''}// 正则替换,去掉代理前缀
            }
        }
    }
}
<template>
	<div>
		<button @click="getData1">获取学生数据</button>
		<button @click="getData2">获取汽车数据</button>
	</div>
</template>
<script>
	import axios from 'axios'
	export default {
		name:'App',
		methods:{
			getData1(){
				axios.get('http://localhost:8080/proxy1/students').then(
					response => {
						console.log('请求成功', response.data);
					},
					error => {
						console.log('请求失败', error.message);
					}
				);
			},
			getData2(){
				axios.get('http://localhost:8080/proxy2/cars').then(
					response => {
						console.log('请求成功', response.data);
					},
					error => {
						console.log('请求失败', error.message);
					}
				);
			}
		}
	}
</script>

在本地的5000和5001端口上分别有一个处理/students和一个处理/cars的服务。在App.vue中,使用axios请求的时候,如果需要走代理,那么请求路径的最前面要带上代理前缀,如果不走代理,就不需要带了,控制更加灵活了,而且支持配置多个代理。
关于changeOrigin,如果是true,服务器收到请求头的host是localhost:5000,如果是false,服务器收到请求头的host是localhost:8080。

GitHub用户搜索案例

这个案例,把页面分成两个组件:搜索组件和列表组件。通过变量控制请求中不同时刻显示的内容不同。
当首次进入页面的时候,显示“欢迎词”,点击搜索发送请求后,显示“加载中”,请求收到后,显示具体信息,如果请求报错了,显示错误信息。这就要在搜索组件搜索方法中控制遍历的值,来动态切换列表组件的展示状态,因此这里用到了组件间传值,这里使用全局事件总线的写法。
main.js

// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
// 关闭Vue的生产提示
Vue.config.productionTip = false;
// 创建vm
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 Search from './components/Search'
	import List from './components/List'
	export default {
		name:'App',
		components:{Search,List}
	}
</script>

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"/>&nbsp;
			<button @click="searchUsers">Search</button>
		</div>
	</section>
</template>
<script>
	import axios from 'axios'
	export default {
		name:'Search',
		data() {
			return {
				keyWord:''
			}
		},
		methods: {
			searchUsers(){
				// 请求前更新List的数据
				this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false});
				// github的这个接口携带了特殊的响应头,通过CORS解决了跨域
				// 这里是ES6的模板字符串写法
				axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
					response => {
						console.log('请求成功了');
						// 请求成功后更新List的数据
						this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items});
					},
					error => {
						// 请求后更新List的数据
						this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]});
					}
				);
			}
		}
	}
</script>

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() {
			this.$bus.$on('updateListData',(dataObj)=>{
				// 这里的语法是ES6的解构赋值
				this.info = {...this.info,...dataObj};
			});
		}
	}
</script>
<style scoped>
	.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>

Vue中两个Ajax库

axios

vue-resource

使用npm i vue-resource安装,使用vue-resource后,会在组件或Vue实例上添加一个$http的属性。

// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
// 引入vue-resource插件
import vueResource from 'vue-resource'
// 关闭Vue的生产提示
Vue.config.productionTip = false;
// 使用插件
Vue.use(vueResource);
//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	beforeCreate() {
		Vue.prototype.$bus = this
	},
})

之前axios.get()的写法换成this.$http.get()写法,目前不怎么维护了,还是使用axios吧。

slot插槽

父组件向子组件传递带数据的标签。子组件中的结构不确定时,需要使用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="(g,index) in games" :key="index">{{g}}</li>
			</ul>
		</Category>
		<Category title="电影">
			<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
		</Category>
	</div>
</template>
<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category},
		data() {
			return {
				foods:['火锅','烧烤','小龙虾','牛排'],
				games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
				films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']
			}
		}
	}
</script>
<style scoped>
	.container{
		display: flex;
		justify-content: space-around;
	}
</style>

Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
	</div>
</template>

<script>
	export default {
		name:'Category',
		props:['title']
	}
</script>
<style scoped>
	.category{
		background-color: skyblue;
		width: 200px;
		height: 300px;
	}
	h3{
		text-align: center;
		background-color: orange;
	}
	video{
		width: 100%;
	}
	img{
		width: 100%;
	}
</style>

具名插槽

当有多个插槽的时候,就需要给插槽指定名称了,不同的内容可以根据插槽的名称,指定放到哪个插槽内。
Catagory.vue中的模板部分,给slot添加了name。

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
		<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
		<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
	</div>
</template>

App.vue中的模板部分,指明填充到哪个插槽中,使用<template>标签的时候,有一种简写方法(不过挺鸡肋,只能用在<template>标签上)。

<template>
	<div class="container">
		<Category title="美食">
			<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
			<a slot="footer" href="http://www.atguigu.com">更多美食</a>
		</Category>
		<Category title="游戏">
			<ul slot="center">
				<li v-for="(g,index) in games" :key="index">{{g}}</li>
			</ul>
			<div class="foot" slot="footer">
				<a href="http://www.atguigu.com">单机游戏</a>
				<a href="http://www.atguigu.com">网络游戏</a>
			</div>
		</Category>
		<Category title="电影">
			<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
			<template v-slot:footer>
				<div class="foot">
					<a href="http://www.atguigu.com">经典</a>
					<a href="http://www.atguigu.com">热门</a>
					<a href="http://www.atguigu.com">推荐</a>
				</div>
				<h4>欢迎前来观影</h4>
			</template>
		</Category>
	</div>
</template>

作用域插槽

应用场景:数据在插槽中(Category.vue中)定义,使用插槽的结构是由插槽使用者(App.vue)决定的,但是使用者中是没有数据的,需要插槽组件(Category.vue)将数据传给插槽的使用者(App.vue)。
Category.vue

<template>
	<div class="category">
		<h3>{{title}}分类</h3>
		<!--通过games参数将data中的games数据传给插槽的使用者-->
		<slot :games="games" msg="hello">我是默认的一些内容</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;
	}
	video{
		width: 100%;
	}
	img{
		width: 100%;
	}
</style>

App.vue

<template>
	<div class="container">
		<Category title="游戏">
			<!--必须写template标签-->
			<!--使用atguigu变量接收Category.vue组件中slot传过来的对象-->
			<template scope="atguigu">
				<ul>
					<li v-for="(g,index) in atguigu.games" :key="index">{{g}}</li>
				</ul>
				<!--slot可以传递多个值,这里传递了games和msg-->
				<h4>{{atguigu.msg}}</h4>
			</template>
		</Category>
		<Category title="游戏">
			<!--ES6解构赋值语法-->
			<template scope="{games}">
				<ol>
					<li style="color:red" v-for="(g,index) in games" :key="index">{{g}}</li>
				</ol>
			</template>
		</Category>
		<Category title="游戏">
			<!--slot-scope是新的写法,和scope效果一样-->
			<template slot-scope="{games}">
				<h4 v-for="(g,index) in games" :key="index">{{g}}</h4>
			</template>
		</Category>
	</div>
</template>
<script>
	import Category from './components/Category'
	export default {
		name:'App',
		components:{Category}
	}
</script>
<style scoped>
	.container,.foot{
		display: flex;
		justify-content: space-around;
	}
	h4{
		text-align: center;
	}
</style>

App.vue拿到了Category.vue中的数据,可以根据需要自定义显示效果。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值