本文将会结合 ESLint、Prettier、husky、lint-stage、gulp.js 等工具使得项目一键化操作,减少在格式化、代码检查等操作上浪费时间,因为大前端真的太多东西学了,不学会“偷懒”的话,我们就要落后更多了。
本系列文章的示例 Demo 在这里 👉 GitHub: wechat_applet_demo。
分为两篇文章介绍:
使Prettier一键格式化WXSS(上集)
使Prettier一键格式化WXSS(下集)
强烈建议看我在简书更新的文章 👆,习惯在简书更新文章。微信社区发表文章使用 Markdown 用着不顺手,所以本文只做一个大概的简述。
一、前世今生
最近在做公司部门前端项目由 SVN 迁移 Git 的事情,由于历史代码在此之前并没有引入类似 ESLint、Prettier 的代码检查或者格式约束等工具。
目前部门仅剩我一人维护这十几个小程序、H5 前端项目。现在只要接触以前那些没有经手的项目,就头疼不想改。虽然思想是这样,但很无奈,谁让我只是一个“打工仔”呢!
二、必备
创建小程序项目,使用 yarn 作为包管理工具、安装 vscode 相关插件。
三、配置 ESLint、Prettier
这些都不难了,很多人都懂了。还是那句话,看我上面的原文。
其实 ESLint、Prettier 在目前的 H5 项目中,几乎所有前端框架或库都兼容得很好了,一系列配套的工具简直用得不要太爽了。Prettier 支持 CSS、LESS、SCSS 等,但是呢,他遇到小程序的 wxss 或者 acss 却不行了,会报错:
[error] No parser could be inferred for file: app.wxss
原因是 Prettier 并没有解析器去解析它们,它不知道怎么处理,所以就抛出错误了。
这就是本文主要要解决的问题。在此之前,我是通过安装 vscode 插件 Prettier - Code formatter 一个一个来格式化的,太傻了。
四、如何让 Prettier 识别小程序的层叠样式呢?
此前我去网上搜索了一番,似乎真没有人去做这件事,也可能是没公开或者没写出来,反正我还没找到,哈哈。
我使用的是 Gulp.js 来处理。如果对 Gulp 不太熟悉了,点击这里了解一下。
简单说下 Gulp.js 的工作方式,它使用的是 Node.js 中的 stream
(流),首先获取到需要的 stream
,然后通过 stream
的 pipe()
方法把流导入到你想要的地方。比如 Gulp 插件中,经过插件处理后的流又可以导入到其他插件汇总,当然也可以把流写入文件中,所以 Gulp 是以 stream
为媒介的,它不需要频繁的生成临时文件,这也是 Gulp 的速度比 Grunt 快的一个原因。
下面我们只用到 Gulp 的其中两个 API, gulp.src()
和 gulp.dest()
。
思路:使用
gulp.src()
获取流,然后使用 Gulp 插件对流分别作重命名(gulp-rename)、格式化(gulp-prettier
)、再重命名回来(gulp-rename
)、最后导出(gulp.dest()
)。过程中有利用gulp-debug
插件来查看一些信息。
// gulpfile.js
const { series, parallel, src, dest } = require('gulp')
const rename = require('gulp-rename')
const debug = require('gulp-debug')
const clean = require('gulp-clean')
const prettier = require('gulp-prettier')
const config = require('./.prettierrc')
// wxss 一键格式化
const wxssPrettier = () => {
return src('./**/*.wxss')
.pipe(
// 可以利用插件,查看一些 debug 信息
debug()
)
.pipe(
// 重写扩展名为 css,才能被 Prettier 识别解析
rename({
extname: '.css'
})
)
.pipe(
// Prettier 格式化
prettier(config)
)
.pipe(
// 重新将扩展名改为 wxss
rename({
extname: '.wxss'
})
)
.pipe(
// 导出文件
dest(__dirname)
)
}
// acss 一键格式化
const acssPrettier = () => {
return src('./**/*.acss')
.pipe(debug())
.pipe(
rename({
extname: '.css'
})
)
.pipe(prettier(config))
.pipe(
rename({
extname: '.acss'
})
)
.pipe(dest(__dirname))
}
// 这里导出多个 task,通过 gulp xxx 就能来调用了,如 gulp all
// 关于 series、parallel API 分别是按顺序执行(同步)、同时执行(并行)
module.exports = {
all: parallel(wxssPrettier, acssPrettier),
wxss: wxssPrettier,
acss: acssPrettier
}
调用 Gulp 任务:
// package.json
{
"scripts": {
"prettier:wxss": "gulp wxss",
"prettier:accs": "gulp acss",
"prettier:wxss:acss": "gulp all"
}
}
五、Git-Hooks
利用 Git Hooks 钩子实现提交代码自动执行此前的 ESLint、Prettier 命令,以保证我们提交的代码是不丑的。
我们这里使用到的是 pre-commit。
六、husky
husky 是一个为 Git 客户端增加 hook
的工具。当其安装到所在仓库时,它会自动在 .git/hooks
增加相应的钩子实现在 pre-commit
阶段就执行一系列保证每一个 commit
的正确性。
当然,pre-commit
阶段执行的命令,当然要保证其速度不要太慢,每次 commit
都等很久也不是好的体验。
// package.json
{
"husky": {
"hooks": {
"pre-commit": "yarn run format:all"
}
}
}
七、lint-staged
上面的流程,我们只提交了一个文件的变动,但是它对所有文件进行了扫描,这里是存在体验性问题的。
假如我们有 N 多个暂存文件,那么每当我们 git commit
一次就所有检查所有文件一遍,这导致我们的体验非常不好,过程很慢,显然不是我们想要的。
那么如何解决呢?我们需要用到它 👉 lint-staged。
// package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged --config .lintstagedrc.js"
}
}
}
// .lintstagedrc.js
const path = require('path')
module.exports = {
'*.js': ['prettier --config .prettierrc.js --write', 'eslint --fix --ext .js'],
'*.json': 'prettier --config .prettierrc.js --write',
'*.wxss': absolutePaths => {
// 获取相对路径
// const cwd = process.cwd()
// const relativePaths = absolutePaths.map(file => path.relative(cwd, file))
// return `gulp wxss --path ${relativePaths.join(' ')}`
return 'gulp wxss'
},
'*.acss': 'gulp acss'
}
八、至此
上面介绍的都很粗糙,不完整。有兴趣的请看 👉 原文。