本篇文章主要讲述Unocss的使用和个人使用之后的感想
在此之前还写过一篇关于反对在Vue里使用tailwind CSS的一篇文章(主写Vue的,React等框架中没有抵抗)。当然,当时写那篇文章的时候脑子也有是有点迷糊的,后来被怼的的很惨,不敢吭声。
近一段时间,公司里给安排了一个新项目,这次我选择安排上了Unocss+sass来写样式。但为什么本身就比较反对在Vue中使用原子化CSS的我,却在新的项目中使用了Unocss呢?
理由很简单:主要还是原子化CSS最近被炒的非常热,前端技术本来更新的就快,并不是说讨厌某一个技术就要极力去抵抗的(当然也不是说讨厌,只是觉得Vue的css处理已经非常棒了,在Vue中可能不会那么需要一个额外的css框架来支持),还是有就是当时说tailwind CSS不适合用在Vue中使用的时候,有部分人说我没写过tailwind CSS。哎,我就不写tailwind CSS我写Unocss (。・ω・。)
下载安装Unocss
npm i unocss或yarn add unocss
安装之后为了更好的IDE使用体验,官方是比较建议创建一个单独的文件来配置Unocss
在项目的根目录下创建uno.confin.{js,ts,mjs,mts}文件
//uno.config.tsimport { defineConfig } from "unocss";export default defineConfig({ //...Unocss的配置项 看下面配置介绍});
还需要在vite.config.ts中引入一下
//vite.config.tsimport { defineConfig } from "vite";import vue from "@vitejs/plugin-vue";import UnoCss from "unocss/vite";export default defineConfig(({ command }) => ({ plugins: [ vue(), //插件中也可以选择指定uno文件地址 参数{configFile: './uno.config.ts'} //当然默认不传参数也是可以正常运行的 这里就不选择传入参数了 UnoCss() ], //.... }) //另外webpack5中 webpack.config.js const UnoCSS = require('@unocss/webpack').default module.exports = { plugins: [UnoCSS()], optimization: { realContentHash: true } }
这样在uno.config.ts文件中配置好后,就可以写现在火热的原子化样式了
<div class="h-full text-center flex select-none all:transition-400"> <p text-16 px-10 shadow="[0_0_10px_4px_#dedede]">iceCode</p> </div>
Unocss的配置还是挺多的,但是一般情况下很少全部用到,仅有几个配置就可以覆盖大多数的场景
这个配置主要制定自己样式的规则,这个个人觉得可能会有一部分
rules: [ //静态规则 生成 .m-1 { margin: 0.25rem; }的样式 ['m-1', { margin: '0.25rem' }], //动态规则 使用正则表达式进行匹配 可以动态的进行匹配 //m-3 转化成css .m-3 { margin: 0.75rem; } m-100 转化成css .m-100 { margin: 25rem; } [/^m-(\d+)$/, ([, d]) => ({ margin: `${d / 4}rem` })], //如果有需要 还可以更高级 当然大多数情况下用不到 [ /^custom-(.+)$/, ([, name], { rawSelector, currentSelector, variantHandlers, theme }) => { // 丢弃不匹配的规则 if (name.includes('something')) return // 如果你想的话,可以禁用这个规则的变体 if (variantHandlers.length) return const selector = e(rawSelector) // 返回一个字符串而不是对象 return `${selector} { font-size: ${theme.fontSize.sm};}/* 你可以有多条规则 */${selector}::after { content: 'after';}.foo > ${selector} { color: red;}/* 或媒体查询 */@media (min-width: ${theme.breakpoints.sm}) { ${selector} { font-size: ${theme.fontSize.sm}; }}` } ] ]
可以自己设置一些自己的特定场景来指定自己的预设,当然也可以使用Unocss社区提供的预设来进行配置
创建要给自己的预设
// my-preset.tsimport { Preset } from 'unocss'export default function myPreset(options: MyPresetOptions): Preset { return { name: 'my-preset', rules: [ // ... ], variants: [ // ... ] // 它支持您在根配置中拥有的大多数配置 }}
在使用的的时候引入到 presets 中去即可
// unocss.config.tsimport { defineConfig } from 'unocss'import myPreset from './my-preset'export default defineConfig({ presets: [ myPreset({ /* 预设选项 */ }) ]})
主要还是看个人需求,预设这个东西还是用别人制定好的最香。
该预设继承preset-wind和preset-mini两个预设
该预设尝试提供流行的功能优先框架(包括 Tailwind CSS、Windi CSS、Bootstrap、Tachyons 等)的共同超集。所以怎么写规则怎么使用可以完全按照这些官网上的写
// uno.config.tsimport { defineConfig,presetUno } from 'unocss'export default defineConfig({ presets: [presetUno()]})
例如,ml-3(Tailwind)、ms-2(Bootstrap)、ma4(Tachyons)和 mt-10px(Windi CSS)都是有效的。
.ma4 { margin: 1rem;}.ml-3 { margin-left: 0.75rem;}.ms-2 { margin-inline-start: 0.5rem;}.mt-10px { margin-top: 10px;}
使用属性化预设可以更好的简化模板中css样的代码
// uno.config.tsimport { defineConfig,presetUno,presetAttributify } from 'unocss'export default defineConfig({ presets: [ presetUno(), presetAttributify() // ... ]})
当使用tailwind CSS写一个工具类的按钮时,过长的样式就会使项目难以维护
<button class="bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600"> Button</button>
可以这时属性化预设就可以很大程度上的简化这些写作的样式
//这样写 是否清晰很多<button bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600" text="sm white" font="mono light" p="y-2 x-4" border="2 rounded blue-200"> Button</button>
当然也有一般情况下并不会写这么多样式在模板上
<button class="border border-red">Button</button>//还可以这样使用<button border="~ red">Button</button>//也可以抛弃class<div m-2 rounded text-teal-400 />
可以使用纯css图标,首先还需要下载icon
npm install -D @iconify/json 也可以只下载 某一个你要使用的图标 npm install -D @iconify-json/[the-collection-you-want]//或者yarn add @iconify/json 也可以只下载 某一个你要使用的图标 yarn add @iconify-json/[the-collection-you-want]
// uno.config.tsimport { defineConfig,presetIcons } from 'unocss'export default defineConfig({ presets: [ presetIcons({ /* options */ }) // ...other presets ]})
使用时只需要写class即可
<!-- Phosphor 图标中的基本锚点图标 --><div class="i-ph-anchor-simple-thin" /><!-- 来自 Material Design 图标的橙色闹钟 --><div class="i-mdi-alarm text-orange-400" />
可以通过配置的provider属性来使用字体
目前仅支持:
// uno.config.tsimport { defineConfig,presetWebFonts,presetUno } from 'unocss'export default defineConfig({ presets: [ presetUno(), presetWebFonts({ provider:'none', /* options */ }) ]})
UnoCSS 的 TailWind/Windi CSS 预设。继承于preset-mini。
UnoCSS 的基本预设,仅包含最基本的实用工具。
可以将css样式作为HTML的标签使用,当使用单个样式的时候会比较好用。
// uno.config.tsimport { defineConfig,presetTagify } from 'unocss'export default defineConfig({ presets: [ presetTagify({ /* options */ }) // ...other presets ]})
配置标签预设之后,单个样式可以作为标签使用了
<text-red>red text</text-red><flex>flexbox</flex><!--可以理解为--><span class="text-red">red text</span> <div class="flex">flexbox</div>
众所周知,这个css框架使用的都是rem作为单位,如果想要在Unocss中使用px需要写上固定的px单位,这样显得不够简洁,这里就可以使用这个预设自动转将rem转化为px
这个预设不包含在unocss包中,需要单独的额外下载
npm install -D @unocss/preset-rem-to-px//或yarn add -D @unocss/preset-rem-to-px
// uno.config.tsimport { defineConfig } from 'unocss'import presetRemToPx from '@unocss/preset-rem-to-px'export default defineConfig({ presets: [ presetRemToPx({ baseFontSize: 4, //基准字体大小 官方的默认预设16(1单位 = 0.25rem)所以这里为4 为1:1 }) // ...other presets ]})
这里在使用样式的时候,直接写数字就是px为单位的样式
<div class="m-2"></div>//css.m-2 { margin: 2px;}
Transformers 用于转换源代码以支持约定。
它提供了一个统一的接口来转换源代码以支持约定。
// my-transformer.tsimport { createFilter } from '@rollup/pluginutils'import { SourceCodeTransformer } from 'unocss'export default function myTransformers( options: MyOptions = {}): SourceCodeTransformer { return { name: 'my-transformer', enforce: 'pre', // 在其他transformer之前执行 idFilter() { // 只转换 .tsx 和 .jsx 文件 return id.match(/\.[tj]sx$/) }, async transform(code, id, { uno }) { // code 是一个 MagicString 实例 code.appendRight(0, '/* my transformer */') } }}
当然,他也有着自己的几个转换器
为 UnoCSS 启用 Windi CSS 的 变体组特性。
// uno.config.tsimport { defineConfig,transformerVariantGroup } from 'unocss'export default defineConfig({ // ... transformers: [ transformerVariantGroup(), ],})
启用这个预设的时候,就可以将一些前缀相同的属性以组的形式来写
<div class="hover:(bg-gray-400 font-medium) font-(light mono)"/>//转化为<div class="hover:bg-gray-400 hover:font-medium font-light font-mono"/>//个人感觉这种不如上面的 属性化预设 使用属性化预设可以写成 看个人喜好<div hover='bg-gray-400 font-medium' font='light mono'/>
启用指令转换器将支持 @apply、@screen 和 theme() 指令。
// uno.config.tsimport { defineConfig,transformerDirectives } from 'unocss'export default defineConfig({ // ... transformers: [ transformerDirectives(), ],})
.custom-div { @apply text-center my-0 font-medium;}//转化为.custom-div { margin-top: 0rem; margin-bottom: 0rem; text-align: center; font-weight: 500;}
.grid { --uno: grid grid-cols-2;}@screen xs { .grid { --uno: grid-cols-1; }}@screen sm { .grid { --uno: grid-cols-3; }}//转换为.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));}@media (min-width: 320px) { .grid { grid-template-columns: repeat(1, minmax(0, 1fr)); }}@media (min-width: 640px) { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }}
.btn-blue { background-color: theme('colors.blue.500');}//转化为.btn-blue { background-color: #3b82f6;}
这个转换器可以将写在一个元素上的样式编译成一个类名
// uno.config.tsimport { defineConfig,transformerCompileClass } from 'unocss'export default defineConfig({ // ... transformers: [ transformerCompileClass(), ],})
在类字符串的开头加上:uno:即可将后面所写的样式编译成一个类名,不支持属性化预设
<div class=":uno: text-center sm:text-left"> <div class=":uno: text-sm font-bold hover:text-red"/></div><!--将编译成 --><div class="uno-qlmcrp"> <div class="uno-0qw2gr"/></div>//css.uno-qlmcrp { text-align: center;}.uno-0qw2gr { font-size: 0.875rem; line-height: 1.25rem; font-weight: 700;}.uno-0qw2gr:hover { --un-text-opacity: 1; color: rgba(248, 113, 113, var(--un-text-opacity));}@media (min-width: 640px) { .uno-qlmcrp { text-align: left; }}
这个转换器主要作用于 在jsx文件内写 属性化预设的样式
// uno.config.tsimport { defineConfig, presetAttributify,transformerAttributifyJsx } from 'unocss'export default defineConfig({ // ... presets: [ // ... presetAttributify()//属性化预设 ], transformers: [ transformerAttributifyJsx(), // <-- ],})
如果没有这个转换器,在jsx中使用属性化预设将会将无值的样式识别成布尔值
export function Component() { return (<div text-red text-center text-5xl animate-bounce> unocss </div>)}//这里将会转换为export function Component() { return ( <div text-red="" text-center="" text-5xl="" animate-bounce=""> unocss </div> )}
个人感觉剩下的配置一般情况下都不太能使用的到,也看项目环境吧。
可以在属性内配置一些原子化样式的快捷方式,并且可以类似于Rules一样配置动态快捷方式。
shortcuts: { // 使用原子化样式定义快捷类 'btn': 'py-2 px-4 font-semibold rounded-lg shadow-md', 'btn-green': 'text-white bg-green-500 hover:bg-green-700', //使用动态创建快捷方式 [/^btn-(.*)$/, ([, c]) => `bg-${c}-400 text-${c}-100 py-2 px-4 rounded-lg`]}
UnoCSS 也支持像 Tailwind / Windi 中的主题系统。在用户级别上,您可以在配置中指定 theme 属性,它将与默认主题进行深度合并。
theme: { // ... colors: { 'veryCool': '#0000ff', // class="text-very-cool" 'brand': { 'primary': 'hsla(var(--hue, 217), 78%, 51%)', //class="bg-brand-primary" } },}//在rules中使用rules: [ [ /^text-(.*)$/, ([, c], { theme }) => { if (theme.colors[c]) return { color: theme.colors[c] } } ]]
允许对现有规则应用一些变化,例如hover:变体
variants: [ // hover: (matcher) => { if (!matcher.startsWith('hover:')) return matcher return { // 去掉前缀并将其传递给下一个变体和规则 matcher: matcher.slice(6), selector: s => `${s}:hover`, } }],rules: [ [/^m-(\d)$/, ([, d]) => ({ margin: `${d / 4}rem` })],]
内部实现
提取器用于从源代码中提取工具的使用情况。
默认情况下,会使用 extractorSplit 进行拆分。该提取器会将源代码拆分为标记,然后直接提供给引擎。
从配置中注入原始 css 作为预处理。解析的 theme 可用于自定义 css。
preflights: [ { getCSS: ({ theme }) => ` * { color: ${theme.colors.gray?.[700] ?? '#333'}; padding: 0; margin: 0; } ` }]
主要用于css的顺序的优先级问题,css的顺序影响他们的优先级,这了可以在自定义样式时,添加图层来固定css顺序
rules: [ [/^m-(\d)$/, ([, d]) => ({ margin: `${d / 4}rem` }), { layer: 'utilities' }],//添加图层 // 当您省略图层时,它将是 `default` ['btn', { padding: '4px' }]]//也可以在与检查中调用预设样式,进行图层设置preflights: [ { layer: 'my-layer', getCSS: async () => (await fetch('my-style.css')).text() }]
图层自定义
ts
在智能建议在演示平台和VS Code 扩展中可以进行定制。针对于智能提示的属性
autocomplete: { templates: [ // 主题推断 'bg-$color/<opacity>', // 简写 'text-<font-size>', // 逻辑或组合 '(b|border)-(solid|dashed|dotted|double|hidden|none)', // 常量 'w-half', ], shorthands: { // 等同于 `opacity: "(0|10|20|30|40|50|60|70|90|100)"` 'opacity': Array.from({ length: 11 }, (_, i) => i * 10), 'font-size': '(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)', // 覆盖内置的简写 'num': '(0|1|2|3|4|5|6|7|8|9)' }, extractors: [ // ...extractors ]}
import { defineConfig,presetUno, presetAttributify, presetTypography, presetIcons,transformerVariantGroup, transformerAttributifyJsx,transformerCompileClass,transformerDirectives,} from "unocss";import presetRemToPx from "@unocss/preset-rem-to-px";export default defineConfig({ //自定义规则 rules: [[/^bg-?([0123456789abcdef]+)$/i, ([_, rgb]) => ({ background: `#${rgb}` })]], //预设规则 有前两个预设可以满足95%以上的需求 presets: [ //此预设规则可以看Tailwind CSS、Windi CSS、Bootstrap、Tachyons官网了解相关规则 presetUno(), //m-10 理解为 margin:10rem 或者 m-10px 理解为 margin:10px presetAttributify(), //归因模式 bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600" 背景颜色的简写 也可以再元素上不加class 直接写属样式 例如 <div m-2 p-10 bg-000></div> // presetTypography(), //排版预设 详细排版看https://unocss.dev/presets/typography#colors 使用这个前两个必须 // presetIcons(), //css图标 支持图标看 https://icones.js.org/ 需要下载 // 这里看个人需求是否要使用px presetRemToPx({ baseFontSize: 4, //基准字体大小 官方的默认预设(1单位 = 0.25rem) html的字体是16 所以这里为4 }), //默认unocss默认是rem 转换成 px单位 ], //看个人需求添加转换器 transformers: [ transformerVariantGroup(), transformerAttributifyJsx(), transformerCompileClass(), transformerDirectives() ], //以下可以按个人需求添加 shortcuts:{}, layers: {}, theme: {}, variants: [], extractors: [], preflights:[]});
问题主要针对 属性化预设的问题,由于 属性化预设的简洁、书写方便,大多数场景下可使用属性化来写样式,但是属性化存在着一些问题
<div w100% shadow-[0_0_10px_#dedede] bg-#333></div>
以上这些写法都是会报错的,属性中不允许包含%\[]\#等一些特殊符号的,所以包含颜色或者自定义等含有特殊符号无使用属性化来写样式
属性化支持有值多属性写法,可以使用这种形式来写属性化样式,当然中写的还不如直接一个class的好
<div w="100%" shadow="[0_0_10px_#dedede]" bg="#333"></div>
不过当一些有着多属性时,这种写法较为舒服
<div border="1px solid #dedede" h="[calc(100vh-500px)]" text="16 #000 center"></div><!--对比tailwind CSS --><div class="border-1 border-solid border-[#dedede] h-[calc(100vh-500px)] text-16 text-[#000] text-center"></div>
另外class和属性化的负数值写法是不同
<!--这里mb--20 可以理解为 margin-bootom:-20px--><p text="24 center #222 hover:color-red-400" fw-800 mb--20> Lorem ipsum, dolor sit amet consectetur adipisicing elit. Explicabo veniam aut esse iure mollitia. Earum omnis aliquid minus porro nulla commodi dignissimos, voluptatem accusamus cumque reprehenderit, ea nisi perferendis quis. </p> <!--如果使用 class 来写负值--> <!--这里写负值写在前面即可 -mb-20 理解为 margin-bootom:-20px--> <p class="text-24 text-center text-#222 fw-800 hover:text-red-400 -mb-20"> Lorem ipsum, dolor sit amet consectetur adipisicing elit. Explicabo veniam aut esse iure mollitia. Earum omnis aliquid minus porro nulla commodi dignissimos, voluptatem accusamus cumque reprehenderit, ea nisi perferendis quis. </p>
创建单独的uno.config.ts文件就是为了在IDE中配合插件使用,在一个页面中如果这样写可能会看着很乱,当配合插件之后,会比较清晰明了一些
在应用商店直接搜Unocss
安装完成之后,你页面中所写的样式都会给标明出来
另外在安装最新的插件之后,有可能会产生没有样式标识和样式提示的问题,这里需要在settings.json(这里是针对vscode的问题) 文件中添加一个属性
//settings.json{//其他配置//...//假如你的项目在D:/git-item/my-item/你所保存的所有项目,那么这里的属性值就可以是D:/git-item/my-item(或者加上你的详细项目)"unocss.root": "你当前项目的绝对路径",}
原子化样式目前来说是一种CSS的发展趋势,可以使用。也可以选择不用,毕竟现在的CSS已经发展很成熟了,包括Sass、Less等一些预编译器也发展的很成熟,他们都有自己变量函数等一些操作,如果想要写一个主题,使用变量来实现是一个很好选择。
当然,最后个人觉得原子化样式在Vue、Servlet等这种对CSS的处理很好的框架中不是很好的选择。如果是React等这种对CSS处理没有那么好的框架中,除了CSS-in-JS还是很不错的选择
原文链接:
https://juejin.cn/post/7322401091237068854