• 第三章 使用UnoCSS原子化CSS


    第三章 使用UnoCSS原子化CSS

    原子样式也有很多选择,最著名的就是 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" 
    
    • 1
    • 2

    其中的@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()],
        })
      ],
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    下面就可以在插件中引入 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>
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • index.ts 中添加一个测试代码

    文件名: src/index.ts

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

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

    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>
    
    • 1
    • 2

    这个警告的意思是:组件提供 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: [
            ...
            // 插件
      ]
     })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    到此为止,说明 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 定义属性类型并注册组件属性

    文件名: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,  // 注册属性
      ...
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 属性变量拼装 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>
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 修改index.ts文件,添加测试用例

    文件名:src/index.ts

    import { createApp } from "vue";
    import SmartyUI from './entry'
    
    createApp({
      template:`
      
    蓝色按钮 绿色按钮 灰色按钮 黄色按钮 红色按钮
    `
    }) .use(SmartyUI) .mount("#app");
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 启动项目
    pnpm dev
    
    • 1
    • 在浏览器页面中查看

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

    仔细研究 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()],
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • vite配置中引入重构的unocss配置

    文件名:vite.config.ts

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

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


    task3】Icon 图标按钮实现

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

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

    iconfy官方网站

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

    文件名: config/unocss.ts

    export default () =>
      Unocss({
        safelist,
        presets: [
            presetUno(),   
            presetAttributify(), 
            presetIcons(),  // 添加图标预设
        ]
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 定制图标安全列表

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

    文件名:config/unocss.ts

    const safelist = [
      ...[
        "search",
        "edit",
        "check",
        "message",
        "star-off",
        "delete",
        "add",
        "share",
      ].map((v) => `i-ic-baseline-${v}`),
    ];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • Button组件中注册icon属性

    文件名:src/button/index.tsx

    export const props = {
      icon: { // 注册icon属性
        type: String,
        default: ''
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • index.ts中添加测试用例

    文件名:src/index.ts

    import { createApp } from 'vue'
    import SmartyUI from './entry'
    
    createApp({
      template: `
     
    `
    , }) .use(SmartyUI) .mount('#app')
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 重启项目
    pnpm dev
    
    • 1
    • 在浏览器查看结果

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

    后续属性优化可以参考其他组件库,如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/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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 解决方案

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

    文件名:vite.config.ts

     build: {
        ...
        cssCodeSplit: true,   // 追加
        ...
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

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

    • 再次进行项目打包
    pnpm build
    
    • 1

    此时并无报错。

    测试组件库

    • 修改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>Documenttitle>
      <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: `
      
    蓝色按钮 绿色按钮 灰色按钮 黄色按钮 红色按钮
    `
    , }) .use(SmartyUI) .mount('#app')
    script> body> html>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 重启项目
    pnpm dev
    
    • 1
    • 浏览器查看结果

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

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

  • 相关阅读:
    Linux 之 Ubuntu 代码开发工具 Visual Studio Code(VSCode) 的安装与一些常用插件配置的简单整理
    知识关联视角下金融证券知识图谱构建与相关股票发现
    java计算机毕业设计基于springboo+vue的个人家庭理财记账管理系统
    9、8锁现象彻底的理解锁(锁的只有两个东西,一个是对象,一个是Class模板(static修饰))
    利用go制作微信机器人
    Flink系列文档-(YY01)-初识Flink
    网络安全(黑客)自学
    Boosting以及代表算法(Adaboost、GBDT)介绍
    C++ 大作业/课程设计 小型公司工资管理软件
    【正厚软件】0基础学IT,来Linux的发展历史吧
  • 原文地址:https://blog.csdn.net/qq_36362721/article/details/127827711