前言

在 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 官方也提出了可以替代的方法。

事件总线模式可以被替换为使用外部的、实现了事件触发器接口的库,例如 mitt 或 tiny-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小技巧内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Vue中$once的两个实用小技巧分享的更多相关文章

  1. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  2. vue自定义加载指令v-loading占位图指令v-showimg

    这篇文章主要为大家介绍了vue自定义加载指令和v-loading占位图指令v-showimg的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. vue使用动画实现滚动表格效果

    这篇文章主要为大家详细介绍了vue使用动画实现滚动表格效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  4. 关于Vue 监控数组的问题

    这篇文章主要介绍了Vue 监控数组的示例,主要包括Vue 是如何追踪数据发生变化,Vue 如何更新数组以及为什么有些数组的数据变更不能被 Vue 监测到,对vue监控数组知识是面试比较常见的问题,感兴趣的朋友一起看看吧

  5. Vue子组件props从父组件接收数据并存入data

    这篇文章主要介绍了Vue子组件props从父组件接收数据并存入data的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  6. Vue h函数的使用详解

    本文主要介绍了Vue h函数的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  7. VUE响应式原理的实现详解

    这篇文章主要为大家详细介绍了VUE响应式原理的实现,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

  8. vue+Element ui实现照片墙效果

    这篇文章主要为大家详细介绍了vue+Element ui实现照片墙效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. vue+elemet实现表格手动合并行列

    这篇文章主要为大家详细介绍了vue+elemet实现表格手动合并行列,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. iview+vue实现导入EXCEL预览功能

    这篇文章主要为大家详细介绍了iview+vue实现导入EXCEL预览功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部