Vue3栅格(Grid)

简介: 该文章介绍了一种基于24栅格系统的布局方案,通过`Row`和`Col`组件实现灵活的内容排布。`Row`负责水平方向的一组`Col`,`Col`则用于承载实际内容。栅格系统支持1到24的列值,超过24的`Col`会自动换行。此外,系统还支持Flex布局,允许子元素进行多种对齐方式,并可通过响应式属性适应不同屏幕尺寸。文章提供了详细的API说明和代码示例,展示了如何在实际项目中应用这一布局系统。

24 栅格系统

概述

布局的栅格化系统,我们是基于行(Row)和列(Col)两个组件相互配合来定义信息区块的外部框架,以保证页面的每个区域能够稳健地排布起来。
下面简单介绍一下它的工作原理:

  • 通过Row在水平方向建立一组Col
  • 你的内容应当放置于Col内,并且,只有Col可以作为Row的直接元素
  • 栅格系统中的列是指 1 到 24 的值来表示其跨越的范围。例如,三个等宽的- 列可以使用 :span="8" 来创建
  • 如果一个Row中的Col总和超过 24,那么多余的Col会作为一个整体另起一行排列

Flex 布局

我们的栅格化系统支持 Flex 布局,允许子元素在父节点内的水平对齐方式 - 居左、居中、居右、等宽排列、分散排列。子元素与子元素之间,支持顶部对齐、垂直居中对齐、底部对齐的方式。同时,支持使用 order 来定义元素的排列顺序。 Flex 布局是基于 24 栅格来定义每一个『盒子』的宽度,但不拘泥于栅格

效果如下图:在线预览

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

APIs

Row

参数 说明 类型 默认值
width 行宽度,单位 px string | number ‘auto’
gutter 栅格间隔,可以写成像素值或支持响应式的对象写法来设置水平间隔 { xs: 8, sm: 16, md: 24}。或者使用数组形式同时设置 [水平间距, 垂直间距] number | [number | Responsive, number | Responsive] | Responsive 0
wrap 是否自动换行 boolean false
align 垂直对齐方式 ‘top’ | ‘middle’ | ‘bottom’ | ‘stretch’ ‘top’
justify 水平排列方式 ‘start’ | ‘end’ | ‘center’ | ‘space-around’ | ‘space-between’ | ‘space-evenly’ ‘start’

Responsive Type

名称 说明 类型 默认值
xs <576px 响应式栅格 number undefined
sm ≥576px 响应式栅格 number undefined
md ≥768px 响应式栅格 number undefined
lg ≥992px 响应式栅格 number undefined
xl ≥1200px 响应式栅格 number undefined
xxl ≥1600px 响应式栅格 number undefined

Col

参数 说明 类型 默认值
span 栅格占位格数,取 0,1,2...24,为 0 时相当于 display: none,优先级低于 xs, sm, md, lg, xl, xxl number undefined
offset 栅格左侧的间隔格数,取 0,1,2...24 number 0
flex flex 布局填充 string | number undefined
order 栅格顺序,取 0,1,2... number 0
xs <576px 响应式栅格 number | {span?: number, offset?: number} undefined
sm ≥576px 响应式栅格 number | {span?: number, offset?: number} undefined
md ≥768px 响应式栅格 number | {span?: number, offset?: number} undefined
lg ≥992px 响应式栅格 number | {span?: number, offset?: number} undefined
xl ≥1200px 响应式栅格 number | {span?: number, offset?: number} undefined
xxl ≥1600px 响应式栅格 number | {span?: number, offset?: number} undefined

创建行组件Row.vue

其中引入使用了以下工具函数:

<script setup lang="ts">
import { ref, computed } from 'vue'
import { throttle, useEventListener } from '../utils'
interface Responsive {
  xs?: number // <576px 响应式栅格
  sm?: number // ≥576px 响应式栅格
  md?: number // ≥768px 响应式栅格
  lg?: number // ≥992px 响应式栅格
  xl?: number // ≥1200px 响应式栅格
  xxl?: number // ≥1600px 响应式栅格
}
interface Props {
  width?: string | number // 行宽度,单位 px
  // 推荐使用 (16+8n)px 作为栅格间隔(n 是自然数:0,1,2,3...)
  gutter?: number | [number | Responsive, number | Responsive] | Responsive // 栅格间隔,可以写成像素值或支持响应式的对象写法来设置水平间隔 { xs: 8, sm: 16, md: 24}。或者使用数组形式同时设置 [水平间距, 垂直间距]
  wrap?: boolean // 是否自动换行
  align?: 'top' | 'middle' | 'bottom' | 'stretch' // 垂直对齐方式
  justify?: 'start' | 'end' | 'center' | 'space-around' | 'space-between' | 'space-evenly' // 水平排列方式
}
const props = withDefaults(defineProps<Props>(), {
  width: 'auto',
  gutter: 0,
  wrap: false,
  align: 'top',
  justify: 'start'
})
const alignProperties = {
  top: 'flex-start',
  middle: 'center',
  bottom: 'flex-end',
  stretch: 'stretch'
}
const viewportWidth = ref(window.innerWidth)
function getViewportWidth() {
  viewportWidth.value = window.innerWidth
}
const throttleEvent = throttle(getViewportWidth, 100)
useEventListener(window, 'resize', throttleEvent)
const xGap = computed(() => {
  if (typeof props.gutter === 'number') {
    return props.gutter
  }
  if (Array.isArray(props.gutter)) {
    if (typeof props.gutter[0] === 'object') {
      return getResponsiveGap(props.gutter[0])
    }
    return props.gutter[0]
  }
  if (typeof props.gutter === 'object') {
    return getResponsiveGap(props.gutter)
  }
  return 0
})
const yGap = computed(() => {
  if (Array.isArray(props.gutter)) {
    if (typeof props.gutter[1] === 'object') {
      return getResponsiveGap(props.gutter[1])
    }
    return props.gutter[1]
  }
  return 0
})
const rowWidth = computed(() => {
  if (typeof props.width === 'number') {
    return props.width + 'px'
  }
  return props.width
})
function getResponsiveGap(gutter: any) {
  if (viewportWidth.value >= 1600 && gutter.xxl) {
    return gutter.xxl
  }
  if (viewportWidth.value >= 1200 && gutter.xl) {
    return gutter.xl
  }
  if (viewportWidth.value >= 992 && gutter.lg) {
    return gutter.lg
  }
  if (viewportWidth.value >= 768 && gutter.md) {
    return gutter.md
  }
  if (viewportWidth.value >= 576 && gutter.sm) {
    return gutter.sm
  }
  if (viewportWidth.value < 576 && gutter.xs) {
    return gutter.xs
  }
  return 0
}
</script>
<template>
  <div
    class="m-grid-row"
    :class="{ 'gutter-row': gutter }"
    :style="`--xGap: ${(xGap as number) / 2}px; --justify: ${justify}; --align: ${alignProperties[align]}; width: ${rowWidth}; margin-left: -${(xGap as number) / 2}px; margin-right: -${(xGap as number) / 2}px; row-gap: ${yGap}px;`"
  >
    <slot></slot>
  </div>
</template>
<style lang="less" scoped>
.m-grid-row {
  display: flex;
  flex-flow: row wrap;
  justify-content: var(--justify);
  align-items: var(--align);
  min-width: 0;
  font-size: 14px;
  color: rgba(0, 0, 0, 0.88);
  transition: all 0.3s;
}
</style>

创建列组件Col.vue

<script setup lang="ts">
import { ref, computed } from 'vue'
import { throttle, useEventListener } from '../utils'
interface Props {
  span?: number // 栅格占位格数,取 0,1,2...24,为 0 时相当于 display: none,优先级低于 xs, sm, md, lg, xl, xxl
  offset?: number // 栅格左侧的间隔格数,取 0,1,2...24
  flex?: string | number //    flex 布局填充
  order?: number // 栅格顺序,取 0,1,2...
  xs?: number | { span?: number; offset?: number } // <576px 响应式栅格
  sm?: number | { span?: number; offset?: number } // ≥576px 响应式栅格
  md?: number | { span?: number; offset?: number } // ≥768px 响应式栅格
  lg?: number | { span?: number; offset?: number } // ≥992px 响应式栅格
  xl?: number | { span?: number; offset?: number } // ≥1200px 响应式栅格
  xxl?: number | { span?: number; offset?: number } // ≥1600px 响应式栅格
}
const props = withDefaults(defineProps<Props>(), {
  span: undefined,
  offset: 0,
  flex: undefined,
  order: 0,
  xs: undefined,
  sm: undefined,
  md: undefined,
  lg: undefined,
  xl: undefined,
  xxl: undefined
})
const flexValue = computed(() => {
  if (typeof props.flex === 'number') {
    return `${props.flex} ${props.flex} auto`
  }
  return props.flex
})
const responsiveProperties = computed(() => {
  return [
    {
      width: 1600,
      value: props.xxl
    },
    {
      width: 1200,
      value: props.xl
    },
    {
      width: 992,
      value: props.lg
    },
    {
      width: 768,
      value: props.md
    },
    {
      width: 576,
      value: props.sm
    },
    {
      width: 0,
      value: props.xs
    }
  ]
})
const viewportWidth = ref(window.innerWidth)
function getViewportWidth() {
  viewportWidth.value = window.innerWidth
}
const throttleEvent = throttle(getViewportWidth, 100)
useEventListener(window, 'resize', throttleEvent)
const responsiveValue = computed(() => {
  for (const responsive of responsiveProperties.value) {
    if (responsive.value && viewportWidth.value >= responsive.width) {
      if (typeof responsive.value === 'object') {
        return {
          span: responsive.value.span || props.span,
          offset: responsive.value.offset || props.offset
        }
      } else {
        return {
          span: responsive.value,
          offset: props.offset
        }
      }
    }
  }
  return {
    span: props.span,
    offset: props.offset
  }
})
</script>
<template>
  <div
    :class="`grid-col col-${responsiveValue.span} offset-${responsiveValue.offset}`"
    style="padding-left: var(--xGap); padding-right: var(--xGap)"
    :style="`flex: ${flexValue}; order: ${order};`"
  >
    <slot></slot>
  </div>
</template>
<style lang="less" scoped>
.grid-col {
  position: relative;
  max-width: 100%;
  min-height: 1px;
  font-size: 14px;
  color: rgba(0, 0, 0, 0.88);
  line-height: 1.5714285714285714;
  transition: all 0.3s;
}
.col-0 {
  display: none;
}
.col-1 {
  display: block;
  flex: 0 0 4.16666666666666%;
  max-width: 4.16666666666666%;
}
.offset-1 {
  margin-inline-start: 4.16666666666666%;
}
.col-2 {
  display: block;
  flex: 0 0 8.33333333333333%;
  max-width: 8.33333333333333%;
}
.offset-2 {
  margin-inline-start: 8.33333333333333%;
}
.col-3 {
  display: block;
  flex: 0 0 12.5%;
  max-width: 12.5%;
}
.offset-3 {
  margin-inline-start: 12.5%;
}
.col-4 {
  display: block;
  flex: 0 0 16.66666666666666%;
  max-width: 16.66666666666666%;
}
.offset-4 {
  margin-inline-start: 16.66666666666666%;
}
.col-5 {
  display: block;
  flex: 0 0 20.83333333333333%;
  max-width: 20.83333333333333%;
}
.offset-5 {
  margin-inline-start: 20.83333333333333%;
}
.col-6 {
  display: block;
  flex: 0 0 25%;
  max-width: 25%;
}
.offset-6 {
  margin-inline-start: 25%;
}
.col-7 {
  display: block;
  flex: 0 0 29.16666666666666%;
  max-width: 29.16666666666666%;
}
.offset-7 {
  margin-inline-start: 29.16666666666666%;
}
.col-8 {
  display: block;
  flex: 0 0 33.33333333333333%;
  max-width: 33.33333333333333%;
}
.offset-8 {
  margin-inline-start: 33.33333333333333%;
}
.col-9 {
  display: block;
  flex: 0 0 37.5%;
  max-width: 37.5%;
}
.offset-9 {
  margin-inline-start: 37.5%;
}
.col-10 {
  display: block;
  flex: 0 0 41.66666666666666%;
  max-width: 41.66666666666666%;
}
.offset-10 {
  margin-inline-start: 41.66666666666666%;
}
.col-11 {
  display: block;
  flex: 0 0 45.83333333333333%;
  max-width: 45.83333333333333%;
}
.offset-11 {
  margin-inline-start: 45.83333333333333%;
}
.col-12 {
  display: block;
  flex: 0 0 50%;
  max-width: 50%;
}
.offset-12 {
  margin-inline-start: 50%;
}
.col-13 {
  display: block;
  flex: 0 0 54.16666666666666%;
  max-width: 54.16666666666666%;
}
.offset-13 {
  margin-inline-start: 54.16666666666666%;
}
.col-14 {
  display: block;
  flex: 0 0 58.33333333333333%;
  max-width: 58.33333333333333%;
}
.offset-14 {
  margin-inline-start: 58.33333333333333%;
}
.col-15 {
  display: block;
  flex: 0 0 62.5%;
  max-width: 62.5%;
}
.offset-15 {
  margin-inline-start: 62.5%;
}
.col-16 {
  display: block;
  flex: 0 0 66.66666666666666%;
  max-width: 66.66666666666666%;
}
.offset-16 {
  margin-inline-start: 66.6666666666666%;
}
.col-17 {
  display: block;
  flex: 0 0 70.83333333333333%;
  max-width: 70.83333333333333%;
}
.offset-17 {
  margin-inline-start: 70.83333333333333%;
}
.col-18 {
  display: block;
  flex: 0 0 75%;
  max-width: 75%;
}
.offset-18 {
  margin-inline-start: 75%;
}
.col-19 {
  display: block;
  flex: 0 0 79.16666666666666%;
  max-width: 79.16666666666666%;
}
.offset-19 {
  margin-inline-start: 79.16666666666666%;
}
.col-20 {
  display: block;
  flex: 0 0 83.33333333333333%;
  max-width: 83.33333333333333%;
}
.offset-20 {
  margin-inline-start: 83.33333333333333%;
}
.col-21 {
  display: block;
  flex: 0 0 87.5%;
  max-width: 87.5%;
}
.offset-21 {
  margin-inline-start: 87.5%;
}
.col-22 {
  display: block;
  flex: 0 0 91.66666666666666%;
  max-width: 91.66666666666666%;
}
.offset-22 {
  margin-inline-start: 91.66666666666666%;
}
.col-23 {
  display: block;
  flex: 0 0 95.83333333333333%;
  max-width: 95.83333333333333%;
}
.offset-23 {
  margin-inline-start: 95.83333333333333%;
}
.col-24 {
  display: block;
  flex: 0 0 100%;
  max-width: 100%;
}
.offset-24 {
  margin-inline-start: 100%;
}
</style>

在要使用的页面引入

其中引入使用了以下组件:

<script setup lang="ts">
import Row from './Row.vue'
import Col from './Col.vue'
import { reactive } from 'vue'

const colCountOptions = [
  {
    label: 2,
    value: 2
  },
  {
    label: 3,
    value: 3
  },
  {
    label: 4,
    value: 4
  },
  {
    label: 6,
    value: 6
  },
  {
    label: 8,
    value: 8
  },
  {
    label: 12,
    value: 12
  },
  {
    label: 24,
    value: 24
  }
]
const state = reactive({
  horizontalGutter: 16,
  verticalGutter: 16,
  colCount: 4
})
</script>
<template>
  <div>
    <h1>{
  { $route.name }} {
  { $route.meta.title }}</h1>
    <h2 class="mt30 mb10">基本使用</h2>
    <Row class="row">
      <Col :span="24">col</Col>
    </Row>
    <Row class="row">
      <Col :span="12">col-12</Col>
      <Col :span="12">col-12</Col>
    </Row>
    <Row class="row">
      <Col :span="8">col-8</Col>
      <Col :span="8">col-8</Col>
      <Col :span="8">col-8</Col>
    </Row>
    <Row class="row">
      <Col :span="6">col-6</Col>
      <Col :span="6">col-6</Col>
      <Col :span="6">col-6</Col>
      <Col :span="6">col-6</Col>
    </Row>
    <h2 class="mt30 mb10">水平区块间隔</h2>
    <Row :gutter="16">
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
    </Row>
    <h2 class="mt30 mb10">响应式区块间隔</h2>
    <Row :gutter="{ xs: 8, sm: 16, md: 24, lg: 32 }">
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
    </Row>
    <br />
    <a-row :gutter="{ xs: 8, sm: 16, md: 24, lg: 32 }">
      <a-col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </a-col>
      <a-col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </a-col>
      <a-col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </a-col>
      <a-col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </a-col>
    </a-row>
    <h2 class="mt30 mb10">垂直区块间隔</h2>
    <Row :gutter="[16, 24]">
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
    </Row>
    <h2 class="mt30 mb10">响应式垂直区块间隔</h2>
    <Row :gutter="[16, { xs: 8, sm: 16, md: 24, lg: 32 }]">
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
      <Col class="gutter-row" :span="6">
        <div class="gutter-box">col-6</div>
      </Col>
    </Row>
    <h2 class="mt30 mb10">左右偏移</h2>
    <Row class="row">
      <Col :span="8">col-8</Col>
      <Col :span="8" :offset="8">col-8</Col>
    </Row>
    <Row class="row">
      <Col :span="6" :offset="6">col-6 col-offset-6</Col>
      <Col :span="6" :offset="6">col-6 col-offset-6</Col>
    </Row>
    <Row class="row">
      <Col :span="12" :offset="6">col-12 col-offset-6</Col>
    </Row>
    <Row class="row">
      <Col :span="6" :md="{ offset: 4 }" :xl="{ offset: 6 }">col-6 col-md-offset-4 col-xl-offset-6</Col>
      <Col :span="6" :md="{ offset: 4 }" :xl="{ offset: 6 }">col-6 col-md-offset-4 col-xl-offset-6</Col>
    </Row>
    <h2 class="mt30 mb10">排版方式</h2>
    <Divider orientation="left">sub-element align left</Divider>
    <Row class="row" justify="start">
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
    </Row>
    <Divider orientation="left">sub-element align center</Divider>
    <Row class="row" justify="center">
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
    </Row>
    <Divider orientation="left">sub-element align right</Divider>
    <Row class="row" justify="end">
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
    </Row>
    <Divider orientation="left">sub-element align space-between</Divider>
    <Row class="row" justify="space-between">
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
    </Row>
    <Divider orientation="left">sub-element align space-around</Divider>
    <Row class="row" justify="space-around">
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
    </Row>
    <Divider orientation="left">sub-element align space-evenly</Divider>
    <Row class="row" justify="space-evenly">
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
      <Col :span="4">col-4</Col>
    </Row>
    <h2 class="mt30 mb10">对齐方式</h2>
    <Divider orientation="left">Align Top</Divider>
    <Row class="row" style="background: rgba(128, 128, 128, 0.08)" justify="center" align="top">
      <Col :span="4">
        <p class="height-100">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-50">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-120">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-80">col-4</p>
      </Col>
    </Row>
    <Divider orientation="left">Align Middle</Divider>
    <Row class="row" style="background: rgba(128, 128, 128, 0.08)" justify="space-around" align="middle">
      <Col :span="4">
        <p class="height-100">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-50">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-120">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-80">col-4</p>
      </Col>
    </Row>
    <Divider orientation="left">Align Bottom</Divider>
    <Row class="row" style="background: rgba(128, 128, 128, 0.08)" justify="space-between" align="bottom">
      <Col :span="4">
        <p class="height-100">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-50">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-120">col-4</p>
      </Col>
      <Col :span="4">
        <p class="height-80">col-4</p>
      </Col>
    </Row>
    <h2 class="mt30 mb10">flex 填充</h2>
    <Divider orientation="left">Percentage columns</Divider>
    <Row class="row">
      <Col :flex="2">2 / 5</Col>
      <Col :flex="3">3 / 5</Col>
    </Row>
    <Divider orientation="left">Fill rest</Divider>
    <Row class="row">
      <Col flex="0 0 100px">100px</Col>
      <Col flex="auto">auto</Col>
    </Row>
    <Divider orientation="left">Raw flex style</Divider>
    <Row class="row">
      <Col flex="1 1 200px">1 1 200px</Col>
      <Col flex="0 1 300px">0 1 300px</Col>
    </Row>
    <Row class="row">
      <Col flex="none">
        <div style="padding: 0 16px">none</div>
      </Col>
      <Col flex="auto">auto with no-wrap</Col>
    </Row>
    <h2 class="mt30 mb10">响应式布局</h2>
    <Row class="row">
      <Col :xs="2" :sm="4" :md="6" :lg="8" :xl="10">Col</Col>
      <Col :xs="20" :sm="16" :md="12" :lg="8" :xl="4">Col</Col>
      <Col :xs="2" :sm="4" :md="6" :lg="8" :xl="10">Col</Col>
    </Row>
    <h2 class="mt30 mb10">span 和 offset 属性的响应式</h2>
    <Row class="row">
      <Col :xs="{ span: 5, offset: 1 }" :sm="{ span: 6, offset: 2 }">Col</Col>
      <Col :xs="{ span: 11, offset: 1 }" :sm="{ span: 6, offset: 2 }">Col</Col>
      <Col :xs="{ span: 5, offset: 1 }" :sm="{ span: 6, offset: 2 }">Col</Col>
    </Row>
    <h2 class="mt30 mb10">栅格配置器</h2>
    <Row :gutter="24">
      <Col :span="8">
        <Flex vertical gap="large" align="flex-start">
          Horizontal Column Gutter:
          <Slider :step="8" :max="48" v-model:value="state.horizontalGutter" />
        </Flex>
      </Col>
      <Col :span="8">
        <Flex vertical gap="large" align="flex-start">
          Vertical Row Gutter:
          <Slider :step="8" :max="48" v-model:value="state.verticalGutter" />
        </Flex>
      </Col>
      <Col :span="8">
        <Flex vertical align="flex-start">
          Col Count:
          <Radio :options="colCountOptions" v-model:value="state.colCount" button />
        </Flex>
      </Col>
    </Row>
    <Row class="mt30" :gutter="[state.horizontalGutter, state.verticalGutter]">
      <Col class="gutter-row" v-for="n in state.colCount" :key="n" :span="24 / state.colCount">
        <div class="gutter-box">Col-{
  { 24 / state.colCount }}</div>
      </Col>
      <Col class="gutter-row" v-for="n in state.colCount" :key="n" :span="24 / state.colCount">
        <div class="gutter-box">Col-{
  { 24 / state.colCount }}</div>
      </Col>
      <Col class="gutter-row" v-for="n in state.colCount" :key="n" :span="24 / state.colCount">
        <div class="gutter-box">Col-{
  { 24 / state.colCount }}</div>
      </Col>
    </Row>
  </div>
</template>
<style lang="less" scoped>
.row {
  .m-col {
    min-height: 30px;
    margin-top: 8px;
    margin-bottom: 8px;
    color: #fff;
    text-align: center;
    border-radius: 0;
    padding: 16px 0;
    background: #1677ff;
  }
  :deep(> :nth-child(2n + 1)) {
    background: #1677ffbf;
  }
}
.gutter-row {
  .m-col {
    min-height: 30px;
    color: #fff;
    text-align: center;
    border-radius: 0;
  }
  .gutter-box {
    background: #0092ff;
    padding: 8px 0;
  }
}
.height-50 {
  height: 50px;
  line-height: 50px;
}
.height-80 {
  height: 80px;
  line-height: 80px;
}
.height-100 {
  height: 100px;
  line-height: 100px;
}
.height-120 {
  height: 120px;
  line-height: 120px;
}
</style>
相关文章
|
6天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
109 64
|
6天前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
|
1月前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
2月前
|
API
vue3知识点:provide 与 inject
vue3知识点:provide 与 inject
35 4
vue3知识点:provide 与 inject
|
2月前
|
API
vue3知识点:readonly 与 shallowReadonly
vue3知识点:readonly 与 shallowReadonly
26 1
vue3知识点:readonly 与 shallowReadonly
|
28天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
57 7
|
29天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
47 3
|
28天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
46 1
|
28天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
49 1
|
1月前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef