将CSScomb集成到微信小程序项目中

简介: 将CSScomb集成到微信小程序项目中

前言


最近在看 AlloyTeam 团队的 Code Guide 代码书写习惯,其中一项是 CSS 属性编写顺序


虽然 CSS 属性顺序先后并不影响我们的程序,那为什么要做这件事呢?我理解是可分类、有迹可循、有利于排查错误。


由于属性众多,那么人总是会犯错的,所以我们借助一个工具来处理,它就是 CSScomb,用于 CSS 格式化、且可排序。官方描述是:CSScomb is a coding style formatter for CSS.


本文旨在使得 CSScomb 支持格式化小程序样式 .wxss同时也支持支付宝小程序 .acss,而百度小程序样式文件的扩展名是 .css,所以它天生就可以直接使用了。


正文


一、CSScomb 安装使用


创建一个微信小程序项目,因过于简单,忽略该步骤。


1. 本地安装

$ yarn add csscomb --dev


2. 添加配置文件


在项目根目录下,添加配置文件 .csscomb.json

若无配置文件,CSScomb 的默认配置请看 csscomb.js/config/csscomb.json

而本文均采用 AlloyTeam 推荐的排序规则,而非默认。但由于 sort-order 很长,影响文章篇幅,部分配置没贴上去,完整配置请看看  csscomb-mini/.csscomb.json


关于该配置文件是按我平常编写代码的习惯调整有所调整的,并非 CSScomb 默认配置。具体配置项所指可细看  CSScomb Configuration Options。注意 CSScomb 所能做的事情并非就只有属性排序哦,比如设置缩进、颜色值十六进制大小写、前导零也是可以的。

{
  "exclude": [".git/**", "node_modules/**", "bower_components/**"],
  "verbose": true,
  "always-semicolon": true,
  "block-indent": 2,
  "color-case": "lower",
  "color-shorthand": true,
  "element-case": "lower",
  "eof-newline": true,
  "leading-zero": false,
  "quotes": "single",
  "remove-empty-rulesets": false,
  "space-before-colon": 0,
  "space-after-colon": 1,
  "space-before-combinator": 1,
  "space-after-combinator": 1,
  "space-before-opening-brace": 1,
  "space-after-opening-brace": "\n",
  "space-before-closing-brace": "\n",
  "space-before-selector-delimiter": 0,
  "space-after-selector-delimiter": "\n",
  "space-between-declarations": "\n ",
  "strip-spaces": true,
  "unitless-zero": true,
  "vendor-prefix-align": false,
  "sort-order": []
}


3. 添加 NPM 脚本


为了测试效果,根目录下创建一个 app.css 文件。

{
  "scripts": {
    "csscomb": "csscomb app.css"
  }
}


4. 运行脚本

$ yarn run csscomb
✓ app.css
  Done in 0.45s.


5. 效果


按以上配置文件的排序规则,padding-right 属性在 padding-left 之前,所以可以看到修改前后的对比如下:

/* before */
view {
  padding-left: 30px;
  padding-right: 30px;
}
/* after */
view {
  padding-right: 30rpx;
  padding-left: 30rpx;
}


二、微信小程序如何使用 csscomb


由于 CSScomb 仅支持扩展名为 .css.sass.scss.less 的文件,那么怎么处理呢?


只能利用 Gulp 来曲线救国了。

大致思路,利用 Gulp 将 WXSS 文件临时转为 CSS 扩展名,使用 CSScomb 格式化之后,再将其转换回 WXSS 的扩展名,以达到曲线救国的目的。


插了一下,恰巧有一款 Gulp 插件 gulp-csscomb 可用,话不多说。


1. 安装相关依赖包

$ yarn add --dev gulp gulp-debug gulp-csscomb gulp-rename


2. 创建 Gulp 任务


在项目根目录下创建 gulpfile.js 文件。而 gulp-csscomb 的使用方法很简单,于是我们很快可以写出:

const { src, dest } = require('gulp')
const rename = require('gulp-rename')
const debug = require('gulp-debug')
const csscomb = require('gulp-csscomb')
const wxssTask = cb => {
  return src('app.wxss')
    .pipe(debug()) // 打印一些调试信息
    .pipe(
      rename({
        extname: '.css'
      })
    )
    .pipe(csscomb()) // 格式化操作
    .pipe(
      rename({
        extname: '.wxss'
      })
    )
    .pipe(dest(file => file.base))
}
module.exports = {
  wxssTask
}


修改 NPM 脚本:

{
  "scripts": {
    "csscomb": "gulp wxssTask"
  }
}


执行脚本 yarn run csscomb 可以看到:

$ yarn run csscomb
yarn run v1.22.4
$ gulp wxssformat
[15:34:11] Using gulpfile ~/Desktop/Web/MyGitHub/csscomb-mini/gulpfile.js
[15:34:11] Starting 'wxssformat'...
[15:34:11] gulp-debug: app.wxss
Failed to configure "remove-empty-rulesets" option:
 Value must be one of the following: true.
Failed to configure "vendor-prefix-align" option:
 Value must be one of the following: true.
[15:34:11] gulp-debug: 1 item
[15:34:11] Finished 'wxssformat' after 98 ms
  Done in 1.05s.


此时 app.wxss 文件已经被格式化了,但很遗憾我们看到两行 Failed 信息:

Failed to configure "remove-empty-rulesets" option:
 Value must be one of the following: true.
Failed to configure "vendor-prefix-align" option:
 Value must be one of the following: true.


它不支持 remove-empty-rulesetsvendor-prefix-align 配置选项,估计是 gulp-csscomb “年久失修”了。当前安装 csscomb 是最新版本 4.3.0(本文编写时),而 gulp-csscomb 里面引用的 csscomb 版本还是 3.1.7,猜测是旧版本不支持该选项。

# 查看依赖包版本
$ npm view <package> versions


再次曲线救国一下,干脆自己实现一个 csscomb-gulp 插件的功能。


三、编写 Gulp 插件


顺便学习一下,如何写一个 Gulp 插件哦。


第一次写的同学,可简单看下这篇文章:Gulp 插件编写入门


我看了 gulp-csscombCSScomb Core 的源码,发现自己实现一个插件不难,同时还发现可以少一步 wxsscss 来回切换的转换过程。


首先是 gulp-csscomb 的源码(gulp-csscomb),截取了一部分下来:

// 部分源码
var comb = new Comb(config || 'csscomb'); // 根据配置实例化对象
var syntax = options.syntax || file.path.split('.').pop(); // 获取 syntax,从参数或者扩展名获取,即 css、less、sass、scss
try {
  var output = comb.processString( // 关键就是这步,对我们的文件进行格式化
    file.contents.toString('utf8'), {
      syntax: syntax,
      filename: file.path
    });
  file.contents = new Buffer(output); // 将格式化后的字符串替换回文件中
} catch (err) {
  this.emit('error', new PluginError(PLUGIN_NAME, file.path + '\n' + err));
}


从以上源码可以看到,关键在于 comb.processString() 方法。于是找到 CSScomb 的核心源码(CSScomb Core),该方法的描述如下:

comb.processString(string, options)
Params:
  {String} Code to process
  {{context: String, filename: String, syntax: String}} Options (optional) where context is Gonzales PE rule, filename is a file's name that is used to display errors and syntax is syntax name with css being a default value.
Return: {Promise} Resolves with processed string.


syntax is syntax name with css being a default value.


所以只要在调用 comb.processString() 方法时,对微信小程序的样式文件,传值为 {syntax: 'css'} 即可。


上代码,框架大致是这样:


先安装必要依赖

$ yarn add --dev gulp-csscomb through2

// gulpfile.js
const { src, dest } = require('gulp')
const rename = require('gulp-rename')
const debug = require('gulp-debug')
const csscomb = require('gulp-csscomb')
const through = require('through2')
// csscomb 插件
const csscombPlugin = () => {
  return through.obj(function (file, enc, cb) {
    // 暂时什么都没做...
    return cb()
  })
}
// Gulp 任务
const csscombTask = cb => {
  try {
    return src('app.wxss')
      .pipe(debug())
      .pipe(csscombPlugin())
      .pipe(dest(file => file.base))
  } catch (e) {
    console.warn(e)
  }
}
module.exports = {
  csscombTask
}


创建新脚本,并运行 yarn run csscomb:mini

{
  "scripts": {
    "csscomb:mini": "gulp csscombTask"
  }
}


看样子是可以正常跑起来的,接下来就实现 csscomb 转化过程。

frankie@iMac csscomb-mini %  yarn run csscomb:mini
yarn run v1.22.10
$ gulp csscombTask
[20:29:25] Using gulpfile ~/Desktop/Web/Git/csscomb-mini/gulpfile.js
[20:29:25] Starting 'csscombTask'...
[20:29:25] gulp-debug: app.wxss
[20:29:25] gulp-debug: 1 item
[20:29:25] Finished 'csscombTask' after 14 ms
  Done in 0.51s.


如对 Gulpthrough2 不了解,可去官网了解一下。through2 是快速创建一个 transform stream 的工具函数。


编写 csscombPlugin 实现


安装必要依赖


$ yarn add --dev plugin-error

const fs = require('fs')
const path = require('path')
const { src, dest } = require('gulp')
const rename = require('gulp-rename')
const debug = require('gulp-debug')
const Comb = require('csscomb')
const csscomb = require('gulp-csscomb')
const through = require('through2')
const PluginError = require('plugin-error') // 用于处理异常
// csscomb 插件
const csscombPlugin = () => {
  // 默认支持扩类型
  const defaultExts = ['.css', '.sass', '.scss', '.less']
  // 扩展类型,假设以后兼容字节跳动小程序,可加上 .ttss
  const expandExts = ['.wxss', '.acss']
  const supportExts = defaultExts.concat(expandExts)
  return through.obj(async function (file, enc, cb) {
    let syntax = 'css'
    const filePath = file.path
    const extname = path.extname(filePath)
    // 获取 csscomb 配置
    const combConfigPath = Comb.getCustomConfigPath(path.resolve(__dirname, '.csscomb.json'))
    const combConfig = Comb.getCustomConfig(combConfigPath)
    if (file.isNull()) {
      // 文件为空不做处理,直接返回进入下一个 pipe()
      return cb()
    } else if (file.isStream()) {
      // 不支持对流(Stream)进行操作,抛出异常
      this.emit('error', new PluginError('csscombPlugin', 'Streams are not supported!'))
      return cb()
    } else if (file.isBuffer() && supportExts.includes(extname)) {
      // 默认支持类型,通过扩展名获取。其余当做 css
      if (defaultExts.includes(extname)) {
        syntax = extname.split('.').pop()
      }
      // 找不到配置文件
      if (combConfigPath && !fs.existsSync(combConfigPath)) {
        this.emit('error', new PluginError('csscombPlugin', 'Configuration file not found: ' + configPath))
        return cb()
      }
      try {
        // 实例化
        const comb = new Comb(combConfig)
        // 对所要转换的文件进行格式化
        const output = await comb.processString(
          file.contents.toString('utf8'),
          {
            syntax,
            filename: filePath
          }
        )
        // 创建 Buffer,并将格式化后的字符串替换原本的 contents
        file.contents = Buffer.from(output, 'utf-8')
        this.push(file)
        return cb()
      } catch (e) {
        this.emit('error', new PluginError('csscombPlugin', filePath + '\n' + e))
      }
    } else {
      // 其余情况,直接返回。例如 file 为 JS 文件等
      return cb()
    }
  })
}
// Gulp 任务
const csscombTask = cb => {
  try {
    return src('app.wxss')
      .pipe(debug())
      .pipe(csscombPlugin())
      .pipe(dest(file => file.base))
  } catch (e) {
    console.warn(e)
  }
}
module.exports = {
  csscombTask
}


我们再次运行脚本 yarn run csscomb:mini,可以看到 app.wxss 文件的变化:

/* before */
.container {
  height: 100%;
  padding: 200rpx 0;
  display: flex;
  align-items: center;
  flex-direction: column;
  justify-content: space-between;
  box-sizing: border-box;
}
/* after */
.container {
  display: flex;
  box-sizing: border-box;
  padding: 200rpx 0;
  height: 100%;
  align-items: center;
  flex-direction: column;
  justify-content: space-between;
}


至此,我们的工作完成了 90%... 还可以继续优化。

你注意到我们 csscombTask 方法里面是针对一个相对固定的路径或者文件了吗?假设我每次格式化其他目录的文件,都需要修改此方法,显然是不合理的。


我们可在 NPM 脚本进行传参,然后通过 process.argv 来获取,我们处理下再传入 gulp.src() 方法即可。


四、优化


先约定好传参规则:


  • --path 表示符合 glob 文件匹配模式的路径,多个路径是用 , 隔开,并用单引号 ' 括起来,还有我限制了仅支持项目下的文件。
  • --ext 表示扩展名,如 .css.wxss 等。(此选项目前没什么用,保留下来后续优化用)


若对 Glob 模式不了解,可看 Glob 详解。其中 gulp.src(globs[, options]) 第一个参数就是这种模式的。


{
  "scripts": {
    "csscomb:mini": "gulp csscombTask --path '<filepath>' --ext <extension>"
  }
}


我们借助 minimist 来获取 NPM 参数,通过

安装必要依赖,

$ yarn add --dev minimist

// gulp.js
// 这里省略其他部分,仅修改了 csscombTask 方法
const minimist = require('minimist')
// Gulp 任务
const csscombTask = cb => {
  try {
    // 获取参数,如 { _: [ 'csscomb:mini' ], path: 'xxx', ext: 'xxx' }
    const options = minimist(process.argv.slice(2))
    // 过滤掉非项目下的路径
    const paths = options.path.split(',').filter(item => {
      const re1 = /^\//
      const re2 = /^\.\.\//
      return item && !re2.test(item) && (!re1.test(item) || (re1.test(item) && item.includes(__dirname)))
    })
    // 去重
    const newPaths = Array.from(new Set(paths))
    if (!newPaths.length) {
      return cb()
    }
    // allowEmpty 选项是为了避免在没有找到匹配的文件时抛出错误
    // Error: File not found with singular glob: xxx (if this was purposeful, use `allowEmpty` option)
    return src(newPaths, { allowEmpty: true })
      .pipe(debug())
      .pipe(csscombPlugin())
      .pipe(dest(file => file.base))
  } catch (e) {
    console.warn(e)
  }
}


修改 NPM 脚本,并创建一个支付宝小程序的样式文件 app.acss 测试一下:

{
  "scripts": {
    "csscomb:mini": "gulp csscombTask --path './**/*.wxss,app.acss'"
  }
}


运行脚本,发现可以一键格式化了,至此基本完成了。


五、GitHub


项目放在 GitHub 上  csscomb-mini 欢迎 Star 。


六、参考


目录
相关文章
|
6月前
|
弹性计算 机器人 应用服务中间件
一键部署开源Qwen3并集成到钉钉、企业微信
Qwen3系列模型现已正式发布并开源,包含8款“混合推理模型”,其中涵盖两款MoE模型(Qwen3-235B-A22B与Qwen3-30B-A3B)及六个Dense模型。阿里云计算巢已支持Qwen3-235B-A22B和Qwen3-32B的私有化部署,用户可通过计算巢轻松完成部署,并借助AppFlow集成至钉钉机器人或企业微信。文档详细介绍了从模型部署、创建应用到配置机器人的全流程,帮助用户快速实现智能助手的接入与使用。
470 19
一键部署开源Qwen3并集成到钉钉、企业微信
|
27天前
|
安全 Java 数据库
SpringSecurity认证授权及项目集成
本文介绍了基于Spring Security的权限管理框架,涵盖认证、授权与鉴权核心概念,通过快速入门示例演示集成流程,并结合数据库实现用户认证。进一步扩展实现正常登录,JWT登录及鉴权管理器,实现灵活的安全控制,适用于前后端分离项目中的权限设计与实践。
178 4
|
1月前
|
资源调度 JavaScript 前端开发
在Vue 3项目中集成Element Plus组件库的步骤
总结起来,在集成过程当中我们关注于库本身提供功能与特性、环境搭建与依赖管理、模块化编程思想以及前端工程化等方面知识点;同时也涵盖前端性能优化(比如上文提及“按需加载”)与定制化开发(例如“自定义主题”)等高级话题.
152 16
|
1月前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
3月前
|
人工智能 小程序 前端开发
小程序、网站 vs. APP:成本差异究竟在哪里?技术栈如何决定项目上限?优雅草卓伊凡
小程序、网站 vs. APP:成本差异究竟在哪里?技术栈如何决定项目上限?优雅草卓伊凡
247 0
小程序、网站 vs. APP:成本差异究竟在哪里?技术栈如何决定项目上限?优雅草卓伊凡
|
3月前
|
JSON 分布式计算 大数据
springboot项目集成大数据第三方dolphinscheduler调度器
springboot项目集成大数据第三方dolphinscheduler调度器
184 3
|
3月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
307 3
|
3月前
|
Java 关系型数据库 MySQL
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
springboot项目集成dolphinscheduler调度器 实现datax数据同步任务
400 2
|
3月前
|
分布式计算 Java 大数据
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
springboot项目集成dolphinscheduler调度器 可拖拽spark任务管理
211 2
|
3月前
|
物联网 Linux 开发者
快速部署自己私有MQTT-Broker-下载安装到运行不到一分钟,快速简单且易于集成到自己项目中
本文给物联网开发的朋友推荐的是GMQT,让物联网开发者快速拥有合适自己的MQTT-Broker,本文从下载程序到安装部署手把手教大家安装用上私有化MQTT服务器。
990 5

热门文章

最新文章