第三章 使用UnoCSS原子化CSS
原子样式也有很多选择,最著名的就是 Tailwind
。 Tailwind
虽然好,但是性能上有一些不足。由于Tailwind
会生成大量样式定义。全量的CSS
文件往往体积会多至数 MB
。这个对于页面性能是完全不可接受的。如果在开发时进行动态的按需剪裁,又会影响编译性能,降低开发体验。为了解决性能问题,开源界一个叫做 Antfu
的大神设计了 UnoCSS
。UnoCSS
是一个拥有高性能且具灵活性的即时原子化 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 () => <button
class={`
py-2
px-4
font-semibold
rounded-lg
shadow-md
text-white
bg-green-500
hover:bg-green-700
border-none
cursor-pointer
`}
>
{slots.default ? slots.default() : ''}
</button>
}
});
- 在
index.ts
中添加一个测试代码
文件名: src/index.ts
import { createApp } from "vue";
import SmartyUI from "./entry"
createApp({
template:`
<div>
<SButton>普通按钮</SButton>
</div>
`
})
.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 <App>
这个警告的意思是:组件提供 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属性定制颜色。
<div>
<SButton color="blue">蓝色按钮</SButton>
<SButton color="green">绿色按钮</SButton>
<SButton color="gray">灰色按钮</SButton>
<SButton color="yellow">黄色按钮</SButton>
<SButton color="red">红色按钮</SButton>
</div>
- 定义属性类型并注册组件属性
文件名: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<IColor>,
default: 'blue' // 设定默认颜色
},
}
export default defineComponent({
name: "SButton",
props, // 注册属性
...
}
});
- 属性变量拼装
UnoCSS
文件名:src/button/index.tsx
export default defineComponent({
name: "SButton",
props,
setup(props, {slots}) {
return () => <button
class={`
py-2
px-4
font-semibold
rounded-lg
shadow-md
text-white
bg-${props.color}-500
hover:bg-${props.color}-700
border-none
cursor-pointer
m-1
`}
>
{slots.default ? slots.default() : ''}
</button>
}
});
- 修改
index.ts
文件,添加测试用例
文件名:src/index.ts
import { createApp } from "vue";
import SmartyUI from './entry'
createApp({
template:`
<div>
<SButton color="blue">蓝色按钮</SButton>
<SButton color="green">绿色按钮</SButton>
<SButton color="gray">灰色按钮</SButton>
<SButton color="yellow">黄色按钮</SButton>
<SButton color="red">红色按钮</SButton>
</div>
`
})
.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
图标框架中大量的图表集。
- 开始引入这个功能,首先在
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 () => <button
class={`
...
mx-1
`}
>
{ props.icon !== "" ? <i class={`i-ic-baseline-${props.icon} p-3`}></i> : ""}
{slots.default ? slots.default() : ''}
</button>
- 在
index.ts
中添加测试用例
文件名:src/index.ts
import { createApp } from 'vue'
import SmartyUI from './entry'
createApp({
template: `
<div style="margin-top:20px;">
<SButton color="blue" icon="search" ></SButton>
<SButton color="green" icon="edit"></SButton>
<SButton color="gray" icon="check"></SButton>
<SButton color="yellow" icon="message"></SButton>
<SButton color="red" icon="delete"></SButton>
</div>
`,
})
.use(SmartyUI)
.mount('#app')
- 重启项目
pnpm dev
- 在浏览器查看结果
可以看到有字体图标的按钮了。
后续属性优化可以参考其他组件库,如round
、size
等属性。
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/1579
at 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:27
at 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.<anonymous> (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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="../../dist/style.css">
</head>
<body>
<h1>全量加载组件</h1>
<div id="app"></div>
<script type="module">
import { createApp } from "../../node_modules/vue/dist/vue.esm-bundler.js";
import SmartyUI from "../../dist/smarty-ui.mjs";
createApp({
template: `
<div>
<SButton color="blue">蓝色按钮</SButton>
<SButton color="green">绿色按钮</SButton>
<SButton color="gray">灰色按钮</SButton>
<SButton color="yellow">黄色按钮</SButton>
<SButton color="red">红色按钮</SButton>
</div>
<div style="margin-top:20px;">
<SButton color="blue" icon="search" ></SButton>
<SButton color="green" icon="edit"></SButton>
<SButton color="gray" icon="check"></SButton>
<SButton color="yellow" icon="message"></SButton>
<SButton color="red" icon="delete"></SButton>
</div>
`,
})
.use(SmartyUI)
.mount('#app')
</script>
</body>
</html>
- 重启项目
pnpm dev
- 浏览器查看结果
查看地址:http://localhost:5173/demo/esm/index.html
组件的颜色和字体图标正常显示。
- 点赞
- 收藏
- 关注作者
评论(0)