使Prettier一键格式化WXSS(上集)
本文将会结合 ESLint、Prettier、husky、lint-stage、gulp.js 等工具使得项目一键化操作,减少在格式化、代码检查等操作上浪费时间,因为大前端真的太多东西学了,不学会“偷懒”的话,我们就要落后更多了。
本系列文章的示例 Demo 在这里 👉 GitHub: wechat_applet_demo。
分为两篇文章介绍:
最近在做公司部门前端项目由 SVN 迁移 Git 的事情,由于历史代码在此之前并没有引入类似 ESLint、Prettier 的代码检查或者格式约束等工具。
目前部门仅剩我一人维护这十几个小程序、H5 前端项目。现在只要接触以前那些没有经手的项目,就头疼不想改。虽然思想是这样,但很无奈,谁让我只是一个“打工仔”呢!
吐槽完,入正题。
一、必备
1. 新建一个微信小程序项目
此处过于简单省略一万字...
# 或者克隆 wechat_applet_demo 项目下来
$ cd your_folder
$ git clone git@github.com:toFrankie/wechat_applet_demo.git
2. 使用 yarn 作为包管理工具
yarn
相关的安装不在本系列教程,相信你们都懂。也不再赘述,自行搜索。
3. 使用 Visual Studio Code 作为编辑器
虽然从业有一段时间了,不好意思,前端开发我只用 VS Code,将来好长一段时间应该还是它。至于什么 WebStorm、Atom、Sublime Text 等,用过但现在已经不会了。
Anyway,什么开发工具不重要,自己用着舒服就好。
下面介绍几个与本项目相关的 VS Code 插件
ESLint:自动检测 ESLint Rule,不符合规则时,在编辑页面会有警告 ️
Prettier - Code formatter:可用于格式化
按照以上两个插件之后,需要对编辑器做添加一些配置。
考虑到多人开发的场景,而每个人的开发工具配置不尽相同,所以我把以下配置放到项目根目录下中,并将其加入 Git 版本控制中,这样每个人拿到项目都有此配置了。
路径是:your_project/.vscode/settings.json
{
"files.associations": {
"*.wxss": "css",
"*.wxs": "javascript",
"*.acss": "css",
"*.axml": "html",
"*.wxml": "html",
"*.swan": "html"
},
"files.trimTrailingWhitespace": true,
"eslint.workingDirectories": [{ "mode": "auto" }],
"eslint.enable": true, // 是否开启 vscode 的 eslint
"eslint.options": {
// 指定 vscode 的 eslint 所处理的文件的后缀
"extensions": [".js", ".ts", ".tsx"]
},
"eslint.validate": ["javascript"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"git.ignoreLimitWarning": true
}
二、要开始了
1. yarn
初始化生成 package.json
:
2. 安装 ESLint、Prettier 相关依赖
若要使用 ESLint,往往需要配置很多繁杂的 rules 规则,如果每个人都要这种做的话,显然会耗费很多精力。于是就有人站了出来,并在 GitHub 上开源了他们的代码规范库,比较流行的有 airbnb、standard、prettier 等。
在这里我选择的是国内腾讯 AlloyTeam 团队出品的 eslint-config-alloy 开源规范库。
其实他们团队最开始使用 Airbnb 规则,但是由于它过于严格,部分规则还是需要个性化,导致后来越改越多,最后决定重新维护一套。经过两年多的打磨,现在 eslint-config-alloy 已经非常成熟了。
我选择它的几点原因:
- 适用于 React/Vue/Typescript 项目
- 样式相关规则由 Prettier 管理
- 有中文文档和网站示例(就我那蹩脚的英语水平,这点极吸引我,哈哈)
- 更新快,且拥有官方维护的 vue、typescript、react+typescript 规则
$ yarn add --dev babel-eslint@10.0.3
$ yarn add --dev eslint@6.7.1
$ yarn add --dev eslint-config-alloy@3.7.1
$ yarn add --dev eslint-config-prettier@6.10.0
$ yarn add --dev eslint-plugin-prettier@3.1.4
$ yarn add --dev prettier@2.0.5
$ yarn add --dev prettier-eslint-cli@5.0.0
3. 安装完依赖,那么就要加上 ESLint、Prettier 的配置文件
他们的配置文件可以有多种,这里使用 JavaScript 格式分别是 .eslintrc.js
、.prettierrc.js
,都放置在项目根目录下。
对于配置我就不展开说了,若有疑惑的,可以自行搜索查找答案或者评论留言给我。
// .eslintrc.js
module.exports = {
root: true,
parser: 'babel-eslint',
env: {
browser: true,
es6: true,
node: true,
commonjs: true
},
extends: ['alloy'],
plugins: ['prettier'],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
__DEV__: true,
__WECHAT__: true,
__ALIPAY__: true,
App: true,
Page: true,
Component: true,
Behavior: true,
wx: true,
my: true,
swan: true,
getApp: true,
getCurrentPages: true
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module'
},
rules: {
'no-debugger': 2,
'no-unused-vars': 1,
'no-var': 0,
'no-param-reassign': 0,
'no-irregular-whitespace': 0,
'no-useless-catch': 1,
'max-params': ['error', 3],
'array-callback-return': 1,
eqeqeq: 0,
indent: ['error', 2, { SwitchCase: 1 }]
}
}
// .prettierrc.js
module.exports = {
printWidth: 120,
tabWidth: 2,
useTabs: false,
semi: false,
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: false,
// 末尾不需要逗号
trailingComma: 'none',
// 大括号内的首尾需要空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 箭头函数,只有一个参数的时候,无需括号
arrowParens: 'avoid',
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: 'preserve',
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// 换行符使用 lf
endOfLine: 'lf'
}
4. 配置 ESLint、Prettier 忽略规则
对应的文件是 .eslintignore
、.prettierignore
,同样的都放在项目根目录下。
这些就根据自己项目实际情况做调整了,以下仅供参考:
# .eslintignore
*.min.js
typings
node_modules
# .prettierignore
*.min.js
/node_modules
/dist
# OS
.DS_Store
.idea
.editorconfig
.npmrc
package-lock.json
# Ignored suffix
*.log
*.md
*.svg
*.png
*ignore
## Built-files
.cache
dist
5. 添加 .editorconfig
配置文件
它是用来抹平不同编辑器之间的差异的。同样放置在项目根目录下。
# .editorconfig
# http://editorconfig.org
# https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
# 根目录的配置文件,编辑器会由当前目录向上查找,如果找到 `roor = true` 的文件,则不再查找
root = true
# 匹配所有的文件
[*]
# 缩进风格:space
indent_style = space
# 缩进大小 2
indent_size = 2
# 换行符 lf
end_of_line = lf
# 字符集 utf-8
charset = utf-8
# 不保留行末的空格
trim_trailing_whitespace = true
# 文件末尾添加一个空行
insert_final_newline = true
# 运算符两遍都有空格
spaces_around_operators = true
# 对所有的 js 文件生效
[*.js]
# 字符串使用单引号
quote_type = single
[*.md]
trim_trailing_whitespace = false
6. 添加 npm scripts
添加三条脚本指令:
"eslint": "eslint ./ --ext .js"
"eslint:fix": "eslint --fix ./ --ext .js"
"prettier:fix": "prettier --config .prettierrc.js --write './**/*.{js,css,less,scss,json}'"
通过 yarn run <command>
即可执行一键格式化和修复了,当然了 ESLint 使用 --fix
只能修复一部分,剩余的只能手动解决了。
{
"name": "wechat_applet_demo",
"version": "1.0.0",
"description": "微信小程序 Demo",
"main": "app.js",
"repository": "git@github.com:toFrankie/wechat_applet_demo.git",
"author": "Frankie <1426203851@qq.com>",
"license": "MIT",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"eslint": "eslint ./ --ext .js",
"eslint:fix": "eslint --fix ./ --ext .js",
"prettier:fix": "prettier --config .prettierrc.js --write './**/*.{js,css,less,scss,json}'"
},
"devDependencies": {
"babel-eslint": "10.0.3",
"eslint": "6.7.1",
"eslint-config-alloy": "3.7.1",
"eslint-config-prettier": "6.10.0",
"eslint-plugin-prettier": "3.1.4",
"prettier": "2.0.5",
"prettier-eslint-cli": "5.0.0"
}
}
三、你以为完了?
不不不,本文我最想分享的是下面这个,前面的内容都比较简单,很多人都懂了。
Prettier 支持的 JavaScript、JSX、Angular、Vue、Flow、TypeScript、CSS、Less、Scss、HTML、JSON、GraphQL、Markdown(GFM、MDX)、YAML 的代码格式化。
但其实是不能识别 wxss
、acss
等小程序特有的层叠样式,尽管它们规则与 CSS 无异,但是 Prettier 并没有解析器去解析它们。
我们试图去调整脚本命令为(添加 *.wxss
扩展名的文件):
{
"scripts": {
"prettier:fix": "prettier --config .prettierrc.js --write './**/*.wxss'",
}
}
然后去执行的时候就会报错,如下:
[error] No parser could be inferred for file: app.wxss
既然这样走不通的话,总不能利用 VS Code 的 Prettier 插件一个一个地去格式化 *.wxss
的文件吧,那样工作量太大了,不符合我们“偷懒”的做法。
那么如何解决呢?
我使用的是 Gulp.js 来处理。如果对 Gulp 不太熟悉了,点击这里了解一下。
四、Gulp.js
简单说下 Gulp.js 的工作方式,它使用的是 Node.js 中的 stream
(流),首先获取到需要的 stream
,然后通过 stream
的 pipe()
方法把流导入到你想要的地方。比如 Gulp 插件中,经过插件处理后的流又可以导入到其他插件汇总,当然也可以把流写入文件中,所以 Gulp 是以 stream
为媒介的,它不需要频繁的生成临时文件,这也是 Gulp 的速度比 Grunt 快的一个原因。
我刚开始时的想法是:首先将
wxss
(acss
)转换并导出为css
,接着删除wxss
(acss
)文件,再者使用 Prettier 对css
文件进行格式化,转回wxss
(acss
)之后,再删除掉css
文件。这个过程会频繁的生成临时文件,思路是有点像 Grunt。但是了解了 Gulp 的思想后,其实它帮我们省掉了频繁增删文件的环节,全部放在内存中操作,也会更快一些,所以此前的方案被我否掉了。
下面我们只用到 Gulp 的其中两个 API, gulp.src()
和 gulp.dest()
。
1. gulp.src()
这个方法是用来获取流的,但要注意这个流里面的内容不是原始的文件流,而是一个虚拟文件对象流(Vinyl files),这个虚拟文件对象中存储着原始文件的路径、文件名、内容等信息。(这里不深入,点到为止,有兴趣自行了解)
语法:gulp.src(globs[, options])
- globs:是文件匹配模式,用来匹配文件路径(包括文件名)
- options:为可选参数,通常情况我们不需要用到
*关于参数详细说明,请看文档。
2. gulp.dest()
该方法是用来写文件的
gulp.dest(path[, options])
- path:是写入文件的路径
- options:为可选参数,通常情况我们不需要用到
要想使用好 gulp.dest()
这个方法,就要理解给它传入的路径参数与最终生成的文件的关系。
Gulp 的使用流程一般是:首先通过 gulp.src()
方法获取到我们想要处理的文件流,然后把文件流通过 pipe()
方法导入到 Gulp 的插件中,最后把经过插件处理后的流再通过 pipe()
方法导入到 gulp.dest()
中,gulp.dest()
方法则把流中的内容写入到文件中。
这里需要弄清楚的一点是,我们给 gulp.dest()
传入的路径参数,只能用来指定要生成的文件的目录,而不能指定生成文件的文件名,它生成文件的文件名使用的是导入到它的文件流自身的文件名,所以生成的文件名是由导入到它的文件流决定的,即使我们给它传入一个带有文件名的路径参数,然后它也会把这个文件名当做是目录名,例如:
const gulp = require('gulp')
gulp.src('script/jquery.js').pipe(gulp.dest('dist/foo.js'))
// 最终生成的文件路径为 dist/foo.js/jquery.js,而不是 dist/foo.js
若需要修改文件名,需要使用插件 gulp-rename。
五、开始配置
首先,安装 Gulp 相关依赖包。
$ yarn add --dev gulp@4.0.2
$ yarn add --dev gulp-clean@0.4.0
$ yarn add --dev gulp-debug@4.0.0
$ yarn add --dev gulp-prettier@3.0.0
$ yarn add --dev gulp-rename@2.0.0
接着,我们在项目根目录下创建一个 gulpfile.js
文件。
Gulp.js 官网快速入门的教程,很简单,这里不在赘述。
思路:使用
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
}
通过以下方式调用就好了
// package.json
{
"scripts": {
"prettier:wxss": "gulp wxss",
"prettier:accs": "gulp acss",
"prettier:wxss:acss": "gulp all"
}
}
执行命令,我们看到如下结果,说明配置成功了。
六、Git-Hooks
上面已经实现了对 wxss
、acss
扩展名的文件进行一键格式化了。
还可以“更懒”一些,利用 git-hooks 我们可实现在 commit
之前,对项目进行 ESLint、Prettier 检测和格式化,一旦出现错误,将停止 commit
操作。
由于本文篇幅已经很长了,所以我们放到下一篇继续写...
七、插个题外话
由于本项目的 npm 包仅用于代码检查与格式化,并未参与页面代码逻辑中。所以我在小程序本地项目配置文件中添加上打包配置选项。
packOptions 用以配置项目在打包过程中的选项。打包是预览、上传时对项目进行的必须步骤。
目前可以指定
packOptions.ignore
字段,用以配置打包时对符合指定规则的文件或文件夹进行忽略,以跳过打包的过程,这些文件或文件夹将不会出现在预览或上传的结果内。
*需要注意的是支付宝小程序,在编写本文时还未支持类似 ignore
选项。
// project.config.js
{
"packOptions": {
"ignore": [
{
"type": "regexp",
"test": "\\.md$"
},
{
"type": "folder",
"test": "node_modules"
}
]
}
}