Vue中$once的兩個實用小技巧分享
前言
在 Vue 中有很多 API 都有很實用的地方,只是需要挖掘適用的場景,這里整理出了 $once 的兩個小技巧,也是在平常工作中用的比較多的,分享給大家,希望對大家能有幫助。
清除定時器
定時器大家用的應該也不少,像我一開始一般都是這樣寫的,在 created 中創(chuàng)建定時器,在 beforeDestroy 中銷毀定時器。
<script> export default { name: "Timer", data() { return { timer: null, count: 0, }; }, created() { this.timer = setInterval(() => { this.count++; }, 1000); }, beforeDestroy() { clearInterval(this.timer); }, }; </script>
開始的時候也沒有發(fā)現(xiàn)有什么問題,沒啥毛病。
后面慢慢的了解了更多,才發(fā)現(xiàn)里面確實是有幾個問題存在的:
- 定時器的創(chuàng)建和銷毀放在了兩個生命周期中,很容易就忘記在 beforeDestroy 中去銷毀,從而導致內(nèi)存的不必要消耗,并且可讀性太差了,后續(xù)維護變困難;
- 如果定時器的創(chuàng)建和銷毀在同一個生命周期中的話,那么 timer 其實也就沒必要使用響應式了,可以減少性能的浪費,直接在 created 中定義一個變量即可;
- 在要銷毀的時候,只是清空了定時器,但是卻沒有把 timer 重置為 null,用完重置是個好習慣(定時器返回的值可以理解為這個定時器的 ID,通過這個 ID 來銷毀這個定時器)。
優(yōu)化后的版本是這樣的,可讀性好了很多,有點 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 實現(xiàn) Dialog 同步執(zhí)行
需求背景是這樣的:在全局有一個配置項showDialog,在點擊 查詢 的時候,如果這個配置項為 true,則需要 dialog 彈框讓用戶填寫一些數(shù)據(jù),當這個 dialog 彈框關(guān)閉之后,才能發(fā)出 confirm 的接口給后端,配置項為 false 時,則直接發(fā)送 confirm 的請求。
這里會有兩個問題:
- 這個彈框和 confirm 這個操作并不是強相關(guān),我不能把 confirm 的請求邏輯放置在 dialog 彈框里;
- 當控制彈框顯示的變量 visible 設為 true 時,js 邏輯會繼續(xù)往下執(zhí)行,即把 confirm 的請求邏輯執(zhí)行完了,請求就發(fā)送出去了,這不是我想要的結(jié)果。
我們來模擬一下這個過程,如下圖所示:
在點擊查詢之后,先輸出了 form submit(用來模擬點擊查詢后的發(fā)出請求),然后在點擊 dialog 彈框的確定之后,才輸出了 dialog confirm??梢钥吹近c擊查詢的接口先發(fā)出,點擊 dialog 彈框 確認的接口后發(fā)出。
解決這個問題可以從以下兩個方面入手:
- dialog 的確認邏輯 與 confirm 發(fā)送請求的邏輯要解耦,不能寫在一起,不利于復用
- confirm 的發(fā)送請求邏輯,要等 dialog 關(guān)閉之后,才能執(zhí)行,那我們就需要知道 dialog 彈框是什么時候關(guān)閉的。
有了這兩點之后,就可以想到可以利用 $once/$emit + promise + async/ await 來實現(xiàn)這一邏輯。
通過 $once/$emit 來進行通信,告知 dialog 關(guān)閉,通過 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="活動區(qū)域"> <el-select v-model="formInline.region" placeholder="活動區(qū)域"> <el-option label="區(qū)域一" value="shanghai"></el-option> <el-option label="區(qū)域二" 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, // 在業(yè)務中這是一個配置項 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確認的邏輯在點擊查詢的請發(fā)邏輯之前執(zhí)行。
詳細代碼具體分析,可以看到主要的邏輯就是在 dialog 關(guān)閉之前,$emit 出一個事件,來告訴父組件,dialog 要關(guān)閉了。
// dialog 組件 close() { // 通知父組件dialog要關(guān)閉了 this.$emit("before-dialog-close"); // 關(guān)閉 dialog this.$emit("update:dialogVisible", false); },
在父組件中,創(chuàng)建一個 promise,通過 $once 來等到 dialog 關(guān)閉的信號 。
// 發(fā)出信號 <Dialog :dialogVisible.sync="visible" @before-dialog-close="() => this.$emit('beforeDialogClose')" /> // 接收信號 awaitDialogClose() { return new Promise((resolve) => { // 當 dialog 沒彈框的時候,走這個邏輯,promise 直接結(jié)束 if (!this.visible) { resolve(null); } // 當 dialog 要關(guān)閉的時候,$once 接收到了信號,promise 結(jié)束 this.$once("beforeDialogClose", () => { resolve(null); }); }); },
在 confirm 的點擊邏輯中,用一個 await 來保證 promsie 結(jié)束后,才往下繼續(xù)執(zhí)行。
async onSubmit() { // 當配置為 true 時,需要 dialog 彈框 if (this.showDialog) { this.visible = true; } // promise 結(jié)束后,才會繼續(xù)往下執(zhí)行,否則就一直等待 await this.awaitDialogClose(); setTimeout(() => { console.log("form submit!"); }, 1000); },
至此,功能就完成了,這個功能適用場景還是很廣的(我也是請教了大佬才學會的),大家有興趣的也可以挖掘一些其他的使用場景。具體代碼在這里,有興趣的可以看一看呀。
可是在 Vue3 中,$once 被移除了,不過沒關(guān)系,Vue 官方也提出了可以替代的方法。
事件總線模式可以被替換為使用外部的、實現(xiàn)了事件觸發(fā)器接口的庫,例如 mitt 或 tiny-emitter。
import emitter from 'tiny-emitter/instance' export default { $once: (...args) => emitter.once(...args), $emit: (...args) => emitter.emit(...args), } 復制代碼
總結(jié)
沒有無用的 API,只是沒有找到適用的場景。如果大家有更好的解決方法,也可以在評論區(qū)告訴我,讓我學習學習。
到此這篇關(guān)于Vue中$once實用小技巧的文章就介紹到這了,更多相關(guān)Vue $once小技巧內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue組件實現(xiàn)移動端九宮格轉(zhuǎn)盤抽獎
這篇文章主要為大家詳細介紹了vue組件實現(xiàn)移動端九宮格轉(zhuǎn)盤抽獎,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-10-10vue使用axios實現(xiàn)動態(tài)追加數(shù)據(jù)
在vuejs中使用axios時,有時候需要追加數(shù)據(jù),比如,移動端下拉觸底加載,分頁加載,滑動滾動條等,下面小編就來為大家介紹一下如何使用使用axios實現(xiàn)動態(tài)追加數(shù)據(jù)吧2023-10-10vue-cli是什么及創(chuàng)建vue-cli項目的方法
vue-cli是 vue 官方提供的、快速生成 vue 工程化項目的工具,支持創(chuàng)建vue2和vue3的項目,本文給大家詳細講解vue-cli是什么及創(chuàng)建vue-cli項目的方法,感興趣的朋友跟隨小編一起看看吧2023-04-04