props / $emit
$parent / $children
$dispatch
一直递归找父组件,然后执行父组件中对应的 $emit 方法
1 2 3 4 5 6 7
| Vue.prototype.$dispatch = function (eventName, newValue) { let parent = this.$parent; while (parent) { parent.$emit(eventName, newValue); parent = parent.$parent; } };
|
$broadcast
一直往归找子元素,执行对应的方法执行
1 2 3 4 5 6 7 8 9 10 11 12
| Vue.prototype.$broadcast = function (eventName, newValue) { let children = this.$children; function broad(children) { children.forEach((child) => { child.$emit(eventName, newValue); if (child.$children) { broad(child.$children); } }); } broad(children); };
|
.sync / v-model
props 和 $emit 的语法糖
- 只需要将 $emit(fn) 中的 fn 改为 update:xxx 就可以了,其中 xxx 为父组件传递给子组件的数据名
- 在子组件中调用 $emit(fn, val) 的时候 fn 的名字一定是这样的格式的 update:xxx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <Son @update:number="newValue =>number = newValue" :number="number" />
<button @click="change">更新父组件的方法</button> methods: { change() { this.$emit('update:number', 200) } }
<Son @update:number="newValue => number = newValue" :number="number" />
<Son :number.sync="number" />
|
$attrs 和 $listeners
$attrs 和 v-bind
- 场景: 要将父组件的数据传递给孙组件,但是我们的子组件又没有使用到这些数据,这个时候可以使用 $attrs 了
- 局限性: 子组件不可以接收父组件的数据,也就是不可以有 props 钩子
1 2 3 4 5 6 7 8
| <Son :number="number" :count="count" />
<Grandson v-bind="$attrs" />
{{ $attrs }}
|
$listeners 和 v-on
- 通过 $listeners 和 v-on 的配合可以将全部事件传递给孙组件,子组件也是可以通过 $listeners 来接收父组件传递的全部事件
1 2 3 4 5 6 7 8 9 10
| <Son :number="number" @change="change" @say="say" :count="count" />
<Grandson v-on="$listeners" />
mounted() { console.log(this.$listeners) }
|
eventbus
1 2 3 4 5 6 7 8 9
| Vue.prototype.$bug = function () { return new Vue(); };
this.$bus.$on("change", () => {});
this.$bus.$emit("change");
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class EventEmitter { constructor() { this.events = {}; }
on(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push(callback); }
emit(eventName, ...args) { if (this.events[eventName]) { this.events[eventName].forEach((cb) => { cb(...args); }); } }
off(eventName, callback) { if (this.events[eventName]) { this.events[eventName] = this.events[eventName].filter( (cb) => cb !== callback ); } } }
const ee = new EventEmitter();
ee.on("sayHi", () => console.log("Hello!"));
ee.emit("sayHi");
const cb = () => console.log("Hello!"); ee.on("sayHi", cb); ee.off("sayHi", cb); ee.emit("sayHi");
|
ref
provide / inject
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| export default { provide() { return {vm: this} }, }
export default { inject: ['vm'], mounted() { console.log(this.vm) } }
|