首页
/ Material UI + Vite + TypeScript 官方示例深度解析:项目结构、配置项与上手指南

Material UI + Vite + TypeScript 官方示例深度解析:项目结构、配置项与上手指南

2026-09-07 14:12:10作者:冯梦姬Eddie

Material UI 官方在仓库中维护了一组与主流构建工具集成的“最小可运行示例”,本篇文章聚焦其中基于 Vite + TypeScript 的示例(examples/material-ui-vite-ts)。它演示了如何用现代 ESM 工程化方式把 @mui/material 及其依赖(含 Emotion 样式引擎)接入一个热更新的开发环境,涵盖脚手架结构、工程化配置、TypeScript 项目引用关系、样式书写方式以及从启动到产出的完整命令链。读完本文,你将掌握一套可直接复制的 Material UI + Vite + TS 工程骨架,并理解每个文件与每个配置项的用途。

一、示例解决什么问题

原文档 examples/material-ui-vite-ts/README.md 对示例的定位是一句话概括:演示如何将 Material UI 与 Vite、TypeScript 组合使用,并内置 @mui/material 及其 peer 依赖,包括 Emotion——Material UI 的默认样式引擎。

在 Material UI 官方文档中,这个示例也是 TypeScript 入门链路里的指定参照物:TypeScript 使用指南 明确说明 Material UI 要求 TypeScript 4.9 及以上,并直接把这个 Vite + TS 示例作为推荐起点。它不是一个“空壳”,而是一个信息完整的可运行 Demo:

  • 首页渲染标题、版权行与一个“灯泡”Pro tip 提示;
  • 使用 Container / Box / Typography / Link / SvgIcon 等核心组件演示布局与排版;
  • 通过 sx 属性演示 Material UI 的样式系统,无需编写单独的 CSS 文件;
  • 通过 src/App.tsxsrc/ProTip.tsx 等文件演示“组件拆分 + 自定义图标 + 复用”的组织方式。

也就是说,它同时承担了“Vite 模板示范”“TS 工程化示范”和“MUI 基础用法示范”三重角色。

二、快速启动:安装、开发与构建

示例目录下的文件清单如下:

examples/material-ui-vite-ts/
├── index.html              # Vite 的 HTML 入口
├── package.json            # 依赖与脚本定义
├── vite.config.ts          # Vite 配置
├── tsconfig.json           # TypeScript 项目引用(根)
├── tsconfig.app.json       # 应用代码编译配置
├── tsconfig.node.json      # Vite 配置文件编译配置
├── public/vite.svg
└── src/
    ├── App.tsx             # 根组件
    ├── ProTip.tsx          # 提示条(含自定义 SvgIcon)
    ├── main.tsx            # React 挂载入口
    └── vite-env.d.ts       # Vite 客户端类型声明

获得示例后,安装依赖并启动开发服务器:

cd examples/material-ui-vite-ts
npm install
npm run dev

dev 命令启动 Vite 开发服务器后,浏览器会自动打开页面,能看到带 Roboto 字体的 Material Design 风格页面。原文档还提供在线体验方式(CodeSandbox 与 StackBlitz 的 “Open in …” 入口),适合不下载代码快速预览。

示例在 package.json 中预置了三类脚本,覆盖“开发—构建—预览”完整链路:

命令 底层实现 作用
npm run dev vite 启动开发服务器,带 HMR 热更新
npm run build tsc -b && vite build 先用 TypeScript 项目引用模式做全量类型检查,再由 Vite 产出生产包
npm run preview vite preview 本地预览构建产物,验证生产包效果

值得注意 build 脚本的设计:tsc -b(build 模式)会按照 tsconfig.json 中声明的 references 依次检查 tsconfig.app.jsontsconfig.node.json 两个子项目,只有类型检查通过后才进入 vite build。这与“先类型安全、再打包”的最佳实践一致。

三、逐文件剖析工程化配置

1. vite.config.ts:极简即正确

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
});

Vite 对 React + TS 场景的开箱即用程度很高,只需要接入官方 React 插件 @vitejs/plugin-react(提供 Fast Refresh 与 JSX 转换支持),无需额外配置路径别名、CSS 预处理或 polyfill。Material UI 的 ESM 产物可被 Vite 原生处理,这也是该示例保持配置极简的原因。

2. index.html:字体与移动端视口

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="initial-scale=1, width=device-width" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
    />
    <title>Vite + Material UI + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

几个细节值得关注:

  • 采用 initial-scale=1, width=device-width 的移动端视口写法,这正是 Material UI 官方建议的移动优先页面配置;
  • 使用 preconnect 提前建立到 Google Fonts 的连接,并加载 Roboto 字重 300/400/500/700,与 Material Design 排版体系和 Material UI 默认主题的字体栈相契合;
  • 页面不包含任何可见 HTML,只保留 id="root" 的挂载点,全部 UI 由 React 渲染;
  • 脚本以 <script type="module"> 加载 /src/main.tsx,这是 Vite 基于原生 ES Modules 的开发模型。

3. src/main.tsx:React 18+ 的挂载方式

import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import App from './App.tsx';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);
  • 使用 react-dom/clientcreateRoot 并发渲染 API;
  • document.getElementById('root')! 通过非空断言告诉 TypeScript 该节点一定存在;
  • <React.StrictMode> 包裹根组件,帮助在开发期暴露副作用问题(如不纯的渲染函数、过期闭包),便于尽早发现隐患;
  • 注意 import App from './App.tsx' 显式携带 .tsx 扩展名——这是 allowImportingTsExtensions 开启后的合法写法(见下文 tsconfig),与日常省略扩展名的习惯不同,但同样有效。

4. 三份 tsconfig:项目引用(Project References)的拆分配置

tsconfig.json 本身不做编译,只做“路由”:

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}

这种拆分是 Vite 官方脚手架(create-vite)的推荐做法:

  • tsconfig.app.json:负责 src/ 下的应用代码。关键项包括 target: ES2020lib: [ES2020, DOM, DOM.Iterable]module: ESNextmoduleResolution: bundlerjsx: react-jsxnoEmit: true,并开启 strictnoUnusedLocalsnoUnusedParameters 等严格检查;
  • tsconfig.node.json:负责 vite.config.ts 等 Node 侧工具链文件,target: ES2022lib: [ES2023]、同样 moduleResolution: bundlernoEmit

对 Material UI 开发者而言,tsconfig.app.json 中两个配置与体验直接相关:

  • "moduleResolution": "bundler":面向打包器(Vite/Webpack 等)的解析策略,让 MUI 包内类型声明能被准确解析,并允许按包名直接导入子路径(如 @mui/material/Container);
  • "jsx": "react-jsx":自动 JSX runtime,无需在每个文件里 import React

由于两个子项目都设置 noEmit: true,类型检查不会产出任何 .js 文件,真正“产出”代码的是 Vite,二者职责互不干扰。

此外 src/vite-env.d.ts 中仅有一行 /// <reference types="vite/client" />,它为 Vite 特有语法(如 import.meta.env)和静态资源导入提供类型支持。

5. src/App.tsx:布局与样式系统的第一个样例

import * as React from 'react';
import Container from '@mui/material/Container';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import ProTip from './ProTip';

function Copyright() {
  return (
    <Typography
      variant="body2"
      align="center"
      sx={{
        color: 'text.secondary',
      }}
    >
      {'Copyright © '}
      <Link color="inherit" href="https://example.com/">
        Your Website
      </Link>{' '}
      {new Date().getFullYear()}.
    </Typography>
  );
}

export default function App() {
  return (
    <Container maxWidth="sm">
      <Box sx={{ my: 4 }}>
        <Typography variant="h4" component="h1" sx={{ mb: 2 }}>
          Material UI Vite example in TypeScript
        </Typography>
        <ProTip />
        <Copyright />
      </Box>
    </Container>
  );
}

(示例文件中 Linkhref 为 MUI 官网的占位地址,实际项目替换为自己的站点即可,正文中以通用占位符展示。)

这段代码是理解 Material UI 用法的浓缩样本:

  • 导入风格:全部使用 @mui/material/xxx 子路径导入。这有利于摇树优化(tree-shaking)与类型解析,是现代推荐写法;依赖中同时包含 @mui/icons-material,在需要图标时可按 @mui/icons-material/Star 类似方式导入;
  • 布局体系Container maxWidth="sm" 创建居中的响应式容器,Box sx={{ my: 4 }} 提供垂直外边距——mymargin-top + margin-bottom 的速记,4 对应主题间距的 4 个基准单位(4 × 8px = 32px);
  • Typography 的 variant 与 componentvariant="h4" 决定视觉样式,component="h1" 决定渲染出的 DOM 标签(<h1>),语义与视觉解耦,这是 MUI 的典型能力;
  • sx 简写属性mb: 2(margin-bottom)、color: 'text.secondary'(直接引用主题色板语义 token),说明 sx 是贯穿 MUI 的主题化样式入口;
  • 模式化组件Copyright 是一个返回组件的纯函数,body2 小号正文 + align="center" + text.secondary 弱化色,形成典型的页脚版权样式,并通过 new Date().getFullYear() 自动更新年份。

6. src/ProTip.tsx:自定义 SvgIcon 的完整示例

import * as React from 'react';
import Link from '@mui/material/Link';
import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';
import Typography from '@mui/material/Typography';

function LightBulbIcon(props: SvgIconProps) {
  return (
    <SvgIcon {...props}>
      <path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z" />
    </SvgIcon>
  );
}

export default function ProTip() {
  return (
    <Typography sx={{ mt: 6, mb: 3, color: 'text.secondary' }}>
      <LightBulbIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
      {'Pro tip: See more '}
      <Link href="https://example.com/">templates</Link>
      {' in the Material UI documentation.'}
    </Typography>
  );
}

(同上,Linkhref 在原文件指向 MUI 文档中的模板区,正文以占位符展示。)

ProTip 展示了两个进阶用法:

  • 自定义图标与 SvgIcon 基座:只要把 Material 图标字体的 SVG <path> 数据塞进 SvgIcon,即可获得尺寸继承、颜色继承等与内置图标完全一致的行为。SvgIconProps 保证了类型安全,{...props} 透传让调用方可以通过 sx={{ mr: 1, verticalAlign: 'middle' }} 就地调整间距与对齐;
  • 通过 sx 完成行内排版mt: 6 / mb: 3 制造标题与提示之间的节奏,text.secondary 弱化提示文字,与 Copyright 的配色语言保持一致。

可以看到,整个页面没有写一行传统 CSS——布局、间距、颜色全部经由 sx 直连 Material UI 主题设计令牌(design tokens),这正是该示例想传递的“样式即主题”工作方式。

四、依赖清单:每个包的作用

原文档强调示例“包含 @mui/material 及其 peer 依赖”。对照 package.json 可以把依赖分为两组:

运行时依赖:

包名 角色
@mui/material Material UI 组件库本体(Button、Container、Typography、SvgIcon、Link、Box 等)
@mui/icons-material 基于 Material 图标的 React 图标集合,按需导入
@emotion/react Emotion 的 React 绑定;Material UI 默认样式引擎的核心运行依赖
@emotion/styled Emotion 的 styled() API,供 MUI 内部 styled 组件与自定义 styled 使用
react / react-dom 组件运行所需的 React 运行时

开发依赖:

包名 角色
vite 开发服务器与打包器
@vitejs/plugin-react React Fast Refresh 与 JSX 转换
typescript 类型检查与类型安全
@types/react / @types/react-dom React 类型声明

关于 Emotion 需要特别说明:Material UI 支持多种样式引擎,而 Emotion 是默认引擎。这也是为什么即使示例页面没有显式写 CSS,也必须安装 @emotion/react@emotion/styled——组件内部(包括 sx 属性和样式覆盖机制)依赖它们完成样式注入。官方仓库还提供可替换的 styled-components 适配层(见 packages/mui-styled-engine-sc),默认场景则走 packages/mui-styled-engine。如果要换用 styled-components,仅替换样式引擎是不够的,还需要满足其 peer 依赖要求——这正是本示例把 Emotion 明确定位为“内置依赖”的原因。

另外注意 package.json 声明了 "type": "module",整个示例以 ESM 运行,与 Vite 原生 ESM 模型保持一致。

五、与仓库内其他脚手架示例的关系

该示例不是孤立存在的。Material UI 在 examples 目录下维护了一整套生态集成示例,便于对比不同技术栈的差异:

选择哪一个,取决于目标运行时:

  • 纯前端 SPA → 本示例(Vite + TS)是 Vite 场景下的首选起点;
  • 需要 SSR/SEO → 选用 Next.js 系列示例;
  • 需要与 Tailwind 协同 → 参考 Vite + Tailwind + TS 组合示例。

六、下一步:从此处走向模板与真实业务

原文档在 “What's next?” 中给出的建议是:你已拥有一个可运行示例项目,接下来回到文档浏览 Templates(模板)专区,挑选更接近真实业务形态的起点(如 Dashboard、Sign-in、Blog 等页面级模板),把示例中验证过的“组件 + sx + 主题”工作流迁移过去。

在当前仓库中可以继续深入的配套资料包括:

七、小结

examples/material-ui-vite-ts 是一个“小而全”的样板工程:用 Vite 提供极速的模块化开发体验,用双 tsconfig 项目引用保证类型检查与打包互不干扰,用 Container/Box/Typography/sx 展示了 Material UI 的主题化样式心智模型,再用 SvgIcon + 组件拆分示范了如何组织可复用 UI 片段。对于希望以“Vite + TypeScript + Material UI”起步的团队或个人开发者而言,把这份示例作为基线再叠加 CssBaseline、主题定制与路由,即可平稳过渡到真实项目开发。

登录后查看全文
热门项目推荐
相关项目推荐