Vue中$once的两个实用小技巧分享

目录
  • 前言
  • 清除定时器
  • $once/$emit + async/await 实现 Dialog 同步执行
  • 总结

前言

在 Vue 中有很多 API 都有很实用的地方,只是需要挖掘适用的场景,这里整理出了 $once 的两个小技巧,也是在平常工作中用的比较多的,分享给大家,希望对大家能有帮助。

清除定时器

定时器大家用的应该也不少,像我一开始一般都是这样写的,在 created 中创建定时器,在 beforeDestroy 中销毁定时器。

<script>
export default {
  name: "Timer",
  data() {
    return {
      timer: null,
      count: 0,
    };
  },
  created() {
    this.timer = setInterval(() => {
      this.count++;
    }, 1000);
  },
  beforeDestroy() {
    clearInterval(this.timer);
  },
};
</script>

开始的时候也没有发现有什么问题,没啥毛病。

后面慢慢的了解了更多,才发现里面确实是有几个问题存在的:

  • 定时器的创建和销毁放在了两个生命周期中,很容易就忘记在 beforeDestroy 中去销毁,从而导致内存的不必要消耗,并且可读性太差了,后续维护变困难;
  • 如果定时器的创建和销毁在同一个生命周期中的话,那么 timer 其实也就没必要使用响应式了,可以减少性能的浪费,直接在 created 中定义一个变量即可;
  • 在要销毁的时候,只是清空了定时器,但是却没有把 timer 重置为 null,用完重置是个好习惯(定时器返回的值可以理解为这个定时器的 ID,通过这个 ID 来销毁这个定时器)。

优化后的版本是这样的,可读性好了很多,有点 composition API 那味儿了:),示例可以戳这里

export default {
  name: "OnceTimer",
  data() {
    return {
      count: 0,
    };
  },
  created() {
    let timer = setInterval(() => {
      this.count++;
    }, 1000);
    this.$once("hook:beforeDestroy", () => {
      clearInterval(timer);
      timer = null;
    });
  },
};

$once/$emit + async/await 实现 Dialog 同步执行

需求背景是这样的:在全局有一个配置项showDialog,在点击 查询 的时候,如果这个配置项为 true,则需要 dialog 弹框让用户填写一些数据,当这个 dialog 弹框关闭之后,才能发出 confirm 的接口给后端,配置项为 false 时,则直接发送 confirm 的请求。

这里会有两个问题:

  • 这个弹框和 confirm 这个操作并不是强相关,我不能把 confirm 的请求逻辑放置在 dialog 弹框里;
  • 当控制弹框显示的变量 visible 设为 true 时,js 逻辑会继续往下执行,即把 confirm 的请求逻辑执行完了,请求就发送出去了,这不是我想要的结果。

我们来模拟一下这个过程,如下图所示:

在点击查询之后,先输出了 form submit(用来模拟点击查询后的发出请求),然后在点击 dialog 弹框的确定之后,才输出了 dialog confirm。可以看到点击查询的接口先发出,点击 dialog 弹框 确认的接口后发出。

解决这个问题可以从以下两个方面入手:

  • dialog 的确认逻辑 与 confirm 发送请求的逻辑要解耦,不能写在一起,不利于复用
  • confirm 的发送请求逻辑,要等 dialog 关闭之后,才能执行,那我们就需要知道 dialog 弹框是什么时候关闭的。

有了这两点之后,就可以想到可以利用 $once/$emit + promise + async/ await 来实现这一逻辑。

通过 $once/$emit 来进行通信,告知 dialog 关闭,通过 promise + async/ await 来使逻辑从异步变同步

我们来看下具体的代码:

// dialog 组件
<template>
  <el-dialog
    title="提示"
    :visible.sync="dialogVisible"
    width="30%"
    :before-close="close"
  >
    <span>这是一段信息</span>
    <span slot="footer" class="dialog-footer">
      <el-button @click="close">取 消</el-button>
      <el-button type="primary" @click="confirm">确 定</el-button>
    </span>
  </el-dialog>
</template>

<script>
export default {
  props: ["dialogVisible"],
  data() {
    return {};
  },
  methods: {
    close() {
      this.$emit("before-dialog-close");
      this.$emit("update:dialogVisible", false);
    },
    confirm() {
      console.log("dialog confirm");
      this.close();
    },
  },
};
</script>
<template>
  <div>
    <el-form :inline="true" :model="formInline" class="demo-form-inline">
      <el-form-item label="审批人">
        <el-input v-model="formInline.user" placeholder="审批人"></el-input>
      </el-form-item>
      <el-form-item label="活动区域">
        <el-select v-model="formInline.region" placeholder="活动区域">
          <el-option label="区域一" value="shanghai"></el-option>
          <el-option label="区域二" value="beijing"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
      </el-form-item>
    </el-form>
    <Dialog
      :dialogVisible.sync="visible"
      @before-dialog-close="() => this.$emit('beforeDialogClose')"
    />
  </div>
</template>
<script>
import Dialog from "./dialog";
export default {
  name: "Promise",
  components: {
    Dialog,
  },
  data() {
    return {
      formInline: {
        user: "",
        region: "",
      },
      // 控制 dialog 的展示
      visible: false,
      // 在业务中这是一个配置项
      showDialog: true,
    };
  },
  methods: {
    awaitDialogClose() {
      return new Promise((resolve) => {
        if (!this.visible) {
          resolve(null);
        }
        this.$once("beforeDialogClose", () => {
          resolve(null);
        });
      });
    },
    async onSubmit() {
      if (this.showDialog) {
        this.visible = true;
      }
      await this.awaitDialogClose();
      setTimeout(() => {
        console.log("form submit!");
      }, 1000);
    },
  },
};
</script>

效果如下:

在点击查询之后,我刻意的停留的一下,就是为了显示点击dialog确认的逻辑点击查询的请发逻辑之前执行。

详细代码具体分析,可以看到主要的逻辑就是在 dialog 关闭之前,$emit 出一个事件,来告诉父组件,dialog 要关闭了。

// dialog 组件
close() {
  // 通知父组件dialog要关闭了
  this.$emit("before-dialog-close");
  // 关闭 dialog
  this.$emit("update:dialogVisible", false);
},

在父组件中,创建一个 promise,通过 $once 来等到 dialog 关闭的信号 。

// 发出信号
<Dialog
  :dialogVisible.sync="visible"
  @before-dialog-close="() => this.$emit('beforeDialogClose')"
/>
// 接收信号
awaitDialogClose() {
  return new Promise((resolve) => {
    // 当 dialog 没弹框的时候,走这个逻辑,promise 直接结束
    if (!this.visible) {
      resolve(null);
    }
    // 当 dialog 要关闭的时候,$once 接收到了信号,promise 结束
    this.$once("beforeDialogClose", () => {
      resolve(null);
    });
  });
},

在 confirm 的点击逻辑中,用一个 await 来保证 promsie 结束后,才往下继续执行。

async onSubmit() {
  // 当配置为 true 时,需要 dialog 弹框
  if (this.showDialog) {
    this.visible = true;
  }
  // promise 结束后,才会继续往下执行,否则就一直等待
  await this.awaitDialogClose();
  setTimeout(() => {
    console.log("form submit!");
  }, 1000);
},

至此,功能就完成了,这个功能适用场景还是很广的(我也是请教了大佬才学会的),大家有兴趣的也可以挖掘一些其他的使用场景。具体代码在这里,有兴趣的可以看一看呀。

可是在 Vue3 中,$once 被移除了,不过没关系,Vue 官方也提出了可以替代的方法。

事件总线模式可以被替换为使用外部的、实现了事件触发器接口的库,例如 mitttiny-emitter

import emitter from 'tiny-emitter/instance'

export default {
  $once: (...args) =&gt; emitter.once(...args),
  $emit: (...args) =&gt; emitter.emit(...args),
}
复制代码

总结

没有无用的 API,只是没有找到适用的场景。如果大家有更好的解决方法,也可以在评论区告诉我,让我学习学习。

到此这篇关于Vue中$once实用小技巧的文章就介绍到这了,更多相关Vue $once小技巧内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue.js 事件修饰符的使用教程

     一.前言 熟悉了 Vue 的指令系统后,在实际开发中,不可避免的会使用到对于事件的操作,如何处理 DOM 事件流,成为我们必须要掌握的技能.不同于传统的前端开发,在 Vue 中给我们提供了事件修饰符这一利器,使我们可以便捷的处理 DOM 事件,本章,一起来学习如何使用事件修饰符来实现对于 DOM 事件流的操作. 仓储地址: https://github.com/Lanesra712/VueTrial/tree/master/Chapter01-Rookie/directives  二.干货合集

  • vue使用once修饰符,使事件只能触发一次问题

    目录 once修饰符,使事件只能触发一次 vue事件修饰符(once:prev:stop) 注释说的很详细 once修饰符,使事件只能触发一次 多个修饰符可以同步使用 <!--定义vue的操作对象--> <div id="app">     <!-- 使用self修饰符 点击标签自身时才会执行事件 -->     <!-- 使用once修饰符 使事件只能触发一次  -->     <!-- 多个修饰符可以同时使用   -->

  • vue中的事件修饰符once,prevent,stop,capture,self,passive

    目录 vue中的事件修饰符 1. once 2. prevent 3. stop 4. capture和self 5. passive与prevent相反 vue事件处理(修饰符) 事件修饰符 按键修饰符 系统修饰键 鼠标按钮修饰符 vue中的事件修饰符 1. once 只执行一次 <div v-on:click.once='alert("1")'></div> 只有在第一次点击时会执行,再次点击不会起作用 2. prevent 阻止默认程序,比如form表单中

  • Vue中$once的两个实用小技巧分享

    目录 前言 清除定时器 $once/$emit + async/await 实现 Dialog 同步执行 总结 前言 在 Vue 中有很多 API 都有很实用的地方,只是需要挖掘适用的场景,这里整理出了 $once 的两个小技巧,也是在平常工作中用的比较多的,分享给大家,希望对大家能有帮助. 清除定时器 定时器大家用的应该也不少,像我一开始一般都是这样写的,在 created 中创建定时器,在 beforeDestroy 中销毁定时器. <script> export default { na

  • 关于TypeScript开发的6六个实用小技巧分享

    目录 1. 开发之前确定实体类型 2. 请求接口时只需要定义自己需要用到的字段 3. 使用枚举类型 4. DOM元素的类型要正常给 5.对象的类型要怎么给 6.结构赋值时类型怎么给 总结 本文总结一下使用TypeScript开发应用程序的一点小经验.说之前,推荐一个VSCODE立即执行TS代码的插件quokka.js, 使用方式,ctrl+shipt+p,输入关键字quokka 回车之后,输入代码之后会立即执行 1. 开发之前确定实体类型 在正式编码之前,如果没有接口文档的话,最好能拿到数据字典

  • Kotlin开发的一些实用小技巧总结

    前言 随着Google I/O大会的召开,Google宣布将支持Kotlin作为Android的开发语言,最近关于Kotlin的文章.介绍就异常的活跃. 本文主要给大家介绍了关于Kotlin开发的一些实用小技巧,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1.Lazy Loading(懒加载) 延迟加载有几个好处.延迟加载能让程序启动时间更快,因为加载被推迟到访问变量时. 这在使用 Kotlin 的 Android 应用程序而不是服务器应用程序中特别有用.对于 Androi

  • JavaScript编程的10个实用小技巧

    在这篇文章中,我将列出10个Javascript实用小技巧,主要面向Javascript新手和中级开发者.希望每个读者都能至少从中学到一个有用的技巧. 1.变量转换 看起来很简单,但据我所看到的,使用构造函数,像Array()或者Number()来进行变量转换是常用的做法.始终使用原始数据类型(有时也称为字面量)来转换变量,这种没有任何额外的影响的做法反而效率更高. 复制代码 代码如下: var myVar   = "3.14159",str     = ""+ m

  • 常用的10个Python实用小技巧

    大家好,都说追女孩方法大于态度,学Python也是,今天就给大家分享的是我在用Python编写程序时常用的一些小技巧. 1.多次打印同一个字符 在Python中,不用特地写一个函数来重复打印同一个字符,直接使用Print就可以 tem = 'I Love Python ' print(tem * 3) I Love Python I Love Python I Love Python 2.在函数内部使用生成器 在写Python程序时,我们可以在函数内部直接使用生成器,这样可以使代码更简洁. su

  • pandas参数设置的实用小技巧

    前言 在日常使用pandas的过程中,由于我们所分析的数据表规模.格式上的差异,使得同样的函数或方法作用在不同数据上的效果存在差异. 而pandas有着自己的一套参数设置系统,可以帮助我们在遇到不同的数据时灵活调节从而达到最好的效果,本文就将介绍pandas中常用的参数设置方面的知识. 图1 1 设置DataFrame最大显示行数 pandas设置参数中的display.max_rows用于控制打印出的数据框的最大显示行数,我们使用pd.set_option()来有针对的设置参数,如下面的例子:

  • 你可能不知道的typescript实用小技巧

    目录 前言 函数重载 映射类型 Partial, Readonly, Nullable, Required Pick, Record Exclude, Omit ReturnType 类型断言 枚举 元组 范型 infer 总结 前言 用了很久的 typescript,用了但感觉又没完全用.因为很多 typescript 的特性没有被使用,查看之前写的代码满屏的 any,这样就容易导致很多 bug,也没有发挥出 typescript 真正的"类型"威力.本文总结了一些使用 typesc

  • 总结MySQL建表、查询优化的一些实用小技巧

    MySQL建表阶段是非常重要的一个环节,表结构的好坏.优劣直接影响着后续的管理维护,赶在明天上班前分享总结个人MySQL建表.MySQL查询优化积累的一些实用小技巧. 技巧一.数据表冗余记录添加时间与更新时间 我们用到的很多数据表大多情况下都会有表记录的"添加时间(add_time)",我建议大家再新增一个记录"更新时间(update_time)"字段,在我的工作里需要为市场部.运营部等建立各种报表,而很多报表里的数据都是需要到大记录表里去查询的,如果直接查询大表的

  • jQuery实用小技巧_输入框文字获取和失去焦点的简单实例

    jQuery实用小技巧_输入框文字获取和失去焦点的简单实例 <input id="txt" class="text1" type="text" /> <script src="js/jquery-1.7.1.min.js"></script> <script type="text/javascript"> $(function () { $("inp

  • vue项目中less的一些使用小技巧

    目录 前言 一.样式穿透 1.  什么是样式穿透? 2.  如何使用? 二.混入 1.  什么是混入? 2.  如何使用? 三. less自动化导入 1. 自动化导入好处 2.  如何实现? 总结 前言 我们所能看到的美观的网页都是经过UI精心设计后,由前端攻城狮搭建的.网页想要有炫酷的样式,就需要用到css来处理,其中不乏会出现大量重复.冗余的代码,这时,像less.sass.scss等样式预处理器就出现了,极大地精简了css代码,提高了开发效率.今天跟着本文一起看看在vue项目中使用less

随机推荐