原子样式也有很多选择,最著名的就是 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
是字体图标库
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
等属性。
使用 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
组件的颜色和字体图标正常显示。