本文將會結合 ESLint、Prettier、husky、lint-stage、gulp.js 等工具使得項目一鍵化操做,減小在格式化、代碼檢查等操做上浪費時間,由於大前端真的太多東西學了,不學會「偷懶」的話,咱們就要落後更多了。javascript
本系列文章的示例 Demo 在這裏 👉 GitHub: wechat_applet_demo。css
分爲三篇文章介紹:html
在簡書也有更新 👉 這裏。前端
最近在作公司部門前端項目由 SVN 遷移 Git 的事情,因爲歷史代碼在此以前並無引入相似 ESLint、Prettier 的代碼檢查或者格式約束等工具。vue
目前部門僅剩我一人維護這十幾個小程序、H5 前端項目。如今只要接觸之前那些沒有經手的項目,就頭疼不想改。雖然思想是這樣,但很無奈,誰讓我只是一個「打工仔」呢!java
吐槽完,入正題。node
此處過於簡單省略一萬字...react
# 或者克隆 wechat_applet_demo 項目下來 $ cd your_folder $ git clone git@github.com:toFrankie/wechat_applet_demo.git
yarn
相關的安裝不在本系列教程,相信大家都懂。也再也不贅述,自行搜索。jquery
雖然從業有一段時間了,很差意思,前端開發我只用 VS Code,未來好長一段時間應該仍是它。至於什麼 WebStorm、Atom、Sublime Text 等,用過但如今已經不會了。git
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 }
yarn
初始化生成 package.json
:若要使用 ESLint,每每須要配置不少繁雜的 rules 規則,若是每一個人都要這種作的話,顯然會耗費不少精力。因而就有人站了出來,並在 GitHub 上開源了他們的代碼規範庫,比較流行的有 airbnb、standard、prettier 等。
在這裏我選擇的是國內騰訊 AlloyTeam 團隊出品的 eslint-config-alloy 開源規範庫。
其實他們團隊最開始使用 Airbnb 規則,可是因爲它過於嚴格,部分規則仍是須要個性化,致使後來越改越多,最後決定從新維護一套。通過兩年多的打磨,如今 eslint-config-alloy 已經很是成熟了。
我選擇它的幾點緣由:
$ 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
他們的配置文件能夠有多種,這裏使用 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' }
對應的文件是 .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
.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
添加三條腳本指令:
"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" } }
往下看以前,有必要說明一下:接下來涉及 Gulp.js 內容,是爲了讓 Prettier 處理 Gulp.js 轉換出來的
css
,以達到最終 Prettier 格式化處理wxss
的目的。可是上述方式我確實走了一些彎路,其實經過 Prettier Configuration Overrides 配置是能夠指定
.wxss
等擴展名文件使用指定的解析器的。換句話說就是,咱們能夠在處理.wxss
文件時使用 CSS 解析器去處理它就行了。(具體看系列文章之結局篇)但個人建議是將前面兩篇看完,再看結局篇。
不不不,本文我最想分享的是下面這個,前面的內容都比較簡單,不少人都懂了。
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 的工做方式,它使用的是 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()
。
這個方法是用來獲取流的,但要注意這個流裏面的內容不是原始的文件流,而是一個虛擬文件對象流(Vinyl files),這個虛擬文件對象中存儲着原始文件的路徑、文件名、內容等信息。(這裏不深刻,點到爲止,有興趣自行了解)
語法:gulp.src(globs[, options])
*關於參數詳細說明,請看文檔。
該方法是用來寫文件的
gulp.dest(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" } }
執行命令,咱們看到以下結果,說明配置成功了。
上面已經實現了對 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" } ] } }