四、第四部分


19、全局通信之全局事件总线

<body>

  <script>
    // 1、全局事件总线:任意组件间通信

    // 首先既然能实现全局通信,那么你可以理解,这个东西肯定是在Vue上的
    // 那么你先在ts文件里引入Vue,声明一个bus总线,并导出
    import Vue from 'vue'
    export const jjwuBus = new Vue();

    // 在Vue其他组件里引用这个Bus
    import { jjwuBus } from '@/pages/...';
    // 在一个组件里给总线绑定上一个事件
    const str = '你好,我叫jjwu';
    jjwuBus.$emit('sayHello', str);

    // 在另一个组件创建的时候给bus添加上一个监听事件,正常是写在mounted里面,就能拿到这个str
    // 组件销毁的时候,需要注意销毁哦,虽然最后Vue销毁的时候会清空这些
    // 但是组件都已经销毁了,那他肯定用不上jjwuBus了嘛,不要占用资源嘛
    mounted() {
      jjwuBus.$on('sayHello', (val) => {
        this.myStr = val;
      });
    }

    beforeDestroy() {
      jjwuBus.$off('sayHello');
    }

    // 这里有一个需要注意的地方就是,我在讯飞的AI资源平台做的项目里面
    // 创建了很多总线,大家各用各的,这样其实不好,因为一个项目一个
    // 总线就够了,所以实际应该是在创建项目的man.js中,new Vue实例的时候,把总线装上,具体看下
    new Vue({
      beforCreate() {
        Vue.prototype.$bus = this //这里的this就是vm实例自己,也就是全局的Vue实例对象,$bus就是当前应用的vm了,所有组件都能访问
      }
    })

    // 为什么大家都能访问,我给你捋一捋:组件(vc)通过隐式原型链找=> VueComponent.prototype => 通过内置关系找到VueComponent.prototype.__proto__.找到了
    // => Vue.prototype => 然后就找到了Vue.prototype.$bus,也就是vm,所以所有的组件都能访问到vm,而且访问的都是同一个vm,这个vm里($bus)里有我们需要的$on、$off、$emit

  </script>
</body>

20、全局通信之消息订阅与发布

<body>

  <script>
    // 1、消息订阅与发布(A组件需要把数据传给B组件)
    // 这个得借用第三方库,最推荐: pubsub-js
    // 所以先安装包:npm i pubsub-js

    // 在B中引入这个库
    import pubsub from 'pubsub-js';
    // 在B中订阅
    // 先全局顶一个名称
    const subId;
    mounted() {
      // 这里一定要写箭头函数,不然this为undefined
      this.subId = pubsub.subscribe('transferData', (name, val)=> {
        // 这里的name的参数是消息的名称,有点多余
        this.data = val;
      })
    }
    // 在B销毁的时候,把订阅关闭
    beforeDestroy() {
      // 每次订阅的时候会返回一个名称,我们取消订阅的时候传入这个名称
      pubsub.unsubscribe(this.subId);
    }

    // 在A中发布消息,在你想发布这条消息的的函数中写
    function setData() {
      pubsub.publish('transferData', this.allData)
    }

  </script>
</body>

21、Vue的过渡动画

<!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>Document</title>
  <!-- 第一种写法 -->
  <style>
    .hello-enter-active {
      animation: test 0.5s linear;
    }
    /* reverse是反转的效果 */
    .hello-leave-active {
      animation: test 0.5s linear reverse;
    }

    @keyframes test {
      from {
        transform: translateX(-100%);
      }
      to {
        transform: translateX(0px);
      }
    }
  </style>

  <!-- 第二种写法 -->
  <style>
    /* 进入的起点、离开的终点 */
    .hello-enter, .hello-leave-to {
      transform: translateX(-100%);
    }

    /* 过度过程 */
    .hello-enter-active, .hello-leave-active {
      transition: 0.5s linear;
    }

    /* 进入的终点、离开的起点 */
    .hello-leave, .hello-enter-to {
      transform: translateX(0px);
    }
  </style>


</head>

<body>
  <button @click="isShow = !isShow">显示/隐藏</button>
  <transition name="hello" apper>
    <h1 v-show="isShow">你好啊!!!</h1>
  </transition>

  <script>
    // 1、Vue的过度与动画:transition标签,搭配v-show使用

    // apper开启是首次渲染就有动画效果

    // name是绑定一个名称然后写样式的时候要匹配上

    // 需要注意的是transition标签最后并不会当作标签使用,浏览器是不认得,只有Vue本身在用它

    // 当v-show为true时,会执行from => to的动画,当v-show为false的时候,会执行to =>from

    // 值得注意的是:transition只能包裹一个标签,如果需要包裹多个标签用transition-group
    // 效果是一样的,但是用transition-group的时候需要给每一个包裹的子标签加上key属性


    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // 推荐一个特别好用的动画库: animate.css 具体怎么用看官网

    // 用法:
    // 安装:npm install animate.css --save
    // 引用:import 'animate.css';
    <transition-group apper
     name="animate__animated animate__bounce"
     enter-active-class="animate__swing"
     leave-active-class="animate__backOutUp"
    >
      <h1>看我</h1>
    </transition-group>

  </script>
</body>

</html>

22、解决跨域的问题

<body>

  <script>
    // 1、那说到跨域,我们先来说说发送ajax请求的常用方法有哪些

    // 1.1、xhr:new XMLHttpRequest()、xhr.open()、xhr.send()
    // 他是发送ajax请求的鼻祖,但是很少用,一般都是在他的基础上做一些封装

    // 1.2、jQuery:基于xhr封装的, $.get $.post

    // 1.3、axios: 大名鼎鼎,经常用它
    // 优势:他是promise风格的,具有请求拦截器和相应拦截器,并且体积小是jQuery的四分之一

    // 1.4、fetch: 他和xhr是平级的,xhr是js内置的对象可以直接用
    // fetch也是,它是一个内置的方法,不需要封装了,而且他也是promise风格的
    // fetch用的也不少,但是没有axios用的多
    // fetch的致命问题:做了两层Promise封装,你得点两次then;,最可怕的是兼容问题,IE浏览器不兼容fetch


    // jQuery的核心目的是为了封装对DOM的操作(80%),而Vue的目的就是为了减少对DOM的操作
    // 所以他俩的存在本身就冲突啦



    // 2、好啦那我们用axios
    // 2.1、发个请求吧
    // 假如有个地址用来获取学生信息,服务器地址是:http://localhost:5000/students
    // 还有个地址用来获取汽车信息,服务器地址是:

    // 2.2、下载引入axios
    // npm i axios
    import axios from 'axios';

    // 2.2、在方法里请求
    function getData() {
      axios.get('http://localhost:5000/students').then(
        response => {
          console.log(response.data)
        },
        error => {
          console.log(error.message)
        }
      )
    }

    // 你会发现你报错了,报错信息里有CORS以及Access-control-Allow-Origin
    // 马上知道你跨域了,违背了同源策略:协议名(http)、主机名(localhost)、端口号(5000)
    // 你在本地请求为什么不对,因为你的协议名和主机名对上了,但是你的端口号是8080啊!!!!!
    // 你违背了呀,你的ajax请求违背了同源策略呀,过程是这样的,服务器收到了你的请求,
    // 并且把数据给了浏览器,但是浏览器没有把数据给你,因为他发现你跨域了,算了数据不给你了

    // 2.2、那么怎么解决跨域呢????????????
    // 2.2.1、首先最标准的方式是cors,前端啥都不用干,后端在返回响应的时候加几个特殊的响应头
    // 这个其实不太好,因为你配置好了,那任何人都能找你的服务器要数据,而且麻烦后端

    // 2.2.2、jsonp,用script的src属性,它不受同源策略的影响,但是基本没人用,首先它只能解决
    // get请求,其次他需要前后端配合写东西,麻烦

    // 2.2.3、用的比较多的:代理服务器
    // 8080   =>    proxy(一个代理服务器,把代理的端口号写为8080)    =>     5000
    // 这样8080和proxy的协议域名端口号就是完全一样了,不受同源策略的影响
    // 同时proxy本身是代理服务器,5000是服务器,服务器和服务器之间又不用ajax没有同源策略一说,可以互通数据

    // 2.2.4、那怎么才能代理proxy代理服务器呢
    // 最好的就是nginx代理,但是学习成本比较高
    // 比较合适的是用vue-cli给我们准备好的

    // 2.3、怎么配置代理服务器
    // 看下一节直接在vue.config.js中配置

  </script>
</body>

23、Vue-cli配置代理服务器

const { defineConfig } = require('@vue/cli-service')
// 这种暴露方式是common.j的暴露模式
module.exports = defineConfig({
  transpileDependencies: true,
  // 关闭语法检查
  lintOnSave: false,

  // 方式一!!!!!!!!!!!!!!!!!
  /* devServer: {
    // 代理5000这个服务器
    proxy: 'http://localhost:5000'
  } */

  //配置完之后找服务器要数据不要再找5000了,而是找代理服务器要
  /* function getData() {
    axios.get('http://localhost:8080/students').then(
      response => {
        console.log(response.data)
      },
      error => {
        console.log(error.message)
      }
    )
  } */

  // 需要注意的是我们的8080本地就是我们哪个public文件夹,比如我们访问
  // http://localhost:8080/favicon.ico 就可以直接找到哪个图标
  // 所以需要注意的是,代理服务器会判断你要请求的资源本地有没有,如果有的话
  // 就不会发出请求,而是直接给你本地的,没有的话再去发请求,请求真正的服务器

  // 总结上面的代理配置有两个缺陷!!!!!!!!!!!!!!!!!!
  // 第一个是:单一代理,只能配置一个,所有的请求都被代理去5000了
  // 第二个是:没有办法控制代理先找本地再找服务器



  // 第二种配置代理的方式!!!!!!!!!!!!!!!!!!!!!!!!
  // 增加请求前缀
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:5000',
        // 我们访问的服务器路径不能有api,这个只是帮助我们自己区分代理的,
        // 所以实际访问的时候需要把路径里的api去掉,以/api开头的全都替换掉
        pathRewrite: {'^/api': ''},
        ws: true, // 这个配置适用于支持websocket的
        changeOrigin: true // false: 代理服务器向服务器实话实说,我是8080端口; true: 代理服务器向服务器说谎,我是5000端口
        // 所以changeOrigin这个配置是用来控制请求头中的host值的
      },
      '/jjwu27': {
        target: 'http://localhost:5001',
        pathRewrite: {'^/jjwu27': ''},
        ws: true,
        changeOrigin: true
      }
    }
  }

  // 在请求的时候需要注意地址要加上请求前缀,紧跟端口号
  /* function getData() {
    axios.get('http://localhost:8080/api/students').then(
      response => {
        console.log(response.data)
      },
      error => {
        console.log(error.message)
      }
    )
  } */

  /* function getData() {
    axios.get('http://localhost:8080/jjwu27/cars').then(
      response => {
        console.log(response.data)
      },
      error => {
        console.log(error.message)
      }
    )
  } */

  // 有了前缀就不会走本地了,即使本地有资源也不会请求
})

24、插槽

<script>
  // 1、插槽
  // 插槽有默认插槽、具名插槽、作用域插槽,前两个都很简单,只说最后一个


  // 对了说一个有意思的东西
  // 定义插槽组件: slotVue
  <div>
    <h2>我是携带了插槽的组件,别人调用我的时候可以往里面随便塞东西!</h2>
    <slot name="content"></slot>
  </div>

  // 对比两种写法,都能实现, 对比一下
  // 调用这个插槽组件
  // 第一种写法
  <slotVue>
    <h1 slot="content">我要塞进插槽里</h1>
    <h2 slot="content">我也要塞进插槽里</h2>
  </slotVue>

  // 第二种写法
  <slotVue>
    <div slot="content">
      <h1>我要塞进插槽里</h1>
      <h2>我也要塞进插槽里</h2>
    </div>
  </slotVue>

  // 对比一下都知道,第二种写法更舒服,但是第二种写法会平白无故的增加一个div标签
  // 用template更舒服嘛,省了一层结构,但是用template写法的话就得这么写了 v-slot:footer
  <slotVue>
    <template v-slot:content>
      <h1>我要塞进插槽里</h1>
      <h2>我也要塞进插槽里</h2>
    </template>
  </slotVue>

  // 哈哈哈哈哈哈现在我们开始来说作用域插槽
  // 继续先写一有插槽的组件: slotVue
  <div>
    <h3>我有插槽</h3>
    <slot name="content" :games="games" :people="peoples"></slot>
  </div>

  // 然后呢这个组件里有数据
  const games = ['红色警戒', '穿越火线', '劲舞团', '超级玛丽']
  const peoples = ['吴俊杰', '南瓜饼']
  // 那我们在其他组件里调用这个有插槽的组件并往插槽里写内容组件的时候,怎么访问到
  // games数组呢,就是上方的绑定数据就好了
  // 那就是作用域插槽了
  // 我们来使用这个有插槽的组件,想要拿到作用域插槽传的值就必须用template包裹
  <slotVue>
    <template v-slot:content=slotProps>
      <div v-for="(item, index) in slotProps.games" :key="index">{{item}}</div>
      <div v-for="(item, index) in slotProps.peoples" :key="index">{{item}}</div>
    </template>
  </slotVue>
</script>

文章作者: 吴俊杰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吴俊杰 !
  目录