• vue 组件通讯


    14 种组件通讯

    1. props

      这个就是父传子属性, props 值可以是一个数组或对象

    // 数组:不建议使用
    props:[]
    
    // 对象
    props:{
     inpVal:{
      type:Number, //传入值限定类型
      // type 值可为String,Number,Boolean,Array,Object,Date,Function,Symbol
      // type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认
      required: true, //是否必传
      default:200,  //默认值,对象或数组默认值必须从一个工厂函数获取如 default:()=>[]
      validator:(value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
     }
    }

    2. $emit

      这个也非常常见, 触发父组件的自定义事件, 其实就是父传子的方法

    1 // 父组件
    2 <home @title="title">
    3 // 子组件
    4 this.$emit('title',[{title:'这是title'}])

    3. vuex

      vuex 是一个状态管理器,

      一个独立的插件, 适合数据共享多的项目里面, 因为如果只是简单的通讯, 使用起来会比较重

    1 state:定义存贮数据的仓库 ,可通过this.$store.state 或mapState访问
    2 getter:获取 store 值,可认为是 store 的计算属性,可通过this.$store.getter 或 mapGetters访问
    4 mutation:同步改变 store 值,为什么会设计成同步,因为mutation是直接改变 store 值,
    5          vue 对操作进行了记录,如果是异步无法追踪改变.可通过mapMutations调用
    6 action:异步调用函数执行mutation,进而改变 store 值,可通过 this.$dispatch或mapActions 访问
    8 modules:模块,如果状态过多,可以拆分成模块,最后在入口通过...解构引入

    4. $attrs 和 $listeners

       2.4.0 新增 这两个是不常用的属性, 但是高级用法很常见; 

      $attrs 场景: 如果父传子有很多值, 那么在子组件需要定义多个

      props解决: attrs 获取子传父中未在props定义的值(在 $attrs里面只会有props没有注册的属性) -> (class 和 style 除外)

    1 // 父组件
    2 <home title="这是标题" width="80" height="80" imgUrl="imgUrl"/>
    3 
    4 // 子组件
    5 mounted() {
    6   console.log(this.$attrs) //{title: "这是标题",  "80", height: "80", imgUrl: "imgUrl"}
    7 },

        

    1 props: {
    2    {              // 父组件的width 在子组件props中注册后, 那么在$attrs上取不到
    3     type: String,
    4     default: ''
    5   }
    6 },
    7 mounted() {
    8   console.log(this.$attrs) //{title: "这是标题", height: "80", imgUrl: "imgUrl"}
    9 },

        

    $listeners 场景: 子组件需要调用父组件的方法解决: 父组件的方法可以通过 v-on="listeners" 传入内部组件 ----- 在创建更高层次的组件时非常有用 

    1 // 父组件
    2 <home @change="change"/>
    3 
    4 // 子组件
    5 mounted() {
    6   console.log(this.$listeners) //即可拿到 change 事件
    7 }

    $inheritAttrs 

    组件内未被注册的属性将作为普通html元素属性被渲染

     1 // 父组件
     2 <home title="这是标题" width="80" height="80" imgUrl="imgUrl"/>
     3 
     4 // 子组件
     5 mounted() {
     6   console.log(this.$attrs) //{title: "这是标题",  "80", height: "80", imgUrl: "imgUrl"}
     7 },
     8 
     9 inheritAttrs默认值为 true,也就是父组件上的属性会显示到根组件上
    10 如果设置为 false 就会隐藏

    5. provide 和 inject

        2.2.0 版本新增

      使用场景: 以允许一个祖先组件向其所有子孙后代注入一个依赖, 不论组件层次有多深, 并在起上下游关系成立的时间里始终生效。

       provide :一个对象或返回一个对象的函数

       inject : 一个字符串数组,或一个对象,对象的key是本地的绑定名

     1 <template>         // 父组件
     2     <div id="app">
     3     </div>
     4 </template>
     5     <script>
     6         export default {
     7             data () {
     8                     return {
     9                         datas: [
    10                             {
    11                                 id: 1,
    12                                 label: '产品一'
    13                             },
    14                             {
    15                                 id: 1,
    16                                 label: '产品二'
    17                             },
    18                             {
    19                                 id: 1,
    20                                 label: '产品三'
    21                             }
    22                         ]
    23                     }
    24             },
    25             provide {
    26                 return {
    27                     datas: this.datas
    28                 }
    29             }
    30         }
    31     </script>
    <template>    // 后代组件
        <div>
            <ul>
            <li v-for="(item, index) in datas" :key="index">
                {{ item.label }}
            </li>
            </ul>
        </div>
    </template>
        <script>
            export default {
                inject: ['datas']
            }
        </script>

     注意: provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个监听的对象,那么其对象的属性还是可响应的。

     响应式示例:

    父组件中提供
      provide() {
        return {
          map_nodeObj: { map_node: this.obj }
          // 提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
        }
      },
    
    子组件中引入
      inject: {
        map_nodeObj: {
          default: () => {
            return {map_node: '0'}
          }
        }
      },
    使用: this.map_nodeObj.map_node
    
    // 运行顺序
    data
    provide
    created  // 在这个阶段$el还未生成,在这先处理privide的逻辑,子孙组件才可以取到inject的值
    mounted
    ...

    6. $parent 和 $children

        $parent:指代的父组件, 返回的是一个组件集合 

      用法:this.$parent     (如果当前组件没有父组件,那么返回当前组件)

      $children:指代的子组件,返回的是一个组件集合

      用法: this.$children   (如果你能清楚的知道子组件的顺序,可以使用下标来表示)

    注意:(1) 组件只能有一个根节点

       (2) 可以在子组件中使用this.$parent.属性值, 或者函数

       (3) 在父组件中可以使用this.$children[i].属性

       (4) 需要注意this的指向

    7. ref 和 $refs

      ref 有三种用法:

      (1) ref加在普通的元素上,用this.$refs.name 获取到的是dom元素

      (2) ref 加在子组件上, 用this.$refs.name 获取到的是组件实例,可以使用组件的所有方法

      ref 和 v-for 在一起的情况

            

                                 

    8. $root

      $root 设置全局属性

    let app = new Vue({
        el: '#app',
        // 全局数据,在其他页面或者组建可改变
        data: function () {
        return {
            s: ''
        }
        }, 
        router,
        store,
        template: '<router-view></router-view>'
    })
    
    // a.vue
        this.$root.s = '设置了s属性'
    
    // b.vue
        console.log(this.$root.s)  //设置了s属性

    9.  .sync

      从2.3.0起重新引入的 .sync 修饰符,.sync被作为一个编译时的语法糖,它会被扩展为一个自动更新父组件属性的 v-on 监听器

    // 父组件
    <comp :foo.sync="bar"></comp>
    
    // 编译时会被扩展为:
    <comp :foo="bar" @update:foo="val => bar = val"></comp>
    
    
    //子组件
    
    //当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:
    // 所以子组件可以通过$emit 触发 update 方法改变
    this.$emit('update:foo', newValue)

    10. v-slot 插槽

      2.6.0新增

      (1) 匿名插槽

      默认插槽, 没有命名, 有且只有一个

     1 // 父组件
     2 <todo-list> 
     3     <template v-slot:default>
     4        任意内容
     5        <p>我是匿名插槽 </p>
     6     </template>
     7 </todo-list> 
     8 
     9 // 子组件
    10 <slot>我是默认值</slot>
    11 //v-slot:default写上感觉和具名写法比较统一,容易理解,也可以不用写

      (2) 具名插槽

      相对匿名插槽组件slot标签带有name命名的

     1 // 父组件
     2 <todo-list> 
     3     <template v-slot:todo>
     4        任意内容
     5        <p>我是匿名插槽 </p>
     6     </template>
     7 </todo-list> 
     8 
     9 //子组件
    10 <slot name="todo">我是默认值</slot>

      (3) 作用域插槽

      子组件内数据可以被父页面拿到(解决了数据只能从父页面传递给子组件)

     1 // 父组件
     2 <todo-list>
     3  <template v-slot:todo="slotProps" >
     4    {{slotProps.user.firstName}}
     5  </template> 
     6 </todo-list> 
     7 //slotProps 可以随意命名
     8 //slotProps 接取的是子组件标签slot上属性数据的集合所有v-bind:user="user"
     9 
    10 // 子组件
    11 <slot name="todo" :user="user" :test="test">
    12     {{ user.lastName }}
    13  </slot> 
    14 data() {
    15     return {
    16       user:{
    17         lastName:"Zhang",
    18         firstName:"yue"
    19       },
    20       test:[1,2,3,4]
    21     }
    22   },
    23 // {{ user.lastName }}是默认数据  v-slot:todo 当父页面没有(="slotProps")

      注意: 1. 父组件可以利用v-slot:header="slotProps" 接收组件中的消息, 组件中只需要在 <slot name="header" :header="header"><slot/>

          2. 如果被提供的内容只有一个默认插槽时, 组件的标签可以直接被当做插槽的模板来使用<template v-slot="slotProps">

          3. 动态参数也可是使用到插槽中 <template v-slot="{ user : person}">

          4. v-slot 缩写是#, 但是使用#的话,必须始终始终使用具名插槽来代替<template #default="slotProps">

        

    11. EventBus

      1.  就是声明一个全局Vue实例变量 EventBus, 把所有的通信数据, 事件监听都存粗到这个变量上;

      2. 类似于Vuex, 但这种方式只使用与极小的项目

      3. 原理就是利用 $on 和 $emit 并实例化一个全局vue 实现数据共享

      4. 可以实现平级, 嵌套组件传值, 但是对应的事件名 eventTarget 必须是全局唯一的

     1 // 在 main.js
     2 Vue.prototype.$eventBus=new Vue()
     3 
     4 // 传值组件
     5 this.$eventBus.$emit('eventTarget','这是eventTarget传过来的值')
     6 
     7 // 接收组件
     8 this.$eventBus.$on("eventTarget",v=>{
     9   console.log('eventTarget',v);//这是eventTarget传过来的值
    10 })

    12. broadcast 和 dispatch

    Vue 1.x 有这两个方法, 事件广播和派发, 但是 vue 2.x 删除了 下面是对两个方法进行的封装

     1 /*
     2  broadcast 事件广播
     3  @param {componentName} 组件名称
     4  @param {eventName} 事件名
     5  @param {params} 参数
     6  遍历寻找所有子孙组件,假如子孙组件和componentName组件名称相同的话,则触发$emit的事件方法,数据为 params.
     7  如果没有找到 则使用递归的方式 继续查找孙组件,直到找到为止,否则继续递归查找,直到找到最后一个都没有找到为止。 
     8  */
     9 function broadcast(componentName, eventName, params) {
    10   this.$children.forEach(child => {
    11     const name = child.$options.name;
    12     if (name === componentName) {
    13       child.$emit.apply(child, [eventName].concat(params));
    14     } else {
    15       broadcast.apply(child, [componentName, eventName].concat([params]));
    16     }
    17   })
    18 }
    19 /* 
    20  * dispatch 查找所有父级,直到找到要找到的父组件,并在身上触发指定的事件。
    21  @param { componentName } 组件名称
    22  @param { eventName } 事件名
    23  @param { params } 参数
    24  */
    25 export default {
    26   methods: {
    27     dispatch(componentName, eventName, params) {
    28       let parent = this.$parent || this.$root;
    29       let name = parent.$options.name;
    30 
    31       while (parent && (!name || name !== componentName)) {
    32         parent = parent.$parent;
    33 
    34         if (parent) {
    35           name = parent.$options.name;
    36         }
    37       }
    38       if (parent) {
    39         parent.$emit.apply(parent, [eventName].concat(params));
    40       }
    41     },
    42     broadcast(componentName, eventName, params) {
    43       broadcast.call(this, componentName, eventName, params);
    44     }
    45   }
    46 };

    13.  路由传参

    方案一

     1 // 路由定义
     2 {
     3   path: '/describe/:id',
     4   name: 'Describe',
     5   component: Describe
     6 }
     7 // 页面传参
     8 this.$router.push({
     9   path: `/describe/${id}`,
    10 })
    11 // 页面获取
    12 this.$route.params.id

    方案二

     1 // 路由定义
     2 {
     3   path: '/describe',
     4   name: 'Describe',
     5   omponent: Describe
     6 }
     7 // 页面传参
     8 this.$router.push({
     9   name: 'Describe',
    10   params: {
    11     id: id
    12   }
    13 })
    14 // 页面获取
    15 this.$route.params.id

    方案三

    // 路由定义
    {
      path: '/describe',
      name: 'Describe',
      component: Describe
    }
    // 页面传参
    this.$router.push({
      path: '/describe',
        query: {
          id: id
      `}
    )
    <router-link :to="{ path: '/describe', query: { id: 1111}}">click to page</router-link>
    // 页面获取 this.$route.query.id

    注意: 三种方案对比, 方案二参数不会拼接在路由后面, 页面刷新参数会丢失, 方案一和三参数拼接在后面容易暴露信息

    14. Vue.observable

      2.6.0新增

      用法: 让一个对象可响应。Vue内部会用它来处理data函数返回的对象;返回的对象可以直接用渲染函数和计算属性内, 并且会在发生改变是触发响应的更新;也可以作为最小化的跨组件状态存储器,用于简单的场景。通讯原理实质上是利用Vue.observable 实现一个简易的vuex

     1 // 文件路径 - /store/store.js
     2 import Vue from 'vue'
     3 
     4 export const store = Vue.observable({ count: 0 })
     5 export const mutations = {
     6   setCount (count) {
     7     store.count = count
     8   }
     9 }
    10 
    11 //使用
    12 <template>
    13     <div>
    14         <label for="bookNum">数 量</label>
    15             <button @click="setCount(count+1)">+</button>
    16             <span>{{count}}</span>
    17             <button @click="setCount(count-1)">-</button>
    18     </div>
    19 </template>
    20 
    21 <script>
    22 import { store, mutations } from '../store/store' // Vue2.6新增API Observable
    23 
    24 export default {
    25   name: 'Add',
    26   computed: {
    27     count () {
    28       return store.count
    29     }
    30   },
    31   methods: {
    32     setCount: mutations.setCount
    33   }
    34 }
    35 </script>
  • 相关阅读:
    Django【二】自定义web框架
    Django【一】web框架的本质
    Django【零】HTTP协议
    【前端】bootstrap引入
    【前端】jquery基础学习
    socket模块粘包现象理解以及解决思路
    面向对象编程学习笔记
    socket模块
    CPU缓存体系对Go程序的影响
    测试Go代码 单元测试
  • 原文地址:https://www.cnblogs.com/shenjilin/p/11649867.html
Copyright © 2020-2023  润新知