【问题标题】:code refactoring, grid layout component VueJS代码重构,网格布局组件VueJS
【发布时间】:2019-11-23 16:04:38
【问题描述】:

我正在开发一个 vue.js / nuxt.js 项目,应用Atomic Design 方法,我需要做一个设置网格布局并使用CSS Grid Layout

我已经做了组件

GridLayout.vue

<template>
  <div class="grid">
    <style>
    {{ css }}
    </style>
    <slot />
  </div>
</template>

<script>
export default {
  props: {
    columns: String,
    rows: String,
    areas: String,
    gap: String,
    columnGap: String,
    rowGap: String,
    // breakpoints
    small: Object,
    medium: Object,
    large: Object
  },
  computed: {
    css () {
      let large = ''
      let finalStyle = ''

      // generic
      let generic = ''
      if (this.columns) generic += `grid-template-columns: ${this.columns};`
      if (this.rows) generic += `grid-template-rows: ${this.rows};`
      if (this.areas) generic += `grid-template-areas: "${this.areas}";`
      if (this.gap) generic += `grid-gap: ${this.gap};`
      if (this.columnGap) generic += `grid-column-gap: ${this.columnGap};`
      if (this.rowGap) generic += `grid-row-gap: ${this.rowGap};`
      finalStyle += ` .grid { ${generic} }`

      // small
      let small = ''
      if (this.small) {
        if (this.small.columns) small += `grid-template-columns: ${this.small.columns};`
        if (this.small.rows) small += `grid-template-rows: ${this.small.rows};`
        if (this.small.areas) small += `grid-template-areas: "${this.small.areas}";`
        if (this.small.gap) small += `grid-gap: ${this.small.gap};`
        if (this.small.columnGap) small += `grid-column-gap: ${this.small.columnGap};`
        if (this.small.rowGap) small += `grid-row-gap: ${this.small.rowGap};`
        finalStyle += `@media (max-width: 600px) { .grid { ${small} } } `
      }

      // medium
      let medium = ''
      if (this.medium) {
        if (this.medium.columns) medium += `grid-template-columns: ${this.medium.columns};`
        if (this.medium.rows) medium += `grid-template-rows: ${this.medium.rows};`
        if (this.medium.areas) medium += `grid-template-areas: "${this.medium.areas}";`
        if (this.medium.gap) medium += `grid-gap: ${this.medium.gap};`
        if (this.medium.columnGap) medium += `grid-column-gap: ${this.medium.columnGap};`
        if (this.medium.rowGap) medium += `grid-row-gap: ${this.medium.rowGap};`
        finalStyle += `@media (min-width: 600px) and (max-width: 992px) { .grid { ${medium} } } `
      }

      return finalStyle
    },
  },
}
</script>

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

在任何 page.vue 上使用组件

<template>
  <GridLayout
    columns="1fr 1fr 1fr 1fr"
    rows="auto"
    gap="10px"
    :medium="{
      columns: '1fr 1fr',
      rows:'auto auto'
    }"
    :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 {
  components: {
    GridLayout
  }
}
</script>

问题

1 - &lt;template&gt; 内的样式标签 &lt;style&gt; 需要限定范围,仅适用于组件本身

2 - 每当我想要 GridLayout 组件的新属性时,例如,child align,我必须在计算中的任何地方添加,即genericsmallmediumlarge

我该如何解决这些问题?也许让我的代码更小、更智能、更少重复

提前致谢

【问题讨论】:

  • 我认为这个问题更适合代码审查:codereview.stackexchange.com
  • stackoverflow.com/help/dont-ask。您能否编辑您的问题以专注于特定的编码问题而不是意见?
  • @chipit24,我更新了我的问题,我不征求更多意见,我明确提出问题。
  • 1) 如果您的样式只适用于单个节点,则需要id。我对vue不是很了解,但是如果它有状态,你可以在生成实例时创建一个随机id并存储它。和 2) 你知道什么是重复性工作的真正优势:函数或循环 *wink*
  • Vue 不仅与原子设计兼容,而且实际上基于相同的原则。你只需要遵循 Vue 的特定语法和约定。在 Vue 中,作用域 CSS 位于标记之外并且是作用域的。阅读the docs。昨天我回答了一个关于 Vue 中作用域 CSS 的问题,我为此做了这个codesandbox。您可能会发现它很有用。

标签: javascript vue.js vuejs2 nuxt.js


【解决方案1】:
  1. 在模板中放置样式标签会产生警告;该警告解释了为什么应该避免它:

    模板应该只负责将状态映射到 UI。避免在模板中放置带有副作用的标签,例如&lt;style&gt;,因为它们不会被解析。

    你有一个 SFC(单文件组件),它已经定义了一个作用域样式块——将它用于任何预定义的样式。对于动态生成的样式,您必须使用样式绑定,例如:

    <div class="grid" :style="css">
      <slot />
    </div>
    ...
      computed: {
        css () {
          let generic = {}
          if (this.columns) generic['grid-template-columns'] = this.columns;
          if (this.columns) generic['grid-template-rows'] = this.rows;
          ...
          return [generic, large, mediumn, small];
    

    您可以在此处的 Vue 文档中阅读更多相关信息:https://vuejs.org/v2/guide/class-and-style.html#Binding-Inline-Styles

    您也可以查看vue-styled-componentshttps://github.com/styled-components/vue-styled-components。我只将它用于 React,但我认为它可能同样适用于您在 Vue 中的情况。

  2. 将重复的代码提取到一个函数中,并根据需要多次调用该代码。这称为使您的代码DRY。例如:

    methods: {
      generateGridStyles(options) {
        return {
          'grid-template-columns': options.columns || this.columns,
          'grid-template-rows': options.rows || this.rows,
          'grid-template-areas': options.areas || this.areas,
          'grid-gap': options.gap || this.gap,
          'grid-column-gap': options.columnGap || this.columnGap,
          'grid-row-gap': options.rowGap || this.rowGap,
        }
      }
    }
    

    这是该功能的使用方式:

    let small;
    if (this.small) {
      small = generateGridStyles(this.small);
    }
    

抽象带来了开销:需要维护和推理的另一个组件。想想你的整体设计以及GridLayout 组件的目标是什么。

在我看来,CSS 网格布局已经相当简单了,你的组件似乎只是简单地通过 CSS 网格属性并将它们映射到一个包装器中;它不会抽象出使用 CSS 网格的任何工作。当维护和阅读“原始”CSS 网格样式以及使用媒体查询来调整样式会更容易时,我看不到这样做的好处。

【讨论】:

  • 一个漂亮的组织,非常感谢您的回复。但是我在您的代码中发现了一个问题,或者是我的另一个困难.. 如何设置媒体查询并将其传递给:style="css"?如果你给我看,我会很高兴的。
  • 您可以使用Window.matchMedia:developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia 以编程方式获取它。在此处查看相关的 SO 问题:stackoverflow.com/questions/31511001/…。然后根据这些结果修改您的 css 对象。
  • 所有不在断点内的 props,smallmediumlarge,都是通用的,那么如何挂载通用样式对象呢? see an image of my current code
  • 我已经更新了答案中的示例。要获得通用样式,您可能不想将其包装在 if 块中,并且您可以(如果使用更新的代码)只需使用 generic = generateGridStyles()。尽管“通用”可能不是您想要的措辞,但听起来更像是默认值或备用值。
  • 当我调用 generic = generateGridStyles() 时,我在 options.columns 中收到错误 Cannot read property 'columns' of undefined
猜你喜欢
  • 2012-06-04
  • 2016-04-17
  • 2018-08-24
  • 1970-01-01
  • 1970-01-01
  • 2020-08-31
  • 2023-03-03
  • 2018-07-06
  • 1970-01-01
相关资源
最近更新 更多