第三章 使用UnoCSS原子化CSS
迪丽瓦拉
2024-02-01 20:52:59
0

第三章 使用UnoCSS原子化CSS

  • UnoCSS的GitHub地址

  • UnoCSS的文档地址

原子样式也有很多选择,最著名的就是 TailwindTailwind 虽然好,但是性能上有一些不足。由于Tailwind 会生成大量样式定义。全量的 CSS 文件往往体积会多至数 MB。这个对于页面性能是完全不可接受的。如果在开发时进行动态的按需剪裁,又会影响编译性能,降低开发体验。为了解决性能问题,开源界一个叫做 Antfu 的大神设计了 UnoCSSUnoCSS 是一个拥有高性能且具灵活性的即时原子化 CSS 引擎,可以兼顾产物体积和开发性能。

本章任务

  • 引入 UnoCSS 样式

  • 实现组件属性定制按钮样式

  • 实现【Icon图标按钮】

task1】引入UnoCSS样式

  • 安装依赖
pnpm i -D unocss@"0.45.6"
pnpm i -D @iconify-json/ic@"1.1.4" 

其中的@iconify-json/ic 是字体图标库

  • 在Vite配置文件中添加UnoCSS插件

文件名:vite.config.ts

import { presetUno, presetAttributify, presetIcons } from "unocss";
import Unocss from "unocss/vite";
export default defineConfig({plugins: [...// 添加UnoCSS插件Unocss({presets: [presetUno(), presetAttributify(), presetIcons()],})],
});

下面就可以在插件中引入 UnoCSS 了。加载 Unocss 插件后,Vite 会通过分析 class 的使用状况提供相应的样式定义。

  • Button 组件中引入 UnoCSS

文件名:src/button/index.tsx

注意: 这个地方文件名已经从 index.ts变为 index.tsx

import { defineComponent,PropType,toRefs} from "vue";
import "uno.css";
export default defineComponent({name: "SButton",setup(props, {slots}) {return () => }
});
  • index.ts 中添加一个测试代码

文件名: src/index.ts

import { createApp } from "vue";
import SmartyUI from "./entry"createApp({template:`
普通按钮
` }) .use(SmartyUI) .mount("#app");
  • 启动项目
pnpm dev
  • 在浏览器输入地址查看按钮组件
  VITE v3.0.7  ready in 644 ms➜  Local:   http://localhost:5173/➜  Network: use --host to expose

此时并没有看见页面出现按钮组件,且浏览器控制台抛出警告:

runtime-core.esm-bundler.js:38 [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue. Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js". at 

这个警告的意思是:组件提供 template 选项,但是在Vue的这个构建中不支持运行时编译,在你的打包工具里配置别名“vue: vue/dist/vue.esm-bundler.js”

  • 分析原因

项目的 vue/dist 目录下有很多不同的 Vue.js 构建版本,不同的环境使用不同的构建版本。使用构建工具的情况下,默认使用的是 vue.runtime.esm-bundler.js 这个仅运行时版本,不能处理 template 选项是字符串的情况,template 选项是字符串的情况要使用包含运行时编译器的版本 vue.esm-bundler.js

  • 解决方案

在vite配置文件中配置别名resolve

文件名:vite.config.ts

export default defineConfig({resolve: {alias: {vue: 'vue/dist/vue.esm-bundler.js',},},plugins: [...// 插件]})

修改好别名后,保存就可以在浏览器看见一个绿色按钮了。

到此为止,说明 UnoCSS 已经正常引入了。


task2】实现组件属性定制按钮样式

根据属性定制按钮样式功能,就是可以修改组件的属性来达到你想要的目的,例如,通过color属性定制颜色。

 
蓝色按钮绿色按钮灰色按钮黄色按钮红色按钮
  • 定义属性类型并注册组件属性

文件名:src/button/index.tsx

import { defineComponent,PropType,toRefs} from "vue";
import "uno.css";// 颜色类型声明
export type IColor = 'black' | 'gray' | 'red' | 'yellow' | 'green'|'blue'|'indigo'|'purple'|'pink'export const props = {color: {type: String as PropType,default: 'blue'  // 设定默认颜色},
}
export default defineComponent({name: "SButton",props,  // 注册属性...}
});
  • 属性变量拼装 UnoCSS

文件名:src/button/index.tsx

export default defineComponent({name: "SButton",props,setup(props, {slots}) {return () => }
});
  • 修改index.ts文件,添加测试用例

文件名:src/index.ts

import { createApp } from "vue";
import SmartyUI from './entry'createApp({template:`
蓝色按钮绿色按钮灰色按钮黄色按钮红色按钮
` }) .use(SmartyUI) .mount("#app");
  • 启动项目
pnpm dev
  • 在浏览器页面中查看

可以看到组件,但是但是灰色的,并没有看到我们给组件属性配置的颜色。这是为什么?

仔细研究 UnoCSS 的文档才发现问题。主要原因是 UnoCSS 默认是按需生成方式。也就是说只生成代码中使用过的样式。那如果在 class 属性中使用变量,是无法分析变量的取值的。这样也就无法动态生成样式了。

为了解决这个问题,UnoCSS 提供了安全列表选项。也就是说,把样式定义中变量的取值添加到 Safelist 中去。这样 UnoCSS 就会根据 Safelist 生成样式了。

  • 开始定制安全列表

安全列表属性应该定义在 UnoCSS 插件的配置中。

这里面要做一个配置上的重构。考虑到后续会在 Safelist 中添加大量配置,所以我们将 UnoCSS 配置拆成一个新的 ts 模块,然后引用到 vite.config.ts 中。

项目在搭建的过程中会不断地进行重构。希望大家在开发的过程中,一定要积极思考如何编写更加合理易于维护的代码。

文件名:config/unocss.ts

import { presetUno, presetAttributify, presetIcons } from "unocss";
import Unocss from "unocss/vite";const colors = ["white","black","gray","red","yellow","green","blue","indigo","purple","pink",
];const safelist = [...colors.map((v) => `bg-${v}-500`),...colors.map((v) => `hover:bg-${v}-700`),
];export default () =>Unocss({safelist,presets: [presetUno(), presetAttributify(), presetIcons()],});
  • vite配置中引入重构的unocss配置

文件名:vite.config.ts

import Unocss from "./config/unocss";export default defineConfig({plugins: [// 重构后的unocss配置Unocss(),],})
  • 重启项目
pnpm dev
  • 在浏览器查看结果

此时可以看到组件,以及我们想要的对应的组件属性所配置的颜色了。


task3】Icon 图标按钮实现

接下来要给按钮增加图标定制功能。实现图标按钮,首先需要引入字体图标库。

UnoCSS 中引入图标,只需要加载 @unocss/preset-icons 预设就可以了。它提供了 iconify 图标框架中大量的图表集。

iconfy官方网站

  • 开始引入这个功能,首先在 Unocss 插件中添加 presetIcons 预设。

文件名: config/unocss.ts

export default () =>Unocss({safelist,presets: [presetUno(),   presetAttributify(), presetIcons(),  // 添加图标预设]});
  • 定制图标安全列表

为了能够在 UnoCSS 中使用变量定义字体图标,需要将使用的图标名加入到 safelist 中。

文件名:config/unocss.ts

const safelist = [...["search","edit","check","message","star-off","delete","add","share",].map((v) => `i-ic-baseline-${v}`),
];
  • Button组件中注册icon属性

文件名:src/button/index.tsx

export const props = {icon: { // 注册icon属性type: String,default: ''}
}
  • Button组件 中添加字体图标

文件名:src/button/index.tsx

return () => 
  • index.ts中添加测试用例

文件名:src/index.ts

import { createApp } from 'vue'
import SmartyUI from './entry'createApp({template: `
`, }).use(SmartyUI).mount('#app')
  • 重启项目
pnpm dev
  • 在浏览器查看结果

可以看到有字体图标的按钮了。

后续属性优化可以参考其他组件库,如roundsize等属性。


Build 时单独导出 CSS

使用 unocss 后,如果运行 pnpm build 的时候会报错。

vite v3.0.7 building for production...
✓ 7 modules transformed.
dist/smarty-ui.mjs   1.58 KiB / gzip: 0.69 KiB
dist/style.css       8.17 KiB / gzip: 1.75 KiB
Entry module "src/entry.ts" is using named and default exports together. Consumers of your bundle will have to use `SmartyUI["default"]` to access the default export, which may not be what you want. Use `output.exports: "named"` to disable this warning
rendering chunks (1)...[unocss:global:build:generate] [unocss] does not found CSS placeholder in the generated chunks
It seems you are building in library mode, it's recommanded to set `build.cssCodeSplit` to true.
See https://github.com/vitejs/vite/issues/1579
error during build:
Error: [unocss] does not found CSS placeholder in the generated chunks
It seems you are building in library mode, it's recommanded to set `build.cssCodeSplit` to true.
See https://github.com/vitejs/vite/issues/1579at Object.generateBundle (D:\MyWorkSpace\VUE3_WORKSPACE\StudyVueUI\node_modules\.pnpm\registry.npmmirror.com+@unocss+vite@0.45.6_vite@3.0.7\node_modules\@unocss\vite\dist\index.cjs:374:22)at process.processTicksAndRejections (node:internal/process/task_queues:95:5)at async Bundle.generate (file:///D:/MyWorkSpace/VUE3_WORKSPACE/StudyVueUI/node_modules/.pnpm/registry.npmmirror.com+rollup@2.77.3/node_modules/rollup/dist/es/shared/rollup.js:15973:9)at async file:///D:/MyWorkSpace/VUE3_WORKSPACE/StudyVueUI/node_modules/.pnpm/registry.npmmirror.com+rollup@2.77.3/node_modules/rollup/dist/es/shared/rollup.js:23709:27at async catchUnfinishedHookActions (file:///D:/MyWorkSpace/VUE3_WORKSPACE/StudyVueUI/node_modules/.pnpm/registry.npmmirror.com+rollup@2.77.3/node_modules/rollup/dist/es/shared/rollup.js:23041:20)at async doBuild (file:///D:/MyWorkSpace/VUE3_WORKSPACE/StudyVueUI/node_modules/.pnpm/registry.npmmirror.com+vite@3.0.7/node_modules/vite/dist/node/chunks/dep-0f13c890.js:43585:26)at async build (file:///D:/MyWorkSpace/VUE3_WORKSPACE/StudyVueUI/node_modules/.pnpm/registry.npmmirror.com+vite@3.0.7/node_modules/vite/dist/node/chunks/dep-0f13c890.js:43413:16)at async CAC. (file:///D:/MyWorkSpace/VUE3_WORKSPACE/StudyVueUI/node_modules/.pnpm/registry.npmmirror.com+vite@3.0.7/node_modules/vite/dist/node/cli.js:747:9)ELIFECYCLE  Command failed with exit code 1.
  • 解决方案

解决办法是根据提示在vite配置文件中增加编译选项: cssCodeSplit

文件名:vite.config.ts

 build: {...cssCodeSplit: true,   // 追加...},

简单解释一下原因: cssCodeSplit 这个选项是为了决定在编译的时候是否要独立输出 css。显然这里面应该选择为 true

同样在调用组件库的时候需要引入 style.css 才可以让样式生效。

  • 再次进行项目打包
pnpm build

此时并无报错。

测试组件库

  • 修改demo测试文件

文件名:demo/esm/index.html



Document

全量加载组件

  • 重启项目
pnpm dev
  • 浏览器查看结果

查看地址:http://localhost:5173/demo/esm/index.html

组件的颜色和字体图标正常显示。

相关内容