在原生裏面,要偵測螢幕尺寸可以使用
window
,在 Vue 的
<script>
裡面也可以使用
window
的屬性
innerWidth
,
innerHeight
。
但如果在 Vue檔的
<template>
寫上
{{ window }}
會顯示:
"[object Window]"
在
<template>
是拿不到
window
的,除非在
main.js
裏面新增:
app.config.globalProperties.window = window;
就可以在 <template>
使用 window 的屬性了。
然而這個只能拿到當下的螢幕尺寸大小,如果有人在那邊拉來拉去(就是你們這些前端仔!),讓網頁的寬度改變的話,window
是無法偵測到的。
如果今天要在螢幕在 576px 以上讓 element 的 class 新增一個改變容器置中的功能,該怎麼做到 即時偵測螢幕寬度 呢?
以下會介紹使用 VueUse 跟 使用原生的用法:
VueUse
主要是以 composition API 做成的 Vue函式工具庫,在 Vue2, Vue3 都可以使用。
而且只會打包有使用到的程式碼。
大多數的函式都會返回一個ref()
直接用 npm install 安裝到專案中:
npm i @vueuse/core
裏面有非常多的函式可以被 use,找到一個最符合要偵測螢幕尺寸大小的。
useWindowSize
安裝之後,就可以直接使用 width
, height
了。
import { useWindowSize } from '@vueuse/core'
const { width, height } = useWindowSize()
接著再使用 Vue 的 watch
就可以及時偵測目前螢幕變化的大小~
const isCentered = ref(null);
watch(width, (newWidth, oldWidth) => {
if (newWidth >= 576) {
isCentered.value = true;
} else {
isCentered.value = false;
於是在 <template>
也可以使用這個響應式變化了!
在 576px 以上會自動新增 modal-dialog-centered
class名稱上去,其他 size 則自動沒有。
class="modal-dialog modal-fullscreen-sm-down modal-lg"
:class="{ 'modal-dialog-centered': isCentered }"
好奇去看了一下 useWindowSize的原始碼,發現 useWindowSize
吃的是 resize
事件的呢
useEventListener('resize', update, { passive: true })
他只會在有改變螢幕大小時觸發,如果在一進去頁面就不會有任何改變,如果還要為一開始進來偵測螢幕大小給不同狀態的話,還是先得用 window
去抓當下的螢幕尺寸,然後剩下就給調整大小時,才有 watch
的改變!
Vue 使用原生偵測
在寫鐵人賽的當下我只會使用 computed, watch,不過後來學習到 watchEffect 發現更適合。以下會介紹兩種不同的寫法。
computed
雖然我使用了 VueUse 做到這件事,但因為當初沒有想到可以使用 resize 事件來偵測,所以又用原生試試看,發現也可以啊 XD
在 onMounted()
裡面放一個 window.addEventListener('resize')
在外面放一個響應式變數 let windowWidth = ref(window.innerWidth);
在 listener 一直對 windowWidth 做重新賦值的動作
外面在放一個 computed 的變數去 return windowWidth
,這樣只要 windowWidth
改變,這個 computed 就會去計算
最後在 <template>
放上 computed 的變數就好了!
const resizeFont = ref("");
const isLess500 = ref(false);
const isBigger500 = ref(false);
let windowWidth = ref(window.innerWidth);
const resize = computed(() => {
return windowWidth;
onMounted(() => {
window.addEventListener("resize", function () {
windowWidth.value = window.innerWidth;
if (windowWidth.value < 500) {
resizeFont.value = "less than 500px";
isLess500.value = true;
isBigger500.value = false;
} else {
resizeFont.value = "greater than 500px";
isBigger500.value = true;
isLess500.value = false;
再試著在resize的時候去改文字顏色也有成功
<span>window.innerWidth : </span>
<span>{{ resize }}</span>
<p :class="{ changeResizeColor: isLess500, blue: isBigger500 }">
{{ resizeFont }}
watchEffect
因為其實我們需要第一次的值,這樣使用 watchEffect 的話,第一次就算沒有 resize 但還是會有初始值
watchEffect 沒有 oldValue, newValue 直接給一個 callback , watch 會自動建立 callback 裡面的變數來產生依賴。
我們在組件被創建起來的時候去監聽,在onMounted裡面監聽 window 的 resize 事件。
在裡面把目前的 window.innerWidth
賦值給響應式變數 windowWidth
為了要讓他去改變其他變數,再用 watchEffect 去改變 result1 的值。
const result1 = ref("");
onMounted(() => {
window.addEventListener("resize", function () {
windowWidth.value = window.innerWidth;
// window width
const windowWidth = ref(window.innerWidth);
watchEffect(() => {
if (windowWidth.value < 300) {
result1.value = "its less than 300px";
} else {
result1.value = "its greatter than 300px";
程式碼變很少吧!
resize 是屬於 window 的事件,所以一定只能綁在window上
在 Vue 裡面要監聽 window 要放在 onMounted()
裏面
VueUse + watch 可以去偵測螢幕寬度
用 VueUse + watch 比較快,但是對於新手來說有點困難(我),感覺就是誤打誤撞XD
如果還有更好的方法請推薦給我吧~歐內該~
怎麼會突然就進到Vue
了呢XD
剩下一個禮拜了~~~~
參考資料:
VueUse
Vue js 3 - watch window.innerWidth does not working
Day_24: 讓 Vite 來開啟你的Vue 之 VueUse
Vanilla JS 與 Vue 的生命週期 Day 28
Add support for a v-on:resize event #1915