十個(gè)有用的 Vue.js 自定義 Hook
Vue.js 是我使用的第一個(gè) JavaScript 框架。 我可以說(shuō) Vue.js 是我進(jìn)入 JavaScript 世界的第一扇門(mén)之一。

目前,Vue.js 仍然是一個(gè)很棒的框架。 我認(rèn)為有了組合 API,Vue.js 只會(huì)增長(zhǎng)得更多。 在本文中,我將向分享 10 個(gè)可以使用 Vue.js 制作的有用的自定義鉤hook。
01、使用窗口調(diào)整大小
這是一個(gè)基本的hook。 因?yàn)樗谠S多項(xiàng)目中使用,并且使用純 JavaScript 或任何框架構(gòu)建它太容易了。
與 Vue 相同,只需幾行代碼即可構(gòu)建它。
這是我的代碼:
import { ref, onMounted, onUnmounted } from 'vue';
export function useWindowResize() {
const width = ref(window.innerWidth);
const height = ref(window.innerHeight);
const handleResize = () => {
width.value = window.innerWidth;
height.value = window.innerHeight;
}
onMounted(() => {
window.addEventListener('resize', handleResize)
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
return {
width,
height
}
}不僅構(gòu)建簡(jiǎn)單,而且使用起來(lái)也很容易。 只需要調(diào)用這個(gè)鉤子即可獲取窗口的寬度和高度:
setup() {
const { width, height } = useWindowResize();
}02、使用存儲(chǔ)
您想通過(guò)將數(shù)據(jù)值存儲(chǔ)在會(huì)話(huà)存儲(chǔ)或本地存儲(chǔ)中并將該值綁定到視圖來(lái)持久保存數(shù)據(jù)嗎? 只需一個(gè)簡(jiǎn)單的hook——useStorage,一切就變得如此簡(jiǎn)單。
我們只需要?jiǎng)?chuàng)建一個(gè)hook,返回從存儲(chǔ)中獲取的數(shù)據(jù),以及一個(gè)在我們想要更改數(shù)據(jù)時(shí)將數(shù)據(jù)存儲(chǔ)在存儲(chǔ)中的函數(shù)。
這是我的代碼。
import { ref } from 'vue';
const getItem = (key, storage) => {
let value = storage.getItem(key);
if (!value) {
return null;
}
try {
return JSON.parse(value)
} catch (error) {
return value;
}
}
export const useStorage = (key, type = 'session') => {
let storage = null;
switch (type) {
case 'session':
storage = sessionStorage;
break;
case 'local':
storage = localStorage;
break;
default:
return null;
}
const value = ref(getItem(key, storage));
const setItem = (storage) => {
return (newValue) => {
value.value = newValue;
storage.setItem(key, JSON.stringify(newValue));
}
}
return [
value,
setItem(storage)
]
}在我的代碼中,我使用 JSON.parse 和 JSON.stringify 來(lái)格式化數(shù)據(jù)。
如果您不想格式化它,可以將其刪除。 這是如何使用此hook的示例。
const [token, setToken] = useStorage('token');
setToken('new token');03、使用網(wǎng)絡(luò)狀態(tài)
這是一個(gè)有用的hook,支持檢查網(wǎng)絡(luò)連接的狀態(tài)。 為了實(shí)現(xiàn)這個(gè)hook,我們需要為“在線(xiàn)”和“離線(xiàn)”事件添加事件監(jiān)聽(tīng)器。
在事件中,我們只是調(diào)用一個(gè)回調(diào)函數(shù),參數(shù)為網(wǎng)絡(luò)狀態(tài)。
這是我的代碼:
import { onMounted, onUnmounted } from 'vue';
export const useNetworkStatus = (callback = () => { }) => {
const updateOnlineStatus = () => {
const status = navigator.onLine ? 'online' : 'offline';
callback(status);
}
onMounted(() => {
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
});
onUnmounted(() => {
window.removeEventListener('online', updateOnlineStatus);
window.removeEventListener('offline', updateOnlineStatus);
})
}只是簡(jiǎn)單易用。
目前,我使用參數(shù)“online”/“offline”調(diào)用回調(diào)函數(shù)。 您可以將其更改為真/假或任何您想要的。
useNetworkStatus((status) => {
console.log(`Your network status is ${status}`);
}04、使用復(fù)制到剪貼板
將文本復(fù)制到剪貼板是每個(gè)項(xiàng)目中都很流行的功能。 我知道我們可以創(chuàng)建一個(gè)函數(shù)來(lái)代替鉤子來(lái)做到這一點(diǎn)。
但我喜歡數(shù)字 10,所以我決定在這篇文章中加入這個(gè)hook。 這個(gè)hook非常簡(jiǎn)單,只需返回一個(gè)支持將文本復(fù)制到剪貼板的函數(shù)即可。
function copyToClipboard(text) {
let input = document.createElement('input');
input.setAttribute('value', text);
document.body.appendChild(input);
input.select();
let result = document.execCommand('copy');
document.body.removeChild(input);
return result;
}
export const useCopyToClipboard = () => {
return (text) => {
if (typeof text === "string" || typeof text == "number") {
return copyToClipboard(text);
}
return false;
}
}在我的代碼中,我在函數(shù) copyToClipboard 中將邏輯復(fù)制文本放入剪貼板。 我知道我們有很多方法可以做到這一點(diǎn)。 你可以在此功能中嘗試最適合你的方法。
至于如何使用,調(diào)用即可。
const copyToClipboard = useCopyToClipboard();
copyToClipboard('just copy');05、使用主題
只需一個(gè)簡(jiǎn)短的鉤子即可更改網(wǎng)站的主題。 它可以幫助我們輕松切換網(wǎng)站的主題,只需用主題名稱(chēng)調(diào)用此hook即可。 這是我用來(lái)定義主題變量的 CSS 代碼示例。
html[theme="dark"] {
--color: #FFF;
--background: #333;
}
html[theme="default"], html {
--color: #333;
--background: #FFF;
}要更改主題,我們只需要?jiǎng)?chuàng)建一個(gè)自定義掛鉤,它將返回一個(gè)通過(guò)主題名稱(chēng)更改主題的函數(shù)。
這是我的這個(gè)鉤子的代碼:
export const useTheme = (key = '') => {
return (theme) => {
document.documentElement.setAttribute(key, theme);
}
}而且使用起來(lái)太方便了。
const changeTheme = useTheme();
changeTheme('dark');06、使用頁(yè)面可見(jiàn)性
有時(shí),當(dāng)客戶(hù)不關(guān)注我們的網(wǎng)站時(shí),我們需要做一些事情。 為此,我們需要一些東西來(lái)讓我們知道用戶(hù)是否集中注意力。 這是一個(gè)定制的hook。
我稱(chēng)之為 usePageVisibility,下面是該hook的代碼:
import { onMounted, onUnmounted } from 'vue';
export const usePageVisibility = (callback = () => { }) => {
let hidden, visibilityChange;
if (typeof document.hidden !== "undefined") {
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
const handleVisibilityChange = () => {
callback(document[hidden]);
}
onMounted(() => {
document.addEventListener(visibilityChange, handleVisibilityChange, false);
});
onUnmounted(() => {
document.removeEventListener(visibilityChange, handleVisibilityChange);
});
}要使用這個(gè)hook,我們只需要?jiǎng)?chuàng)建一個(gè)帶有客戶(hù)端隱藏狀態(tài)(焦點(diǎn)狀態(tài))參數(shù)的回調(diào)函數(shù)。
usePageVisibility((hidden) => {
console.log(`User is${hidden ? ' not' : ''} focus your site`);
});07、使用視口
在第一個(gè)自定義hook中,我們構(gòu)建了useWindowRezie,它可以幫助我們查看窗口的當(dāng)前寬度和高度。
我認(rèn)為這對(duì)于那些想要構(gòu)建適用于多種屏幕尺寸的東西的人很有幫助。
在我處理過(guò)的案例中,我們經(jīng)常使用寬度來(lái)檢測(cè)當(dāng)前用戶(hù)設(shè)備。 它可以幫助我們?cè)谒麄兊脑O(shè)備上安裝一些東西。
在此hook中,我將使用 useWindowResize 構(gòu)建相同的內(nèi)容,但它返回設(shè)備名稱(chēng)而不是寬度和高度值。
這是這個(gè)hook的代碼。
import { ref, onMounted, onUnmounted } from 'vue';
export const MOBILE = 'MOBILE'
export const TABLET = 'TABLET'
export const DESKTOP = 'DESKTOP'
export const useViewport = (config = {}) => {
const { mobile = null, tablet = null } = config;
let mobileWidth = mobile ? mobile : 768;
let tabletWidth = tablet ? tablet : 922;
let device = ref(getDevice(window.innerWidth));
function getDevice(width) {
if (width < mobileWidth) {
return MOBILE;
} else if (width < tabletWidth) {
return TABLET;
}
return DESKTOP;
}
const handleResize = () => {
device.value = getDevice(window.innerWidth);
}
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
return {
device
}
}它是如此容易。 除了默認(rèn)的設(shè)備尺寸之外,當(dāng)我們使用包含手機(jī)和平板電腦尺寸的參數(shù)對(duì)象調(diào)用該hook時(shí),用戶(hù)可以修改它。 這是我們使用它的方式:
const { device } = useViewport({ mobile: 700, table: 900 });08、使用OnClickOutside
目前,模態(tài)被用于許多應(yīng)用程序中。 它對(duì)于許多用例(表單、確認(rèn)、警告等)確實(shí)很有幫助。
我們經(jīng)常用它處理的流行操作之一是用戶(hù)在模式之外單擊。 useOnClickOutside 對(duì)于這種情況是一個(gè)有用的hook。
我們只需要一個(gè) ref 元素、回調(diào)函數(shù)并將其綁定到窗口事件中。 這是我的代碼(適用于桌面和移動(dòng)設(shè)備):
import { onMounted, onUnmounted } from 'vue';
export const useOnClickOutside = (ref = null, callback = () => {}) => {
function handleClickOutside(event) {
if (ref.value && !ref.value.contains(event.target)) {
callback()
}
}
onMounted(() => {
document.addEventListener('mousedown', handleClickOutside);
})
onUnmounted(() => {
document.removeEventListener('mousedown', handleClickOutside);
});
}正如我所說(shuō),要使用它,我們只需要使用參數(shù) ref 元素和回調(diào)函數(shù)來(lái)調(diào)用它。
<template>
<div ref="container">View</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const container = ref(null);
useOnClickOutside(container, () => {
console.log('Clicked outside');
})
}
}
</script>09、使用滾動(dòng)到底部
除了分頁(yè)列表之外,加載更多(或延遲加載)是加載數(shù)據(jù)的一種友好方式。 特別是對(duì)于移動(dòng)設(shè)備,幾乎在移動(dòng)設(shè)備上運(yùn)行的應(yīng)用程序都會(huì)在其 UI 中加載更多負(fù)載。 為此,我們需要檢測(cè)用戶(hù)滾動(dòng)到列表底部并觸發(fā)該事件的回調(diào)。
useScrollToBottom 是一個(gè)有用的hook來(lái)支持您這樣做。 這是我構(gòu)建該hook的代碼:
import { onMounted, onUnmounted } from 'vue';
export const useScrollToBottom = (callback = () => { }) => {
const handleScrolling = () => {
if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
callback();
}
}
onMounted(() => {
window.addEventListener('scroll', handleScrolling);
});
onUnmounted(() => {
window.removeEventListener('scroll', handleScrolling);
});
}在我的hook中,我通過(guò)條件“((window.innerHeight + window.scrollY) >= document.body.scrollHeight)”檢測(cè)到底部。
我們有很多方法來(lái)檢測(cè)它。 如果您的項(xiàng)目符合其他條件,我們就使用它們。 以下是如何使用此hook的示例:
useScrollToBottom(() => { console.log('Scrolled to bottom') })10、使用定時(shí)器
最后,我們來(lái)到最后一個(gè)鉤子。 這個(gè)鉤子的代碼比其他鉤子要長(zhǎng)一些。 useTimer 將支持我們運(yùn)行帶有一些選項(xiàng)的計(jì)時(shí)器,例如開(kāi)始、暫停/恢復(fù)、停止。
為此,我們需要使用 setInterval 方法,在該方法中,我們將推送處理函數(shù)。 在那里,我們需要檢查計(jì)時(shí)器的暫停狀態(tài)。
如果計(jì)時(shí)器沒(méi)有暫停,我們只需要調(diào)用一個(gè)回調(diào)函數(shù),該函數(shù)由用戶(hù)作為參數(shù)傳遞。
為了支持用戶(hù)了解該計(jì)時(shí)器的當(dāng)前暫停狀態(tài),除了 useTimer 操作之外,還為他們提供一個(gè)變量 isPaused ,其值作為計(jì)時(shí)器的暫停狀態(tài)。
這是我構(gòu)建該hook的代碼:
import { ref, onUnmounted } from 'vue';
export const useTimer = (callback = () => { }, step = 1000) => {
let timerVariableId = null;
let times = 0;
const isPaused = ref(false);
const stop = () => {
if (timerVariableId) {
clearInterval(timerVariableId);
timerVariableId = null;
resume();
}
}
const start = () => {
stop();
if (!timerVariableId) {
times = 0;
timerVariableId = setInterval(() => {
if (!isPaused.value) {
times++;
callback(times, step * times);
}
}, step)
}
}
const pause = () => {
isPaused.value = true;
}
const resume = () => {
isPaused.value = false;
}
onUnmounted(() => {
if (timerVariableId) {
clearInterval(timerVariableId);
}
})
return {
start,
stop,
pause,
resume,
isPaused
}
}這是使用 useTimer hook的一種方法:
function handleTimer(round) {
roundNumber.value = round;
}
const {
start,
stop,
pause,
resume,
isPaused
} = useTimer(handleTimer);到這里,我已經(jīng)跟大家分享了10 個(gè)有用的 Vue.js hook。 我認(rèn)為它們的構(gòu)建和使用都很簡(jiǎn)單。 我只是為那些想要在 Vue.js 中使用這些鉤子的人提供一些注釋。
請(qǐng)記住刪除要添加到窗口的事件。 Vue 為我們提供了一個(gè)有用的組合 API onUnmounted ,它可以幫助我們?cè)谛遁dhook之前運(yùn)行我們的操作。
在我構(gòu)建的每個(gè)鉤子中,我總是刪除 onUnmounted 中的事件偵聽(tīng)器。
僅在真正需要時(shí)才使用反應(yīng)變量。 如果您想使用一個(gè)存儲(chǔ)某些內(nèi)容的變量,并且希望在數(shù)據(jù)更改時(shí)同步其數(shù)據(jù),那么,讓我們使用反應(yīng)式變量。
但如果它只是一個(gè)在我們的hook中存儲(chǔ)數(shù)據(jù)的變量(計(jì)數(shù)器、標(biāo)志......),我認(rèn)為你不需要使用反應(yīng)變量。
如果可以的話(huà),不要在鉤子中進(jìn)行硬編碼(設(shè)置固定值)。
我認(rèn)為我們只需要將邏輯存儲(chǔ)在我們的hook中。 關(guān)于配置值,我們應(yīng)該讓用戶(hù)填寫(xiě)它(例如:useViewport)。
最后,在我的文章中,我與您分享了10 個(gè)有用的 Vue 自定義hook,我希望它們對(duì)您有所幫助。 Vue.js 是一個(gè)很棒的框架,我希望你可以用它構(gòu)建更多很棒的東西。































