1、引用关系说明

  • 最终目的:使用Konva 库绘制组件,该组件由两个按钮、一个电平表、一个增益控制推杆,这些子组件组合起来构成所需组件,并将其绘制到Stage中的Layer
    • StageLayer只有一个,所以应当写在App.vue中,使用时将Layer传递给子组件(且这个Layer应当是响应式的);且由于要绘制所需组件,因此自然是要引用所需组件,即所需组件是App.vue的子组件
    • 所需组件应当引用各个子组件,它是各个子组件的父亲
  • 综上,是一个三层的继承关系,此外,由于有了StageLayer才能绘制所需组件,有了所需组件才能绘制各个子组件,此时,各个控件的初始化顺序与生命周期刚好相反

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>
      
    
07-15 08:31