前端Vue

vue最全的组件通信和插槽,看这一篇就够了

2021-10-09  本文已影响0人  江川哇

组件通信常用方式
props
父给子传值

//child
props: {msg: String}
//parent
<HelloWord msg="Hello"/>

自定义事件
子给父传值
$emit

//child
this.$emit('add', good)

//parent
<Cart @add="cartAdd($event)"></cart>

$bus事件总线
任意两个组件之间传值常用事件总线 或 vuex的方式。

1 .Vue.prototype.$bus = new Vue()
2.自己手写Bus 事件派发 监听和回调管理 有点类似手写promise里面的 resolve reject
class Bus {
  constructor() {
    this.callbacks = {}
  }
  $emit(name,args) {
    if(this.callbacks[name]) {
      this.callbacks[name].forEach(cb => cb(args))
    }
  }
  $on(name,fn) {
    this.callbacks[name] = this.callbacks[name] || []
    this.callbacks[name].push(fn)
  }
}

//main.js
Vue.prototype.$bus = new Bus()
//child1
this.$bus.$on('foo',handle)
//child2
this.$bus.$emit('foo') 

event bus

vuex
唯一的全局数据管理者store,通过它管理数据并通知组件状态变更。

边界情况
$parent/$root
兄弟组件之间通信可以通过共同祖辈搭桥 $parent 或$root

//brother1
this.$parent.$on('foo', handle)
//brother2
this.$parent.$emit('foo')

$children

父组件可以通过$children访问子组件实现父子通信

//parent
this.$children[0].xxx = 'xxx'
//注意:$children 不能保证子元素顺序 

$parent $children $root不建议使用 因为他们耦合性太强了。

$refs
获取子节点引用

//parent
<Helloworld ref='hw'></Hellowrold>
mounted() {
  this.$refs.hw.xx = 'xxx'
}

provide/inject
能够实现祖先和后代之间的传值

provide() {
  return {foo: 'foo'}
}
inject:['foo']

$attrs $listeners
非prop特性 ,可以包含所有父作用域的绑定(class style除外)并且可以通过v-bind=“$attrs”传入组件内部,创建高级组件时特别有用

//child :并未在props中声明foo
<p>{{$attrs.foo}}</p>
//parent
<HelloWorld foo="foo"></Helloworld>

插槽

插槽语法是vue实现的内容分发api 适合复合组件开发,在通用组件库的开发中大量使用。

匿名插槽

//comp1
<div>
  <slot></slot>
</div>

//parent
<comp>Hello</comp>

具名插槽
将内容分发到子组件指定位置

//comp2
<div>
  <slot></slot>
  <slot name="content"></slot>
</div>

//parent
<comp2>
//默认插槽将default 作为参数
  <template v-slot:default></template>
//具名插槽将插槽名作为参数
  <template v-slot:content></template>
</copm2>

作用域插槽
分发内容到子组件的数据

//comp3
<div>
  <slot :foo='foo'></slot>
</div>
//parent
<comp3>
//用v-slot的值指定作用域上下文对象
  <template v-slot:default="slotProps">
    来自子组件数据{{slotProps.foo}}
 </template>

</comp3>

实现弹窗组件

弹窗这类组件的特点是他们在当前vue实例之外独立存在(Telport),通常挂载在body,,他们是通过js动态创建的,不需要在任何组件中声明。常见使用姿势
this.$create(Nitice, {
  title: '标题',
  message: '弹窗主体内容',
  duration: '1000'
}).show()

create函数

//传入一个组件的配置
//创建她的实例,并且挂载到body上
import Vue from 'vue'
export default function Create (Component,props) {

//方式1、Vue.extend()
  const Ctor = Vue.extend( )
//创建组件实例
 const comp = new Ctor({propsData: props})
 comp.$mount()
 ducument.body.appenChild(comp.$el)
 comp.remove = function () {
  document.body.removeChild(comp.$el)
  comp.$destroy()
}



//方式2 借鸡生蛋 借助vue的实例的render函数
  const vm = new Vue({
    render(h) {
      return h (Component, { props })
    }
  }).$mount()
  document.body.appendChild(vm.$el)
  //删除函数
  const comp = vm.$children[0]
  comp.remove = () => {
    vm.$destroy()
    document.body.removeChild(vm.$el)
  }
  return comp
}
通知组件

建通知组件,Notice.vue

<template>
  <div class="box" v-if="isShow">
    <h3>{{title}}</h3>
    <p class="box-content">{{message}}</p>
  </div>
</template>

<script>
export default {
  props: {
    title: {
      type: String,
      default: ""
    },
    message: {
      type: String,
      default: ""
    },
    duration: {
      type: Number,
      default: 1000
    }
  },
  data() {
    return {
      isShow: false
    };
  },
  methods: {
    show() {
      this.isShow = true;
      setTimeout(this.hide, this.duration);
    },
    hide() {
      this.isShow = false;
      this.remove();
    }
  }
};
</script>

<style>
.box {
  position: fixed;
  width: 100%;
  top: 16px;
  left: 0;
  text-align: center;
  pointer-events: none;
  background-color: #fff;
  border: grey 3px solid;
  box-sizing: border-box;
}
.box-content {
  width: 200px;
  margin: 10px auto;
  font-size: 14px;  
  padding: 8px 16px;
  background: #fff;
  border-radius: 3px;
  margin-bottom: 8px;
}
</style>
使用create API
<script>
  import create from '@/utils/create'
  import Notice from '@/components.Notice'
  export default {
    methods: {
      submitForm(form) {
        const notice = create(Notice, {
            title: '弹窗标题',
            message:'弹窗内容',
            duration: 1000,
        })
       notice.show()
      }
    }
  }
</script>
上一篇下一篇

猜你喜欢

热点阅读