跳至內容
+

從 JSS 遷移 (選用)

本指南說明在從 Material UI v4 更新至 v5 時,如何從 JSS 遷移至 Emotion。

Material UI v5 遷移

  1. 開始使用
  2. 重大變更第一部分:樣式與主題
  3. 重大變更第二部分:元件
  4. 從 JSS 遷移 👈 您在這裡
  5. 疑難排解

從 JSS 遷移至 Emotion

v5 中最大的變更之一是以 Emotion (或 styled-components 作為替代方案) 取代 JSS 作為預設的樣式解決方案。

請注意,即使遷移至 v5 之後,您仍然可以繼續使用 JSS 來為元件新增覆寫 (例如 makeStyleswithStyles)。然後,如果您在任何時候想要轉換到新的樣式引擎,您可以逐步重構您的元件。

本文檔回顧了從 JSS 遷移所需的所有步驟。

雖然您可以使用以下兩個選項中的任何一個,但第一個選項被認為更佳

1. 使用 styled 或 sx API

Codemod

我們提供一個 codemod,以協助將 JSS 樣式遷移至 styled API,但此方法會增加 CSS 的特異性。

npx @mui/codemod@latest v5.0.0/jss-to-styled <path>

範例轉換

 import Typography from '@mui/material/Typography';
-import makeStyles from '@mui/styles/makeStyles';
+import { styled } from '@mui/material/styles';

-const useStyles = makeStyles((theme) => ({
-  root: {
-    display: 'flex',
-    alignItems: 'center',
-    backgroundColor: theme.palette.primary.main
-  },
-  cta: {
-    borderRadius: theme.shape.radius
-  },
-  content: {
-    color: theme.palette.common.white,
-    fontSize: 16,
-    lineHeight: 1.7
-  },
-}))
+const PREFIX = 'MyCard';
+const classes = {
+  root: `${PREFIX}-root`,
+  cta: `${PREFIX}-cta`,
+  content: `${PREFIX}-content`,
+}
+const Root = styled('div')(({ theme }) => ({
+  [`&.${classes.root}`]: {
+    display: 'flex',
+    alignItems: 'center',
+    backgroundColor: theme.palette.primary.main
+  },
+  [`& .${classes.cta}`]: {
+    borderRadius: theme.shape.radius
+  },
+  [`& .${classes.content}`]: {
+    color: theme.palette.common.white,
+    fontSize: 16,
+    lineHeight: 1.7
+  },
+}))

 export const MyCard = () => {
-  const classes = useStyles();
   return (
-    <div className={classes.root}>
+    <Root className={classes.root}>
       {/* The benefit of this approach is that the code inside Root stays the same. */}
       <Typography className={classes.content}>...</Typography>
       <Button className={classes.cta}>Go</Button>
-    </div>
+    </Root>
   )
 }

手動

我們建議使用 sx API 而不是 styled 來建立響應式樣式或覆寫次要 CSS。在此處閱讀更多關於 sx 的資訊

 import Chip from '@mui/material/Chip';
-import makeStyles from '@mui/styles/makeStyles';
+import Box from '@mui/material/Box';

-const useStyles = makeStyles((theme) => ({
-  wrapper: {
-    display: 'flex',
-  },
-  chip: {
-    padding: theme.spacing(1, 1.5),
-    boxShadow: theme.shadows[1],
-  }
-}));

 function App() {
-  const classes = useStyles();
   return (
-    <div className={classes.wrapper}>
-      <Chip className={classes.chip} label="Chip" />
-    </div>
+    <Box sx={{ display: 'flex' }}>
+      <Chip label="Chip" sx={{ py: 1, px: 1.5, boxShadow: 1 }} />
+    </Box>
   );
 }

在某些情況下,您可能想要在檔案中建立多個 styled 元件,而不是增加 CSS 特異性。

例如

-import makeStyles from '@mui/styles/makeStyles';
+import { styled } from '@mui/material/styles';

-const useStyles = makeStyles((theme) => ({
-  root: {
-    display: 'flex',
-    alignItems: 'center',
-    borderRadius: 20,
-    background: theme.palette.grey[50],
-  },
-  label: {
-    color: theme.palette.primary.main,
-  }
-}))
+const Root = styled('div')(({ theme }) => ({
+  display: 'flex',
+  alignItems: 'center',
+  borderRadius: 20,
+  background: theme.palette.grey[50],
+}))

+const Label = styled('span')(({ theme }) => ({
+  color: theme.palette.primary.main,
+}))

 function Status({ label }) {
-  const classes = useStyles();
   return (
-    <div className={classes.root}>
-      {icon}
-      <span className={classes.label}>{label}</span>
-    </div>
+    <Root>
+      {icon}
+      <Label>{label}</Label>
+    </Root>
   )
 }

2. 使用 tss-react

此 API 與 JSS makeStyles 類似,但在底層,它使用 @emotion/react。與 v4 的 makeStyles 相比,它還具有更好的 TypeScript 支援。

為了使用它,您需要將其新增至專案的依賴項

使用 npm

npm install tss-react

使用 yarn

yarn add tss-react

Codemod

我們提供一個 codemod,以協助將 JSS 樣式遷移至 tss-react API。

npx @mui/codemod@latest v5.0.0/jss-to-tss-react <path>

範例轉換

 import * as React from 'react';
-import makeStyles from '@material-ui/styles/makeStyles';
+import { makeStyles } from 'tss-react/mui';
 import Button from '@mui/material/Button';
 import Link from '@mui/material/Link';

-const useStyles = makeStyles((theme) => {
+const useStyles = makeStyles()((theme) => {
   return {
     root: {
       color: theme.palette.primary.main,
     },
     apply: {
       marginRight: theme.spacing(2),
     },
   };
 });

 function Apply() {
-  const classes = useStyles();
+  const { classes } = useStyles();

   return (
     <div className={classes.root}>
       <Button component={Link} to="https://support.mui.com" className={classes.apply}>
         Apply now
       </Button>
     </div>
   );
 }

 export default Apply;

如果您使用 $ 語法和 clsx 來組合多個 CSS 類別,則轉換將如下所示

 import * as React from 'react';
-import { makeStyles } from '@material-ui/core/styles';
-import clsx from 'clsx';
+import { makeStyles } from 'tss-react/mui';

-const useStyles = makeStyles((theme) => ({
+const useStyles = makeStyles<void, 'child' | 'small'>()((theme, _params, classes) => ({
   parent: {
     padding: 30,
-    '&:hover $child': {
+    [`&:hover .${classes.child}`]: {
       backgroundColor: 'red',
     },
   },
   small: {},
   child: {
     backgroundColor: 'blue',
     height: 50,
-    '&$small': {
+    [`&.${classes.small}`]: {
       backgroundColor: 'lightblue',
       height: 30
     }
   },
 }));

 function App() {
-  const classes = useStyles();
+  const { classes, cx } = useStyles();
   return (
     <div className={classes.parent}>
       <div className={classes.child}>
         Background turns red when the mouse hovers over the parent.
       </div>
-      <div className={clsx(classes.child, classes.small)}>
+      <div className={cx(classes.child, classes.small)}>
         Background turns red when the mouse hovers over the parent.
         I am smaller than the other child.
       </div>
     </div>
   );
 }

 export default App;

以下是一個全面的範例,使用了 $ 語法、useStyles() 參數、從 classes prop 合併類別 (請參閱文件) 以及 樣式表的明確名稱 (對於偵錯和主題樣式覆寫很有用)。

-import clsx from 'clsx';
-import { makeStyles, createStyles } from '@material-ui/core/styles';
+import { makeStyles } from 'tss-react/mui';

-const useStyles = makeStyles((theme) => createStyles<
-  'root' | 'small' | 'child', {color: 'primary' | 'secondary', padding: number}
->
-({
-  root: ({color, padding}) => ({
+const useStyles = makeStyles<{color: 'primary' | 'secondary', padding: number}, 'child' | 'small'>({name: 'App'})((theme, { color, padding }, classes) => ({
+  root: {
     padding: padding,
-    '&:hover $child': {
+    [`&:hover .${classes.child}`]: {
       backgroundColor: theme.palette[color].main,
     }
-  }),
+  },
   small: {},
   child: {
     border: '1px solid black',
     height: 50,
-    '&$small': {
+    [`&.${classes.small}`]: {
       height: 30
     }
   }
-}), {name: 'App'});
+}));

 function App({classes: classesProp}: {classes?: any}) {
-  const classes = useStyles({color: 'primary', padding: 30, classes: classesProp});
+  const { classes, cx } = useStyles({
+    color: 'primary',
+    padding: 30
+  }, {
+    props: {
+      classes: classesProp
+    }
+  });

   return (
     <div className={classes.root}>
       <div className={classes.child}>
         The Background take the primary theme color when the mouse hovers the parent.
       </div>
-      <div className={clsx(classes.child, classes.small)}>
+      <div className={cx(classes.child, classes.small)}>
         The Background take the primary theme color when the mouse hovers the parent.
         I am smaller than the other child.
       </div>
     </div>
   );
 }

 export default App;

執行 codemod 後,搜尋您的程式碼中是否有 "TODO jss-to-tss-react codemod",以尋找 codemod 無法可靠處理的情況。

可能還有其他超出那些帶有 TODO 註解的情況未被 codemod 完全處理—特別是當樣式的部分是由函數傳回時。

如果函數中埋藏的樣式使用 $ 語法或 useStyles 參數,則這些樣式將不會被適當地遷移。

為了確保您的類別名稱始終包含元件的實際名稱,您可以提供 name 作為隱式命名的鍵 (name: { App })。

請參閱 此 tss-react 文件 以取得詳細資訊。

如果您解構多個項目,您可能會遇到 eslint 警告 像這樣

請隨時停用 eslint(prefer-const)像這樣在常規專案中,或 像這樣在 CRA 中。

withStyles()

tss-react 還具有 類型安全實作v4 的 withStyles()

-import Button from '@material-ui/core/Button';
+import Button from '@mui/material/Button';
-import withStyles from '@material-ui/styles/withStyles';
+import { withStyles } from 'tss-react/mui';

 const MyCustomButton = withStyles(
+  Button,
   (theme) => ({
     root: {
       minHeight: '30px',
     },
     textPrimary: {
       color: theme.palette.text.primary,
     },
     '@media (min-width: 960px)': {
       textPrimary: {
         fontWeight: 'bold',
       },
     },
   }),
-)(Button);
+);

 export default MyCustomButton;

主題樣式覆寫

TSS 開箱即用支援全域主題覆寫

請按照重大變更文件中相關章節的說明進行操作,並makeStyles 提供 name

在 Material UI v5 中,樣式覆寫也接受回呼函數

預設情況下,TSS 只能提供主題。如果您想要提供 props 和 ownerState請參考此文件

完成遷移

遷移所有樣式後,透過解除安裝套件來移除不必要的 @mui/styles

使用 npm

npm uninstall @mui/styles

使用 yarn

yarn remove @mui/styles