解决开发环境Ajax跨域问题(使用代理服务器)、github 用户搜索案例、vue-resource、插槽(默认插槽、具名插槽、作用域插槽)——Vue中的Ajax

文章详细介绍了在Vue开发环境中如何通过配置Vue.config.js设置代理服务器解决Ajax跨域问题。方法包括将所有未知请求代理到指定服务器以及定义具体的代理规则,以适应不同的API请求路径。此外,还提到了Vue项目的静态资源管理和常用的Ajax库axios与vue-resource的使用。
摘要由CSDN通过智能技术生成
  1. 解决开发环境Ajax跨域问题(使用代理服务器)

通过Vue脚手架配置代理

本案例需要下载axios库npm install axios

配置参考文档 Vue-Cli devServer.proxy

module.exports = {
  devServer: {
    proxy: 'http://localhost:4000'   //端口号为被请求的服务器端口号
  }
}

vue.config.js 是一个可选的配置文件,和 package.json 同级的

利用服务器之间访问不会有跨域,在服务器与浏览器端中间开启一个代理服务器,代理服务器端口号和项目浏览器端口号一样

1.1 配置代理服务器的方法一

在vue.config.js中添加如下配置:

Server.proxy 可以是一个指向开发环境 API 服务器的字符串,这会告诉开发服务器将任何未知请求 (没有匹配到静态文件的请求) 代理到http://localhost:5000

devServer:{
    proxy:"http://localhost:5000"   //5000为被请求的服务器端口号
}

说明:

  1. 优点配置简单,请求资源时直接发给前端(8080)即可。

  1. 缺点不能配置多个代理,不能灵活的控制请求是否走代理

代理服务器不是把所有的请求都转发给被请求的服务器,当请求的资源,代理服务器自身就有时,就不会转发请求,脚手架中的public文件夹就相当于代理服务器的根路径,也就是代理服务器里面有什么内容,就看public文件夹中有什么

  1. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端静态资源

App.vue

<template>
    <div>
        <button @click="getStudent">获取学生信息</button>
      
    </div>
</template>

<script>
    import axios from 'axios'
    export default {
        name: 'App',
        methods: {
            getStudent() {
                axios.get('http://localhost:8084/students').then(
                    response => {
                        console.log('请求成功了',response.data)
                    },
                    error => {
                        console.log('请求失败了', error.message)
                    }
                )
            }
        },
    }
</script>

1.2 配置代理服务器的方法二

编写vue.config.js配置具体代理规则:

module.exports ={
    devServer:{
        proxy:{
            '/api1':{// 匹配所有以 '/api1'开头的请求路径
                target:'http://localhost:5000',// 代理目标的基础路径
                pathRewrite:{'^/api1':''}// 代理往后端服务器的请求去掉 /api1 前缀
                changeOrigin:true, //用于控制请求头中的host值
                ws:true,//用于支持websocket,默认值为true
             },
             '/demo':{// 匹配所有以 '/api2'开头的请求路径
                target:'http://localhost:5001',// 代理目标的基础路径
                pathRewrite:{'^/demo':''},
                changeOrigin:true,
             }
        }
    }
}
/*
   changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
   changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
   changeOrigin默认值为true
*/

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理

  1. 缺点:配置略微繁琐,请求资源时必须加前缀。

src/App.vue

<template>
    <div>
        <button @click="getStudents">获取学生信息</button>
        <button @click="getCars">获取汽车信息</button>
      
    </div>
</template>

<script>
    import axios from 'axios'
    export default {
        name: 'App',
        methods: {
            getStudents() {
                axios.get('http://localhost:8086/api/students').then(
                    response => {
                        console.log('请求成功了',response.data)
                    },
                    error => {
                        console.log('请求失败了', error.message)
                    }
                )
            },
            getCars() {
                axios.get('http://localhost:8086/demo/cars').then(
                    response => {
                        console.log('请求成功了',response.data)
                    },
                    error => {
                        console.log('请求失败了', error.message)
                    }
                )
            }
        },
    }
</script>
  1. github 用户搜索案例

对于静态页面的中第三方成型的样式放在一个公共的位置(有两种做法):

  1. 在src文件夹下建立 assets文件夹(放置静态资源),在该文件夹下配置css文件夹,然后把第三方成型的样式文件放进去,还需要对其进行引入,可以在main.js中引入(不太推荐),也可以在App.vue中引入,如下:

<script>
    import './assets/css/bootstrap.css'
    export default {
        name: 'App',

    }
</script>

但此时会报错,因为在该案例的第三方样式中引入了字体样式'../fonts/glyphicons-halflings-regular.eot',但在我们的项目文件夹中并未此创建字体样式,所以会报错。其实本案例并不需要该字体,所以不推荐在assets文件夹中配置,因为写在assets问价夹中,就需要用import 引入,那就有严格的检查 。(bootstrap.css用到了不存在的资源,会报错)

  1. public文件夹中建立 css文件夹,然后在页面 index.html中进行引入

    <!-- 开启移动端的理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- 配置页签图标 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 引入第三方样式 -->
    <link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">

本案例分为 UserSearch组件 和 UserList组件

接口地址:https://api.github.com/search/users?q=xxx

完善案例:对于 UserList组件应该做到:

  1. 页面刚加载出来显示 welcome

  1. 页面在搜索的时候,显示 loading

  1. 搜索完毕后,显示用户头像及用户名

  1. 请求错误时,展示error

想实现如上功能,除了配置 users数据外,还应增添三个数据:

        data() {
            return {
                isFirst: true,
                isLoading: false,
                errMsg: '',
                users: []
            }
        }

案例代码:

App.vue

<template>
    <div class="container">
        <UserSearch/>
        <UserList/>
    </div>
</template>

<script>
    import UserSearch from './components/UserSearch'
    import UserList from './components/UserList'
    export default {
        name: 'App',
        components: {
            UserSearch,
            UserList
        }

    }
</script>

UserSearch.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: 'UserSearch',
        data() {
            return {
                keyword: ''
            }
        },
        methods: {
            searchUsers() {
                // 请求前更新List 数据
                this.$bus.$emit('updateListData',{isFirst: false,isLoading: true, errMsg:'', users:[]})
                axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
                    response => {
                        console.log('请求成功了',);
                        // 请求成功后更新List 数据
                        // this.$bus.$emit('updateListData',response.data.items)
                        this.$bus.$emit('updateListData',{isLoading: false,errMsg:'',users:response.data.items})
                    },
                    error => {
                        // 请求失败后更新List 数据
                        console.log('请求失败了', error.message);
                        this.$bus.$emit('updateListData',{isLoading: false, errMsg:error.message, users:[]})
                    }
                )
            }
        },
    }
</script>

UserList.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">Welcome!</h1>
        <!-- 展示加载中 -->
        <h1 v-show="info.isLoading">Loading</h1>
        <!-- 展示错误信息 -->
        <h1 v-show="info.errMsg">{{info.errMsg}}</h1>
    </div>  
</template>

<script>
    export default {
        name: 'UserList',
        data() {
            return {
                info: {
                    isFirst: true,
                    isLoading: false,
                    errMsg: '',
                    users: []
                }
            }
        },
        mounted() {
            this.$bus.$on('updateListData', (dataObj)=>{
                console.log('我是list组件,收到了数据', dataObj)
                // this.isFirst = isFirst
                // this.isLoading = isLoading
                // this.errMsg = errMsg
                // this.users = users
                // this.info = dataObj
                this.info = {...this.info, ...dataObj}
            })
        }
    }
</script>

<style scoped>
    .album {
        min-height: 50rem; /* Can be removed; just added for demo purposes */
        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>

3. vue项目中常用的 2个Ajax库(vue-resource)

  1. axios

通用的 Ajax 请求库, 官方推荐,使用广泛

  1. vue-resource

vue插件库,vue1.x使用广发,官方已不维护。vue-resource是对 xhr的封装。

使用:

  1. 安装插件库 npm i vue-resource

  1. 引入插件,使用插件 Vue.use(xxx)

main.js:

// 引入Vue
import Vue from 'vue'
// 引入 App
import App from './App'
// 引入插件
import vueResource from 'vue-resource'
// 关闭Vue的生产提示
Vue.config.productionTip = false
// 使用插件
Vue.use(vueResource)
new Vue({
    el: '#root',
    render: h => h(App),
    // 安装全局事件总线
    beforeCreate() {
        Vue.prototype.$bus = this
    }

})
  1. 然后在UserSearch组件库中 用this.$http 换掉 axios即可

使用插件后,vc上会多一个属性 $http,vue-resource插件库所提供的 $http 和 axios身上的Api都一模一样,所以this.$http替换axios即可

使用插件 Vue.use(xxx)

4. slot插槽(默认插槽、具名插槽、作用域插槽)

插槽是什么? 插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。简单理解就是子组件中留下个“坑”,父组件可以使用指定内容来补“坑”。

1.作用:让父组件可以向子组件指定位置插入 html结构,也是一种组件间通信的方式

适用于 父组件 ==> 子组件

2. 分类: 默认插槽、具名插槽、作用域插槽

3. 使用方式:

  1. 默认插槽:

父组件中:
    <MyCatagory>
        <div>html结构</div>
    </MyCatagory>
子组件中:
    <template>
      <div>
        <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
        <slot>插槽默认内容...我是一些默认值,当使用者没有传递具体的值时使用</slot>
      </div>
    </template>

注意:如果B组件的 template 中没有包含一个 <slot> 元素,即不使用插槽,则该组件起始标签和结束标签之间的任何内容都会被抛弃。

  1. 具名插槽

所谓具名插槽,顾名思义就是起了名字的插槽。有时我们需要多个插槽,例如当我们想使用某种通用模板:

对于这样的情况,<slot> 元素有一个特殊的 属性name

一个不带 name 的 <slot> 出口会带有隐含的名字“default”。

在向具名插槽提供内容的时候,我们可以在标签上使用 slot属性 即slot="name属性值",同时也可以使用slot 指令,并以 slot 的参数的形式提供其名称即: v-slot: xxx形式,但此形式只能对 template标签使用

父组件中:
    <MyCatagory>
        <template slot="center">
            <div>html结构</div>
        </template>

        <template v-slot:footer>
            <div>html结构</div>
        </template>
    </MyCatagory>
子组件中:
    <template>
      <div>
        <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
        <slot name="center">插槽默认内容...当使用者没有传递具体的值时使用</slot>
        <slot name="footer">插槽默认内容...当使用者没有传递具体的值时使用</slot>
      </div>
    </template>

Tips:这种方式在项目中比较常用,可以当成一个复用(通用)模板组件。如多个组件的布局使用相似模板,只是具体内容不同,那么我们可以使用这种插槽方式封装成一个通用组件,在其他组件使用的时候只需要传对应的内容到对应名字的插槽即可,不需要将该模板在每个组件重新写一遍,减少代码冗余,大大提高开发效率。

  1. 作用域插槽

scope用于父组件往子组件插槽放的html结构接收子组件的数据,也可使用 slot-scope

为了让 子组件中的数据obj在父级的插槽内容中可用,我们可以将 obj作为 <slot> 元素的一个 属性 绑定上去

  1. 理解:数据在组件自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在MyCatagroy组件中,但使用数据所遍历的结构由App决定)

  1. 具体编码:

父组件中:
    <MyCatagory>
        <template scope="scopeData">
            <!-- 生成的是ul列表-->
            <ul>
                <li v-for="(item,index) in scopeData.games" :key="index">{{item}}</li>
            </ul>
        </template>
    </MyCatagory>

    <MyCatagory>

        <template slot-scope="{games}">  <!-- {games}是进行解构赋值-->
            <!-- 生成的是h4 标题-->
            <h4 v-for="(item,index) in games" :key="index">{{item}}</h4>
        </template>
    </MyCatagory>
子组件中:
    <template>
      <div>
        <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
        <slot :games="games">插槽默认内容...当使用者没有传递具体的值时使用</slot>
      </div>
    </template>

     <script>
        export default {
            name: 'MyCatagory',
            props: ['title'],
            //数据在子组件自身
            data() {
                return { 
                    games: ['穿越火线','超级玛丽','wutuan','警戒'],
                }
            }
        }
    </script>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值