在小程序中使用CSS预处理器

该来的一天还是来了,两年前立的Flag还是到了,现在在跟进项目配套的小程序开发,但对于写惯了 Stylus 的我,真的是太煎熬了,每次都忘记写冒号和分号…
所以就想着翻翻开发文档看 .wxss 支持不支持CSS预处理器的写法,比如 SCSS。明显这是不可能的,官方也没有计划支持,所以还是得自己动手。

大概看了一下大家的解决方案,和我想的一样,使用 Gulp 来自动化构建成 .wxss 文件。

一、抄脚本

首先呢,就是安装依赖,这次需要的依赖项有3个:gulpgulp-renamegulp-stylus(也可以是 sassless

npm init -y
npm i gulp gulp-rename gulp-stylus -D

然后创建 Gulp 脚本

最初我直接复制的肉大的脚本改了下,但是遇到了报错信息 TypeError: gulp.src(...).pipe(...).pipe is not a function,Google 后是说,Gulp4 更新后,API调整过了,旧的写法不可以用了。

所以我先选择降低了 Gulp 版本到 v3.9.1,降低版本后继续执行,遇到另一个报错信息 ReferenceError: primordials is not defined,好像是 Node 版本太高了,切换到 11.15.0 可以运行了,但是 侦听文件改变 的部分还有问题,运行之后执行了两次,后边再修改就没有编译了,所以还是用 Gulp4 自己重新调整吧。

二、Gulp4 重写

const gulp = require('gulp');
const stylus = require('gulp-stylus');
const rename = require('gulp-rename');
// 样式文件地址,我设置了全部,排除了node_modules,你也可以自己选择指定目录
const stylusFiles = [
  "./**/*.styl",
  "!./node_modules/**/*.styl",
]
// 编译stylus并且重命名为.wxss,生成在原目录下
const styl2wxss = () => {
  return gulp
    .src(stylusFiles)
    .pipe(stylus({ prefixer: false}))
    .pipe(rename({ extname: '.wxss' }))
    .pipe(gulp.dest('./'));
};
// 编译任务
gulp.task(styl2wxss);
// 监听任务
gulp.task('watch', () => {
  gulp.watch(stylusFiles, styl2wxss);
});

// 如果你有多个任务比如同时使用pug与stylus,可以像这样放入series内,会自动依次执行
gulp.task('dev', gulp.series('styl2wxss', 'watch'));

然后把执行脚本加入到 package.json

"scripts": {
  "watch": "gulp watch"
  "dev": "gulp dev"
}

执行 npm run watch 或者 npm run dev 就可以了。

到这里位置一切都正常,但是我编译完成之后,小程序编译报错了,因为不能加私有兼容前缀,所以还得处理一下,看看 stylus 的配置文档。

2 hours later….

翻了好久文档也没找到解决办法,不过 stylus 提供了一个 vendors 属性,具体看 文档链接
vendors = official 加到文件头部就可以了,但是总觉得怪怪的不舒服,官方好像也没有想处理这个问题的念头?先不管了,之后看有没有遇到其它解决方案吧。

三、使用VSCode扩展

VSCode的扩吧展商店里已经有大佬上传了 Stylus/Sass/Less 转换成 wxss 的插件了,有直接安装就可以用的,而且我上边提到的编译后出现兼容前缀的问题插件转换后没有遇到,还不知道他们是如何解决这个问题的,等空了看看他们的源码怎么写的吧。


扩展

# 如何让图片 url 在编译时自动补全CDN路径?

本地资源图片无法通过 WXSS 获取,可以使用网络图片,或者 base64,或者使用<image/>标签

现阶段的小程序是不允许在 wxss 中使用本地资源了,不然会报错。
如果用图片CDN,每次都输入全网络地址又太麻烦了,所以就想着有没有什么好一些的解决方式。

解决方案1:使用 stylus 全局变量。
例如我的全局变量文件位于 /assets/stylus/variables.styl
那么修改一下脚本文件就可:

const styl2wxss = () => {
  return gulp
    .src(stylusFiles)
    .pipe(stylus({ 
      prefixer: false,
      import: path.join(__dirname,'./assets/stylus/variables.styl'),
    }))
    .pipe(rename({ extname: '.wxss' }))
    .pipe(gulp.dest('./'))
};

就这可以这样书写了

// 例如变量名为 $httpUrl
.className
  background url($httpUrl/img/bg_img.png) center/contain no-repeat
// 编译为
// background: url("http://www.domain.com/assets/img/bg_img.png") center/contain no-repeat;

解决方案2:stylus 编译成 css 自动处理补全 CDN 地址
虽然使用全局变量可以用,但是也不优雅,想着可以在 stylus 编译成 css 的时候同步处理补全 CDN 路径,在书写的时候只需要键入 url(/img/xxx.jpg) 就会得到对应的 CDN 地址 url(https://www.baidu.com/assets/img/xxx.jpg) 了。
但是找了一圈还没找到解决,思路有了得等有时间了尝试一下,先挖个坑。

使用 Pug 和 Stylus 开发小程序的 watch 脚本
gulp.js
Expressive, dynamic, robust CSS — expressive, robust, feature-rich CSS preprocessor

node.js - How to fix ReferenceError: primordials is not defined in node - Stack Overflow
Vendor prefixed properties WITHIN vendor prefixed @keyframes · Issue #882 · stylus/stylus
Bug in vendored keyframes · Issue #1135 · stylus/stylus