项目没有 css 文件?tailwindcss 小程序实践

3,180 阅读6分钟

什么是 tailwindcss?

在解释 tailwindcss 之前,我们先了解一下它的前身 atomic css

atomic css 即原子 css,它提倡并提供一套原子类,类名为其对应 css 的缩写

例如:

class="mt-20" => .mt-20 { margin-top: 20px; }

class="flex" => .flex { display:flex; }

class="hover:bg-red" => .hover:bg-red:hover { background:red; }

看到这里有人会问, atomic css 和直接写内联样式有什么区别呢?

  • atomic css 可以支持伪类,伪元素,甚至媒体查询,而内联样式不支持
  • atomic css 拥有内联样式的缩写,开发效率更快
  • atomic css 由于是 class 名,所以其内容可以个性化定制,而内联样式书写后无法动态修改

但早在7年前就有人提出 atomic css 的缺点

image

tailwindcss 基于 atomic css,但它的出现弥补了 atomic css 的劣势

  • 通过配置文件,实现主题和基准值定制,极大的扩展了灵活性
  • 通过 tree-shaking,过滤未被引用的 css 类,减少生产环境体积
  • 通过 postcss ,使得完美兼容其他 css 预处理器

2020 年 css 框架满意度调研可以发现,越来越多的人开始关注 tailwindcss

对比使用 tailwindcss 和传统 css 的优劣势:

tailwindcss传统css
开发效率tailwind 无需取名,也不需要关注 dom 结构改变后,语义的变动
调试便捷传统 css 可以直接通过输入类名在 ide 中定位对应代码
语义化🤔🤔tailwind 侧重代码语义化,传统 css 侧重业务语义化
主题定制🤔tailwind 天然支持主题/基准值定制,传统 css 需要借助预处理器
学习成本tailwind 需要学习简写语法,有一定学习成本
代码体积tailwind 利用全局 css ,大大减少 css 代码冗余,无需维护 css 文件

tailwindcss 并不是银弹,但它提供了一种新的范式去编写你的 css ,接下来会演示如何小程序项目中使用 tailwindcss

技术栈:

  • taro3
  • vue3
  • tailwindcss

示例代码:github.com/yeyan1996/t…

<template>
  <view class="mb-20 white-children">
    <view class="w-1_4 bg-blue-400 h-40">25% width</view>
    <view class="w-1_2 bg-blue-400 h-40">50% width</view>
    <view class="w-3_4 bg-blue-400 h-40">75% width</view>
    <view class="w-full bg-blue-400 h-40">100% width</view>
  </view>

  <view class="bg-purple-300 mb-20">
    <text class="font-mono">
      font-mono: The quick brown fox jumps over the lazy dog.
    </text>
  </view>

  <view class="flex items-start bg-pink-300 mb-20">
    <view class="mr-20">align-start</view>
    <view class="h-120 w-80 bg-purple-600 rounded text-white">1</view>
    <view class="h-240 w-80 ml-10 bg-purple-600 rounded text-white">2</view>
    <view class="h-160 w-80 ml-10 bg-purple-600 rounded text-white">3</view>
  </view>

  <view class="border-solid border-4 border-blue-500 rounded mb-20 flex justify-center">
      border
  </view>

  <view class="rounded bg-yellow-500 w-100 mb-20 ml-1_3 flex justify-center text-white">rounded</view>

  <view class="bg-gradient-to-r from-yellow-400 via-red-500 to-pink-500 h-30 flex justify-center items-center">
    <text class="text-white">bg-gradient-to-r</text>
  </view>

  <button class="text-sm w-200 py-3 rounded-md font-semi bold text-white bg-blue-500 shadow-2xl mt-20">shadow</button>
</template>

image-20210103102639258

安装

yarn add -D tailwindcss tailwind-mini postcss

tailwindcss

tailwindcss的基础包,包含公共原子类,以及提供为 tailwindcss 提供定制化的插件

tailwind-mini

基于 tailwindcss 在小程序平台适配的插件包,fork 自 taro-tailwind

删除微信小程序不支持的 css,例如响应式/媒体查询/伪类伪元素

image-20210101162821473

修复了一些 bug,恢复了一些可能需要使用的 tailwindcss(渐变/inset 等)

postcss

额外安装 postcss 8.0.0 以上版本,原因是 taro (3.0.12) 的 postcss 版本为 7.0.0 和 tailwindcss 内置插件不兼容

image-20201231114741977

基本配置

  1. 在 taro 中, postcss 的配置移到了 config/index.js 中
// config/index.js
const config = {
  mini: {
    postcss: {
     tailwindcss: { enable: true },
     'tailwind-mini': { enable: true }
      // ...
    },
  },
}

module.exports = function (merge) {
  if (process.env.NODE_ENV === 'development') {
    return merge({}, config, require('./dev'))
  }
  return merge({}, config, require('./prod'))
}

  1. 新建 tailwind.css
touch src/tailwind.css
/** src/tailwind.css */
@tailwind utilities;

由于使用了 postcss 和 tailwindcss 插件,所以能解析自定义语法 @tailwind,而 utilities 代表 tailwindcss 中公共原子类

image-20201228135256489

  1. 在项目入口文件引入 tailwindcss
// src/app.ts
import { createApp } from 'vue'
import './tailwind.css'

const App = createApp({})

App.use(store)
export default App
  1. 根目录新建 tailwind.config.js
touch tailwind.config.js
module.exports = {
  purge: ['./src/**/*.vue'],
  corePlugins: {
    // 涉及到子代选择器(>),wx 小程序不支持
    space:false,
    divideStyle: false,
    divideWidth:false,
    divideColor: false,
    divideOpacity:false,
    // 涉及到通配符(*),wx 小程序不支持
    ringWidth:false,
    ringColor:false,
    ringOpacity:false,
    ringOffsetWidth:false,
    ringOffsetColor:false,
    // web 浏览器相关功能,wx 小程序不支持
    appearance: false,
    cursor: false,
    outline: false,
    placeholderColor: false,
    pointerEvents: false,
    stroke: false,
    tableLayout: false,
    userSelect: false,
  },
  theme: {
    extend:{
      zIndex: {
        '-1': '-1',
      },
    },
    spacing: {
      0: '0',
      1: '2rpx',
      2: '4rpx',
      3: '6rpx',
      4: '8rpx',
      5: '10rpx',
      6: '12rpx',
      7: '14rpx',
      8: '16rpx',
      9: '18rpx',
      10: '20rpx',
      11: '22rpx',
      12: '24rpx',
      14: '28rpx',
      16: '32rpx',
      17: '34rpx',
      18: '36rpx',
      20: '40rpx',
      24: '48rpx',
      28: '56rpx',
      30: '60px',
      32: '64rpx',
      36: '72rpx',
      40: '80rpx',
      48: '96rpx',
      52: '104rpx',
      56: '112rpx',
      60: '120rpx',
      64: '128rpx',
      72: '144rpx',
      76: '152rpx',
      80: '160rpx',
      84: '168rpx',
      88: '196rpx',
      92: '184rpx',
      96: '192rpx',
      100: '200rpx',
      120: '240rpx',
      130: '360rpx',
      140: '280rpx',
      160: '320rpx',
      180: '360rpx',
      200: '400rpx',
      '1_2': '50%',
      '1_3': '33.333333%',
      '2_3': '66.666667%',
      '1_4': '25%',
      '3_4': '75%',
      '1_5': '20%',
      '2_5': '40%',
      '3_5': '60%',
      '4_5': '80%',
      '1_6': '16.666667%',
      '5_6': '83.333333%',
      '1_12': '8.333333%',
      '5_12': '41.666667%',
      '7_12': '58.333333%',
      '11_12': '91.666667%',
      full: '100%',
      auto: 'auto',

    },
    fontSize: (theme) => theme('spacing'),
    borderWidth: (theme) => theme('spacing'),
    lineHeight: (theme) => theme('spacing'),
    translate: (theme) => theme('spacing'),
    inset: theme => theme('spacing'),
    width: (theme) => ({
      ...theme('spacing'),
      screen: '100vw',
    }),
    maxWidth: (theme) => theme('width'),
    minWidth: (theme) => theme('width'),
    height: (theme) => ({
      ...theme('width'),
      screen: '100vh',
    }),
    maxHeight: (theme) => theme('height'),
    minHeight: (theme) => theme('height'),
  },
}

配置项做了以下处理:

  • 重写了长度单位,适配小程序,例如 p-16 => padding: 32rpx => 16px ( 375 x 674 (iphone6)设计图 )
  • 微信小程序 class 名不支持反斜杠,替换为下划线( w-1_2 => width: 50% )
  • 由于 tailwind 引入的公共原子类(即 utilities)非常庞大,通过第二行中 purge 配置,可以过滤掉生产环境项目中未使用的 css,类似 css 版的 tree-shaking
    • 开发环境:image-20210103101856345
    • 生产环境:image-20210103101639292

额外配置

重写长度基准值

如果你的设计图不是 375 x 674 (iphone6),则需重写长度基准值

// tailwind.config.js
module.exports = {
  theme: {   
    spacing: {
          0: '0',
          1: '2rpx',
          2: '4rpx',
          3: '6rpx'
      },
   // 属性值还可以是函数,并继承传入的参数
   // 即 fontSize 会继承 theme 下的 spacing 的长度值
   fontSize: (theme) => theme('spacing'),
   // 等同于
   // fontSize: {
   //       0: '0',
   //       1: '2rpx',
   //       2: '4rpx',
   //       3: '6rpx'
   //   },
  }
}

默认配置已经做了长度处理,使得 text-1 等于 2rpx,在 375 x 674 的设计图上即 1px,最终达到设计图单位 = css 名的效果

.text-0 {
  font-size: 0
}

.text-1 {
  font-size: 2rpx
}

.text-2 {
  font-size: 4rpx
}

.text-3 {
  font-size: 6rpx
}

添加新的 class

如果默认的 class 不满足需求,tailwindcss 支持添加新的 class

前面提到 tailwindcss 可以改变/创建基础变量的颜色,实现主题定制,默认主题如下

62dff956-2cb6-47e1-8606-98f96bc14fea

使用 theme 下的 extend 属性来实现 class 扩展

若直接修改 theme 下的属性,会导致覆盖原有的 class,务必注意

// tailwind.config.js
module.exports = {
   theme: {
     colors:{
       // 这里修改会覆盖原有 class
     },
    extend:{
       colors: {
          red: {
            deep: '#fbbfbc',
            middle: '#fde2e2',
            shallow: '#fef1f1',
          },
        },
    },
  }    
}

以上示例添加了 red-deep/red-middle/red-shallow 的主题色,同时保留了原有的 red 主题色

.bg-red-deep {
  --tw-bg-opacity: 1;
  background-color: rgba(251, 191, 188, var(--tw-bg-opacity))
}

.bg-red-middle {
  --tw-bg-opacity: 1;
  background-color: rgba(253, 226, 226, var(--tw-bg-opacity))
}

.bg-red-shallow {
  --tw-bg-opacity: 1;
  background-color: rgba(254, 241, 241, var(--tw-bg-opacity))
}

.bg-red-50 {
  --tw-bg-opacity: 1;
  background-color: rgba(254, 242, 242, var(--tw-bg-opacity))
}

.bg-red-100 {
  --tw-bg-opacity: 1;
  background-color: rgba(254, 226, 226, var(--tw-bg-opacity))
}

/* ... */

Tips

css/scss/less/css module

使用 tailwind 代表你几乎不需要使用任何 css 预处理器

但少数情况下,你可能还是需要单独写样式,tailwind 提供 @apply 自定义指令,借助 postcss 可以将 tailwind 的语法单独作用于样式表中

<!-- Extracting classes using @apply -->
<button class="btn btn-green">
  Button
</button>
.btn {
  @apply py-2 px-4 font-semibold rounded-lg shadow-md;
}
.btn-green {
  @apply text-white bg-green-500 hover:bg-green-700;
}

动态 class

不要使用动态拼接而成的 class,这会导致 tree-shaking 失效

1f870833-3e43-4fb3-bed0-9f165e4625c7

Ide 集成

Ide 插件会自动解析根目录的 tailwind.config.js,动态生成智能提示

vscode:tailwindcss.com/docs/intell…

5b9951c7-96b6-49b8-aaab-c49c2b00cd61

webstorm(2020.3 自带 tailwind):blog.jetbrains.com/webstorm/20…

639e3202-fb56-4266-828c-cdd1040714b1

92fddc64-f445-4e8a-814b-9c85e14ddaae

参考资料

论原子 CSS 的日益普及