113.【Vue-细刷-04】

news2024/11/15 16:25:02

Vue-03

  • (二十四)、浏览器存储(WebStorage)
    • 1.本地缓存(LocalStorage)
        • (1). 模仿本地缓存-未用JSON转字符串
        • (2).模拟本地缓存-使用JSON转字符串
    • 2.会话缓存(Session Storage)
        • (1).模拟会话缓存
        • (2).会话缓存和本地缓存的区别
        • (3).JSON转换与JSON解析
    • 3.todos案列_本地缓存版
        • (1).mounted和watch的区别
        • (2).本地缓存版本
    • 4.todos案列_深度监视
        • (1).发现问题
        • (2).原理探究
        • (3).深度监视和浅度监视
  • (二十五)、自定义事件 (父子组件)
    • 1.儿子传递数据给父亲 (原始)
    • 2.儿子传递数据给父亲 (自定义事件 $emit) ⭐
    • 3.儿子传递数据给父亲 (ref 自定义事件实现)
    • 4.todoList更换为自定义事件
  • (二十六)、全局事件总线 (兄弟组件)
    • 1.全局事件分析
    • 2.全局事件 $bus
        • (1).Vue原型数据被所有人共享
        • (2).在Vue原型上存放Vue的实列
    • 3.全局事件总线 (Demo1接受Demo2的数据)
    • 4.全局事件总线(双向发送数据)
  • (二十六)、插槽 (slot)
    • 1.问题探究
    • 2.默认插槽
    • 3.命名插槽
  • (二十七)、Vue中的ajax
    • 1.Vue项目中常用的2个ajax库
        • (1).vue-resource
        • (2).axios
    • 2.解决开发环境Ajax跨域问题
    • 3.Github搜索案列_axios_全局总线
        • (1).静态资源
        • (2).静态资源设置成组件
        • (3).github搜索案列_发送请求
        • (4).github搜索案列完成
    • 4.Github搜索案列_订阅与发布_axios
    • 5.Github搜索案列_vue-resource

(二十四)、浏览器存储(WebStorage)

1.本地缓存(LocalStorage)

在我们没有登入唯品会之前的时候,我们搜索耳机和键盘。会在历史记录中给我们展示出:我们的搜索记录
前提是:没有开启无痕浏览的时候,才会显示我们的数据

在这里插入图片描述
查看浏览器缓存放到哪里了
在这里插入图片描述

(1). 模仿本地缓存-未用JSON转字符串

window API: 这里的key和value都只能是字符串

window.localStorage.setItem('name','jsxs');  // 左边是 key 右边是 value

1.存字符串可以

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>localStorage</title>
</head>
<body>
    <h1>localStorage</h1>
    <button id="btn1">保存数据</button>
    <script>
        const btn=document.getElementById('btn1');
        btn.onclick= ()=>{
             window.localStorage.setItem('name','jsxs');  // 左边是 key 右边是 value
        }
    </script>
</body>
</html>

在这里插入图片描述
假如我们关闭这个页面的话,那么我们的缓存并不会因为我们关闭浏览器而清除换粗,除非我们清除浏览器缓存的时候,才会消失
在这里插入图片描述
2.存数组: 会给我们展开

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>localStorage</title>
</head>

<body>
    <h1>localStorage</h1>
    <button id="btn1">保存数据</button>
    <script>
        const btn = document.getElementById('btn1');
        const arr = [1, 2, 3];
        btn.onclick = () => {
            window.localStorage.setItem('name', arr);  // 左边是 key 右边是 value
        }
    </script>
</body>

</html>

在这里插入图片描述
3.存对象:会出现错误

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>localStorage</title>
</head>

<body>
    <h1>localStorage</h1>
    <button id="btn1">保存数据</button>
    <script>
        const btn = document.getElementById('btn1');
        const arr = [1, 2, 3];
        const obj={age:'52',sex:'30'}
        btn.onclick = () => {
            window.localStorage.setItem('name', obj);  // 左边是 key 右边是 value
        }
    </script>
</body>

</html>

在这里插入图片描述

(2).模拟本地缓存-使用JSON转字符串

非字符串的数据尽量都转换为JSON数据、字符串的数据可以不用转换为JSON数据。

  1. 对象-转成JSON字符串之后就正常存储
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>localStorage</title>
</head>

<body>
    <h1>localStorage</h1>
    <button id="btn1">保存数据</button>
    <script>
        const btn = document.getElementById('btn1');
        const arr = [1, 2, 3];
        const obj={age:'52',sex:'30'}
        btn.onclick = () => {
            window.localStorage.setItem('name', JSON.stringify(obj));  // 左边是 key 右边是 value
        }
    </script>
</body>

</html>

在这里插入图片描述

  1. 数组-转JSON字符串之后也能正常存储
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>localStorage</title>
</head>

<body>
    <h1>localStorage</h1>
    <button id="btn1">保存数据</button>
    <script>
        const btn = document.getElementById('btn1');
        const arr = [1, 2, 3];
        const obj={age:'52',sex:'30'}
        btn.onclick = () => {
            window.localStorage.setItem('name', JSON.stringify(arr));  // 左边是 key 右边是 value
        }
    </script>
</body>

</html>

在这里插入图片描述

  1. 保存数据和浏览数据
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>localStorage</title>
</head>

<body>
    <h1>localStorage</h1>
    <button id="btn1">保存数据</button>
    <button id="btn2">浏览数据</button>
    <script>
        const btn = document.getElementById('btn1');
        const btn3 = document.getElementById('btn2');
        const person={name:'jsxs',age:'52',sex:'30'}
        btn.onclick = () => {
            window.localStorage.setItem('person', JSON.stringify(person));  // 左边是 key 右边是 value
        }
        btn3.onclick = () => {
           const result=window.localStorage.getItem('person');
           alert(result);
        }
    </script>
</body>

</html>

在这里插入图片描述

  1. 全部增删改
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>localStorage</title>
</head>

<body>
    <h1>localStorage</h1>
    <button id="btn1">保存数据</button>
    <button id="btn2">浏览数据</button>
    <button id="btn4">删除数据</button>
    <button id="btn5">清除数据</button>
    <script>
        const btn = document.getElementById('btn1');
        const btn3 = document.getElementById('btn2');
        const btn5 = document.getElementById('btn4');
        const btn6 = document.getElementById('btn5');
        const person = { name: 'jsxs', age: '52', sex: '30' }
        const person2 = { name: 'ckqn', age: '0', sex: '0' }
        // 添加存储
        btn.onclick = () => {
            window.localStorage.setItem('person', JSON.stringify(person));  // 左边是 key 右边是 value
            window.localStorage.setItem('person2', JSON.stringify(person2));  // 左边是 key 右边是 value
        }
        // 浏览存储
        btn3.onclick = () => {
            // 输出的是JSON字符串
            const result = window.localStorage.getItem('person');
            try {
                // JSON字符串转换为原生的
                alert(JSON.parse(result));
            } catch (error) { // 假如解析出错,那么清空该数据
                alert(error.message)
                window.localStorage.removeItem('person')
            }
        }
        // 移除存储
        btn5.onclick = () => {
            window.localStorage.removeItem('person');
        }
        // 清除存储
        btn6.onclick = () => {
            window.localStorage.clear();
        }
        
    </script>
</body>

</html>

在这里插入图片描述

  • localStorage的数据: 1.清除浏览器缓存记录 2.clear()清除数据。他不会随着页面的关闭而清除缓存,只有通过上面的两个方法才能清除。

这样做的好处就是: 用户访问的时候有一层缓存会访问的时候加快速度
在这里插入图片描述

2.会话缓存(Session Storage)

它会随着一个会话的开始而创建;随着会话的关闭而失效。也就是浏览器的开和关

在这里插入图片描述

(1).模拟会话缓存

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SessionStorage</title>
</head>

<body>
    <h1>SessionStorage</h1>
    <button id="btn1">保存数据</button>
    <button id="btn2">浏览数据</button>
    <button id="btn4">删除数据</button>
    <button id="btn5">清除数据</button>
    <script>
        const btn = document.getElementById('btn1');
        const btn3 = document.getElementById('btn2');
        const btn5 = document.getElementById('btn4');
        const btn6 = document.getElementById('btn5');
        const person = { name: 'jsxs', age: '52', sex: '30' }
        const person2 = { name: 'ckqn', age: '0', sex: '0' }
        // 添加存储
        btn.onclick = () => {
            window.sessionStorage.setItem('person', JSON.stringify(person));  // 左边是 key 右边是 value
            window.sessionStorage.setItem('person2', JSON.stringify(person2));  // 左边是 key 右边是 value
        }
        // 浏览存储
        btn3.onclick = () => {
            // 输出的是JSON字符串
            const result = window.sessionStorage.getItem('person');
            try {
                // JSON字符串转换为原生的
                alert(JSON.parse(result));
            } catch (error) { // 假如解析出错,那么清空该数据
                alert(error.message)
                window.sessionStorage.removeItem('person')
            }
        }
        // 移除存储
        btn5.onclick = () => {
            window.sessionStorage.removeItem('person');
        }
        // 清除存储
        btn6.onclick = () => {
            window.sessionStorage.clear();
        }
        
    </script>
</body>

</html>

在这里插入图片描述

(2).会话缓存和本地缓存的区别

  • 本地缓存不会随着浏览器的关闭而关闭会话缓存会随着浏览器的关闭而关闭
  • 本地缓存清除只有两种方式;会话缓存关闭浏览器即可。
  • 存储的内容一般支持 5~10MB(很大了)

(3).JSON转换与JSON解析

  • xxx->转换为JSON字符串: JSON.stringify(xxx)
  • JSON字符串解析为->原有的变量: JSON.parse(JSON.stringify(xxx))
  • 假如说在JSON在解析的时候,解析的数据为null,那么返回值是 null值,而不是字符串

3.todos案列_本地缓存版

(1).mounted和watch的区别

  • mounted: 挂载的意思: 就是和浏览器并发运行。第一次初始化页面的时候就会挂载上去。
  • wathc: 监视的意思:当监视的属性发生变化的时候,就会发生相应的变化。但不会在第一次初始化页面的时候监视到数据。我们只需要添加一个命令即可和mounted有一样的效果。
immediate: true,  // 若immediate为true则handle会在初始化时就会调用一次,以后就看firstName的改变了

(2).本地缓存版本

我们只修改App.vue这个组件就可以了。其余的不用修改.
App.vue
基本思路我们使用:watch进行监视挂载到浏览器上。然后和data区域的数据进行交互目的是为了浏览缓存的数据,假如说解析的时候为null我们就将数组设置为空数组。假如说不为空且数据格式有问题,我们就将其给重置掉数据并将数组继续重置为空数组。

<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list 
      :todosA="todos" 
      :updateFatherA="updateFather"
      :deleteFatherA="deleteFather"
      />
      <!-- 3.底部导航 -->
      <Footer 
      :FooterTodos="todos"
      :updateAllFatherA="updateAllFather"
      :clearAllDoneFatherA="clearAllDoneFather"
      />
    </div>
  </div>
  </div>
</template>

<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    const json_arr=localStorage.getItem('arr_Object'); // 这里我们直接得到的是json数组
    let parse_arr;   // 常量必须复制、let和var不需要
    try {// 有一种可能是用户修改了格式,出现错误。我们需要在这里进行捕获。
      // 把json数组转换为字符串。如果里面的数据为空也会被try到但是
      parse_arr=JSON.parse(json_arr) || [];  //当解析的数据JSON.parse(json_arr)为空的时候,会调用空数组。否则不调用空数组 

        
     
    } catch (error) {
      alert('本地缓存的数据异常-请查看您的数据格式是否正确!!!')
      localStorage.removeItem('arr_Object')  // 重置我们的数据
      parse_arr=[]
    }
    
    return {
      
      todos:parse_arr
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
/*    利用id
    updateFather(id,doneA){
      
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
*/
  // 更新
    updateFather(index,doneA){
      this.todos[index].done=doneA
    },
    // 删除
    deleteFather(index){
    // 根据坐标删除数据
    this.todos.splice(index,1)
  },
  // 全选或者不选
  updateAllFather(doneOption){
    // map():创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
      this.todos=this.todos.map((value)=>{  // 这个参数是遍历的单个对象
        return {...value,done:doneOption} // 返回将done属性改编为doneOption的完整对象
      })
  },
   // 清除已经已经勾选的
   clearAllDoneFather(){
    this.todos=this.todos.filter((value)=>{
      // 假如说done值为false就不用过滤-保留,否则就需要过滤-不保留
      return value.done===false
    })
   }
  },
  watch:{// 我们初步设想的是利用,mounted目的是一上来就挂载上去。但是webStorage数据不会随着新增能新增...
  immediate: true,  // 若immediate为true则handle会在初始化时就会调用一次,以后就看firstName的改变了
    todos(newValue,oldValue){  // 会接受到改变前的值和改变后的值
        localStorage.setItem('arr_Object',JSON.stringify(newValue)); // 这里一定要转换为JSON字符串
    }
  }
 
}
</script>

<style>

  /*base*/
  body {
    background: #fff;
  }

  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    text-align: center;
    vertical-align: middle;
    cursor: pointer;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    border-radius: 4px;
  }

  .btn-danger {
    color: #fff;
    background-color: #da4f49;
    border: 1px solid #bd362f;
  }

  .btn-danger:hover {
    color: #fff;
    background-color: #bd362f;
  }

  .btn:focus {
    outline: none;
  }

  .todo-container {
    width: 600px;
    margin: 0 auto;
  }
  .todo-container .todo-wrap {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
  }

</style>

在这里插入图片描述

4.todos案列_深度监视

(1).发现问题

我们发现我们在勾选单个任务的时候,vue的done值是随着我们勾选变化而变化的但是localSroage本地缓存的数据是不会变化的。原因不是我们代码的问题,而是vue监视的问题。

在这里插入图片描述

在这里插入图片描述

这里我们勾选最下面的全部任务,vue和loacalStorage才会全部变化。

在这里插入图片描述

(2).原理探究

  1. 监视问题

我们通常所说的监视有以下两种形式:

  1. Vue自带的监视:比如在我们没有手动添加watch的情况下,更改某一个数据,Vue会监视到并且给我们改变。Vue会自动给监视data中所有层次对象的属性
  2. Vue的watch手动写的监视。

Vue已经可以自动监视到数据了,为什么我们还要写watch呢?

  1. Vue默认能够监视到所有层次的属性,但Vue的监视它只做两件事情:“第一件事请: 帮我们更改数据”。“第二件事:帮我们更新模板,将页面更换为最新的” 。
  2. 我们自己写的watch监视,是因为Vue默认的监视完成不了我们所需要的业务逻辑,这就需要我们自己定义一个满足我们的业务需求的监视。

(3).深度监视和浅度监视

  • 浅度监视(默认): 只能监视到数据的增加和删除,监视不到数据某一个属性内部的变化
  • 深度监视:能监视到数据的增加和删除,也能监视的到数据某一个属性的变化。
  • 深度监视必须需要用到watch的完整写法且加上一个属性: deep:true。

只有完整的监视方法才能使用到属性。所以深度监视需要用到完整监视方法

  watch:{// 我们初步设想的是利用,mounted目的是一上来就挂载上去。但是webStorage数据不会随着新增能新增...
  todos:{
    immediate: true,  // 若immediate为true则handle会在初始化时就会调用一次,以后就看firstName的改变了
    deep: true, //开启深度监视
    handler(newValue,oldValue){  //假如data是方法旧值获取不到(旧址也是新值),假如data是对象就能获取到旧值
        localStorage.setItem('arr_Object',JSON.stringify(newValue)); // 这里一定要转换为JSON字符串
      }
    }
  }
<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header :addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list 
      :todosA="todos" 
      :updateFatherA="updateFather"
      :deleteFatherA="deleteFather"
      />
      <!-- 3.底部导航 -->
      <Footer 
      :FooterTodos="todos"
      :updateAllFatherA="updateAllFather"
      :clearAllDoneFatherA="clearAllDoneFather"
      />
    </div>
  </div>
  </div>
</template>

<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    // const json_arr=localStorage.getItem('arr_Object'); // 这里我们直接得到的是json数组
    // let parse_arr;   // 常量必须复制、let和var不需要
    // try {// 有一种可能是用户修改了格式,出现错误。我们需要在这里进行捕获。
    //   // 把json数组转换为字符串。如果里面的数据为空也会被try到但是
    //   parse_arr=JSON.parse(json_arr) || [];  //当解析的数据JSON.parse(json_arr)为空的时候,会调用空数组。否则不调用空数组 

        
     
    // } catch (error) {
    //   alert('本地缓存的数据异常-请查看您的数据格式是否正确!!!')
    //   localStorage.removeItem('arr_Object')  // 重置我们的数据
    //   parse_arr=[]
    // }
    
    return {
      
      todos:[]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
/*    利用id
    updateFather(id,doneA){
      
      // 利用map遍历数组todos,获取到的分别是数组对象和对象坐标
      this.todos=this.todos.map((value,index)=>{
          if(id===value.id){  // 假如说子类传递过来的值和map遍历的值一样,那么就返回 所有的属性+修改后的值
              return {...value,done:doneA}
          }else{
            return value
          }
      })
    }
*/
  // 更新
    updateFather(index,doneA){
      this.todos[index].done=doneA
    },
    // 删除
    deleteFather(index){
    // 根据坐标删除数据
    this.todos.splice(index,1)
  },
  // 全选或者不选
  updateAllFather(doneOption){
    // map():创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
      this.todos=this.todos.map((value)=>{  // 这个参数是遍历的单个对象
        return {...value,done:doneOption} // 返回将done属性改编为doneOption的完整对象
      })
  },
   // 清除已经已经勾选的
   clearAllDoneFather(){
    this.todos=this.todos.filter((value)=>{
      // 假如说done值为false就不用过滤-保留,否则就需要过滤-不保留
      return value.done===false
    })
   }
  },
  watch:{// 我们初步设想的是利用,mounted目的是一上来就挂载上去。但是webStorage数据不会随着新增能新增...
  todos:{
    immediate: true,  // 若immediate为true则handle会在初始化时就会调用一次,以后就看firstName的改变了
    deep: true, //开启深度监视
    handler(newValue,oldValue){  //假如data是方法旧值获取不到(旧址也是新值),假如data是对象就能获取到旧值
        localStorage.setItem('arr_Object',JSON.stringify(newValue)); // 这里一定要转换为JSON字符串
      }
    }
  }
 
}
</script>

<style>

  /*base*/
  body {
    background: #fff;
  }

  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    text-align: center;
    vertical-align: middle;
    cursor: pointer;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    border-radius: 4px;
  }

  .btn-danger {
    color: #fff;
    background-color: #da4f49;
    border: 1px solid #bd362f;
  }

  .btn-danger:hover {
    color: #fff;
    background-color: #bd362f;
  }

  .btn:focus {
    outline: none;
  }

  .todo-container {
    width: 600px;
    margin: 0 auto;
  }
  .todo-container .todo-wrap {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
  }

</style>

watch监视 默认开启的是浅度监视并不是深度监视
在这里插入图片描述

(二十五)、自定义事件 (父子组件)

1.A组件想让B组件给自己传递数据,那么就要给B组件绑定自定义事件。
2.自定义回调放在哪里,哪里就能够接收数据。非$emit()是自定义回调函数

1.儿子传递数据给父亲 (原始)

在这里插入图片描述
App.vue

<template>
  <div class="app">
    <h2>我是App组件</h2>
    <!-- 2. 父亲传递给儿子 -->
    <Demo :receive_son="receive_one"/>
    <h2>{{username}}</h2>
  </div>
</template>

<script>
import Demo from "./components/Demo.vue";
export default {
  name: "App",
  components: {
    Demo,
  },
  data() {
    return {
        username:'111'
    }
  },
  // TODO: 1.父亲创建一个接受儿子传递过来的方法
  methods: {
    receive_one(data_one){
        this.username=data_one;
    }
  }
};
</script>

<style>
.app{
    background-color: brown;
    padding: 10px;
}
</style>

Demo.vue 儿子

<template>
  <div class="demo">
    <h2 >我是Demo2组件</h2>
    <button @click="send_data">点我向父类传送数据</button>
  </div>
</template>

<script>
export default {
  name: "Demo",
  data() {
    return {
        name:'ckqn'
    }
  },
//  TODO: 1.儿子接受数据
    props:['receive_son'],
// TODO: 2.儿子创建方法传递给父亲
    methods:{
        send_data(){
            this.receive_son(this.name)
        }
    }
};
</script>

<style>
    .demo{
        background-color: antiquewhite;
    }
</style>

在这里插入图片描述

2.儿子传递数据给父亲 (自定义事件 $emit) ⭐

  • (适用于父子组件、不适用兄弟组件):

一般自定义事件都会放在组件上

1.给子组件自定义事件:自定义事件会放在子组件的vc上
<Demo @jsxs="test"/>

methods: {
    test(data_one){
        this.username = data_one
    }
  },
  

App.vue

<template>
  <div class="app">
    <h2>我是App组件</h2>
    <!-- 
        如下代码是给Demo的组件实列对象定义一个jsxs事件,只要Demo组件实列对象触发了jsxs事件,那么就调用test函数
        组件->一般添加的是自定义事件
     -->
    <Demo @jsxs="test"/>
    <h2>{{username}}</h2>
  </div>
</template>

<script>
import Demo from "./components/Demo.vue";
export default {
  name: "App",
  components: {
    Demo,
  },
  data() {
    return {
        username:'111'
    }
  },
  methods: {
    test(data_one){
        this.username = data_one
    }
  },
};
</script>

<style>
.app{
    background-color: brown;
    padding: 10px;
}
</style>

Demo.vue

2.子组件通过$emit向父亲传递数据 : 第一个参数是自定义事件名,后面的是参数无限制
    methods:{
        send_data(){
        // 当jsxs这个自定义的事件被触发的时候会像父App.vue传递一个数据
            this.$emit('jsxs',this.name) //把子类的参数传递给父类
        }
    }
<template>
  <div class="demo">
    <h2 >我是Demo2组件</h2>
    <button @click="send_data">点我</button>
  </div>
</template>

<script>
export default {
  name: "Demo",
  data() {
    return {
        name:'ckqn'
    }
  },
    methods:{
        send_data(){
            this.$emit('jsxs',this.name) //把子类的参数传递给父类
        }
    }
};
</script>

<style>
    .demo{
        background-color: antiquewhite;
    }
</style>

在这里插入图片描述

3.儿子传递数据给父亲 (ref 自定义事件实现)

1.给子组件设置ref。 在组件上设置ref就是获取的组件的实列对象
<Demo ref="demoA"/>
2.利用mounted进行挂载实现自定义
  mounted() {
    this.$refs.demoA.$on('jsxs',this.test)  //第一个参数是自定义自定义事件名和子组件emit对应,第二个是父App.vue绑定的函数
  },

App.vue

<template>
  <div class="app">
    <h2>我是App组件</h2>
    <!-- 
        如下代码是给Demo的组件实列对象定义一个jsxs事件,只要Demo组件实列对象触发了jsxs事件,那么就调用test函数
        组件->一般添加的是自定义事件
     -->
    <Demo ref="demoA"/>
    <h2>{{username}}</h2>
  </div>
</template>

<script>
import Demo from "./components/Demo.vue";
export default {
  name: "App",
  components: {
    Demo,
  },
  data() {
    return {
        username:'111'
    }
  },
  methods: {
    test(data_one){
        this.username = data_one
    }
  },
  mounted() {
    this.$refs.demoA.$on('jsxs',this.test)
  },
};
</script>

<style>
.app{
    background-color: brown;
    padding: 10px;
}
</style>

Demo.vue 不变

<template>
  <div class="demo">
    <h2 >我是Demo2组件</h2>
    <button @click="send_data">点我</button>
  </div>
</template>

<script>
export default {
  name: "Demo",
  data() {
    return {
        name:'ckqn'
    }
  },
    methods:{
        send_data(){
            this.$emit('jsxs',this.name) //把子类的参数传递给父类
        }
    }
};
</script>

<style>
    .demo{
        background-color: antiquewhite;
    }
</style>

在这里插入图片描述

4.todoList更换为自定义事件

App.vue

<!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header @addFatherA="addFather"/>
<template>
  <div>
   <div class="todo-container">
    <div class="todo-wrap">
      <!-- 1.头部 将父APP.VUE的addFather方法传递给子组件-->
      <Header @addFatherA="addFather"/>
      <!-- 2.列表 : 将父APP.VUE的todos数组传递给子组件-->
      <list 
      :todosA="todos" 
      :updateFatherA="updateFather"
      :deleteFatherA="deleteFather"
      />
      <!-- 3.底部导航 -->
      <Footer 
      :FooterTodos="todos"
      :updateAllFatherA="updateAllFather"
      :clearAllDoneFatherA="clearAllDoneFather"
      />
    </div>
  </div>
  </div>
</template>

<script>
  // 1.引入组件
  import Header from './components/Header.vue'
  import List from './components/List.vue'
  import Footer from './components/Footer.vue'
export default {
  name:'App', // 目的是在浏览器VUE插件中的名字都是App不会被改变。
  // 2.注册组件
  components:{
    Header,
    List,
    Footer
  },
  data() {
    return {
      todos:[]
    }
  },
  methods: {
    addFather(todoObj){
      // 这个方法是对todos这个数组的尾部追加对象todoObj
      this.todos.unshift(todoObj)
    },
  // 更新
    updateFather(index,doneA){
      this.todos[index].done=doneA
    },
    // 删除
    deleteFather(index){
    // 根据坐标删除数据
    this.todos.splice(index,1)
  },
  // 全选或者不选
  updateAllFather(doneOption){
    // map():创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
      this.todos=this.todos.map((value)=>{  // 这个参数是遍历的单个对象
        return {...value,done:doneOption} // 返回将done属性改编为doneOption的完整对象
      })
  },
   // 清除已经已经勾选的
   clearAllDoneFather(){
    this.todos=this.todos.filter((value)=>{
      // 假如说done值为false就不用过滤-保留,否则就需要过滤-不保留
      return value.done===false
    })
   }
  },
  watch:{// 我们初步设想的是利用,mounted目的是一上来就挂载上去。但是webStorage数据不会随着新增能新增...
  todos:{
    immediate: true,  // 若immediate为true则handle会在初始化时就会调用一次,以后就看firstName的改变了
    deep: true, //开启深度监视
    handler(newValue,oldValue){  //假如data是方法旧值获取不到(旧址也是新值),假如data是对象就能获取到旧值
        localStorage.setItem('arr_Object',JSON.stringify(newValue)); // 这里一定要转换为JSON字符串
      }
    }
  }
 
}
</script>

<style>

  /*base*/
  body {
    background: #fff;
  }

  .btn {
    display: inline-block;
    padding: 4px 12px;
    margin-bottom: 0;
    font-size: 14px;
    line-height: 20px;
    text-align: center;
    vertical-align: middle;
    cursor: pointer;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    border-radius: 4px;
  }

  .btn-danger {
    color: #fff;
    background-color: #da4f49;
    border: 1px solid #bd362f;
  }

  .btn-danger:hover {
    color: #fff;
    background-color: #bd362f;
  }

  .btn:focus {
    outline: none;
  }

  .todo-container {
    width: 600px;
    margin: 0 auto;
  }
  .todo-container .todo-wrap {
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 5px;
  }

</style>

Header.vue

$emit -> 属于vue实列的, vc是继承了vm
 add(){
        if(this.textA!==null){
           // 根据用户的输入生成一个todo对象
        const todo={id:Date.now(),name:this.textA,done:false}
        // 通知父App.vue添加这个数据
        this.$emit('addFatherA',todo)
        }
        this.textA=''
      }
<template>
  <div>
          <!-- 1.头部 -->
      <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="textA" @keyup.enter="add"/>
      </div>
  </div>
</template>

<script>
export default {
    name:'Header',
    data() {
      return {
        textA:''
      }
    },
    methods: {
      add(){
        if(this.textA!==null){
           // 根据用户的输入生成一个todo对象
        const todo={id:Date.now(),name:this.textA,done:false}
        // 通知父App.vue添加这个数据
        this.$emit('addFatherA',todo)
        }
        this.textA=''
      }
    },
}
</script>

<style scoped>
  /*header*/
  .todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 4px 7px;
  }

  .todo-header input:focus {
    outline: none;
    border-color: rgba(82, 168, 236, 0.8);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
  }

</style>

在这里插入图片描述

(二十六)、全局事件总线 (兄弟组件)

1.全局事件分析

$ emit是触发、$ on是绑定。
兄弟组件可以借助全局事件总线进行数据联系
在这里插入图片描述

  1. 都能够被全部组件能看得到
  2. 支持被绑定事件

满足上面的条件只能是 vm。因为vm被vc继承,放在vm上就能够实现共享。且每一个组件的vc都是经过一次extend(),所以每一个vc都是不一样的。只能放在vm上了。

1. $on('绑定的事件名',触发谁) 绑定自定义事件
2. $emit('触发的事件名',参数) 自定义分发事件
3. $off('解绑的事件名') 解绑自定义监视监听
4. $once('绑定的事件名',触发谁) 绑定事件监听,但只能处理一次

2.全局事件 $bus

(1).Vue原型数据被所有人共享

提示:放在vue的原型上的数据谁都能看的到 Vue.prototype.x=vvv

我们希望越早放数据越好,所以我们想到main.js

import  Vue  from 'vue'  // 引入阉割版本的vue
import  App  from './App.vue' // 引入App.vue组件

Vue.config.productionTip = false;
Vue.prototype.p=200;  // eg: 原型上添加p值为200
new Vue({
    render:h=>h(App)

}).$mount('#app');

App.vue

<template>
  <div class="app">
    <h2>我是App组件</h2>
    <Demo1/>
    <Demo2/>
  </div>
</template>

<script>
import { watch } from 'vue'
// TODO: 引入组件
import Demo1 from './components/Demo1.vue'
import Demo2 from './components/Demo2.vue'
export default {
    name: "APP",
    // TODO: 注册组件
    components:{
      Demo1,
      Demo2,
    },
    data() {
      return {
        name:'jsxs'
      }
    },
    watch:{
      name:{
           immediate:true,
      handler(){
          console.log(this)
      }
      }

    }
}
</script>

<style>
    .app {
      background-color: brown;
      padding: 10px;
    }
</style>

在这里插入图片描述

(2).在Vue原型上存放Vue的实列

利用main.js的生命周期钩子函数帮我们存放Vue的实列

import  Vue  from 'vue'  // 引入阉割版本的vue
import  App  from './App.vue' // 引入App.vue组件

Vue.config.productionTip = false;

new Vue({
    beforeCreate(){
        Vue.prototype.p=this;
    },
    render:h=>h(App)

}).$mount('#app');

我们可以在所有的组件中获取到属性p 也就是对应的Vue实列
在这里插入图片描述
官方推荐我们使用 $bus去替换掉属性p也就是如下。

  • $ bus: 并不是Vue自带的,不像$emit等是Vue自带的
import  Vue  from 'vue'  // 引入阉割版本的vue
import  App  from './App.vue' // 引入App.vue组件

Vue.config.productionTip = false;

new Vue({
    beforeCreate(){
        Vue.prototype.$bus=this;  // 1.安装事件总线
    },
    render:h=>h(App)

}).$mount('#app');

3.全局事件总线 (Demo1接受Demo2的数据)

main.js

import  Vue  from 'vue'  // 引入阉割版本的vue
import  App  from './App.vue' // 引入App.vue组件

Vue.config.productionTip = false;

new Vue({
    beforeCreate(){
        Vue.prototype.$bus=this;
    },
    render:h=>h(App)

}).$mount('#app');

App.vue

<template>
  <div class="app">
    <h2>我是App组件</h2>
    <Demo1/>
    <Demo2/>
  </div>
</template>

<script>
import { watch } from 'vue'
// TODO: 引入组件
import Demo1 from './components/Demo1.vue'
import Demo2 from './components/Demo2.vue'
export default {
    name: "APP",
    // TODO: 注册组件
    components:{
      Demo1,
      Demo2,
    },

}
</script>

<style>
    .app {
      background-color: brown;
      padding: 10px;
    }
</style>

Demo1.vue: 接受数据
发送数据用$on进行绑定事件

mounted() {
      this.$bus.$on('A',this.demo1);  // 假如demo2被触发,我们就调用demo1
    },
<template>
  <div class="demo1">
    <h2>我是Demo1</h2>
    <h2>{{username1}}</h2>
  </div>
</template>

<script>
export default {
    name: "Demo1",
    data() {
      return {
        username1:'demo1',
      }
    },
    methods: {
      demo1(x){
        this.username1=x;
      }
    },
    mounted() {
      this.$bus.$on('A',this.demo1);  // 假如demo2被触发,我们就调用demo1
    },

}
</script>

<style>
    .demo1 {
        background-color: antiquewhite;
    }
</style>

Demo2.vue: 发送数据
接受消息用$emit

$emit 是触发 $on是绑定
 methods: {
      send_data(){
        this.$bus.$emit('A',this.username2);
      }
    },
<template>
  <div class="demo2">
    <h2>我是Demo2</h2>
    <button @click="send_data">点我切换数据</button>
  </div>
</template>

<script>
export default {
    name: "Demo2",
    data() {
      return {
        username2:'demo2',
      }
    },
    methods: {
      send_data(){
        this.$bus.$emit('A',this.username2);
      }
    },
}
</script>

<style>
    .demo2 {
        background-color:  rgb(12, 196, 116);
    }
</style>

未点击按钮
在这里插入图片描述
点击按钮之后
在这里插入图片描述

4.全局事件总线(双向发送数据)

main.js

import  Vue  from 'vue'  // 引入阉割版本的vue
import  App  from './App.vue' // 引入App.vue组件

Vue.config.productionTip = false;

new Vue({
    beforeCreate(){
        Vue.prototype.$bus=this;
    },
    render:h=>h(App)

}).$mount('#app');

App.vue

<template>
  <div class="app">
    <h2>我是App组件</h2>
    <Demo1/>
    <Demo2/>
  </div>
</template>

<script>
import { watch } from 'vue'
// TODO: 引入组件
import Demo1 from './components/Demo1.vue'
import Demo2 from './components/Demo2.vue'
export default {
    name: "APP",
    // TODO: 注册组件
    components:{
      Demo1,
      Demo2,
    },
    data() {
      return {
        name:'jsxs'
      }
    },
    watch:{
      name:{
           immediate:true,
      handler(){
          console.log(this)
      }
      }

    }
}
</script>

<style>
    .app {
      background-color: brown;
      padding: 10px;
    }
</style>

Demo1.vue

<template>
  <div class="demo1">
    <h2>我是Demo1</h2>
    <h2>{{username1}}</h2>
    <button @click="send_data2">点击我发送数据到Demo2</button>
  </div>
</template>

<script>
export default {
    name: "Demo1",
    data() {
      return {
        username1:'demo1',
      }
    },
    methods: {
      demo1(x){
        this.username1=x;
      },
      // 发送消息到demo1
      send_data2(){
        console.log('xxxxxxxxx')
        this.$bus.$emit('B',this.username1);
      }

    },
    // 接受demo2传递过来的日记
    mounted() {
      this.$bus.$on('A',this.demo1);  // 绑定A事件,假如A被触发。就调用demo1这个方法
    },

}
</script>

<style>
    .demo1 {
        background-color: antiquewhite;
    }
</style>

Demo2.vue

<template>
  <div class="demo2">
    <h2>我是Demo2</h2>
    <button @click="send_data">点我切换数据</button>
    <h2>{{username2}}</h2>
  </div>
</template>

<script>
export default {
    name: "Demo2",
    data() {
      return {
        username2:'demo2',
      }
    },
    // 发送数据到demo1
    methods: {
      demo2(x){
        this.username2=x;
      },
      // 发送数据
      send_data(){
        this.$bus.$emit('A',this.username2);// 触发A事件 并传递参数
      }
    },
    // 接受数据
    mounted() {
      this.$bus.$on('B',this.demo2)  //绑定b事件,假如b事件被触发就调用demo2这个方法
    },
}
</script>

<style>
    .demo2 {
        background-color:  rgb(12, 196, 116);
    }
</style>

在这里插入图片描述

(二十六)、插槽 (slot)

1.问题探究

在这里插入图片描述
我们想复用一个组件,然而这个组件的各个数据是不同的。我们通过尝试发现:只能通过对复用的组件 v-show、v-if较为复杂的实现这个功能。否则不能实现
App.vue

<template>
  <div class="app">
    <!-- 组件可以传递静态的也可以传递动态的 -->
    <Category  title="游戏"  :game_arr1="gamArr"/>
    <Category  title="美食"  :food_url1="foodURL"/>
    <Category/>
  </div>
</template>

<script>
  import  Category  from './components/Category.vue'
export default {
  components:{
    Category,
  },
  data() {
    return {
      gamArr:[
        {id:'001',name:'王者荣耀',price:100},
        {id:'002',name:'刺激战场',price:100},
        {id:'003',name:'创越火线',price:100},
        {id:'004',name:'反恐精英和',price:100},
      ],
      foodURL:'https://image.baidu.com/search/detail?ct=503316480&z=&tn=baiduimagedetail&ipn=d&word=%E7%83%A7%E7%83%A4%20200%2A100&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=-1&hd=&latest=&copyright=&cs=3049322705,2682672783&os=365147000,2674100539&simid=3049322705,2682672783&pn=0&rn=1&di=7214885350303334401&ln=1783&fr=&fmq=1683463448132_R&ic=&s=undefined&se=&sme=&tab=0&width=&height=&face=undefined&is=0,0&istype=2&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=1e&objurl=https%3A%2F%2Fview-cache.book118.com%2Fview6%2FM05%2F27%2F16%2FwKh2BGEgidOASTYzAAMVLqY5WWU809.png&rpstart=0&rpnum=0&adpicid=0&nojc=undefined&dyTabStr=MCwxLDYsMyw0LDUsMiw3LDgsOQ%3D%3D'
    }
  },
}
</script>

<style>
  .app{
    width: 1000px;
    height: 400px;
    background-color: rgb(70, 193, 156);
    /* 横向展开 */
    display: flex;  
    /* 自适应排列 */
    justify-content: space-around;
  }
</style>

Category.vue

<template>
  <div class="cate">
    <h3 class="t1">{{title}}分类</h3>
    <ul>
        <li v-for="ga_obj in game_arr1" :key="ga_obj.id">{{ga_obj.name}}</li>
    </ul>
  </div>
</template>

<script>
export default {
    // TODO: 子组件可以接受传过来的动态数据、也可以接受动态的数据。
    props:['game_arr1','title','food_url1']
}
</script>

<style>
.cate {
    width:240px;
    height: 300px;
    background-color: aquamarine;
}
.t1 {
    text-align: center;
}

</style>

在这里插入图片描述

于是我们想: 这样好复杂啊。能不能直接在父组件中直接将一整个模板传递给子组件。

非自闭和标签:我们能够解析到组件中数据,但是不知道放到组件的哪个位置。所以只能弃掉!!!!
在这里插入图片描述

2.默认插槽

所以我们只要借助插槽就能够实现指定放的位置,然后开标签也就不会被丢弃。插槽放在哪里,就是放在哪里。
默认插槽都会接受掉…
App.vue
组件开区间

<template>
  <div class="app">
    <!-- 组件可以传递静态的也可以传递动态的 -->
    <Category  title="游戏">
    <ul>
        <li v-for="ga_obj in gamArr" :key="ga_obj.id">{{ga_obj.name}}</li>
    </ul>
    </Category>
    <Category  title="美食">
      <img :src="foodURL" alt="122">
    </Category>
    <Category title="视频">
      <video  controls :src="videoUL"></video>
    </Category>
  </div>
</template>

<script>
  import  Category  from './components/Category.vue'
export default {
  components:{
    Category,
  },
  data() {
    return {
      gamArr:[
        {id:'001',name:'王者荣耀',price:100},
        {id:'002',name:'刺激战场',price:100},
        {id:'003',name:'创越火线',price:100},
        {id:'004',name:'反恐精英和',price:100},
      ],
      foodURL:'https://image.baidu.com/search/detail?ct=503316480&z=&tn=baiduimagedetail&ipn=d&word=%E7%83%A7%E7%83%A4%20200%2A100&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=-1&hd=&latest=&copyright=&cs=3049322705,2682672783&os=365147000,2674100539&simid=3049322705,2682672783&pn=0&rn=1&di=7214885350303334401&ln=1783&fr=&fmq=1683463448132_R&ic=&s=undefined&se=&sme=&tab=0&width=&height=&face=undefined&is=0,0&istype=2&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=1e&objurl=https%3A%2F%2Fview-cache.book118.com%2Fview6%2FM05%2F27%2F16%2FwKh2BGEgidOASTYzAAMVLqY5WWU809.png&rpstart=0&rpnum=0&adpicid=0&nojc=undefined&dyTabStr=MCwxLDYsMyw0LDUsMiw3LDgsOQ%3D%3D',
      videoUL:'https://www.bilibili.com/video/BV12h4y1n7tt?t=3.5'
  }
  }
}
</script>

<style>
  .app{
    width: 1000px;
    height: 400px;
    background-color: rgb(70, 193, 156);
    /* 横向展开 */
    display: flex;  
    /* 自适应排列 */
    justify-content: space-around;
  }
  video{
    width: 100%;   // 视频占的百分比
  }
</style>

Category.vue
默认插槽

    <!-- 默认插槽 -->
    <slot></slot>
<template>
  <div class="cate">
    <h3 class="t1">{{title}}分类</h3>
    <!-- 默认插槽 -->
    <slot></slot>
  </div>
</template>

<script>
export default {
    // TODO: 子组件可以接受传过来的动态数据、也可以接受动态的数据。
    props:['title']
}
</script>

<style>
.cate {
    width:240px;
    height: 300px;
    background-color: aquamarine;
}
.t1 {
    text-align: center;
}

</style>

在这里插入图片描述

3.命名插槽

App.vue

内嵌一个template标签
    <Category  title="游戏">
      <template slot="game">
           <ul>
        		<li v-for="ga_obj in gamArr" :key="ga_obj.id">{{ga_obj.name}}</li>
          </ul>
      </template>
    </Category>
<template>
  <div class="app">
    <!-- 组件可以传递静态的也可以传递动态的 -->
    <Category  title="游戏">
      <template slot="game">
           <ul>
        <li v-for="ga_obj in gamArr" :key="ga_obj.id">{{ga_obj.name}}</li>
          </ul>
      </template>
   
    </Category>
    <Category  title="美食">
      <template slot="food">
          <img :src="foodURL" alt="122">
      </template>
    </Category>
    <Category title="视频">
      <template slot="vido">
        <video  controls :src="videoUL"></video>
      </template>
      
    </Category>
  </div>
</template>

<script>
  import  Category  from './components/Category.vue'
export default {
  components:{
    Category,
  },
  data() {
    return {
      gamArr:[
        {id:'001',name:'王者荣耀',price:100},
        {id:'002',name:'刺激战场',price:100},
        {id:'003',name:'创越火线',price:100},
        {id:'004',name:'反恐精英和',price:100},
      ],
      foodURL:'https://image.baidu.com/search/detail?ct=503316480&z=&tn=baiduimagedetail&ipn=d&word=%E7%83%A7%E7%83%A4%20200%2A100&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=-1&hd=&latest=&copyright=&cs=3049322705,2682672783&os=365147000,2674100539&simid=3049322705,2682672783&pn=0&rn=1&di=7214885350303334401&ln=1783&fr=&fmq=1683463448132_R&ic=&s=undefined&se=&sme=&tab=0&width=&height=&face=undefined&is=0,0&istype=2&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=1e&objurl=https%3A%2F%2Fview-cache.book118.com%2Fview6%2FM05%2F27%2F16%2FwKh2BGEgidOASTYzAAMVLqY5WWU809.png&rpstart=0&rpnum=0&adpicid=0&nojc=undefined&dyTabStr=MCwxLDYsMyw0LDUsMiw3LDgsOQ%3D%3D',
      videoUL:'https://www.bilibili.com/video/BV12h4y1n7tt?t=3.5'
  }
  }
}
</script>

<style>
  .app{
    width: 1000px;
    height: 400px;
    background-color: rgb(70, 193, 156);
    /* 横向展开 */
    display: flex;  
    /* 自适应排列 */
    justify-content: space-around;
  }
  video{
    width: 100%;
  }
</style>

Category.vue

给插槽起名字,只有名字对应的插槽才能使用
    <slot name="game"></slot>
    <slot name="food"></slot>
    <slot name="vido"></slot>
<template>
  <div class="cate">
    <h3 class="t1">{{title}}分类</h3>
    <!-- 默认插槽 -->
    <slot name="game"></slot>
    <slot name="food"></slot>
    <slot name="vido"></slot>
  </div>
</template>

<script>
export default {
    // TODO: 子组件可以接受传过来的动态数据、也可以接受动态的数据。
    props:['title']
}
</script>

<style>
.cate {
    width:240px;
    height: 300px;
    background-color: aquamarine;
}
.t1 {
    text-align: center;
}

</style>

在这里插入图片描述

(二十七)、Vue中的ajax

1.Vue项目中常用的2个ajax库

(1).vue-resource

  • vue插件,非官方库,vue1.x使用广泛、现在已经弃用。

(2).axios

  • 通用的ajax请求库,官方推荐,vue2.x使用广泛。

2.解决开发环境Ajax跨域问题

  • 使用代理服务器

3.Github搜索案列_axios_全局总线

(1).静态资源

App.vue

<template>
  <div>
    <div class="container">
    <section class="jumbotron">
      <h3 class="jumbotron-heading">Search Github Users</h3>
      <div>
        <input type="text" placeholder="enter the name you search"/>&nbsp;<button>Search</button>
      </div>
    </section>
    <div class="row">
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
    </div>
  </div>
  </div>
</template>

<script>
export default {
  name:'App'
}
</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: .75rem;
  /* 这个意思是每个div的行距是2分米 */
  margin-bottom: 2rem;
  border: 1px solid #efefef;
  text-align: center;
}

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

.card-text {
  font-size: 85%;
}
</style>

bootstrap.css

太多了这里不展示

public: 就是我们引入第三方库的位置 比如:"layui.js,bootstrap.js"

<%= BASE_URL %>  相当于帮助我们找到了pubilic/

在这里插入图片描述

在这里插入图片描述

(2).静态资源设置成组件

App.vue

<template>
  <div>
    <div class="container">
      <Searcher/>
      <List/>
  </div>
  </div>
</template>

<script>
import  Searcher  from './components/Searcher.vue'
import  List  from './components/List.vue'
export default {
  name:'App',
  components:{
    Searcher,
    List
  }
}
</script>

<style>
</style>

Searcher.vue

<template>
  <div>
    <section class="jumbotron">
      <h3 class="jumbotron-heading">Search Github Users</h3>
      <div>
        <input type="text" placeholder="enter the name you search"/>&nbsp;<button>Search</button>
      </div>
    </section>
  </div>
</template>

<script>
export default {
    name:'Searcher'
}
</script>

<style>

</style>

List.vue

<template>
  <div>
        <div class="row">
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
    </div>
  </div>
</template>

<script scoped>
export default {
    name:'List'
}
</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: .75rem;
  /* 这个意思是每个div的行距是2分米 */
  margin-bottom: 2rem;

  border: 1px solid #efefef;
  text-align: center;
}

.card > img {
  margin-bottom: .75rem;
  /* 控制图片的范围 */
  border-radius: 100px;
}

.card-text {
  font-size: 95%;
}
</style>

在这里插入图片描述

(3).github搜索案列_发送请求

下载axios的插件。在控制台我们添加 axios

 npm add axios

在这里插入图片描述
接口地址: https://api.github.com/search/users?q=xxx
注册全局事件总线

import  Vue  from 'vue'  // 引入阉割版本的vue
import  App  from './App.vue' // 引入App.vue组件

Vue.config.productionTip = false;

new Vue({
    // 1.安装全局事件总线
    beforeCreate(){
        Vue.prototype.$bus=this;
    },
    render:h=>h(App)

}).$mount('#app');

App.vue

<template>
  <div>
    <div class="container">
      <Searcher/>
      <List/>
  </div>
  </div>
</template>

<script>
import  Searcher  from './components/Searcher.vue'
import  List  from './components/List.vue'
export default {
  name:'App',
  components:{
    Searcher,
    List
  }
}
</script>

<style>
</style>

List.vue

1.接受兄弟List传递过来的数据
    methods: {
      accept_message(x){
        console.log('搜索的信息->>>>',x)
      }
    },
    mounted() {
      this.$bus.$on('list_A',this.accept_message)
    },
<template>
  <div>
        <div class="row">
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
      <div class="card">
        <a href="https://github.com/reactjs" target="_blank">
          <img src="https://avatars.githubusercontent.com/u/6412038?v=3" style='width: 100px'/>
        </a>
        <p class="card-text">reactjs</p>
      </div>
    </div>
  </div>
</template>

<script scoped>
export default {
    name:'List',
    data() {
      return {
        lists:[],
      }
    },
    methods: {
      accept_message(x){
        // this.lists=x;
        console.log('搜索的信息->>>>',x)
      }
    },
    mounted() {
      this.$bus.$on('list_A',this.accept_message)
    },
}
</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: .75rem;
  /* 这个意思是每个div的行距是2分米 */
  margin-bottom: 2rem;

  border: 1px solid #efefef;
  text-align: center;
}

.card > img {
  margin-bottom: .75rem;
  /* 控制图片的范围 */
  border-radius: 100px;
}

.card-text {
  font-size: 95%;
}
</style>

Searcher.vue

在搜索的组件中: 我们需要在方法上添加 async 标注着这个方法是异步方法。然后通过axios.get进行获取某一个接口,
第一个参数是异步请求的网址,第二个是请求网址的参数。response.data是获取回调的信息。然后需要向兄弟组件中传递我们回调成功的数据。

methods: {
      async Searcher(){
        // TODO:  拼接参数: 左边的是网址右边是拼接参数。 里面的对象名一定要是params。
        // ! https://api.github.com/search/users?q=xxx
        try {
          const response= await axios.get('https://api.github.com/search/users',{params:{q:this.key_words}})
          const {items}=response.data;  // 假如信息调用成功我们就回显
          console.log(items)  // 展示我们查询到的信息
          this.$bus.$emit('list_A',items)
        } catch (error) {
          console.log('信息调用失败了!!')
        }
      }
    },
<template>
  <div>
    <section class="jumbotron">
      <h3 class="jumbotron-heading">Search Github Users</h3>
      <div>
        <input type="text" placeholder="enter the name you search" v-model="key_words"/>&nbsp;
        <button @click="Searcher">Search</button>
      </div>
    </section>
  </div>
</template>

<script>
// TODO: 引入我们添加得axios
import axios from 'axios'
export default {
  
    name:'Searcher',
    data() {
      return {
        key_words:''
      }
    },
    methods: {
      async Searcher(){
        // TODO:  拼接参数: 左边的是网址右边是拼接参数。 里面的对象名一定要是params。
        // ! https://api.github.com/search/users?q=xxx
        
        try {
          const response= await axios.get('https://api.github.com/search/users',{params:{q:this.key_words}})
          const {items}=response.data;  // 假如信息调用成功我们就回显
          console.log(items)  // 展示我们查询到的信息
          this.$bus.$emit('list_A',items)
        } catch (error) {
          console.log('信息调用失败了!!')
        }
        
      }
    },
}
</script>

<style>

</style>

在这里插入图片描述

(4).github搜索案列完成

这里我们新增了是否是第一次访问、是否正在加载中、和加载后数据的处理
App.vue

<template>
  <div>
    <div class="container">
      <Searcher/>
      <List/>
  </div>
  </div>
</template>

<script>
import  Searcher  from './components/Searcher.vue'
import  List  from './components/List.vue'
export default {
  name:'App',
  components:{
    Searcher,
    List
  }
}
</script>

<style>
</style>

Searcher.vue

这里的参数我们传递的是: 对象。对象里面的对象名要和接受这个数据组件里面的data对象名要一致。
分别触发三次: 分别是-> 请求之前、请求成功之后、请求失败之后
    methods: {
      async Searcher(){
        // 请求之前通知List更新它的data
        this.$bus.$emit('list_A',{isFirst:false,isLoading:true})
        // TODO:  拼接参数: 左边的是网址右边是拼接参数。 里面的对象名一定要是params。
        // ! https://api.github.com/search/users?q=xxx
        try {
          const response= await axios.get('https://api.github.com/search/users',{params:{q:this.key_words}})
          const {items}=response.data;  // 假如信息调用成功我们就回显
          console.log(items)  // 展示我们查询到的信息
          // 请求成功之后,通知List组件更新它的data
          this.$bus.$emit('list_A',{isLoading:false,lists:items})
        } catch (error) {
          // 请求失败周会
          this.$bus.$emit('list_A',{isLoading:false,lists:[],errMsg:error.message})
          console.log('信息调用失败了!!')
        }
      }
    },
<template>
  <div>
    <section class="jumbotron">
      <h3 class="jumbotron-heading">Search Github Users</h3>
      <div>
        <input type="text" placeholder="enter the name you search" v-model="key_words"/>&nbsp;
        <button @click="Searcher">Search</button>
      </div>
    </section>
  </div>
</template>

<script>
// TODO: 引入我们添加得axios
import axios from 'axios'
export default {
  
    name:'Searcher',
    data() {
      return {
        key_words:''
      }
    },
    methods: {
      async Searcher(){
        // 请求之前通知list_A更新它的data
        this.$bus.$emit('list_A',{isFirst:false,isLoading:true})
        // TODO:  拼接参数: 左边的是网址右边是拼接参数。 里面的对象名一定要是params。
        // ! https://api.github.com/search/users?q=xxx
        try {
          const response= await axios.get('https://api.github.com/search/users',{params:{q:this.key_words}})
          const {items}=response.data;  // 假如信息调用成功我们就回显
          console.log(items)  // 展示我们查询到的信息
          // 请求成功之后,通知List组件更新它的data
          this.$bus.$emit('list_A',{isLoading:false,lists:items})
        } catch (error) {
          // 请求失败周会
          this.$bus.$emit('list_A',{isLoading:false,lists:[],errMsg:error.message})
          console.log('信息调用失败了!!')
        }
      }
    },
}
</script>

<style>

</style>

Lists.vue

接受数据的时候: 
1. {...this.listInfo,...x} 就是把第一个的key-value替换成第二个key-value。只替换不一样的。
2. 三个点对象 代表展开成key-value形式。
3. {...对象名,属性名:}  就是将第一个对象的属性名替换成右边的假如不同的情况下。
methods: {
      // 我们接受的是一个对象 
      accept_message(x){
        console.log('搜索的信息->>>>',x);
        this.listInfo={...this.listInfo,...x} // 假如左边的key-value与右边的key-value不一样,就替换成右边的
      }
    },
    mounted() {
      this.$bus.$on('list_A',this.accept_message)
    },
}
<template>
  <div>
      <div class="row">
        <h2 v-show="listInfo.isFirst">第一次请求</h2>
        <h2 v-show="listInfo.isLoading">正在加载....</h2>
      <div class="card" v-for="list_obj in listInfo.lists" :key="list_obj.id">
        <a :href="list_obj.html_url" target="_blank">
          <img :src="list_obj.avatar_url" style='width: 100px'/>
        </a>
        <p class="card-text">{{list_obj.login}}</p>
      </div>

    </div>
  </div>
</template>

<script scoped>
export default {
    name:'List',
    data() {
      return {
        listInfo:{
        lists:[],
        // 是否是第一次请求
        isFirst:true,
        // 是否正在加载
        isLoading:false,
        errMsg:''
        }
 
      }
    },
    methods: {
      // 我们接受的是一个对象 
      accept_message(x){
        console.log('搜索的信息->>>>',x);
        this.listInfo={...this.listInfo,...x} // 假如左边的key-value与右边的key-value不一样,就替换成右边的
      }
    },
    mounted() {
      this.$bus.$on('list_A',this.accept_message)
    },
}
</script>

<style>
.card {
    /* 从哪里开始排序 */
  float: left;
  /* 这个目的是一行放三个 */
  width: 33.333%;
  padding: .75rem;
  /* 这个意思是每个div的行距是2分米 */
  margin-bottom: 2rem;
  /* 边框 */
  border: 1px solid #efefef;
  text-align: center;
}
  /* 图片的大小 */
.card > img {
  margin-bottom: .75rem;
  /* 控制图片的范围 */
  border-radius: 100px;
}
/* 文本大小 */
.card-text {
  font-size: 95%;
}
</style>

在这里插入图片描述
在这里插入图片描述

4.Github搜索案列_订阅与发布_axios

安装订阅与发布的插件

npm add pubsub-js

在这里插入图片描述
引入订阅与发布

// 引入订阅与发布的插件
import PubSub from 'pubsub-js'

App.vue

<template>
  <div>
    <div class="container">
      <Searcher/>
      <List/>
  </div>
  </div>
</template>

<script>
import  Searcher  from './components/Searcher.vue'
import  List  from './components/List.vue'
export default {
  name:'App',
  components:{
    Searcher,
    List
  }
}
</script>

<style>
</style>

Searcher.vue 发送者

// TODO: 引入我们添加得axios
import axios from 'axios'
// 引入订阅与发布的插件
import PubSub from 'pubsub-js'
发送信息
// this.$bus.$emit('list_A',{isFirst:false,isLoading:true})
PubSub.publish('list_A',{isFirst:false,isLoading:true})
<template>
  <div>
    <section class="jumbotron">
      <h3 class="jumbotron-heading">Search Github Users</h3>
      <div>
        <input type="text" placeholder="enter the name you search" v-model="key_words"/>&nbsp;
        <button @click="Searcher">Search</button>
      </div>
    </section>
  </div>
</template>

<script>
// TODO: 引入我们添加得axios
import axios from 'axios'
// 引入订阅与发布的插件
import PubSub from 'pubsub-js'
export default {
  
    name:'Searcher',
    data() {
      return {
        key_words:''
      }
    },
    methods: {
      async Searcher(){
        // 请求之前通知List更新它的data
        // this.$bus.$emit('list_A',{isFirst:false,isLoading:true})
        PubSub.publish('list_A',{isFirst:false,isLoading:true})
        // TODO:  拼接参数: 左边的是网址右边是拼接参数。 里面的对象名一定要是params。
        // ! https://api.github.com/search/users?q=xxx
        try {
          const response= await axios.get('https://api.github.com/search/users',{params:{q:this.key_words}})
          const {items}=response.data;  // 假如信息调用成功我们就回显
          console.log(items)  // 展示我们查询到的信息
          // 请求成功之后,通知List组件更新它的data
          // this.$bus.$emit('list_A',{isLoading:false,lists:items})
          PubSub.publish('list_A',{isLoading:false,lists:items})
        } catch (error) {
          // 请求失败周会
          // this.$bus.$emit('list_A',{isLoading:false,lists:[],errMsg:error.message})
          PubSub.publish('list_A',{isLoading:false,lists:[],errMsg:error.message})
          console.log('信息调用失败了!!')
        }
      }
    },
}
</script>

<style>

</style>

List.vue 订阅者

// 引入订阅与发布的插件
import PubSub from 'pubsub-js'
// 订阅
mounted() {
     this.token=PubSub.subscribe('list_A',this.accept_message)
    },
// 取消订阅
    beforeDestroy(){
      PubSub.unsubscribe(this.token);
    }
<template>
  <div>
      <div class="row">
        <h2 v-show="listInfo.isFirst">第一次请求</h2>
        <h2 v-show="listInfo.isLoading">正在加载....</h2>
      <div class="card" v-for="list_obj in listInfo.lists" :key="list_obj.id">
        <a :href="list_obj.html_url" target="_blank">
          <img :src="list_obj.avatar_url" style='width: 100px'/>
        </a>
        <p class="card-text">{{list_obj.login}}</p>
      </div>

    </div>
  </div>
</template>

<script scoped>
// 引入订阅与发布的插件
import PubSub from 'pubsub-js'
export default {
    name:'List',
    data() {
      return {
        listInfo:{
        lists:[],
        // 是否是第一次请求
        isFirst:true,
        // 是否正在加载
        isLoading:false,
        errMsg:''
        }
 
      }
    },
    methods: {
      // 我们接受的是一个对象 
      accept_message(msg,x){  // TODO: 发布与订阅模式中,接收信息会收到两个参数,第一个是msg,第二个才是数据。
        console.log('搜索的信息->>>>',x);
        this.listInfo={...this.listInfo,...x} // 假如左边的key-value与右边的key-value不一样,就替换成右边的
      }
    },
    mounted() {
     this.token=PubSub.subscribe('list_A',this.accept_message)
    },
    beforeDestroy(){
      // 取消订阅
      PubSub.unsubscribe(this.token);
    }
}
</script>

<style>
.card {
    /* 从哪里开始排序 */
  float: left;
  /* 这个目的是一行放三个 */
  width: 33.333%;
  padding: .75rem;
  /* 这个意思是每个div的行距是2分米 */
  margin-bottom: 2rem;
  /* 边框 */
  border: 1px solid #efefef;
  text-align: center;
}
  /* 图片的大小 */
.card > img {
  margin-bottom: .75rem;
  /* 控制图片的范围 */
  border-radius: 100px;
}
/* 文本大小 */
.card-text {
  font-size: 95%;
}
</style>

在这里插入图片描述

5.Github搜索案列_vue-resource

它是vue的官方插件…
安装vue-resorce

npm add vue-resource

在这里插入图片描述
引用Vue-resorce
因为是VUE的官方库、所以我们需要使用到Vue.use

import Vue from "Vue"
import VueResource from 'vue-resource'
Vue.use(VueResource)

引入Vue插件之后: Vue的原型上会新增一个 $http
main.js

import  Vue  from 'vue'  // 引入阉割版本的vue
import  App  from './App.vue' // 引入App.vue组件
import VueResource from 'vue-resource'

Vue.use(VueResource)
Vue.config.productionTip = false;

new Vue({
    // 1.安装全局事件总线
    beforeCreate(){
        Vue.prototype.$bus=this;
    },
    render:h=>h(App)

}).$mount('#app');

App.vue

<template>
  <div>
    <div class="container">
      <Searcher/>
      <List/>
  </div>
  </div>
</template>

<script>
import  Searcher  from './components/Searcher.vue'
import  List  from './components/List.vue'
export default {
  name:'App',
  components:{
    Searcher,
    List
  }
}
</script>

<style>
</style>

Searcher.vue

const response= await this.$http.get('https://api.github.com/search/users',{params:{q:this.key_words}})
<template>
  <div>
    <section class="jumbotron">
      <h3 class="jumbotron-heading">Search Github Users</h3>
      <div>
        <input type="text" placeholder="enter the name you search" v-model="key_words"/>&nbsp;
        <button @click="Searcher">Search</button>
      </div>
    </section>
  </div>
</template>

<script>
// TODO: 引入我们添加得axios
import axios from 'axios'
// 引入订阅与发布的插件
import PubSub from 'pubsub-js'
export default {
  
    name:'Searcher',
    data() {
      return {
        key_words:''
      }
    },
    methods: {
      async Searcher(){
        // 请求之前通知List更新它的data
        // this.$bus.$emit('list_A',{isFirst:false,isLoading:true})
        PubSub.publish('list_A',{isFirst:false,isLoading:true})
        // TODO:  拼接参数: 左边的是网址右边是拼接参数。 里面的对象名一定要是params。
        // ! https://api.github.com/search/users?q=xxx
        try {
          const response= await this.$http.get('https://api.github.com/search/users',{params:{q:this.key_words}})
          const {items}=response.data;  // 假如信息调用成功我们就回显
          console.log(items)  // 展示我们查询到的信息
          // 请求成功之后,通知List组件更新它的data
          // this.$bus.$emit('list_A',{isLoading:false,lists:items})
          PubSub.publish('list_A',{isLoading:false,lists:items})
        } catch (error) {
          // 请求失败周会
          // this.$bus.$emit('list_A',{isLoading:false,lists:[],errMsg:error.message})
          PubSub.publish('list_A',{isLoading:false,lists:[],errMsg:error.message})
          console.log('信息调用失败了!!')
        }
      }
    },
}
</script>

<style>

</style>

List.vue

<template>
  <div>
      <div class="row">
        <h2 v-show="listInfo.isFirst">第一次请求</h2>
        <h2 v-show="listInfo.isLoading">正在加载....</h2>
      <div class="card" v-for="list_obj in listInfo.lists" :key="list_obj.id">
        <a :href="list_obj.html_url" target="_blank">
          <img :src="list_obj.avatar_url" style='width: 100px'/>
        </a>
        <p class="card-text">{{list_obj.login}}</p>
      </div>

    </div>
  </div>
</template>

<script scoped>
// 引入订阅与发布的插件
import PubSub from 'pubsub-js'
export default {
    name:'List',
    data() {
      return {
        listInfo:{
        lists:[],
        // 是否是第一次请求
        isFirst:true,
        // 是否正在加载
        isLoading:false,
        errMsg:''
        }
 
      }
    },
    methods: {
      // 我们接受的是一个对象 
      accept_message(msg,x){  // TODO: 发布与订阅模式中,接收信息会收到两个参数,第一个是msg,第二个才是数据。
        console.log('搜索的信息->>>>',x);
        this.listInfo={...this.listInfo,...x} // 假如左边的key-value与右边的key-value不一样,就替换成右边的
      }
    },
    mounted() {
     this.token=PubSub.subscribe('list_A',this.accept_message)
    },
    beforeDestroy(){
      // 取消订阅
      PubSub.unsubscribe(this.token);
    }
}
</script>

<style>
.card {
    /* 从哪里开始排序 */
  float: left;
  /* 这个目的是一行放三个 */
  width: 33.333%;
  padding: .75rem;
  /* 这个意思是每个div的行距是2分米 */
  margin-bottom: 2rem;
  /* 边框 */
  border: 1px solid #efefef;
  text-align: center;
}
  /* 图片的大小 */
.card > img {
  margin-bottom: .75rem;
  /* 控制图片的范围 */
  border-radius: 100px;
}
/* 文本大小 */
.card-text {
  font-size: 95%;
}
</style>

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/506547.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NVIDIA CUDA驱动安装

1 引言 因为笔记本电脑上运行Milvus图像检索代码&#xff0c;需要安装CUDA驱动。电脑显卡型号是NVIDIA GeForce GTX 1050 Ti Mobile, 操作系统是Ubuntu 20.04&#xff0c;内核版本为Linux 5.15.0-72-generic。 2 CUDA驱动测试 参考网上的资料&#xff1a;https://blog.csdn.…

车载测试ADAS-常用场景仿真软件

ADAS&#xff08;Advanced Driber Assistant System&#xff09;&#xff0c;高级驾驶辅助系统&#xff0c;先进驾驶辅 助系统&#xff0c;作用于辅助汽车驾驶&#xff0c;通过感知、决策和执行&#xff0c;帮助驾驶员察觉可能发生的危险&#xff0c;是提高安全性的主动安全技术…

数据结构与算法基础(王卓)(36):交换排序之快排【第三阶段:深挖解决问题】精华!精华!精华!!!重要的事情说三遍

目录 Review&#xff1a; 具体问题&#xff1a; 操作核心&#xff1a; 注&#xff1a; 操作分解&#xff1a; 操作实现&#xff1a; 问题&#xff08;1&#xff09;&#xff1a;进行不一样次数的 if / else 判断 问题&#xff08;2&#xff09;&#xff1a;通过判断条件…

Element-UI

目录 Layout 布局 按钮组件结合 el-icon 使用 单选框 复选框 日期组件 表格 分页 对话框 表单验证 Element-UI是由饿了么前端团队开发的一套基于Vue.js的桌面端组件库&#xff0c;包含了多个常用的UI组件&#xff0c;如按钮、输入框、表格、弹窗等&#xff0c;可以快速…

5.QT应用程序主窗口

本章代码见文末链接 主窗口框架 新建Qt Wisgets项目mymainwindow&#xff0c;类名默认MainWindow&#xff0c;基类默认QMainWindow 更改文字如图&#xff0c;如果中文无法直接输入&#xff0c;可以试试复制粘贴 “动作编辑器”中&#xff08;默认在右下角&#xff09;&…

AI数字人系统搭建源码

AI数字人系统的功能可以根据具体应用场景而定&#xff0c;以下是一些可能的功能&#xff1a; 语音识别和合成&#xff1a;将自然语言转换为机器可读的文本&#xff0c;或将机器生成的文本转换为自然语言的语音输出。 面部表情捕捉&#xff1a;利用摄像头等设备获取用户…

2023鲁大师评测沟通会:鲁大师尊享版登场、“鲁小车”正式上线

作为硬件评测界的“老兵”&#xff0c;鲁大师不仅有着十几年的硬件评测经验&#xff0c;并且一直都在不断地尝试、不断地推陈出新。在5月9日举行的“2023年鲁大师评测沟通会”上&#xff0c;鲁大师向大众展示了在过去一年间取得的成果。 PC业务迭代升级&#xff0c;鲁大师客户端…

day(2,3)-内核模块

内核模块上 主要内容 向内核添加新功能 内核模块基础代码讲解 内核模块多源文件编程 内核模块信息宏 一、向内核添加新功能 1.1 静态加载法&#xff1a; 即新功能源码与内核其它代码一起编译进uImage文件内 Kconfig是make menuconfig的界面配置文件 1.2动态加载法&am…

Yolov8改进---注意力机制:DoubleAttention、SKAttention,SENet进阶版本

目录 🏆🏆🏆🏆🏆🏆Yolov8魔术师🏆🏆🏆🏆🏆🏆 1. DoubleAttention 2. SKAttention 3.总结

2022-4-4基于单片机的MQ2烟雾传感器报警系统设计

基于单片机的MQ2烟雾传感器报警系统设计 源代码和仿真图及MQ2相关资料可在文末的链接中下载 该系统实现的功能&#xff1a;检测空气中的烟雾浓度并实现超阈值报警 该系统组成由&#xff1a; 单片机最小系统、ADC0832、MQ2烟雾传感器、LCD1602液晶模块、声光报警模块、按键模…

研究人员发现微软Azure API管理服务存在3个漏洞

微软Azure API管理服务中披露了三个新的安全漏洞&#xff0c;恶意行为者可能会滥用这些漏洞来访问敏感信息或后端服务。 据以色列云安全公司Ermetic称&#xff0c;这包括两个服务器端请求伪造(SSRF)漏洞和API管理开发人员门户中的一个不受限制的文件上传功能实例。 安全研究员…

【腾讯云 Finops Crane 集训营】Crane应用实战

文章目录 前言一、Crane是什么&#xff1f;二、Crane的特点三、Crane使用1、环境准备2、安装Crane3、访问dashboard4、页面展示5、功能应用 四、Crane的优势总结 前言 FinOps&#xff08;Financial Operations&#xff09;是一种管理云计算成本的方法&#xff0c;它强调将云计…

使用PyTorch构建神经网络,并使用thop计算参数和FLOPs

文章目录 使用PyTorch构建神经网络&#xff0c;并使用thop计算参数和FLOPsFLOPs和FLOPS区别使用PyTorch搭建神经网络整体代码1. 导入必要的库2. 定义神经网络模型3. 打印网络结构4. 计算网络FLOPs和参数数量5. 结果如下手动计算params手动计算FLOPs注意 使用PyTorch构建神经网络…

车联网强势发展下,有什么隐患?

通过新一代信息通信技术&#xff0c;车联网实现了汽车与云平台&#xff0c;车辆和汽车&#xff0c;道路&#xff0c;汽车和人以及内部的全方位网络链接。车联网使用传感器技术感知车辆的状态信息&#xff0c;并利用无线通信网络和现代智能信息处理技术的帮助实现交通智能化管理…

FPGA入门系列10--按键消抖

文章简介 本系列文章主要针对FPGA初学者编写&#xff0c;包括FPGA的模块书写、基础语法、状态机、RAM、UART、SPI、VGA、以及功能验证等。将每一个知识点作为一个章节进行讲解&#xff0c;旨在更快速的提升初学者在FPGA开发方面的能力&#xff0c;每一个章节中都有针对性的代码…

Redis高可用系列——Set类型底层详解

文章目录 概述intsetintset 和 hashtable 的转换为什么加入了listpackhashtable 的空间开销高hashtable 的碰撞概率高intset 、listpack和hashtable的转换 概述 在讲解set结构之前&#xff0c;需要先说明一下set结构编码的更替&#xff0c;如下 在Redis7.2之前&#xff0c;se…

Ansys Lumerical | CMOS - 光学仿真方法

通过使用更小的像素尺寸和更大的填充因子&#xff0c;基于CMOS图像传感器像素的数码相机系统的成本正在降低。但是&#xff0c;只有在不牺牲图像质量的情况下&#xff0c;CMOS像素尺寸减小才是可以接受的。随着CMOS像素尺寸的不断减小&#xff0c;图像信噪比降低&#xff0c;相…

《我命由我不由天》蔡志忠——笔记一

目录 简介 经典摘录 三岁决定一生 父母该什么时候放手 确定将来要成为什么 积极主动为目标而努力 叛逆是最伟大的创意 父亲给蔡志忠最大的影响是教会他两件事 价值观缺陷导致的后果 人有三个阶段 简介 作者 蔡志忠&#xff0c;李虹。 蔡志忠&#xff1a;漫画家、哲…

力扣-1769. 移动所有球到每个盒子所需的最小操作数

题目&#xff1a; 有 n 个盒子。给你一个长度为 n 的二进制字符串 boxes &#xff0c;其中 boxes[i] 的值为 ‘0’ 表示第 i 个盒子是 空 的&#xff0c;而 boxes[i] 的值为 ‘1’ 表示盒子里有 一个 小球。 在一步操作中&#xff0c;你可以将 一个 小球从某个盒子移动到一个与…

搜索引擎优化SEO和SEM有什么不一样

SEO&#xff08;搜索引擎优化&#xff09;和SEM&#xff08;搜索引擎营销&#xff09;都是用于提高网站在搜索引擎中的排名和能见度的技术。虽然它们的目标是相同的&#xff0c;但它们的方法和重点略有不同&#xff0c;今天和大家聊聊SEO和SEM有什么不同。 一、SEO SEO是指通…