在使用jQuery的时候,前后端交互通常使用$.ajax,但是会存在跨域问题。在jQuery中,有大量操作DOM的方法,但是在Vue中,我们不会主动操作DOM,所以配合Vue使用的时候,大多使用axios,使用npm i axios
进行安装。
首先使用命令node server1.js
和node 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。
这个案例,把页面分成两个组件:搜索组件和列表组件。通过变量控制请求中不同时刻显示的内容不同。
当首次进入页面的时候,显示“欢迎词”,点击搜索发送请求后,显示“加载中”,请求收到后,显示具体信息,如果请求报错了,显示错误信息。这就要在搜索组件搜索方法中控制遍历的值,来动态切换列表组件的展示状态,因此这里用到了组件间传值,这里使用全局事件总线的写法。
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 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的数据
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>
使用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技术,在父组件中定义好结构,传给子组件,子组件通过插槽接收父组件传过来的标签和数据,并放在插槽的位置上。插槽内容是由父组件编译后传给子组件的。
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">我是一些默认值,当使用者没有传递具体结构时,我会出现1slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2slot>
div>
template>
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="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>
<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 scope="atguigu">
<ul>
<li v-for="(g,index) in atguigu.games" :key="index">{{g}}li>
ul>
<h4>{{atguigu.msg}}h4>
template>
Category>
<Category title="游戏">
<template scope="{games}">
<ol>
<li style="color:red" v-for="(g,index) in games" :key="index">{{g}}li>
ol>
template>
Category>
<Category title="游戏">
<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中的数据,可以根据需要自定义显示效果。