Vue JS - 如何在窗口大小改变时获取窗口大小

79
我是vuejs的新手,但我试图在调整窗口大小时获取窗口大小,以便将其与某个值进行比较,以便根据屏幕大小应用所需的功能。我还尝试使用watch属性,但不确定如何处理它,所以可能是这样做不起作用的原因。
  methods: {

    elem() {
     this.size = window.innerWidth;
     return this.size;
   },
  mounted() {
    if (this.elem < 767){ //some code }
   }

2
document.addEventListener('resize', <yourfunction>);?我对vue一无所知。 - Expressingx
请查看 https://dev59.com/JHRB5IYBdhLWcg3wXmVI#49381187。 - str
6个回答

171

将此代码放入您的Vue组件中:

created() {
  window.addEventListener("resize", this.myEventHandler);
},
destroyed() {
  window.removeEventListener("resize", this.myEventHandler);
},
methods: {
  myEventHandler(e) {
    // your code for handling resize...
  }
}

这将在组件创建时注册你的Vue方法,在浏览器窗口大小调整时触发myEventHandler,并在你的组件被销毁后释放内存。


18
可以。建议对这种事件进行去抖动处理。 - Borjante
1
我知道了,只是为了提醒楼主而已。 - Borjante
2
同样的答案还说,'resize'被定义为窗口,但不适用于文档。 - BitLord
1
此代码仅适用于单例组件,因为它将在组件之间共享处理程序。如果您有多个组件,则它们将在第一个组件被销毁时停止工作。正确的方法是在视图模型(数据)中创建绑定函数。 - Vitim.us
只有当浏览器的大小增加时,事件才会被触发。当大小减小时,事件不会被触发。是否有人遇到类似的问题? - phe
显示剩余3条评论

18

对于Vue3,您可以使用以下代码:

mounted() {
  window.addEventListener("resize", this.myEventHandler);
},
unmounted() {
  window.removeEventListener("resize", this.myEventHandler);
},
methods: {
  myEventHandler(e) {
    // your code for handling resize...
  }
}

在Vue3中,destroyed和beforeDestroyed已经被弃用,因此您可能希望使用beforeUnmount和unmounted


9

最简单的方法

https://www.npmjs.com/package/vue-window-size

预览

import Vue from 'vue';
import VueWindowSize from 'vue-window-size';

Vue.use(VueWindowSize);

然后您可以像这样从组件中正常访问它:

<template>
  <div>
    <p>window width: {{ windowWidth }}</p>
    <p>window height: {{ windowHeight }}</p>
  </div>
</template>

3
我不会称通过NPM安装新库为“最简单”的方法 :). 特别是如果您正在使用没有构建工具的CDN纯Vue,它将无法工作。 - M -
我无法安装 vue-window-size/option-api - etayluz

7

我查看了该库vue-window-size的代码,除了额外的逻辑外,它只是在窗口调整大小时添加了一个事件监听器,并且似乎可以指示它进行防抖处理。 源代码

对我来说关键的问题是,当Vue路由器路由改变使<html>元素从1000px变为4000px时,我的Vue SPA应用程序不会发出窗口调整大小的事件,所以它导致我在观察由p5.js控制的画布元素重新绘制壁纸时遇到各种问题,使用p5.resizeCanvas().

我现在有一个不同的解决方案,它涉及主动轮询页面的偏移高度。

首先要注意JavaScript内存管理,为了避免内存泄漏,我将setInterval放在“created”生命周期方法中,并在“beforeDestroy”生命周期方法中清除setInterval:

created() {
    this.refreshScrollableArea = setInterval(() => {
        const { offsetWidth, offsetHeight } = document.getElementById('app');
        this.offsetWidth = offsetWidth;
        this.offsetHeight = offsetHeight;
    }, 100);
},

beforeDestroy() {
    return clearInterval(this.refreshScrollableArea);
},

如上面的代码所示,我也放置了一些初始状态:

data() {
    const { offsetWidth, offsetHeight } = document.querySelector('#app');

    return {
        offsetWidth,
        offsetHeight,
        refreshScrollableArea: undefined,
    };
},

注意:如果您在使用getElementById时像this.id一样(即:此组件中的子元素),document.getElementById(this.id)将为undefined,因为DOM元素是从外到内加载的。因此,如果您看到来自data实例化的错误,请将宽度/高度最初设置为0

然后,我会对offsetHeight添加一个监听器以侦听高度变化并执行业务逻辑:

    watch: {
        offsetHeight() {
            console.log('offsetHeight changed', this.offsetHeight);

            this.state = IS_RESET;
            this.setState(this.sketch);
            return this.draw(this.sketch);
        },
    },

结论:我使用了performance.now()进行了测试,并得出以下结果:

document.querySelector('#app').offsetHeight 
document.getElementById('app').offsetHeight
document.querySelector('#app').getClientBoundingRect().height

所有操作的执行时间几乎相同:0.2ms,因此上述代码在每100毫秒内耗费约0.2毫秒。我发现即使调整了比我的本地机器慢一个数量级的缓慢客户端,我的应用程序仍然可以接受这个开销。

以下是您自己进行研究的测试逻辑:

const t0 = performance.now();
const { offsetWidth, offsetHeight } = document.getElementById('app');
const t1 = performance.now();

console.log('execution time:', (t1 - t0), 'ms');

额外提示:如果您在setInterval函数中执行时间过长出现性能问题,请尝试将其包裹在双重requestAnimationFrame中:

created() {
    this.refreshScrollableArea = setInterval(() => {
        return requestAnimationFrame(() => requestAnimationFrame(() => {
            const { offsetWidth, offsetHeight } = document.getElementById(this.id);
            this.offsetWidth = offsetWidth;
            this.offsetHeight = offsetHeight;
        }));
    }, 100);
},

requestAnimationFrame本身需要进行研究,这超出了本回答的范围。

最后,我后来研究了另一个想法,但没有使用,那就是使用递归的setTimeout函数,并在其上设置动态超时时间(即,在页面加载后减少超时时间)。然而,如果您考虑使用递归的setTimeout技术,请注意调用堆栈/函数队列长度和尾调用优化。堆栈大小可能会失控。


1
如果您正在使用Vue 3中的组合API,可以使用以下代码:
onMounted(() => {
  window.addEventListener("resize", handleWindowSizeChange);
  handleWindowSizeChange();
});
onUnmounted(() => {
  window.removeEventListener("resize", handleWindowSizeChange);
});
const handleWindowSizeChange = () => {
// your code
};

-1

您可以随时随地使用此功能。

methods: {
    //define below method first.
    winWidth: function () {
        setInterval(() => {
            var w = window.innerWidth;
            if (w < 768) {
                this.clientsTestimonialsPages = 1
            } else if (w < 960) {
                this.clientsTestimonialsPages = 2
            } else if (w < 1200) {
                this.clientsTestimonialsPages = 3
            } else {
                this.clientsTestimonialsPages = 4
            }
        }, 100);
    }
},
mounted() {
    //callback once mounted
    this.winWidth()
}

10
确实可以使用该函数,但每秒运行10次并没有必要,因为事件监听器会在需要时触发,就像接受的答案中所述。 - hanenbro
是的,@hanenbro,我很久以前就发布了这个,现在我意识到它可以在没有setInterval的情况下运行,并且只需回调我的方法,就像Goran的答案链接 - Gayan Sandamal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接