1、引用关系说明
- 最终目的:使用
Konva
库绘制组件,该组件由两个按钮、一个电平表、一个增益控制推杆,这些子组件组合起来构成所需组件,并将其绘制到Stage
中的Layer
上Stage
和Layer
只有一个,所以应当写在App.vue中,使用时将Layer
传递给子组件(且这个Layer应当是响应式的);且由于要绘制所需组件,因此自然是要引用所需组件,即所需组件是App.vue的子组件- 所需组件应当引用各个子组件,它是各个子组件的父亲
- 综上,是一个三层的继承关系,此外,由于有了
Stage
和Layer
才能绘制所需组件,有了所需组件才能绘制各个子组件,此时,各个控件的初始化顺序与生命周期刚好相反。
2、两层继承关系示例
App.vue
<template> <div id="app"> <div id="frame"> <!-- 7. 将所需子组件展示到页面 --> <Channel /> </div> </div> </template> <script> import Konva from 'konva'; import { computed } from 'vue'; // 5. 引入所需组件用于绘制和页面展示 import Channel from './components/Channel.vue'; export default { // 2. 父组件将 layer 传递给子组件,子组件没有 layer 就无法绘制组件 provide() { // 依赖注入,所有子组件可获取 return { // 3. 传递给子组件的 layer应当是响应式的,否则对子组件的修改无法同步到父组件的layer layer: computed(() => this.layer), // 4. 响应式的区别,类比C语言的直接传参和指针传参 } }, components: { // 6. 注册子组件 Channel, }, mounted() { // 0.初始化组件 this.initializeKonva(); window.addEventListener("resize", this.handleResize); }, beforeUnmount() { window.removeEventListener("resize", this.handleResize); }, data() { return { stage: null, layer: null, }; }, methods: { initializeKonva() { this.stage = new Konva.Stage({ container: "frame", width: window.innerWidth, height: window.innerHeight, }); // 1. 这里为了解耦和效率,全局使用一个layer this.layer = new Konva.Layer(); this.stage.add(this.layer); }, handleResize() { this.stage.width(window.innerWidth); this.stage.height(window.innerHeight); this.stage.batchDraw(); }, }, }; </script> <style scoped> /* 样式细节不表 */ </style>
Channel.vue
<template> <div> <div ref="container"></div> </div> </template> <script> import Konva from 'konva'; export default { // 1. 接收父组件依赖注入的 layer inject: ['layer'], components: {}, data() {return {};}, mounted() { // 2. 使用 this.$nextTick(() => {}),在DOM更新之后执行回调函数 this.$nextTick(() => { // 3. 初始化 this.initializeKonva(); }); }, methods: { initializeKonva() { this.group = new Konva.Group({ // ... }); const backgroundRect = new Konva.Rect({ // ... }); const textTop = new Konva.Text({ // ... }); this.textLevel = new Konva.Text({ // ... }); this.textGain = new Konva.Text({ // ... }); const textBottom = new Konva.Text({ // ... }); const line1 = new Konva.Line({ // ... }); const line2 = new Konva.Line({ // ... }); const line3 = new Konva.Line({ // ... }); this.group.add(backgroundRect, textTop, this.textLevel, this.textGain, textBottom, line1, line2, line3); // 4. layer 是通过依赖注入传递,inject接收的,使用 this 访问 this.layer.add(this.group); // 5. 更新 layer this.layer.draw(); }, }, }; </script> <style></style>
3、三层及以上继承关系示例
Channel.vue
<template> <div> <div ref="container"></div> <!-- 3. v-if="flag" 控制子组件的初始化时机 --> <SwitchButton :btnNameIndex="0" :x="0" :y="group.height() / 17 + group.height() / 17 / 4" :parent="this.group" v-if="flag" /> <SwitchButton :btnNameIndex="1" :x="0" :y="group.height() / 17 * 3 - group.height() / 17 / 3" :parent="this.group" v-if="flag" /> <!-- 4. :parent="this.group" 将this.group传递给子组件,命名为parent,这种传递方式默认为响应式,无需其他操作 --> <LevelMeter :x="0" :y="group.height() / 17 * 4 + group.height() / 17 / 2" :parent="this.group" v-if="flag" @levelChangeEvent="handleLevelChange" /> <Gain :x="0" :y="group.height() / 17 * 4 + group.height() / 17 / 4" :parent="this.group" v-if="flag" @dBChangeEvent="handleDBChange" /> </div> </template> <script> import Konva from 'konva'; import SwitchButton from './SwitchButton.vue'; import LevelMeter from './LevelMeter.vue'; import Gain from './Gain.vue'; export default { inject: ['layer'], components: { SwitchButton, LevelMeter, Gain, }, data() { return { // ... // 0. 准备一个flag用于确认初始化时机 flag: false, group: null, }; }, mounted() { // 1. 存在父亲,切需要使用父亲中的 layer ,等待父组件初始化完成 this.$nextTick(() => { this.initializeKonva(); // 2. 使用flag判断是否已经初始化完成 this.flag = true; }); }, methods: { initializeKonva() { // ... this.layer.add(this.group); this.layer.draw(); }, handleDBChange(newDB) { // ... }, handleLevelChange(newLevel) { // ... }, }, }; </script> <style></style>