0.4.1 • Published 2 years ago

unplugin-uni-app-tailwind v0.4.1

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

unplugin-uni-app-tailwind

License

npm

支持在 uni-app 中使用 tailwindcss 原有语法开发小程序。

使用

参考 tailwindcss 文档uni-app 项目中安装配置 tailwindcss。你无需禁用 preflight,也无需调整原有语法(如 .w-[200.5rpx] 等),你只需要正常书写类名,该插件会替你处理剩下的事情。

安装依赖。

npm install unplugin-uni-app-tailwind

Vite

// vite.config.ts
import { defineConfig } from 'vite';
import uni from '@dcloudio/vite-plugin-uni';
import tailwindcss from 'tailwindcss';
// @ts-ignore
import postcssPresetEnv from 'postcss-preset-env';
import uniAppTailwind from 'unplugin-uni-app-tailwind';

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    lib: 'es6',
  },
  css: {
    postcss: {
      plugins: [
        tailwindcss(),
        postcssPresetEnv({
          stage: 3,
        }),
      ],
    },
  },
  plugins: [
    uni({
      vueOptions: {
        reactivityTransform: true,
      },
      viteLegacyOptions: {
        targets: ['ios >= 10', 'chrome >= 53'],
      },
    }),
    uniAppTailwind.vite({
      /* options */
    }),
  ],
});

Vue CLI

// .postcssrc.cjs
module.exports = {
  plugins: {
    tailwindcss: {},
    'postcss-preset-env': {
      stage: 3,
    },
  },
};
// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      require('unplugin-uni-app-tailwind').webpack({
        /* options */
      }),
    ],
  },
};

原理

uni-app + tailwindcss 不能编译出小程序正常代码的错误原因有两个:

  • 样式文件中含有不支持的字符,如 []()#!/.:,%' 等;
  • 样式文件中含有不支持的元素,如 html, bodyimgspana* 等。

那么,我们只需要做到以下两点就可以让 tailwindcss 跑在小程序中,而不需要调整 tailwindcss 的语法来增加开发时的心智负担:

  • 使用 postcss 改写样式文件里面的 selector,包括字符和元素;
  • 使用 babel 改写模板文件里面的 class,只包括字符,这是为了和样式文件里面的 selector 相匹配。

这个项目导出的默认配置如下所示。

// 默认 space between 元素映射
// https://tailwindcss.com/docs/space
export const SpaceBetweenElements = [
  'view',
  'button',
  'text',
  'image',
  'uni-view',
  'uni-button',
  'uni-text',
  'uni-image',
];

// 默认 divide width 元素映射
// https://tailwindcss.com/docs/divide-width
export const DivideWidthElements = [
  'view',
  'button',
  'text',
  'image',
  'uni-view',
  'uni-button',
  'uni-text',
  'uni-image',
];

// 默认 uni-app 中元素映射
export const ElementMap: [string, string[]][] = [
  ['html', ['html', 'page']],
  ['body', ['body', 'page']],
  ['img', ['img', 'image', 'uni-image']],
  ['span', ['span', 'text', 'uni-text']],
  ['video', ['video', 'uni-video']],
  [
    'a',
    [
      'a',
      'functional-page-navigator',
      'navigator',
      'uni-functional-page-navigator',
      'uni-navigator',
    ],
  ],
  [
    '*',
    [
      'html',
      'body',
      'page',
      'cover-image',
      'cover-view',
      'match-media',
      'movable-area',
      'movable-view',
      'scroll-view',
      'swiper',
      'swiper-item',
      'view',
      'icon',
      'progress',
      'rich-text',
      'text',
      'button',
      'checkbox',
      'checkbox-group',
      'editor',
      'form',
      'input',
      'label',
      'picker',
      'picker-view',
      'picker-view-column',
      'radio',
      'radio-group',
      'slider',
      'switch',
      'textarea',
      'functional-page-navigator',
      'navigator',
      'audio',
      'camera',
      'image',
      'live-player',
      'live-pusher',
      'video',
      'voip-room',
      'map',
      'canvas',
      'ad',
      'ad-custom',
      'official-account',
      'open-data',
      'web-view',
      'navigation-bar',
      'page-meta',
      'uni-cover-image',
      'uni-cover-view',
      'uni-match-media',
      'uni-movable-area',
      'uni-movable-view',
      'uni-scroll-view',
      'uni-swiper',
      'uni-swiper-item',
      'uni-view',
      'uni-icon',
      'uni-progress',
      'uni-rich-text',
      'uni-text',
      'uni-button',
      'uni-checkbox',
      'uni-checkbox-group',
      'uni-editor',
      'uni-form',
      'uni-input',
      'uni-label',
      'uni-picker',
      'uni-picker-view',
      'uni-picker-view-column',
      'uni-radio',
      'uni-radio-group',
      'uni-slider',
      'uni-switch',
      'uni-textarea',
      'uni-functional-page-navigator',
      'uni-navigator',
      'uni-audio',
      'uni-camera',
      'uni-image',
      'uni-live-player',
      'uni-live-pusher',
      'uni-video',
      'uni-voip-room',
      'uni-map',
      'uni-canvas',
      'uni-ad',
      'uni-ad-custom',
      'uni-official-account',
      'uni-open-data',
      'uni-web-view',
      'uni-navigation-bar',
      'uni-page-meta',
    ],
  ],
];

// 默认 uni-app 中特殊符号映射,用于替换特殊符号
export const CharacterMap: [string, string][] = [
  ['[', '-'],
  [']', '-'],
  ['(', '-'],
  [')', '-'],
  ['#', '-h-'], // hash
  ['!', '-i-'], // important
  ['/', '-s-'], // slash
  ['.', '-d-'], // dot
  [':', '_'], // colon
  [',\\s', '-c-'], // comma
  [',', '-c-'], // comma
  ['%', '-p-'], // percentage
  ["'", '-q-'], // quote
  ['+', '-a-'], // add
  ['\\\\2c\\s', '-c-'], // comma
  ['\\\\2c', '-c-'], // comma
];

// 默认需要替换 * 选择器的环境
export const ReplaceStarSelectorPlatforms = ['MP', 'QUICKAPP'];

// 默认替换 * 选择器的环境判断方法
export const GetShouldReplaceStarSelector = (targetPlatforms: string[], platform: string) =>
  targetPlatforms.some((item) => {
    if (
      (item === 'QUICKAPP' || item.startsWith('QUICKAPP-')) &&
      (platform === 'APP' || platform.startsWith('APP-'))
    ) {
      return false;
    }
    if (
      (platform === 'QUICKAPP' || platform.startsWith('QUICKAPP-')) &&
      (item === 'APP' || item.startsWith('APP-'))
    ) {
      return false;
    }
    return item === platform || item.includes(platform) || platform.includes(item);
  });

export const defaultOptions = {
  spaceBetweenElements: SpaceBetweenElements,
  divideWidthElements: DivideWidthElements,
  elementMap: ElementMap,
  characterMap: CharacterMap,
  replaceStarSelectorPlatforms: ReplaceStarSelectorPlatforms,
  getShouldReplaceStarSelector: GetShouldReplaceStarSelector,
};

SpaceBetweenElements 用于在没有提供自定义配置时,替换 space between 相关类的元素。DivideWidthElements 用于在没有提供自定义配置时,替换 divide width 相关类的元素。它们默认都只包含了 viewbuttontextimage 四个常用元素,这应该能满足绝大部分的需求了。你也可以手动调整。

ElementMap 提供了元素映射,用于替换 preflight 内的元素。这样,你就不需要禁用 preflight 了。

CharacterMap 提供了特殊符号的映射,用于替换特殊符号。这样,你就可以使用 tailwindcss 原有的语法开发,而无需手动调整 tailwindcss 配置了。

ReplaceStarSelectorPlatforms 指定运行到特定平台时替换 * 选择器,默认为 ['MP', 'QUICKAPP']

GetShouldReplaceStarSelector 判断当前运行平台是否属于特定平台,用户可手动调整。

FAQ

rpx 转换

rpx

简而言之,rpx 是一个跟屏幕宽度挂钩的响应式单位,不应该也不需要把所有用到 px 或者 rem 的地方换成 rpx

什么时候必须要用 rpx?我个人的经验是 aside 的宽度需要随屏幕宽度变化、main 根据 aside 宽度变化时,才必须用到 rpx + flexbox 的组合,否则用 flexbox 就已经足够了。

所以,这个插件不支持 rpx 转换。你可以直接 使用任意值,如 .w-[750rpx].w-[200rpx],我相信可以满足绝大部分的需求。

如果你悲伤地发现这没法满足你的需求,可能这个插件不适合你,请看看以下几个项目是否满足你的需求。

windicss / unocss 支持

windicss / unocss 是富具创造性的项目,尽管它们都声称支持 tailwindcss 所有功能,但它们问世时间都较短,我相信 tailwindcss 是目前更为稳妥的选择。

如果 unocss 未来成为 windicss@4 的底层引擎或者直接替代了 windicss(请阅读 重新构想原子化 CSS),我非常乐意再写一个 unplugin-uni-app-unocss 插件(如果有必要的话)。

资源

关联项目

致谢

该项目从以下项目汲取了灵感并参考了代码。在此对它们的开发者表示由衷的感谢。

也感谢以下项目的开发者,如果没有他们,前端开发比现在更加困难。