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

时间:2019-07-18 19:48:27

标签: javascript vue.js vuejs2 nuxt.js nuxt

我正在一个nuxt.js项目中,我需要确定computed属性内的样式,并基于div应用于screen size,如下面的示例所示:

基本示例

<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>

真实示例

<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>

利用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>
  

不起作用,它在windows is note defined中产生错误if (this.mq() === 'small')

这在pure Vue.js上可以很好地工作,但是我知道它在Nuxt.js上不起作用,因为它是服务器端渲染,很有意义,但是我如何使它工作呢?

我最接近的是将样式代码移至mounted方法中或将样式代码包装在if (process.client) {...}中,但是任何其他选择都会生成特定的delay,{{1 }},例如:

process.client vs without the process.client

jump / delay on the layout when uses process.client condition

我该如何立即使它工作?在安装Vue.js的默认行为之前,我如何拥有屏幕宽度?

2 个答案:

答案 0 :(得分:0)

这是一个古老的问题,由于服务器渲染而发生,如下面的github问题所述。

.vue文件

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

}
</script>

nuxt.config.js

  build: {
    vendor: ['aframe']
  }

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

答案 1 :(得分:0)

我怀疑这是因为Nuxt框架正在尝试在没有窗口对象的服务器端进行计算。您需要检查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*/ }
      }
    }
  }
}

关于延迟,它有点“ hacky”,但是如果window不可用,则可以返回null,并在计算出的属性可用后立即显示。您仍然需要等待一段时间才能看到它,因为问题的根源在于该样式将在下一次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>

或者,在下一次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>

但是,从您的问题来看,您似乎想应用自适应布局。最好的方法是将scope放入您的style标记中并使用CSS断点。这样可以解决延迟问题,并使样式和逻辑脱钩。

<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>
顺便说一句,

顺便说一句,对于计算属性,请完全使用正确的if / else语句。使用if (!process.client) return { /* empty style object */}之类的东西有时会在Vue计算的属性中产生一些意外的行为。