vue.js - 如何根据浏览器动态支持的内容在使用网格布局的模板和使用 Flex 的模板之间切换?
问题描述
我有以下代码CODEPEN
HTML
body
#__nuxt
#__layout
//- CSS Grid
.news-item(:key="item.feed_item_id" :ref="item.feed_item_id" role="listitem" tabindex="0")
.news-item-pubdate 1m
.news-item-title(:style="titleStyle") {{item.title}}
span.news-item-link example.com
.news-item-likes
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-thumbs-up
span.news-item-vote-count {{item.likes}}
.news-item-dislikes
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-thumbs-down
span.news-item-vote-count {{item.dislikes}}
.news-item-bullish
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-arrow-up
span.news-item-vote-count {{item.bullish}}
.news-item-bearish
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-arrow-down
span.news-item-vote-count {{item.bearish}}
.news-item-comments
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-comment-alt
span.news-item-vote-count 0
.news-item-tags.has-text-right(:style="tagStyle")
.news-item-tag(v-for='(tag, i) in item.tags' :key='item.tag' @click.stop="updateTag(tag)")
a.button.is-paddingless.is-small.is-uppercase.is-text
|{{tag}}
span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
span(v-else)
//- Flexbox
.news-item2(:key="item.feed_item_id" :ref="item.feed_item_id" role="listitem" tabindex="0")
.news-item-pubdate-wrapper
.news-item-pubdate2 1m
.news-item-content-wrapper
.news-item-title-wrapper
.news-item-title2(:style="titleStyle") {{item.title}}
span.news-item-link2 example.com
.news-item-votes-wrapper
.news-item-likes2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-thumbs-up
span.news-item-vote-count2 {{item.likes}}
.news-item-dislikes2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-thumbs-down
span.news-item-vote-count2 {{item.dislikes}}
.news-item-bullish2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-arrow-up
span.news-item-vote-count2 {{item.bullish}}
.news-item-bearish2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-arrow-down
span.news-item-vote-count2 {{item.bearish}}
.news-item-comments2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-comment-alt
span.news-item-vote-count2 0
.news-item-tags-wrapper
.news-item-tags2.has-text-right(:style="tagStyle")
.news-item-tag2(v-for='(tag, i) in item.tags' :key='item.tag' @click.stop="updateTag(tag)")
a.button.is-paddingless.is-small.is-uppercase.is-text
|{{tag}}
span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
span(v-else)
CSS
/**
Using CSS GRID
*/
:root {
--border-color: lightgray;
}
$grey: hsl(0, 0%, 48%);
$grey-light: hsl(0, 0%, 71%);
$size-7: 0.75rem;
$warning-light: hsl(48, 100%, 67%);
.news-item {
display: grid;
grid-template-areas:
'time title title title title title title tags'
'time likes dislikes bullish bearish comments . tags';
grid-template-rows: 1fr auto;
grid-template-columns: auto auto auto auto auto auto 1fr auto;
border-bottom: 1px solid var(--border-color);
}
.news-item-title {
grid-area: title;
overflow: hidden;
}
.news-item-likes {
grid-area: likes;
}
.news-item-dislikes {
grid-area: dislikes;
}
.news-item-bullish {
grid-area: bullish;
}
.news-item-bearish {
grid-area: bearish;
}
.news-item-comments {
grid-area: comments;
}
.news-item-tags {
grid-area: tags;
overflow: hidden;
}
a.news-item-tag {
color: $grey;
text-decoration: none;
}
.news-item-link {
color: $grey;
font-size: $size-7;
}
.news-item-pubdate {
grid-area: time;
align-self: center;
color: $grey-light;
font-size: $size-7;
}
.news-item-vote-icon {
color: $grey-light;
margin-right: 0.1rem;
}
.news-item-vote-count {
font-size: 0.75rem;
margin-right: 0.5rem;
color: $grey-light;
}
.news-item-selected {
background: $warning-light;
}
/** https://stackoverflow.com/questions/23608346/how-to-style-a-div-like-the-button-element */
.news-item {
cursor: pointer;
user-select: none;
}
a.button.is-paddingless.is-small.is-uppercase.is-text {
text-decoration: none;
line-height: 1.5;
color: $grey;
}
/**
Using FLEXBOX
*/
.news-item2 {
display: flex;
align-items: center;
border-bottom: 1px solid var(--border-color);
}
.news-item-content-wrapper {
flex: 1;
display: flex;
flex-direction: column;
}
.news-item-votes-wrapper {
display: flex;
}
.news-item-title2 {
overflow: hidden;
}
.news-item-link2 {
color: $grey;
font-size: $size-7;
}
.news-item-pubdate2 {
grid-area: time;
align-self: center;
color: $grey-light;
font-size: $size-7;
}
.news-item-vote-icon2 {
color: $grey-light;
margin-right: 0.1rem;
}
.news-item-vote-count2 {
font-size: 0.75rem;
margin-right: 0.5rem;
color: $grey-light;
}
.news-item-tags2 {
overflow: hidden;
}
Vue JS
new Vue({
el: "#__nuxt",
data() {
return {
item: {
feed_item_id: 1,
title: 'The first one is made with a grid layout while the second one is made with a flex layout. The templates are different, how do I load the right one based on what is supported by the browser?',
tags: ['trump', 'putin', 'defi'],
likes: 10,
dislikes: 5,
bullish: 6,
bearish: 0,
},
lineCountTag: 2,
lineCountTitle: 2,
heightPerLineTag: 30,
heightPerLineTitle: 24,
};
},
computed: {
tagStyle() {
return {
height: this.heightPerLineTag * this.lineCountTag + "px",
maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
};
},
titleStyle() {
return {
height: this.heightPerLineTitle * this.lineCountTitle + "px",
maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
};
}
}
});
- 我做了两行,第一行是网格布局
- 第二个使用 flexbox 布局
- 我想要一个 Vue 组件,它基本上可以根据浏览器是否支持 flexbox 或网格来加载其中一个
- 问题是模板彼此不同
- 我该怎么做呢?
解决方案
我想我明白了,需要 2 制作 2 个单独的组件,一个以 flex 为结构,一个以 css 网格为结构,不能同时在一个组件中,因为使用 css 网格和 flexbox 的项目的结构布局不同,制作一个第三个组件,使用计算属性 CSS.supports('display', 'grid') 检查 JS 中对 CSS Grid 的支持
HTML
script#flex(type="text/x-template")
.news-item2(
:key="item.feed_item_id",
:ref="item.feed_item_id",
role="listitem",
tabindex="0"
)
.news-item-pubdate-wrapper
.news-item-pubdate2 1m
.news-item-content-wrapper
.news-item-title-wrapper
.news-item-title2(:style="titleStyle") {{ item.title }}
span.news-item-link2 example.com
.news-item-votes-wrapper
.news-item-likes2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-thumbs-up
span.news-item-vote-count2 {{ item.likes }}
.news-item-dislikes2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-thumbs-down
span.news-item-vote-count2 {{ item.dislikes }}
.news-item-bullish2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-arrow-up
span.news-item-vote-count2 {{ item.bullish }}
.news-item-bearish2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-arrow-down
span.news-item-vote-count2 {{ item.bearish }}
.news-item-comments2
span.icon.is-small.news-item-vote-icon2
i.fa.fa-xs.fa-comment-alt
span.news-item-vote-count2 0
.news-item-tags-wrapper
.news-item-tags2.has-text-right(:style="tagStyle")
.news-item-tag2(
v-for="(tag, i) in item.tags",
:key="item.tag",
@click.stop="updateTag(tag)"
)
a.button.is-paddingless.is-small.is-uppercase.is-text
| {{ tag }}
span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
span(v-else)
script#grid(type="text/x-template")
.news-item(
:key="item.feed_item_id",
:ref="item.feed_item_id",
role="listitem",
tabindex="0"
)
.news-item-pubdate 1m
.news-item-title(:style="titleStyle") {{ item.title }}
span.news-item-link example.com
.news-item-likes
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-thumbs-up
span.news-item-vote-count {{ item.likes }}
.news-item-dislikes
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-thumbs-down
span.news-item-vote-count {{ item.dislikes }}
.news-item-bullish
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-arrow-up
span.news-item-vote-count {{ item.bullish }}
.news-item-bearish
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-arrow-down
span.news-item-vote-count {{ item.bearish }}
.news-item-comments
span.icon.is-small.news-item-vote-icon
i.fa.fa-xs.fa-comment-alt
span.news-item-vote-count 0
.news-item-tags.has-text-right(:style="tagStyle")
.news-item-tag(
v-for="(tag, i) in item.tags",
:key="item.tag",
@click.stop="updateTag(tag)"
)
a.button.is-paddingless.is-small.is-uppercase.is-text
| {{ tag }}
span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
span(v-else)
script#item(type="text/x-template")
template(v-if="supportsCssGrid", :item="item")
grid(:item="item")
template(v-else)
flex(:item="item")
body
#__nuxt
#__layout
item(:item="item")
Vue JS
const lineClamp = {
data() {
return {
lineCountTag: 2,
lineCountTitle: 2,
heightPerLineTag: 30,
heightPerLineTitle: 24
};
},
computed: {
tagStyle() {
return {
height: this.heightPerLineTag * this.lineCountTag + "px",
maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
};
},
titleStyle() {
return {
height: this.heightPerLineTitle * this.lineCountTitle + "px",
maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
};
}
}
};
Vue.component("flex", {
template: "#flex",
mixins: [lineClamp],
props: {
item: {
type: Object,
default: () => {},
required: true
}
}
});
Vue.component("grid", {
template: "#grid",
mixins: [lineClamp],
props: {
item: {
type: Object,
default: () => {},
required: true
}
}
});
Vue.component("item", {
template: "#item",
props: {
item: {
type: Object,
default: () => {},
required: true
}
},
computed: {
supportsCssGrid() {
return CSS.supports("display", "grid");
}
}
});
new Vue({
el: "#__nuxt",
data() {
return {
item: {
feed_item_id: 1,
title:
"The first one is made with a grid layout while the second one is made with a flex layout. The templates are different, how do I load the right one based on what is supported by the browser?",
tags: ["trump", "putin", "defi"],
likes: 10,
dislikes: 5,
bullish: 6,
bearish: 0
},
lineCountTag: 2,
lineCountTitle: 2,
heightPerLineTag: 30,
heightPerLineTitle: 24
};
},
computed: {
tagStyle() {
return {
height: this.heightPerLineTag * this.lineCountTag + "px",
maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
};
},
titleStyle() {
return {
height: this.heightPerLineTitle * this.lineCountTitle + "px",
maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
};
}
}
});
推荐阅读
- python - Don't understand cause of this AttributeError: '_Screen' object has no attribute 'setimage'
- javascript - 如何用两个对象读取 Json?
- python - 检查python中是否存在系统范围内的互斥锁
- c++ - 如何限制我连续拥有的数字数量?
- java - 如何创建弹出消息以提醒用户字段不完整?
- java - 如何使用比较器
作为通用 SortedDoublyLinkedList 中的参数 - java - 如果鼠标正在移动,则重新启动计时器
- polymer - 什么是 Polymer 3 中纸质菜单和纸质子菜单的正确替换?
- python - 动态制作文件名时不理解此字符串连接错误
- ios - 在 Swift 中将单词转换为数字