相关文章推荐
好帅的饺子  ·  vite ts vue 项目提示 . ...·  5 天前    · 
完美的鸡蛋面  ·  vue3 npm run ...·  4 天前    · 
胡子拉碴的豆腐  ·  vue ...·  20 小时前    · 
愤怒的骆驼  ·  SQL ...·  1 年前    · 
无邪的碗  ·  vcpkg Maintainer ...·  1 年前    · 

在原生裏面,要偵測螢幕尺寸可以使用 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

  •