写在前面:

  • 本篇内容内容主要讲述了,在使用 Konva 进行开发过程中遇到的一些问题。(既然是组件加载顺序,主要牵扯到的就是,父子组件的关系,父子组件的生命周期)

  • 众所周知,Vue中父子组件生命周期的执行顺序为:

    // 挂载阶段
    父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted // 更新阶段
    父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated // 销毁阶段
    父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
  • 然而,在某些情况下我们有其他需求,例如我们不得不让子组件的初始化在父组件初始化完成之后再进行(一般是针对mounted),下面将进行详细说明

1、引用关系说明

  • 最终目的:使用Konva 库绘制组件,该组件由两个按钮、一个电平表、一个增益控制推杆,这些子组件组合起来构成所需组件,并将其绘制到Stage中的Layer

    • StageLayer只有一个,所以应当写在App.vue中,使用时将Layer传递给子组件(且这个Layer应当是响应式的);且由于要绘制所需组件,因此自然是要引用所需组件,即所需组件是App.vue的子组件
    • 所需组件应当引用各个子组件,它是各个子组件的父亲
  • 综上,是一个三层的继承关系,此外,由于有了StageLayer才能绘制所需组件,有了所需组件才能绘制各个子组件,此时,各个控件的初始化顺序与生命周期刚好相反

2、两层继承关系示例

假如说目前我只有两层继承关系,App.vue 和所需组件 Channel.vue

代码在下方展示,详细的内容我将在代码中使用注释详细说明,请按照注释编号顺序进行阅读和理解

  • App.vue

    要点:

    • layer 依赖注入,使所有的子组件可以获取
    • 父组件的 layer 进行依赖注入时需要使用响应式,以便于父组件知道 layer 的改变(类比C语言的直接传参和指针传参)
    • 需要将所需组件引用、注册并展示到页面上
    <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

    要点:

    • 接收 layer

    • 使用 this.$nextTick(() => { 初始化代码 }),会使得初始化代码在父组件的初始化完成后再执行

      • this.$nextTick()Vue.js提供的一个方法,用于在DOM更新之后执行回调函数。它的作用是确保在下次DOM更新循环结束之后执行回调函数,以确保操作的准确性和可靠性。

      • Vue.js中,当数据发生改变时,Vue会异步地更新DOM。这意味着在修改数据后立即访问更新后的DOM可能无法得到正确的结果,因为此时DOM可能尚未完成更新。

        通过使用this.$nextTick()方法,我们可以将回调函数延迟到下一次DOM更新循环之后执行。在这个时候,Vue已经完成了所有的异步DOM更新,我们可以放心地操作更新后的DOM元素,确保获取到准确的结果。

    • 绘制完成后更新 layer

    <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、三层及以上继承关系示例

  • 在上面的内容中,使用this.$nextTick(() => { 回调 })解决了两层继承关系中的反向初始化顺序的问题。
  • 但是这本质上更像是一种小聪明,当到了三层以上继承关系的时候这种方法不能有任何效果,因为子组件和孙子组件如果不同时使用 this.$nextTick(() => { 回调 }) 总会有人在父组件之前初始化,而如果都用了 this.$nextTick(() => { 回调 }) 那么它们两个本身的初始化顺序仍然是先子后父,一定会出问题。所以要使用其他的方式来解决这个问题

代码在下方展示,详细的内容我将在代码中使用注释详细说明,请按照注释编号顺序进行阅读和理解

  • Channel.vue

    要点:

    • 接收 layer 等不再赘述
    • 使用 this.$nextTick(() => { 初始化代码 }),会使得初始化代码在父组件的初始化完成后再执行
    • 设置flag用于判断当前组件初始化是否完成,使用v-if="flag"控制子组件初始化时机
    <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>

Vue 先初始化子组件再初始化父组件的方法(自定义父子组件mounted执行顺序)的更多相关文章

  1. 二、Vue组件(component):组件的相互引用、通过props实现父子组件互传值

    一.组件各部分说明及互相引用 1.一个vue组件由三个部分组成 Template 只能存在一个根元素 2.Script 3.Style scoped:样式只在当前组件内生效 1.1 组件的基本引用代码 ...

  2. 子元素使用float 父元素撑开方法

    一个Div包含了多个子Div,并且子Div使用了浮动后,父Div确不能被撑开,如下图: 部分代码如下: 1 <style> 2   #div1{border:1px solid red;f ...

  3. 【转】C# 子窗体如何调用父窗体的方法

    网络上有几种方法,先总结如下: 调用窗体(父):FormFather,被调用窗体(子):FormSub. 方法1: 所有权法       //FormFather:        //需要有一个公共的 ...

  4. 【转】WPF查找子控件和父控件方法

    一.查找某种类型的子控件,并返回一个List集合 public List<T> GetChildObjects<T>(DependencyObject obj, Type ty ...

  5. WPF查找子控件和父控件方法

    一.查找某种类型的子控件,并返回一个List集合 public List<T> GetChildObjects<T>(DependencyObject obj, Type ty ...

  6. 【转】vue父子组件之间的通信

    vue父子组件之间的通信 在vue组件通信中其中最常见通信方式就是父子组件之中的通性,而父子组件的设定方式在不同情况下又各有不同.最常见的就是父组件为控制组件子组件为视图组件.父组件传递数据给子组件使 ...

  7. 关于vue.js父子组件数据传递

    vue.js中使用props down,events up的原则进行父子组件间的通信,先来记录下props down,看个例子: <div id="app2"> < ...

  8. vue组件之间的传值——中央事件总线与跨组件之间的通信($attrs、$listeners)

    vue组件之间的通信有很多种方式,最常用到的就是父子组件之间的传值,但是当项目工程比较大的时候,就会出现兄弟组件之间的传值,跨级组件之间的传值.不可否认,这些都可以类似父子组件一级一级的转换传递,但是 ...

  9. vue组件通信之父子组件通信

    准备工作: 首先,新建一个项目,如果这里有不会的同学,可以参考我转载过的文章 http://www.cnblogs.com/Sky-Ice/p/8875958.html  vue 脚手架安装及新建项目 ...

  10. Vue 非父子组件通信方案

    Vue 非父子组件通信方案 概述 在 Vue 中模块间的通信很普遍 如果是单纯的父子组件间传递信息,父组件可以使用 props 将数据向下传递到子组件,而在子组件中可以使用 events (父组件需要 ...

随机推荐

  1. 如何优雅的申请一个属于自己的ChatGPT账号

    前言 GPT-4是一种语言模型,是基于GPT-3推出的下一代自然语言处理模型.与之前的GPT模型一样,GPT-4是一种基于深度学习技术的神经网络模型,可以自动地生成人类水平的文本.回答问题.完成翻译任 ...

  2. Java设计模式 —— 外观模式

    13 外观模式 13.1 外观模式概述 Facade Pattern: 为子系统的接口提供一组统一的入口.外观模式定义了一个高层接口,这个接口使得子系统的更加容易使用. 在外观模式中,一个子系统的外部 ...

  3. logstash增量同步mysql数据到es

    本篇本章地址:https://www.cnblogs.com/Thehorse/p/11601013.html 今天我们来讲一下logstash同步mysql数据到es 我认为呢,logstash是众 ...

  4. [ [Ynoi2013] 无力回天 NOI2017 ] 解题报告

    [Ynoi2013] 无力回天 NOI2017 首先看到异或,想到能维护异或的东西就那几样(线性基/01trie/数位 dp/FWT),再看到求选任意个数后的异或最大值,线性基无疑了. 这时再看还要维 ...

  5. 【Ubuntu】1. 创建虚拟机

    这一篇主要写了虚拟机的创建,不包含操作系统的安装,中间有些步骤没有提到的根据默认操作即可,也可以根据个人情况设置. 点击创建新的虚拟机 这一步可以选择典型安装,过程更简单些,这里我选择自定义. 在安装 ...

  6. jQuery实现swipe事件

    // jQuery.event.swipe // 0.5 // Stephen Band // Dependencies // jQuery.event.move 1.2 // One of swip ...

  7. 2023-04-21:用go语言重写ffmpeg的metadata.c示例。

    2023-04-21:用go语言重写ffmpeg的metadata.c示例. 答案2023-04-21: 这段 Go 代码演示了如何使用 ffmpeg-go 库中的函数来读取多媒体文件元数据,包括视频 ...

  8. 2022-05-31:某公司游戏平台的夏季特惠开始了,你决定入手一些游戏。现在你一共有X元的预算。 该平台上所有的 n 个游戏均有折扣,标号为 i 的游戏的原价a_i元,现价只要b_i元, 也就是说该

    2022-05-31:某公司游戏平台的夏季特惠开始了,你决定入手一些游戏.现在你一共有X元的预算. 该平台上所有的 n 个游戏均有折扣,标号为 i 的游戏的原价a_i元,现价只要b_i元, 也就是说该 ...

  9. 2022-03-01:k8s安装phpmyadmin,yaml如何写?

    2022-03-01:k8s安装phpmyadmin,yaml如何写? 答案2022-03-01: yaml如下: apiVersion: apps/v1 kind: Deployment metad ...

  10. C++中的字符串编码处理

    今天由于在项目中用到一些与C++混合开发的东西 ,需要通过socket与C++那边交换数据,没啥特别的,字节码而已,两边确定一种编码规则就行了.我们确定的UTF-8.关于C++的 这种又是宽字节 又是 ...