MaterialUI算是现在比较常见的UIKit了,用的人多,社区比较大,而且各种环境和框架的移植也很丰富,用起来比较简单和方便。当然使用的时候因为一些特有的概念和语法糖,还是需要一点上手时间。
官方的教程:Tutorial(基本上没用)。
一些资源:
直接的样式处理及样式的编程,见文档:@material-ui/styles。简单范例:
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
const useStyles = makeStyles({
root: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
border: 0,
borderRadius: 3,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
color: 'white',
height: 48,
padding: '0 30px',
},
});
export default function Hook() {
const classes = useStyles();
return <Button className={classes.root}>Hook</Button>;
}
栅格系统是布局的基础,官方文档在:Grid 栅格。当然,MaterialUI也提供了直接使用Flexbox来进行布局自定义的能力。另外,在做响应式布局的时候,Hidden组件也是非常重要的,能在某些分辨率下隐藏一些组件的显示。
栅格系统使用 Grid “盒子”组件实现:
首先需要了解响应式布局的网格断点,见文档:Breakpoints。要注意,虽然断点的尺寸是有预定义值的,但也是可以自定义改动的。
每个断点(一个键)匹配一个固定的屏幕宽度(一个值):
value |0px 600px 960px 1280px 1920px
key |xs sm md lg xl
screen width |--------|--------|--------|--------|-------->
range | xs | sm | md | lg | xl
// 样式的屏幕尺寸感知,并自适应
const styles = theme => ({
root: {
padding: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
backgroundColor: theme.palette.secondary.main,
},
[theme.breakpoints.up('md')]: {
backgroundColor: theme.palette.primary.main,
},
[theme.breakpoints.up('lg')]: {
backgroundColor: green[500],
},
},
});
一般在做响应式设计的时候,会用到Hidden组件,该组件有3组属性可以控制当前组件在某些断点情况下的显示和隐藏:
up
属性的元素,给定的 子节点 将在断点以及断点以上
时被隐藏。down
属性的元素,给定 子节点 将在断点以及断点以下
时被隐藏。only
属性,给定 子节点 将被隐藏在指定的断点
。innerWidth |xs sm md lg xl
|--------|--------|--------|--------|-------->
width | xs | sm | md | lg | xl
smUp | show | hide
mdDown | hide | show
看几个例子:
Up
*Up:使用任何断点up
属性的元素,给定的 子节点 将在断点以及断点以上
时被隐藏。playground:链接
<div className={classes.container}>
<Hidden xsUp>
<Paper className={classes.paper}>xsUp</Paper>
</Hidden>
<Hidden smUp>
<Paper className={classes.paper}>smUp</Paper>
</Hidden>
<Hidden mdUp>
<Paper className={classes.paper}>mdUp</Paper>
</Hidden>
<Hidden lgUp>
<Paper className={classes.paper}>lgUp</Paper>
</Hidden>
<Hidden xlUp>
<Paper className={classes.paper}>xlUp</Paper>
</Hidden>
</div>
效果:
Down
*Down:使用任何断点down
属性的元素,给定 子节点 将在断点以及断点以下
时被隐藏。playground:链接
<div className={classes.container}>
<Hidden xsDown>
<Paper className={classes.paper}>xsDown</Paper>
</Hidden>
<Hidden smDown>
<Paper className={classes.paper}>smDown</Paper>
</Hidden>
<Hidden mdDown>
<Paper className={classes.paper}>mdDown</Paper>
</Hidden>
<Hidden lgDown>
<Paper className={classes.paper}>lgDown</Paper>
</Hidden>
<Hidden xlDown>
<Paper className={classes.paper}>xlDown</Paper>
</Hidden>
</div>
效果:
Only
*Only:利用断点only
属性,给定 子节点 将被隐藏在指定的断点
。playground:链接
<div className={classes.container}>
<Hidden only="lg">
<Paper className={classes.paper}>Hidden on lg</Paper>
</Hidden>
<Hidden only="sm">
<Paper className={classes.paper}>Hidden on sm</Paper>
</Hidden>
<Hidden only={['sm', 'lg']}>
<Paper className={classes.paper}>Hidden on sm and lg</Paper>
</Hidden>
</div>
效果:
实际组件的空间占用布局是按Fluid grids 流式网格来排放的。举个例子:
import React from 'react';
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
flexGrow: 1,
},
paper: {
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
},
}),
);
export default function CenteredGrid() {
const classes = useStyles();
return (
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper className={classes.paper}>xs=12</Paper>
</Grid>
<Grid item xs={6}>
<Paper className={classes.paper}>xs=6</Paper>
</Grid>
<Grid item xs={6}>
<Paper className={classes.paper}>xs=6</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
<Grid item xs={3}>
<Paper className={classes.paper}>xs=3</Paper>
</Grid>
</Grid>
</div>
);
}
效果:
例子中的xs
是指前面提到的断点。只有屏幕尺寸在xs及其上的断点的情况下,才会按此布局。里面的数值是相对的,第一行最大值为12,后面第二行的6就是其一半。这个例子的playground:链接。
定义尺寸的时候可以对一个组件设置多个网格断点情况下的大小,这样做的话,可以让组件排布顺应不同的屏幕尺寸发生变化,看下面的例子:
例子中,在屏幕尺寸大于sm的情况下(从大到小匹配),sm的尺寸定义发挥作用:第二行的组件占比都为6,第二行2个组件各占一半,同样的第三行占比设为3,一行正好4个组件都放进去。
然后在屏幕尺寸缩小之后,尺寸小于sm,且大于xs,则xs的尺寸定义发挥作用:原来第二行各分一半的2个组件分别占了一行,原来第三行的4个组件分为了两行,每行两个。这个例子的playground:链接。
页面的主要容器,一般来说所有的内容都会放在Container内。Container的最大宽度,使用maxWidth
来设置,里面的值需要设置刚才的断点值:xs、sm、md、lg、xl。另,Container的API文档,属性相关内容都在这里。
<Container maxWidth="sm" />
包装容器,一般用来将某几个组件包装在一起做位置偏移或样式添加等操作。Box默认为一个div,也可以使用component
来设置实际的HTML标签:
<Box component="span" m={1}>
<Button />
</Box>
最基本的容器组件,通过将该组件相互嵌套使用,就可以组成非常复杂的页面布局。另,Grid的API文档,组件属性需要到这里查阅。
嵌套的情况下,container
属性表示是包在外层的Grid,item
则表示是从属于外层包装的Grid。如果有多层嵌套,则中间层的Grid既是container又是item。看个例子,playground在链接:
// defining FormRow
function FormRow() {
return (
<React.Fragment>
<Grid item xs={4}>
<Paper className={classes.paper}>item</Paper>
</Grid>
<Grid item xs={4}>
<Paper className={classes.paper}>item</Paper>
</Grid>
<Grid item xs={4}>
<Paper className={classes.paper}>item</Paper>
</Grid>
</React.Fragment>
);
}
// using FormRow
<Grid container spacing={1}>
<Grid container item xs={12} spacing={3}>
<FormRow />
</Grid>
<Grid container item xs={12} spacing={3}>
<FormRow />
</Grid>
<Grid container item xs={12} spacing={3}>
<FormRow />
</Grid>
</Grid>
效果:
在MaterialUI中设置间距有已经做好的语法糖,直接使用即可,文档:Spacing,还有Theme Spacing。
其中属性是其中之一:
哪边边是其中之一:
上面提到的属性的值,是数字,意思是几份
。比如说my={1}
就是上下添加一份的margin。下面的例子中,一份就是8px(MaterialUI的默认值一份就是8px)。
const theme = {
spacing: 8,
}
<Box m={-2} /> // margin: -16px;
<Box m={0} /> // margin: 0px;
<Box m={0.5} /> // margin: 4px;
<Box m={2} /> // margin: 16px;
见文档:Sizing。
<Box width={1/4} /> // Numbers in [0,1] are multiplied by 100 and converted to % values.
<Box width={300} /> // Numbers are converted to pixel values.
<Box width="75%" /> // String values are used as raw CSS.
<Box width={1} /> // 100%
MaterialUI提供了配色相关的解决方案,见:Color 颜色(如果需要选择颜色代码的话,就直接在这个文档页面上查找),以及Theme Palette,以及Palette 调色。
<Box color="warning.main" />
<Box bgcolor="info.main" />
MaterialUI的阴影直接使用属性boxShadow
就能实现了,文档:Shadow。
<Box boxShadow={1} />
有时候一些元素会在同位置上进行重叠,这时候就需要确定它们的层级来进行相互覆盖的定义。在CSS中我们会使用z-index
来操作,在MaterialUI中,一般使用属性zIndex
来操作,且MaterialUI已经定义了一些,一般直接使用即可,范例:
<Box zIndex="tooltip" />
<Box zIndex="modal" />
组件内文字内容的Typography,MaterialUI有属性可以直接控制,不需要手动编写CSS。见文档:Typography,以及Theme Typography。
<Box textAlign="left" /> // right, left, center
<Box fontWeight="fontWeightRegular" /> // light, regular, medium, 500, bold
<Box fontSize="fontSize" /> // default, h6.fontSize, 16
<Box fontStyle="normal" /> // normal, italic, oblique
<Box letterSpacing={6} /> // 字符间距,这里的数值就是6px
<Box lineHeight="normal" /> // normal, 10
如果需要在页面上直接展示文字,建议使用组件Typography,见文档:Typography。此外,还有API文档,组件的属性需要到这里查阅。
看一个例子,该例子的playground在:链接。
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography gutterBottom variant="subtitle1">
Standard license
</Typography>
<Typography variant="body2" gutterBottom>
Full resolution 1920x1080 • JPEG
</Typography>
<Typography variant="body2" color="textSecondary">
ID: 1030114
</Typography>
</Grid>
<Grid item>
<Typography variant="body2" style={{ cursor: 'pointer' }}>
Remove
</Typography>
</Grid>
</Grid>
<Grid item>
<Typography variant="subtitle1">$19.00</Typography>
</Grid>
</Grid>
效果:
该组件的noWrap
属性默认值为false,也就是说遇到文字过长的情况下会进行回行。如果将这个属性设置为true,则文字过长的部分会被截断,并显示为省略号。
MaterialUI有一套统一的设计方案,方便开发者在App级别对所有的组件进行统一的样式及风格定义。官方文档:@material-ui/system。一般来说项目大的话,肯定有需要进行这部分的调整。如果使用的是官方Store中的一些Template,一般它们已经做好了对应的功能。
这部分比较庞大,细节很多。这里放一个简单例子,直观感受下:
import React from 'react'
import { ThemeProvider } from 'styled-components'
const theme = {
spacing: 4,
palette: {
primary: '#007bff',
},
};
export default function App() {
return (
<ThemeProvider theme={theme}>
{/* children */}
</ThemeProvider>
)
}
更多的细节可以查看MaterialUI的Customization个性化文档。
MaterialUI对Typescript支持很好,官方文档:TypeScript。里面细节还是有点多的,需要慢慢习惯用法。
EOF