《从零开始学习Vite》 第二章:接入 CSS 工程化方案

569 阅读5分钟

🍎 系列文章

在系列中,将从零学习Vite,系统梳理 Vite 本身的知识,也包括 Vite 底层所使用的 Esbuild、Rollup 双引擎、Babel 编译工具链、模块规范标准等等构建生态。

《从零开始学习Vite》 第一章:搭建前端项目

《从零开始学习Vite》 第二章:接入 CSS 工程化方案

《从零开始学习Vite》 第三章:静态资源处理

《从零开始学习Vite》第四章:自动化代码规范工具的使用

前言

CSS 方案是前端工程化必不可缺的,在最原始的开发阶段都是手写原生的 CSS,但原生 CSS 存在浏览器兼容问题、开发体验差、样式污染、打包产物体积过大等诸多问题,因此社区出现了不少 CSS 工程化解决方案,有CSS 预处理器、CSS Modules、PostCSS 、CSS in JS 和 CSS 原子化框架,下面我们将在 Vite 中落地这些 CSS 工程化方案。

CSS 预处理器

这种方案自定义了一套语法来生成 CSS 的程序,让 CSS 可以像编程语言一样定义变量、写条件判断,代码混合,嵌套以及代码模块化。

Vite 本身对 CSS 各种预处理器(Sass、Less、Stylus)做了内置支持,我们无需做任何的配置就可以直接使用Sass、Less和Stylus,但是 Vite 为了实现按需加载,并没有内置这些工具库,需要我们根据自己的需求安装。这里我们以 Sass 预处理器为例,其他预处理器(LessStylus)如果有需要你可以去官方文档中查阅更多的配置项,安装 Sass 的官方库:

pnpm add -D sass

现在我们封装一个全局的主题色,新建 src/styles/variable.scss 文件,内容如下:

$theme-color: blue;

接着在 Vite 中进行自定义配置实现自动引入 variable.scss文件,在vite.config.ts 文件中增加如下的内容:

import path from 'path';
import { normalizePath } from 'vite';

// 全局scss文件的路径
// 用normalizePath 解决window下的路径问题
const variablePath = normalizePath(path.resolve('./src/styles/variable.scss'));

export default defineConfig({
  // css相关的配置
  css: {
    preprocessorOptions: {
      scss: {
      // additionalData 的内容会在每个scss文件的开头自动注入
      additionalData: `@import "${variablePath}";`
      }
    }
  },
  plugins: [react()],
})

新建 src/App.scss 文件,并在App.tsx 文件引用,内容如下:

// App.scss
.scss {
  color: $theme-color;
}

// App.tsx
import './App.scss'

<h3 className='scss'> Sass 预处理器 </h3>

执行pnpm run dev,然后到浏览器上查看效果:

image.png

CSS Modules

CSS Modules能将所有的类名和动画名称默认都有各自的作用域的 CSS 文件,这样就可以避免同名的情况下样式污染的问题。 CSS Modules 在 Vite 也是一个开箱即用的能力,Vite 会对后缀带有 .module 样式文件自动应用 CSS Modules。接下来我们来使用这个功能,新建 src/App.module.scss 文件,并在App.tsx 文件引用,内容如下:

// App.module.scss
.cssModules {
  color: $theme-color;
}

// App.tsx
import styles from './App.module.scss';

<h3 className={styles.cssModules}> CSS Modules </h3>

image.png 到浏览器上查看效果,说明现在 CSS Modules 已经正式生效了,但语义化不友好,可以在配置文件中的css.modules选项来配置 CSS Modules 的功能,添加:

// vite.config.ts
export default {
  css: {
    modules: {
      //其中,name表示当前文件名,local表示类名
      generateScopedName: "[name]__[local]___[hash:base64:5]",
      preprocessorOptions: {
        //省略预处理器配置
      }
    },
  }
}

再次访问页面,类名已经变成了我们自定义的形式:

image.png

PostCSS

PostCSS 是 CSS 后处理器,一个用 JavaScript 工具和插件转换 CSS 代码的工具,可以实现的功能非常丰富,比如将 px 转换为 rem,根据目标浏览器情况自动加上类似于-webkit-、-moz-、-o-的属性前缀等。 我们可以通过postcss.config.js来配置 postcss,也可以直接在 Vite 配置文件中进行操作。我们以安装 autoprefixer这个常用的 PostCSS 插件为例,这个插件主要用来自动为不同的目标浏览器添加样式前缀,从而解决浏览器兼容性问题:

pnpm add -D autoprefixer
// vite.config.ts 增加下面的配置
import autoprefixer from'autoprefixer';

export default {
  css: {
    // PostCSS配置
    postcss: {
      plugins: [
        autoprefixer({
          // 指定目标浏览器
          overrideBrowserslist: [
            'Android 4.1',
            'iOS 7.1',
            'Chrome > 31',
            'ff > 31',
            'ie >= 11',
            'last 2 versions',
          ],
          grid: true,
        })
      ]
    }
  }
}

配置完成后,我们在 App.scss 样式文件中添加一个新的 CSS 属性:

display: grid;

再次访问页面,自动添加了浏览器样式前缀:

image.png 除了 autoprefixer 插件,社区有不少的 PostCSS 插件,常见的插件有:

postcss-pxtorem :用来将 px 转换为 rem 单位

postcss-preset-env :解决最新的 CSS 语法兼容性问题

cssnano :用来压缩 CSS 代码,跟常规的代码压缩工具不一样,它更加智能,比如提取一些公共样式进行复用、缩短一些常见的属性值等

关于 PostCSS 插件,这里给大家推荐一个站点:www.postcss.parts

CSS in JS

CSS in JS方案,顾名思义,这类方案可以实现直接在 JS 中写样式代码,基本包含 CSS 预处理器和CSS Modules 的各项优点,解决了开发体验和全局样式污染的问题。社区中有两款主流的CSS In JS方案 styled-componentsemotion,这里我们以为例进行配置使用:

plugins: [
react({
  babel: {
  // 加入babel插件
  // 以下插件包都需要提前安装
  // 当然,通过这个配置你也可以添加其它的Babel插件
  plugins: [
    // 适配styled-component
    "babel-plugin-styled-components"
    // 适配emotion
    "@emotion/babel-plugin"
  ]},
  // 注意:对于emotion,需要单独加上这个配置
  // 通过`@emotion/react`包编译emotion中的特殊jsx语法
  jsxImportSource: "@emotion/react"
 }
 )]

新建src/components/Button目录,并且新建 index.tsx 文件,代码如下:

// index.tsx
import styled from 'styled-components';

const Button = styled.button`
  background: palevioletred;
  color: white;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

export default Button;

接着我们在 App.tsx 应用这个组件:

// App.tsx
import Button from './components/Button'

function App() {
  return (
    <Button>Primary</Button>
  )
}

export default App

现在你可以执行 pnpm run dev,然后到浏览器上查看效果: image.png

CSS 原子化框架

CSS 原子化框架,如TailwindCSS、WindiCSS UnoCSS,通过类名来指定样式,将这些 class 样式简单化、单一化,大大简化了样式写法,提高了样式开发的效率,主要解决了原生 CSS开发体验的问题。

WindiCSS、UnoCSS可以作为 TailwindCSS 的替换方案,支持所有的 TailwindCSS 属性,实现了按需生成 CSS 类名的功能,属性化模式,变体组等等,但 WindiCSS 不再被积极维护了。这里我们以 UnoCSS 接入为例 首先安装 UnoCSS

pnpm add -D unocss

接着在配置文件中使用它:

// vite.config.ts
import UnoCSS from 'unocss/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    UnoCSS(),
  ],
})

创建 uno.config.ts 文件:

// uno.config.ts
import { defineConfig } from 'unocss'

export default defineConfig({
  // ...UnoCSS options
})

将 virtual:uno.css 添加到你的主入口中:

// main.tsx
import 'virtual:uno.css'

这样我们就完成了 UnoCSS 在 Vite 中的接入,接下来我们在 App.tsx 中使用,添加代码如下:

<div className="m-1 color-blue">Hello UnoCSS</div>

当你启动项目之后可以看到 UnoCSS 的样式已经正常生效了。

image.png

代码仓库

vettel-2024/vite-course: 从零开始Vite学习之旅 (github.com)