[英]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
inif (this.mq() === 'small')
不起作用,它会生成一个错误
windows is note defined
,if (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.