尚硅谷 Vue2.0 + Vue3.0 入门到精通教程学习笔记 (四)

第四章:Vue 中的 ajax

4.1  解决开发环境 Ajax 跨域问题——Vue 脚手架配置代理

本案例采用 axios 作为发送 Ajax 请求的库,因此首先需要下载并引入 axios :

在 VSCode 终端处输入命令:npm i axios

第二步,利用脚手架 vue-cli 开启代理服务器 :

  • vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,  //关闭语法检查
  // 开启代理服务器(方式一)
  // devServer: {
  //   proxy: 'http://localhost:5000'
  // },
   // 开启代理服务器(方式二)
  devServer: {
    proxy: {
      '/atguigu': {
        target: 'http://localhost:5000',
        pathRewrite: {'^/atguigu':''},
        // ws: true,// 用于支持websocket
        // changeOrigin: true //用于控制请求头中的host值
      },
      // '/foo': {
      //   target: '<other_url>'
      // },
      '/demo': {
        target: 'http://localhost:5001',
        pathRewrite: {'^/demo':''},
        // ws: true,// 用于支持websocket
        // changeOrigin: true //用于控制请求头中的host值
      },
    }
  }
})
  • 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:8080/atguigu/students").then(
        (response) => {
          console.log("请求成功了", response.data)
        },
        (error) => {
          console.log("请求失败了", error.message)
        }
      )
    },
    getCars() {
      axios.get("http://localhost:8080/demo/cars").then(
        (response) => {
          console.log("请求成功了", response.data)
        },
        (error) => {
          console.log("请求失败了", error.message)
        }
      )
    },
  },
}
</script>

效果:

总结:

vue 脚手架配置代理

● 方法一

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

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

说明:

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

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

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

● 方法二

   编写 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
*/

说明:

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

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

4.2  GitHub 用户搜索案例

  • public/css/index.html
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
    <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">
    <!-- 引入第三方样式 -->
    <!-- <link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css"> -->
    <!-- 配置网页标题 -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- 当浏览器不支持js时noscript中的元素就会被渲染 -->
    <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>
    <!-- built files will be auto injected -->
  </body>
</html>
  •  src/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
    }
})
  • src/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>
  • src/components/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) => {
      this.info = { ...this.info, ...dataObj }
      // console.log("我是List组件,收到数据:", users)
      // this.isFirst = isFirst
      // this.isLoading = isLoading
      // this.errMsg = errMsg
      // this.users = users
    })
  },
}
</script>

<style>
.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: 0.75rem;
  margin-bottom: 2rem;
  border: 1px solid #efefef;
  text-align: center;
}

.card > img {
  margin-bottom: 0.75rem;
  border-radius: 100px;
}

.card-text {
  font-size: 85%;
}
</style>
  • src/components/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", {
        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", {
            isLoading: false,
            errMsg: "",
            users: response.data.items,
          })
        },
        (error) => {
          console.log("请求失败了", error.message)
          // 请求失败后更新List的数据
          this.$bus.$emit("updateListData", {
            isLoading: false,
            errMsg: error.message,
            users: [],
          })
        }
      )
    },
  },
}
</script>

效果:

 

4.3 vue-resource

在 VSCode 终端处输入命令:npm i vue-resource 下载安装  vue-resource 作为发送 Ajax 请求的库。

  • src/main.js
// 引入 vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
//引入插件
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
    }
})
  • src/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>
  • src/components/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) => {
      this.info = { ...this.info, ...dataObj }
      // console.log("我是List组件,收到数据:", users)
      // this.isFirst = isFirst
      // this.isLoading = isLoading
      // this.errMsg = errMsg
      // this.users = users
    })
  },
}
</script>

<style>
.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: 0.75rem;
  margin-bottom: 2rem;
  border: 1px solid #efefef;
  text-align: center;
}

.card > img {
  margin-bottom: 0.75rem;
  border-radius: 100px;
}

.card-text {
  font-size: 85%;
}
</style>
  • src/components/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>
export default {
  name: "Search",
  data() {
    return {
      keyWord: "",
    }
  },
  methods: {
    searchUsers() {
      // 请求前更新List的数据
      this.$bus.$emit("updateListData", {
        isFirst: false,
        isLoading: true,
        errMsg: "",
        users: [],
      })
      this.$http
        .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) => {
            console.log("请求失败了", error.message)
            // 请求失败后更新List的数据
            this.$bus.$emit("updateListData", {
              isLoading: false,
              errMsg: error.message,
              users: [],
            })
          }
        )
    },
  },
}
</script>

总结:

Vue 项目中常用的 2 个 Ajax 库:

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

2. vue-resource:vue 插件库, vue1.x 使用广泛,官方已不维护。

4.4 slot 插槽 

4.4.1 默认插槽

  •  src/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>
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video {
  width: 100%;
}
</style>
  • src/components/Category.vue
<template>
  <div class="category">
    <h3>{
   
   { title }}分类</h3>
    <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
    <slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  props: ["title"],
}
</script>

<style>
.category {
  background-color: skyblue;
  width: 200px;
  height: 300px;
}
h3 {
  text-align: center;
  background-color: orange;
}
</style>

效果:

4.4.2  具名插槽

  •  src/App.vue
<template>
  <div class="container">
    <Category title="美食">
      <img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" />
      <a class="foot" 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后还有一种写法 -->
      <!-- 写法① -->
      <!-- <template 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> -->
      <!-- <h4>欢迎前来观影</h4> -->
      <!-- </div> -->
      <!-- </template> -->
      <!-- 新的写法②  只适用于含template-->
      <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>

<script>
import Category from "./components/Category"
export default {
  name: "App",
  components: { Category },
  data() {
    return {
      foods: ["火锅", "烧烤", "小龙虾", "牛排"],
      games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
      films: ["《教父》", "《拆弹专家》", "《你好, 李焕英》", "《尚硅谷》"],
    }
  },
}
</script>

<style>
.container,
.foot {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video {
  width: 100%;
}
h4 {
  text-align: center;
}
</style>
  •  src/components/Category.vue
<template>
  <div class="category">
    <h3>{
   
   { title }}分类</h3>
    <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
    <slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
    <slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  props: ["title"],
}
</script>

<style>
.category {
  background-color: skyblue;
  width: 200px;
  height: 300px;
}
h3 {
  text-align: center;
  background-color: orange;
}
</style>

 效果:

4.4.3  作用域插槽

  •  src/App.vue
<template>
  <div class="container">
    <Category title="游戏">
      <template slot-scope="{ games }">
        <ul>
          <li v-for="(g, index) in games" :key="index">{
   
   { g }}</li>
        </ul>
      </template>
    </Category>

    <Category title="游戏">
      <template slot-scope="{ games }">
        <ol>
          <li 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>
.container,
.foot {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video {
  width: 100%;
}
h4 {
  text-align: center;
}
</style>
  •  src/components/Category.vue
<template>
  <div class="category">
    <h3>{
   
   { title }}分类</h3>
    <!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
    <slot :games="games"
      >我是一些默认值,当使用者没有传递具体结构时,我会出现</slot
    >
  </div>
</template>

<script>
export default {
  name: "Category",
  props: ["title"],
  data() {
    return {
      games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
    }
  },
}
</script>

<style>
.category {
  background-color: skyblue;
  width: 200px;
  height: 300px;
}
h3 {
  text-align: center;
  background-color: orange;
}
</style>

 效果:

总结:

插槽:

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

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

3. 使用方式:

    ●  默认插槽:

父组件中:
      <Category>
           <div>html结构1</div>
      </Category>
子组件中:
<template>
      <div>
         <!-- 定义插槽 -->
            <slot>插槽默认内容...</slot>
      </div>
</template>

    ●  具名插槽:

父组件中:
        <Category>
          <template slot="center">
             <div>html结构1</div>
          </template>
      
          <template v-slot:footer>
             <div>html结构2</div>
          </template>
        </Category>
子组件中:
        <template>
          <div>
             <!-- 定义插槽 -->
               <slot name="center">插槽默认内容...</slot>
                <slot name="footer">插槽默认内容...</slot>
          </div>
        </template>

    ●  作用域插槽:

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

        ▲ 具体编码:

父组件中:
         <Category>
         	<template scope="scopeData">
         		<!-- 生成的是ul列表 -->
         		<ul>
         			<li v-for="g in scopeData.games" :key="g">{
   
   {g}}</li>
         		</ul>
         	</template>
         </Category>
         
         <Category>
         	<template slot-scope="scopeData">
         		<!-- 生成的是h4标题 -->
         		<h4 v-for="g in scopeData.games" :key="g">{
   
   {g}}</h4>
         	</template>
         </Category>

子组件中:
         <template>
           <div>
             <slot :games="games"></slot>
           </div>
         </template>
         		
         <script>
            export default {
               name:'Category',
                  props:['title'],
            //数据在子组件自身
               data() {
                  return {
                    games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                  }
               },
             }
         </script>

★★★ 欢迎批评指正!!!

参考:http://t.csdn.cn/HDN6n

猜你喜欢

转载自blog.csdn.net/weixin_44566194/article/details/128428784