简体   繁体   English

如何在计算属性内使用window.innerWidth-NuxtJS

[英]how to use window.innerWidth within computed property - NuxtJS

I'm working on a nuxt.js project, where I need to determine styles within the computed propety and apply on a div based on screen size , as in the example below: 我正在一个nuxt.js项目中,在这里我需要确定computed属性内的样式,并根据screen size应用于div ,如以下示例所示:

basic example 基本例子

<template>
  <div :style="css"></div>
</template>

<script>
export default {
  computed: {
    css () {
      let width = window.innerWidth

      // ... mobile { ... styles }
      // ... desktop { ... styles }

      // ... if width is less than 700, return mobile
      // ... if width greater than 700, return desktop
    }
  }
}
</script>

real example 真实的例子

<template>
  <div :style="css">
    <slot />
  </div>
</template>

<script>
export default {
  props: {
    columns: String,
    rows: String,
    areas: String,
    gap: String,
    columnGap: String,
    rowGap: String,
    horizontalAlign: String,
    verticalAlign: String,
    small: Object,
    medium: Object,
    large: Object
  },
  computed: {
    css () {
      let small, medium, large, infinty

      large = this.generateGridStyles(this.large)
      medium = this.generateGridStyles(this.medium)
      small = this.generateGridStyles(this.small)
      infinty = this.generateGridStyles()

      if (this.mq() === 'small' && this.small) return Object.assign(infinty, small)

      if (this.mq() === 'medium' && this.medium) return Object.assign(infinty, medium)

      if (this.mq() === 'large' && this.large) return Object.assign(infinty, large)

      if (this.mq() === 'infinty') return infinty

    }
  },
  methods: {
    generateGridStyles (options) {
      return {
        'grid-template-columns': (options !== undefined) ? options.columns : this.columns,
        'grid-template-rows': (options !== undefined) ? options.rows : this.rows,
        'grid-template-areas': (options !== undefined) ? options.areas : this.areas,
        'grid-gap': (options !== undefined) ? options.gap : this.gap,
        'grid-column-gap': (options !== undefined) ? options.columnGap : this.columnGap,
        'grid-row-gap': (options !== undefined) ? options.rowGap : this.rowGap,
        'vertical-align': (options !== undefined) ? options.verticalAlign : this.verticalAlign,
        'horizontal-align': (options !== undefined) ? options.horizontalAlign : this.horizontalAlign,
      }
    },
    mq () {
      let width = window.innerWidth

      if (width < 600) return 'small'
      if (width > 600 && width < 992) return 'medium'
      if (width > 992 && width < 1200) return 'large'
      if (width > 1200) return 'infinty'
    }
  }
}
</script>

<style lang="scss" scoped>
div {
  display: grid;
}
</style>

making use of the GridLayout component on pages.vue 利用pages.vue上的GridLayout组件

<template>
  <GridLayout
    columns="1fr 1fr 1fr 1fr"
    rows="auto"
    gap="10px"
    verital-align="center"
    :small="{
      columns: '1fr',
      rows: 'auto auto auto auto',
    }"
  >
    <h1>1</h1>
    <h1>2</h1>
    <h1>3</h1>
    <h1>3</h1>
  </GridLayout>
</template>

<script>
import { GridLayout } from '@/components/bosons'

export default {
  layout: 'blank',
  components: {
    GridLayout
  },
}
</script>


<style lang="scss" scoped>
h1 {
  background: #000;
  color: #fff;
}
</style>

does not work, it generates a error windows is note defined in if (this.mq() === 'small') 不起作用,它会生成一个错误windows is note definedif (this.mq() === 'small')

This works perfectly in pure Vue.js but I understand that it does not work on Nuxt.js because it is server side rendering, it makes perfect sense, but how could I make it work? 这可以在pure Vue.js完美地工作,但是我知道它不能在Nuxt.js上工作,因为它是服务器端渲染,很有意义,但是我如何使它工作呢?

the closest I got was moving the style code into the mounted method or wrapping the style code in if (process.client) {...} , but any of the alternatives would generate a certain delay , jump in content, example: 我得到的最接近的方法是将样式代码移至已mounted方法中,或将样式代码包装在if (process.client) {...} ,但是任何其他选择都会产生一定的delay ,例如内容jump

process.client vs without the process.client process.client与没有process.client的情况

jump / delay on the layout when uses process.client condition 使用process.client条件时在布局上跳转/延迟

how could I make it work without delay? 我该如何立即进行工作? how could I have the screen width before the mounted, default behavior of Vue.js? 在安装Vue.js的默认行为之前,我如何拥有屏幕宽度?

It is an old issue which is happening due to server rendering, as stated in below issue on github. 这是一个由于服务器渲染而发生的老问题,如下面的github问题所述。

.vue file .vue文件

<script>
if (process.browser) {
  require('aframe')
}
export default {

}
</script>

nuxt.config.js nuxt.config.js

  build: {
    vendor: ['aframe']
  }

Reference: 参考:
https://github.com/nuxt/nuxt.js/issues/30#issuecomment-264348589 https://github.com/nuxt/nuxt.js/issues/30#issuecomment-264348589
https://nuxtjs.org/faq/window-document-undefined/ https://nuxtjs.org/faq/window-document-undefined/

I suspect it's because the Nuxt framework is attempting to compute it on the server-side where there is no window object. 我怀疑这是因为Nuxt框架正在尝试在没有窗口对象的服务器端进行计算。 You need to make sure that it computes it in the browser by checking process.client : 您需要通过检查process.client来确保它在浏览器中对其进行计算:

export default {
  computed: {
    css () {
      if (process.client) {
        let width = window.innerWidth

        // ... mobile { ... styles }
        // ... desktop { ... styles }

        // ... if width is less than 700, return mobile
        // ... if width greater than 700, return desktop
      } else {
        return { /*empty style object*/ }
      }
    }
  }
}

Regarding the delay, it's a little bit "hacky" but you could return null if window is not available and simply display once the computed property becomes available. 关于延迟,它有点“ hacky”,但是如果window不可用,您可以返回null,并在计算出的属性变为可用时简单地显示。 You would still have a delay before it becomes visible, as the root of the problem is that the style is getting applied on the next DOM update. 您仍然需要等待一段时间才能看到它,因为问题的根源在于该样式将在下一次DOM更新中应用。

<template>
    <div :style="css" v-show="css">
    </div>
</template>

<script>
export default {
  computed: {
    css () {
      if (process.client) {
        let width = window.innerWidth

        // ... mobile { ... styles }
        // ... desktop { ... styles }

        // ... if width is less than 700, return mobile
        // ... if width greater than 700, return desktop
      } else {
        return null
      }
    }
  }
}
</script>

Alternatively, as the css is applied on the next DOM update you could use a data property with Vue.$nextTick() (but it is essentially the same thing): 另外,在下一次DOM更新中应用css时,您可以将data属性与Vue。$ nextTick()结合使用(但本质上是相同的):

<template>
    <div :style="css" v-show="reveal">
    </div>
</template>

<script>
export default {
  data() {
    return {
      reveal: false
    }
  },
  computed: {
    css () {
      if (process.client) {
        let width = window.innerWidth

        // ... mobile { ... styles }
        // ... desktop { ... styles }

        // ... if width is less than 700, return mobile
        // ... if width greater than 700, return desktop
      } else {
        return { /*empty style object*/ }
      }
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.reveal = true
    });
  }
}
</script>

However, from your question, it appears that you want to apply a responsive layout. 但是,从您的问题来看,您似乎想应用响应式布局。 The best approach would be to scope this into your style tags and use css breakpoints. 最好的办法是scope本到您的style标签和使用CSS断点。 This would solve the delay problem and decouple your style and logic. 这样可以解决延迟问题,并使样式和逻辑脱钩。

<template>
    <div class="my-responsive-component">
    </div>
</template>

<script>
export default {
  computed: { /* nothing to see here! */ }
}
</script>

<style lang="css" scoped>
.my-responsive-component {
    height: 100px;
    width: 100px;
}

@media only screen and (max-width: 700px) {
    .my-responsive-component { background: yellow; }
}

@media only screen and (min-width: 700px) {
    .my-responsive-component { background: cyan; }
}
</style>

Btw, just as a side note, use the proper if/else statement in full for computed properties. 顺便提一句,顺便说一句,对于计算属性,请完全使用正确的if / else语句。 Using things like if (!process.client) return { /* empty style object */} sometimes produces some unexpected behaviour in Vue computed properties. 使用if (!process.client) return { /* empty style object */}有时会在Vue计算属性中产生一些意外行为。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM