# Vue 中的 Ajax

# 1. vue 脚手架配置代理

web app 发送的请求通常都是 POST + JSON,解决跨域通常只有两种方式:

  1. 让目标服务器允许跨域访问,CORS(Cross-origin resource sharing,跨域资源共享)
  2. 绕过同源策略
    • 使用 Nginx 的反向代理
    • 将前端项目用后端项目包起来,在后端配置请求转发

# 1.1. 方法一

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

devServer:{
  proxy:"http://localhost:5000"
}

说明:

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

# 1.2. 方法二

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

module.exports = {
	devServer: {
      proxy: {
      '/api1': {// 匹配所有以 '/api1'开头的请求路径
        target: 'http://localhost:5000',// 代理目标的基础路径
        changeOrigin: true,
        pathRewrite: {'^/api1': ''}
      },
      '/api2': {// 匹配所有以 '/api2'开头的请求路径
        target: 'http://localhost:5001',// 代理目标的基础路径
        changeOrigin: true,
        pathRewrite: {'^/api2': ''}
      }
    }
  }
}
/*
   changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
   changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
   changeOrigin默认值为true
*/

说明:

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
  2. 缺点:配置略微繁琐,请求资源时必须加前缀。

# 2. Github 用户搜索案例

App.vue:

<template>
  <div class="box">
    <Search />
    <List />
  </div>
</template>

Search.vue:

<template>
  <div class="search">
    search github users:
    <input type="text" v-model="keyword" @keypress.enter="doSearch">
  </div>
</template>
<script>
doSearch() {
  this.$bus.$emit('getUsers', { users: [], errorMsg: '', isLoading: true });

  axios.get(`https://api.github.com/search/users?q=${this.keyword}`)
    .then((response) => {
      const { items: users } = response.data;
      this.$bus.$emit('getUsers', { users, errorMsg: '', isLoading: false });
    })
    .catch((e) => {
      this.$bus.$emit('getUsers', { users: [], errorMsg: e.message, isLoading: false });
    });
},
</script>

List.vue:

<template>
  <div class="list">
    <div v-show="model.users.length > 0"
      v-for="user in model.users" :key="user.login" class="item"
    >
      <a :href="user.html_url" target="_blank"><img :src="user.avatar_url" alt=""></a>
      <span>{{ user.login }}</span>
    </div>

    <!-- first -->
    <div v-show="model.isFirst"><h2>欢迎使用</h2></div>

    <!-- loading -->
    <div v-show="model.isLoading"><img src="https://www.icegif.com/wp-content/uploads/loading-icegif-1.gif" alt=""></div>

    <!-- error -->
    <div v-show="model.errorMsg"><h2>{{ model.errorMsg }}</h2></div>
  </div>
</template>
<script>
  mounted() {
    this.$bus.$on('getUsers', (data = { users: [], errorMsg: '', isLoading: false }) => {
      this.model = {
        ...this.model,
        ...data,
        isFirst: false,
      };
    });
  },

  data() {
    return {
      model: {
        isFirst: true,
        isLoading: false,
        errorMsg: '',
        users: [],
      },
    };
  },
</script>

# 3. vue 项目中常用的 2 个 Ajax 库

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

  • vue-resource: vue 插件,vue1.x 使用广泛,官方已不维护,安装后 vue 原型上会多一个 $http 属性

# 4. 插槽

作用:让父组件可以向子组件指定位置插入 HTML 结构,也是一种组件间通信的方式,适用于父组件 ===> 子组件。

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

# 4.1. 默认插槽

将所有未分发的元素放入默认插槽

TestDefaultSlot.vue:

<template>
  <div class="test-default-slot">
    <div class="header">header</div>

    <div class="body">
      <slot>默认内容</slot>
    </div>

    <div class="footer">footer</div>
  </div>
</template>

App.vue:

<template>
  <!-- 没有标签体,则使用插槽的默认内容 -->
  <TestDefaultSlot />

  <!-- 使用自定义的内容,插入默认的插槽 -->
  <TestDefaultSlot>自定义内容</TestDefaultSlot>
</template>

注意:

  • 标签体的内容,既可以使用 App.vue 的局部样式,也可以使用 TestDefaultSlot.vue 的局部样式

# 4.2. 具名插槽

将多个元素插入不同的位置

TestNamedSlot.vue:

<template>
  <div class="test-box">
    <div class="header">header</div>

    <div class="body">
      <slot name="body">body</slot>
    </div>

    <div class="footer">
      <slot name="footer">footer</slot>
    </div>
  </div>
</template>

App.vue:

<!-- 基本使用 -->
<TestNamedSlot>
  <div slot="body">111</div>
  <div slot="footer">222</div>
</TestNamedSlot>

<!-- 挨个放入 body 插槽中 -->
<TestNamedSlot>
  <div slot="body">333</div>
  <div slot="body">444</div>
</TestNamedSlot>

<!-- 一个插槽,放入多个元素 -->
<TestNamedSlot>
  <template slot="body">
    <div>555</div>
    <div>666</div>
  </template>
</TestNamedSlot>

<!-- 一个插槽,放入多个元素,最新写法(只能用在 template 上) -->
<TestNamedSlot>
  <template v-slot:body>
    <div>777</div>
    <div>888</div>
  </template>
</TestNamedSlot>

<!-- # 为 v-slot 的简写 -->
<TestNamedSlot>
  <template #body>
    <div>999</div>
    <div>000</div>
  </template>
</TestNamedSlot>

# 4.3. 作用域插槽

数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。定义插槽时,暴露一些数据给使用者

TestScopeSlot.vue:

<template>
  <div class="test-box">
    <slot person-name="张三" :hobby="hobby">默认内容</slot>
  </div>
</template>
<script>
export default {
  data() {
    return { hobby: ['看书', '看电影'] };
  },
};
</script>

App.vue

<!-- scope 属性已被废弃 -->
<TestScopeSlot>
  <template scope="scope">
    <div>{{ scope.personName }} - {{ scope.hobby }}</div>
  </template>
</TestScopeSlot>

<!-- 解构 -->
<TestScopeSlot>
  <template slot-scope="{ personName, hobby }">
    <div>{{ personName }} - {{ hobby }}</div>
  </template>
</TestScopeSlot>

<!-- 新写法 -->
<TestScopeSlot>
  <template v-slot:default="{ personName, hobby }">
    <div>{{ personName }} - {{ hobby }}</div>
  </template>
</TestScopeSlot>
<TestScopeSlot>
  <template #default="{ personName, hobby }">
    <div>{{ personName }} - {{ hobby }}</div>
  </template>
</TestScopeSlot>
本章目录