javascript - 如何在计算属性中使用 window.innerWidth - NuxtJS
问题描述
我正在开发一个 nuxt.js 项目,我需要在其中确定属性中的样式computed
并在div
based on 上应用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>
使用GridLayout
pages.vue 上的组件
<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
,jump
例如:
process.client 与没有 process.client
使用 process.client 条件时在布局上跳转/延迟
我怎样才能让它毫不拖延地工作?在安装 Vue.js 的默认行为之前,我怎么能拥有屏幕宽度?
解决方案
我怀疑这是因为 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>
或者,当 css 应用于下一个 DOM 更新时,您可以使用 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 计算属性中产生一些意想不到的行为。
推荐阅读
- spring - spring batch JdbcPagingItemReader 作业重启,只选择没有被选中的记录
- python - 获取日期时间的 JSON 可序列化错误
- proxy - GCP - 代理 .pac 文件配置
- sql - SQL Server 一次查询中的两个计数
- python - Python - 如何创建一个位结构?
- php - PDO Prepared 语句即使 MySQL 语句也不会返回任何内容
- xml - XSLT:在父节点中聚合子节点数据
- bash - Git Bash 崩溃了一半的办公室
- php - PHP,来自 PSR-4 项目,使用相同的命名空间打印所有类的所有方法
- c# - 来自 TabControl 的非 UI 线程的 UI 调用处理不一致