基础
父子组件
- 父组件引用子组件
- 父组件给子组件传值(props)
- 子组件触发父组件方法($emit)
- props双向数据绑定(.sync)
- 父组件调用子组件方法和值(ref)
- 子组件使用父组件的值和方法($parent)
兄弟组件
- 子——>父——>子(通过上面的父子组件传递的方法)
- vuex
- event-bus
文章目录
进阶(跨级通信)
##1. provide / inject
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
只能向下传递
// index.vue
export default {
provide: {
name: 'Aresn'
}
}
// C.vue
export default {
inject: ['name']
}
provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
所以,上面 index.vue 的 name 如果改变了,C.vue 的 this.name 是不会改变的,仍然是 Aresn。
//对象是,可响应的。
export default {
data() {
return {
obj: {
name: "Aresn",
age: 12
}
};
},
provide() {
return {
obj: this.obj
};
}
}
2. $attrs
/$listeners
3.派发与广播(dispatch/broadcast)
通过$on
和$emit
实现向上派发(dispatch)和向下广播(broadcast)
- 在子组件调用 dispatch 方法,向上级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该上级组件已预先通过
$on
监听了这个事件; - 相反,在父组件调用 broadcast 方法,向下级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该下级组件已预先通过
$on
监听了这个事件。
具体方法如下,写入到js
( emitter.js)文件中,通过mixins
引入
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);
}
}
};
参数
- 组件的
name
值- 自定义事件名称
- 要传递的数据
使用方法
有 A.vue 和 B.vue 两个组件,其中 B 是 A 的子组件,中间可能跨多级,在 A 中向 B 通信:
<!-- A.vue -->
<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 Vue.js');
}
}
}
</script>
// B.vue
export default {
name: 'componentB',
created () {
this.$on('on-message', this.showMessage);
},
methods: {
showMessage (text) {
window.alert(text);
}
}
}
4. 找到任意组件实例——findComponents 系列方法
它适用于以下场景:
- 由一个组件,向上找到最近的指定组件;
- 由一个组件,向上找到所有的指定组件;
- 由一个组件,向下找到最近的指定组件;
- 由一个组件,向下找到所有指定的组件;
- 由一个组件,找到指定组件的兄弟组件。
// 由一个组件,向上找到最近的指定组件
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 };
5.跨层级组件实例——provide / inject
npm install vue-ref
||yarn add vue-ref
安装vue-ref
插件- 导入
import ref from "vue-ref"
- 使用插件
Vue.use(ref, { name: "ant-ref" });
name是给插件起名
使用方法
//使用`provide` 在根组件提供数据
provide() {
return {
//主动通知 将组件实例绑定在根组件上
setChildrenRef: (name, ref) => {
this[name] = ref;
},
//主动获取 获取绑定的组件
getChildrenRef: name => {
return this[name];
},
// 获取根组件
getRef: () => {
return this;
}
}
}
// 使用`inject` 在子组件中注入数据
inject: {
setChildrenRef: {
default: () => {}
},
getParentRef: {
from: "getRef",
default: () => {}
},
getParentChildrenRef: {
from: "getChildrenRef",
default: () => {}
}
}
//使用指令注册子组件
<ChildrenH v-ant-ref="ref => setChildrenRef('childrenH', ref)" />
//使用指令注册DOM元素
<h3 v-ant-ref="ref => setChildrenRef('childrenE', ref)">E 结点</h3>
vue-ref
插件源码
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = {
install: function install(Vue) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var directiveName = options.name || 'ref';
console.log(arguments)
Vue.directive(directiveName, {
bind: function bind(el, binding, vnode) {
//自定义指令传入值 是函数, 在这里执行 传入组件实例
binding.value(vnode.componentInstance || el, vnode.key); //vnode.key 是使用插件时起的名称
},
update: function update(el, binding, vnode, oldVnode) {
if (oldVnode.data && oldVnode.data.directives) {
var oldBinding = oldVnode.data.directives.find(function (directive) {
var name = directive.name;
return name === directiveName;
});
if (oldBinding && oldBinding.value !== binding.value) {
oldBinding && oldBinding.value(null, oldVnode.key);
// 如果指令绑定的值有变化,则更新 组件实例
binding.value(vnode.componentInstance || el, vnode.key);
return;
}
}
// Should not have this situation
if (vnode.componentInstance !== oldVnode.componentInstance || vnode.elm !== oldVnode.elm) {
binding.value(vnode.componentInstance || el, vnode.key);
}
},
unbind: function unbind(el, binding, vnode) {
binding.value(null, vnode.key);
}
});
}
};