跳转到内容

Material-UI 系统(System)

用于快速创建自定义设计的 CSS 工具集。

Material-UI 的核心包中带有几十个即用型组件。 开始使用这些组件时可能会非常困难,但当涉及到通过定制设计使你的网站脱颖而出时,从这样无风格的状态开始可能更简单。 介绍该系统:

系统让你可以利用主题中所定义的值来快速构建自定义 UI 组件。

演示

(调整窗口大小以查看响应的断点)

The house from the offer.
123 Main St, Phoenix AZ$280,000 — $310,000
CONFIDENCE SCORE 85%
import * as React from 'react';
import Box from '@material-ui/core/Box';
import { alpha } from '@material-ui/core/styles';
import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline';

export default function Demo() {
  return (
    <Box
      sx={{
        display: 'flex',
        flexDirection: { xs: 'column', md: 'row' },
        alignItems: 'center',
        bgcolor: 'background.paper',
        overflow: 'hidden',
        borderRadius: '12px',
        boxShadow: 1,
        fontWeight: 'bold',
      }}
    >
      <Box
        component="img"
        sx={{
          height: 233,
          width: 350,
          maxHeight: { xs: 233, md: 167 },
          maxWidth: { xs: 350, md: 250 },
        }}
        alt="The house from the offer."
        src="https://images.unsplash.com/photo-1512917774080-9991f1c4c750?auto=format&w=350&dpr=2"
      />
      <Box
        sx={{
          display: 'flex',
          flexDirection: 'column',
          alignItems: { xs: 'center', md: 'flex-start' },
          m: 3,
          minWidth: { md: 350 },
        }}
      >
        <Box component="span" sx={{ fontSize: 16, mt: 1 }}>
          123 Main St, Phoenix AZ
        </Box>
        <Box component="span" sx={{ color: 'primary.main', fontSize: 22 }}>
          $280,000 — $310,000
        </Box>
        <Box
          sx={{
            mt: 1.5,
            p: 0.5,
            backgroundColor: (theme) => alpha(theme.palette.primary.main, 0.1),
            borderRadius: '5px',
            color: 'primary.main',
            fontWeight: 'medium',
            display: 'flex',
            fontSize: 12,
            alignItems: 'center',
            '& svg': {
              fontSize: 21,
              mr: 0.5,
            },
          }}
        >
          <ErrorOutlineIcon />
          CONFIDENCE SCORE 85%
        </Box>
      </Box>
    </Box>
  );
}

安装

// 使用 npm
npm install @material-ui/system

// 使用 yarn
yarn add @material-ui/system

为什么要使用系统?

比较同一个统计组件如何使用两种不同的 API 来构建。

Sessions
98.3 K
18.77%
vs last week
  1. ❌ 使用 styled-components's API:
const StatWrapper = styled('div')(
  ({ theme }) => `
  background-color: ${theme.palette.background.paper};
  box-shadow: ${theme.shadows[1]};
  border-radius: ${theme.shape.borderRadius}px;
  padding: ${theme.spacing(2)};
  min-width: 300px;
`,
);

const StatHeader = styled('div')(
  ({ theme }) => `
  color: ${theme.palette.text.secondary};
`,
);

const StyledTrend = styled(TrendingUpIcon)(
  ({ theme }) => `
  color: ${theme.palette.success.dark};
  font-size: 16px;
  vertical-alignment: sub;
`,
);

const StatValue = styled('div')(
  ({ theme }) => `
  color: ${theme.palette.text.primary};
  font-size: 34px;
  font-weight: ${theme.typography.fontWeightMedium};
`,
);

const StatDiff = styled('div')(
  ({ theme }) => `
  color: ${theme.palette.success.dark};
  display: inline;
  font-weight: ${theme.typography.fontWeightMedium};
  margin-left: ${theme.spacing(0.5)};
  margin-right: ${theme.spacing(0.5)};
`,
);

const StatPrevious = styled('div')(
  ({ theme }) => `
  color: ${theme.palette.text.secondary};
  display: inline;
  font-size: 12px;
`,
);

return (
  <StatWrapper>
    <StatHeader>会话</StatHeader>
    <StatValue>98.3 K</StatValue>
    <StyledTrend />
    <StatDiff>18.77%</StatDiff>
    <StatPrevious>与上周相比</StatPrevious>
  </StatWrapper>
);
  1. ✅ 使用系统:
<Box
  sx={{
    bgcolor: 'background.paper',
    boxShadow: 1,
    borderRadius: 1,
    p: 2,
    minWidth: 300,
  }}
>
  <Box sx={{ color: 'text.secondary' }}>会话数</Box>
  <Box sx={{ color: 'text.primary', fontSize: 34, fontWeight: 'fontWeightMedium' }}>
    98.3 K
  </Box>
  <Box
    component={TrendingUpIcon}
    sx={{ color: 'success.dark', fontSize: 16, verticalAlign: 'sub' }}
  />
  <Box
    sx={{
      color: 'success.dark',
      display: 'inline',
      fontWeight: 'fontWeightMedium',
      mx: 0.5,
    }}
  >
    18.77%
  </Box>
  <Box sx={{ color: 'text.secondary', display: 'inline', fontSize: 12 }}>
    与上周相比
  </Box>
</Box>

问题已经解决

这套系统重点是解决如下三个主要问题:

**1. 切换上下文会浪费时间。 **

用户没有必要在样式组件的用法和定义的地方不断跳转。 有了这个系统,直接就可以在你需要的组件上面进行样式定制。

2. 命名是一件很难的事情。

你有没有发现自己在为一个样式组件找一个好名字而苦恼? 该系统可以直接将样式映射到元素。 所以你要做的就是只关心实际的样式属性。

**3。 UI 中要达成一致是很困难的。 **

当不止一个人在构建应用程序时尤其如此,因为团队成员之间必须就设计标记的选择和使用方式进行一些协调,主题结构的哪些部分应该使用哪些 CSS 属性等等。

系统可直接访问主题中的数值。 这样做可以在设计时更容易受到约束。

sx 属性

sx 属性作为系统的主要部分,为了解决了这些问题,它提供了一种快速 & 简单的方式,也就是将特定 CSS 属性的正确设计标记直接应用到 React 元素中。 上面的这个演示 展示了如何使用它来创建一次性设计。

该属性提供了一个 CSS 的超集,根据使用的 CSS 属性,直接从主题中映射值。 同时,它允许一个简单的方式来定义响应式的值,来对应于主题中定义的断点。

何时使用?

  • styled-components:该 API 适用于构建需要支持各种上下文的组件。 这些组件将被应用在许多不同的部位,支持不同的属性组合。
  • sx 属性:该 API 非常适合创造一次性的样式。 因此它被叫做“工具集”。

性能开销

该系统依赖 CSS-in-JS。 它可以同时和 emotion 以及 styled-components 一起工作。

优点:

  • 📚 它允许 API 具有很大的灵活性。 sx 属性支持 CSS 的超集。 所以不需要重学 CSS。 只要你学会了标准化的 CSS 语法,就可以了,很安全,十年来都没有变化。 当然如果你想要节省时间的话,也可以选择学习速记语法。
  • 📦 自动清除。 只有页面上使用过的 CSS 才会被发送到客户端。 所以初始化该捆绑包的大小成本是灵活的。 它的大小不会随着使用 CSS 属性的数量变多而同时增长。 你只需要在 @emotion/react@material-ui/system 上考虑打包大小。 在 gzip 的环境下,它们大概占用约 15kb 的空间。 如果你已经正在使用核心组件,那么将不会带来额外的捆绑包资源占用。

缺点:

  • 运行时会造成性能影响:

    基准测试 代码片段 花费时间
    a. 渲染 1,000 个基元 <div className="…"> 100ms
    b. 渲染 1,000 个组件 <Div> 120ms
    c. 渲染 1,000 个样式组件 <StyledDiv> 160ms
    d. 渲染一千个分组(Box) <Box sx={…}> 370ms

    这里是可复现的 性能测试文件夹

    我们相信,对于大多数用途来说,它已经足够快了****,但当性能变得至关重要时,也有一些简单的解决方法。 例如,当渲染一个有许多项目的列表时,你可以使用一个 CSS 子选择器来拥有一个单一的“样式注入”点(使用 d. 作为包装器,a. 应用到每个项目)。

使用

主题中的设计标记

你可以探索 系统属性 页面来发现不同的 CSS(和自定义)属性是如何映射到主题键的。

速记语法

CSS 属性中有大量的速记语法。 这些语法在之后的文档中都有记录,例如 间距。 如下是一个使用它们的例子:

<Box
  sx={{
    boxShadow: 1, // theme.shadows[1]
    color: 'primary.main', // theme.palette.primary.main
    m: 1, // margin: theme.spacing(1)
    p: {
      xs: 1, // [theme.breakpoints.up('xs')]: : { padding: theme.spacing(1) }
    },
    zIndex: 'tooltip', // theme.zIndex.tooltip
  }}
>

这些速记语法是可选的,虽然使用这些能够快速编写样式,但是也要考虑到学习自定义 API 的时间成本。 你可能想要跳过这部分并专注于使用标准几十年都没有变化的 CSS 规则,那么请跳转到 下一节

CSS 超集

作为属性的一部分,你也可以使用任何常规的 CSS:child 或者 pseudo-selectors,媒体查询(media queries), raw CSS values,等等。 以下是几个例子:

  • 使用伪类选择器:

    <Box
      sx={{
        // some styles
        ":hover": {
          boxShadow: 6,
        },
      }}
    >
  • 使用媒体查询:

    <Box
      sx={{
        // some styles
        '@media print': {
          width: 300,
        },
      }}
    >
  • 使用嵌套选择器:

    <Box
      sx={{
        // some styles
        '& .ChildSelector': {
          bgcolor: 'primary.main',
        },
      }}
    >

响应式的值

如果你想要你的 CSS 属性是响应式的,那么可以使用断点速记语法。 确定断点有两种方法:

1. 将断点作为对象

定义断点的第一种选择是将断点定义为一个对象,将断点作为其键。 这里又是前面的例子,使用的是对象语法。

This box has a responsive width.
<Box sx={{ width: { xs: 100, sm: 200, md: 300, lg: 400, xl: 500 } }}>
  This box has a responsive width.
</Box>

2. 将断点作为数组

第二种选择是将你的断点沿着最小到最大来进行定义。

This box has a responsive width.
<Box sx={{ width: [100, 200, 300] }}>This box has a responsive width.</Box>

⚠️ 只有当主题的断点数量有限时,才建议使用这个选项,例如 3.
。 如果你需要使用更多的断点,那么首选对象 API。 例如,Material-UI 的默认主题是 5.

你可以使用 null 值来跳过断点:

<Box sx={{ width: [null, null, 300] }}>该分组的宽度是响应式的。</Box>

自定义断点

你也可以指定自定义断点,并在定义断点对象时将其作为键。 下面是一个如何操作的例子。

import * as React from 'react';
import Box from '@material-ui/core/Box';
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';

const theme = createMuiTheme({
  breakpoints: {
    values: {
      tablet: 640,
      laptop: 1024,
      desktop: 1280,
    },
  },
});

export default function CustomBreakpoints() {
  return (
    <ThemeProvider theme={theme}>
      <Box
        sx={{
          width: {
            tablet: 100,
            laptop: 300,
            desktop: 500,
          },
        }}
      >
        该分组的宽度是响应式的
      </Box>
    </ThemeProvider>
  );
}

如果你使用的是 TypeScript,那么将需要使用 模块扩展(module augmentation) 来让主题接收上述值。

declare module "@material-ui/core/styles/createBreakpoints" {
  interface BreakpointOverrides {
    xs: false; // 移除 `xs` 断点
    sm: false;
    md: false;
    lg: false;
    xl: false;
    tablet: true; // 添加 `tablet` 断点
    laptop: true;
    desktop: true;
  }
}

主题获取

如果你想用主题来处理系统不支持的 CSS 属性,那么你可以使用一个函数作为值,在其中你就可以访问主题对象。

Border color with theme value.
<Box
  sx={{
    p: 1,
    border: 1,
    borderColor: (theme) => theme.palette.primary.main,
  }}
>
  Border color with theme value.
</Box>

实现

sx 属性可以用于四个不同的位置:

1. 核心组件

所有的 Material-UI 核心组件都支持 sx 属性。

2. Box 分组

Box 是一个轻量级组件,它可以以工具集的方式通过包装其他组件来达到访问其 sx 属性的目的。 默认情况下将渲染一个 <div> 元素。

3。 自定义组件

除了 Material-UI 组件,你也可以通过使用 @material-ui/core/styles 中的 experimentalStyled 工具集来将 sx 属性添加到你的自定义组件中。

import { experimentalStyled as styled } from '@material-ui/core/styles';

const Div = styled('div')``;

4、 使用 babel 插件的任何元素

等待开发 #23220