VUE——组件通信

Posted 长不大的大灰狼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VUE——组件通信相关的知识,希望对你有一定的参考价值。

组件间的通信一般有以下几种形式:

一、ref

ref: 给元素或组件注册引用信息

用ref访问组件实例:

//子组件component-a
export default	
	data()
		return
			title: 'hello ref'
		
	,
	methods:
		sayHello()
			window.alert('world');
		
	

//父组件
<template>
  <component-a ref = "comA"></component-a>
</template>

<script>
  export default
    mounted()
		const comA = this.$refs.comA;
		console.log(comA.title);
		comA.sayHello();
	
 
</script>

二、$parent/$children

1、$children 获取子组件数据和调用子组件方法

//父组件
<template>
  <component-a></component-a>
</template>

<script>
  export default
       methods:
            do() 
               this.$children[0].sayHello()    // 调用子组件方法   $children获取的是父组件的直接子组件数组,不获取孙组件
               this.$children[0].title= 'hello'  // 改变子组件数据
            
        
</script>

2、$parent 获取父组件数据和调用父组件方法

//子组件component-a
export default	
	data()
		return
			title: 'hello ref'
		
	,
	methods:
		sayHello()
			window.alert('world');
		,
		show()
			this.$parent.do() //获取直属父组件
		
	

三、props

1、prop属性
prop定义了组件中可配置的属性。props最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值。

//子组件<i-button>
<template>
  <button :class="'i-button-size' + size" :disabled="disabled"></button>
</template>

<script>
  export default
    props:
    	size:
    	  default: 'default'
		,
		disabled:
			type: Boolean,
			default: false
		
	
  
</script>
//父组件
<template>
  <i-button size="large"></i-button>
    <i-button disabled = true></i-button>
</template>

2、html特性参数
使用组件时,也可以传入一些标准的html特性,比如id、class:

<i-button id="btn" class = "btn-submit"> </i-button>

HTML特性在组件内的<button>元素上会继承,并不需要在props中定义。

注意:
组件中定义的props都是单向数据流,只能通过父组件修改。

四、插槽slot

给子组件i-button添加一些文字内容:

//子组件<i-button>
<template>
  <button :disabled="disabled">
	<slot></slot>
  </button>
</template>
//父组件
<template>
   <i-button>
		<strong>按钮</strong>
   </i-button>
</template>

当需要多个插槽时,需要用到具名slot,比如再设置一个图标组件:

//子组件<i-button>
<template>
  <button :disabled="disabled">
    <slot name="icon"></slot>
	<slot></slot>
  </button>
</template>
//父组件
<template>
   <i-button>
   		<i-icon slot="icon" type = "checkmark"></i-icon>
		<strong>按钮</strong>
   </i-button>
</template>

注意:
在slot中也可以定义一些默认的内容,当父组件没有写任何内容时就会出现:

<slot> 默认内容 </slot>

五、自定义事件event

通过$emit可以在子组件中自定义事件on-click,在父组件中通过@on-click来监听。

//子组件<i-button>
<template>
  <button @click="handleClick"></button>
</template>

<script>
  export default
   methods:
   handleClick(event)
		this.$emit('on-click',event);
	
   
  
</script>
//父组件
<i-button @on-click="handleClick"></i-button>

六、provide/inject

1、基本用法
在父组件中通过provide将变量name提供给所有的子组件。

//父组件
export default
	provide:
		name: 'Aresn'
	
  

在子组件中通过inject注入name变量,便可以直接使用this.name访问这个变量了。

//子组件
export default
	inject: ['name'],
	mounted()
		console.log(this.name);
	
  

注意:
provide和indect绑定不是可响应的,即父组件的name发生了变更,子组件中并不会变。

2、使用provide/inject代替Vuex

(1)基本用法

Vue通常会导入一个入口组件app.vue作为根组件。我们可以将app.vue用来存储所有需要的全局数据和状态,甚至是计算属性、方法等。因为项目中的所有组件的父组件都是app.vue,所以我们可以将app.vue实例,通过provide对外提供。

<template>
    <div id="app">
        <router-view/>
    </div>
</template>
<script>
    export default 
		provide()
			return
				app: this
			
		
    
</script>

接下来,任何组件通过inject注入app.vue的app,都可以直接通过this.app.xxx来访问app.vue的data、computed、methods等内容。

注意:
app.vue是整个项目第一个被渲染的组件,而且只会渲染一次,利用这个特性可以做一次性全局的状态数据管理。

(2)进阶用法——引入混合mixins

当项目比较复杂时,app.vue中的代码就会变得结构复杂难以维护。这时可以使用Vue.js的混合mixins,将不同的逻辑分到不同的js文件中。

//user.js
 export default 
	data()
		return
			userInfo:null
		
	,
	methods:
		getUserInfo()
			this.userInfo = 'hello';
		
	
    

引入混合

//user.js
<script>
 import mixins_user from './mixins/user.js'
 export default 
 	mixins: [mixins_user],
	data()
		return
		
	
    
</script>

七、自定义dispatch和broadcast方法

Vue.js 2.x中已经废弃的方法dispatch和broadcast方法。
dispatch: 用于向上级派发事件,它的父级(一级或多级)都可以通过$on监听到 。
broadcast: 用于从上级向下级广播事件。

示例:dispatch

//子组件
<template>
  <button @click="handleClick"></button>
</template>

<script>
  export default
   methods:
   handleClick(event)
		this.$dispatch('on-click','hello');
	
   
  
</script>
//父组件
  export default
   mounted()
		this.$on('on-click',(text)=>
			console.log(text);
		)
   
  

想要在Vue2.x 以上版本使用dispatch和broadcast方法需要自己实现:

//emitter.js
function broadcast(componentName, eventName, params) 
    this.$children.forEach(child => 
        const name = child.$options.name;

        if (name === componentName) 
            child.$emit.apply(child, [eventName].concat(params));
         else 
            broadcast.apply(child, [componentName, eventName].concat([params]));
        
    );

export default 
    methods: 
        dispatch(componentName, eventName, params) 
            let parent = this.$parent || this.$root;
            let name = parent.$options.name;

            while (parent && (!name || name !== componentName)) 
                parent = parent.$parent;

                if (parent) 
                    name = parent.$options.name;
                
            
            if (parent) 
                parent.$emit.apply(parent, [eventName].concat(params));
            
        ,
        broadcast(componentName, eventName, params) 
            broadcast.call(this, componentName, eventName, params);
        
    
;

使用:

//componentA
<template>
  <button @click="handleClick"></button>
</template>

<script>
  import Emitter from './mixins/emitter.js';
  export default
   name: 'componentA',
   mixins: [Emitter],
   methods:
   handleClick()
		this.$broadcast('componentB','on-message','Hello ComB');
	
   
  
</script>
//componentB
 export default
   name: 'componentB',
   created()
		this.$on('on-message',this.showMessage);
	
   methods:
     showMessage(text)
		window.alert(text);
	
   

参数说明:
broadcast(componentName, eventName, params)

  • componentName: 组件名
  • eventName:事件名
  • params:只能传一个参数,多参数可通过对象的形式传递。

来源:《Vue.js组件精讲》

八、找到任意组件实例

//assist.js
// 由一个组件,向上找到最近的指定组件
function findComponentUpward (context, componentName) 
    let parent = context.$parent;
    let name = parent.$options.name;

    while (parent && (!name || [componentName].indexOf(name) < 0)) 
        parent = parent.$parent;
        if (parent) name = parent.$options.name;
    
    return parent;

export  findComponentUpward ;

// 由一个组件,向上找到所有的指定组件
function findComponentsUpward (context, componentName) 
    let parents = [];
    const parent = context.$parent;

    if (parent) 
        if (parent.$options.name === componentName) parents.push(parent);
        return parents.concat(findComponentsUpward(parent, componentName));
     else 
        return [];
    

export  findComponentsUpward ;

// 由一个组件,向下找到最近的指定组件
function findComponentDownward (context, componentName) 
    const childrens = context.$children;
    let children = null;

    if (childrens.length) 
        for (const child of childrens) 
            const name = child.$options.name;

            if (name === componentName) 
                children = child;
                break;
             else 
                children = findComponentDownward(child, componentName);
                if (children) break;
            
        
    
    return children;

export  findComponentDownward ;

// 由一个组件,向下找到所有指定的组件
function findComponentsDownward (context, componentName) 
    return context.$children.reduce((components, child) => 
        if (child.$options.name === componentName) components.push(child);
        const foundChilds = findComponentsDownward(child, componentName);
        return components.concat(foundChilds);
    , []);

export  findComponentsDownward ;

// 由一个组件,找到指定组件的兄弟组件
function findBrothersComponents (context, componentName, exceptMe = true) 
    let res = context.$parent.$children.filter(item => 
        return item.$options.name === componentName;
    );
    let index = res.findIndex(item => item._uid === context._uid);
    if (exceptMe) res.splice(index, 1);
    return res;

export  findBrothersComponents ;

function typeOf(obj) 
    const toString = Object.prototype.toString;
    const map = 
        '[object Boolean]'  : 'boolean',
        '[object Number]'   : 'number',
        '[object String]'   : 'string',
        '[object Function]' : 'function',
        '[object Array]'    : 'array',
        '[object Date]'     : 'date',
        '[object RegExp]'   : 'regExp',
        '[object Undefined]': 'undefined',
        '[object Null]'     : 'null',
        '[object Object]'   : 'object'
    ;
    return map[toString.call(obj)];

// deepCopy
function deepCopy(data) 
    const t = typeOf(data);
    let o;

    if (t === 'array') 
        o = [];
     else if ( t === 'object') 
        o = ;
     else 
        return data;
    

    if (t === 'array') 
        for (let i = 0; i < data.length; i++) 
            o.push(deepCopy(data[i]));
        
     else if ( t === 'object') 
        for (let i in data) 
            o[i] = deepCopy(data[i]);
        
    
    return o;


export deepCopy;

使用:

<script>
	import findComponentUpward from '../utils/assist.js';

	export default
		name: 'comPonentB';
		mounted(以上是关于VUE——组件通信的主要内容,如果未能解决你的问题,请参考以下文章

Vue父子组件间通信(数据传递)

vue2组件通信之$parent/$root

VUE:组件与组件之间的通信

Vue 组件通信

vue组件间通信

Vue 非父子组件通信方案