寫在前面 在開始之前,我們先思考下,創(chuàng)建一個(gè)規(guī)范的項(xiàng)目我們需要關(guān)注哪些點(diǎn)?我覺得吧,第一個(gè)是創(chuàng)建信息的完整性,一個(gè)信息完整的項(xiàng)目可以引導(dǎo)讀者與作者交流與合作,這個(gè)在后面的package.json
里面向大家介紹;第二個(gè)是代碼的規(guī)范性和兼容性,正所謂,沒有規(guī)矩不成方圓,良好的代碼規(guī)范會(huì)巧妙地杜絕屎上雕花的行為發(fā)生,這個(gè)后面跟大家介紹下eslint+prettier+babal
的知識(shí)點(diǎn);第三個(gè)項(xiàng)目目錄的規(guī)范性,這個(gè)也會(huì)在后面介紹?;谇懊嫒c(diǎn),我們可以做出一個(gè)自產(chǎn)自銷的項(xiàng)目,如果把這個(gè)過程比作拉翔,那它會(huì)很通暢,拉的很舒服,不會(huì)便秘。但是遠(yuǎn)遠(yuǎn)不夠,就比如程序員A拿到程序員B寫的項(xiàng)目,那么程序員B怎么去證明給A看,我的槍好使且我的話可信。這就引入了后面兩個(gè)話題,第四點(diǎn)就是把你的作品發(fā)出去讓別人能看得到,《何以笙簫默》中有句臺(tái)詞,“如果我們走散你找不到我,那我就站在最高的舞臺(tái)中央讓你看見我?!蹦型麄兟牰藛??你想要脫單,一個(gè)不成熟的建議,站在舞臺(tái)中央,發(fā)出滋滋滋的求偶聲,跳出Michael Jackson
妖嬈的舞步,just beat it
,just beat it
.喜歡你的說不定就有了,主動(dòng)一點(diǎn)就會(huì)有故事。這個(gè)后面筆者介紹下git
工作流以及npm
的發(fā)包;第五點(diǎn)就是測(cè)試,提高可信度。這里我會(huì)結(jié)合karma
、mocha
、chai
、travis
、codecov
來向大家介紹單元測(cè)試、持續(xù)集成、代碼覆蓋率測(cè)試。最后的話,我會(huì)結(jié)合相關(guān)的開發(fā)工具做一個(gè)簡(jiǎn)單的搭配使用介紹吧。好的,我們開始吧。
項(xiàng)目創(chuàng)建 注意: 因?yàn)楣P者目前前端接觸的比較多,所以這個(gè)庫(kù)的定義就是給前端環(huán)境用的,不是很推薦用在nodejs開發(fā)上使用,因?yàn)槠浜竺嫔婕暗搅艘恍〥OM之類的操作是對(duì)nodjs沒什么卵用的,所以采用ES Module
的語法來書寫,若想在node環(huán)境使用,請(qǐng)配合babel
,webpack
等工具使用,請(qǐng)確保電腦上安裝了nodejs
環(huán)境。
舉個(gè)例子,比如我要?jiǎng)?chuàng)建一個(gè)項(xiàng)目叫utils
,可以怎么做?(考慮到0基礎(chǔ)的同學(xué),我會(huì)講的比較細(xì),老司機(jī)請(qǐng)直接跳過這章節(jié))
如果你只是想玩玩,不想一步一步去配置,那么你只需要執(zhí)行mkdir utils && npm init -y
, 這句話的意思是說創(chuàng)建了一個(gè)文件夾叫utils
,然后初始化一個(gè)npm
管理的項(xiàng)目,-y
表示yes
,也就是都選是。這個(gè)時(shí)候它就會(huì)在項(xiàng)目文件夾下創(chuàng)建一個(gè)粗糙的package.json
文件。
新手我還是建議你一步一個(gè)腳印走一遍,執(zhí)行mkdir utils && npm init
,它會(huì)一步一步讓你確認(rèn)該項(xiàng)目的相關(guān)描述啊,協(xié)議啊,聯(lián)系方式啊,項(xiàng)目地址啥的,這里筆者貼出一份該項(xiàng)目的npm配置。
配置說明:
version
: 項(xiàng)目版本號(hào)scripts
: 項(xiàng)目執(zhí)行npm命令repository
: 項(xiàng)目倉(cāng)庫(kù)keywords
: 項(xiàng)目關(guān)鍵詞devDependencies
: 開發(fā)環(huán)境依賴,不會(huì)隨項(xiàng)目打包, 使用npm i @ataola/utils -D
安裝dependencies
: 開發(fā)環(huán)境依賴,會(huì)隨著項(xiàng)目打包,使用npm i @ataola/utils -S
安裝husky
: 在本地提交之前,做一次lint反饋,這個(gè)需要安裝相關(guān)npm包再配置lint-staged
: 只會(huì)校驗(yàn)提交修改的部分,這個(gè)也是需要安裝相關(guān)npm包再配置,建議你和樓上那位一起用{ "name" : "@ataola/utils" , "version" : "0.1.5" , "description" : "ataola's utils: maybe publish a feature one week, to record something i think or meet." , "main" : "index.js" , "scripts" : { "push" : "./push" , "pull" : "./pull" , "codecov" : "codecov" , "eslint" : "eslint . --ext .js --fix" , "husky:prepare" : "husky install" , "husky:add" : "husky add .husky/pre-commit 'npm run lint'" , "git:add" : "git add -A" , "lint" : "lint-staged" , "karma:init" : "karma init ./karma.conf.js" , "karma:test" : "karma start ./karma.conf.js" , "format" : "prettier --write '**/*.{js,jsx,ts,tsx,json,md}'" }, "repository" : { "type" : "git" , "url" : "git+https://github.com/ataola/utils.git" }, "keywords" : [ "javascript" , "utils" ], "author" : "ataola (zjt613@gmail.com)" , "license" : "MIT" , "bugs" : { "url" : "https://github.com/ataola/utils/issues" }, "homepage" : "https://github.com/ataola/utils#readme" , "devDependencies" : { "@babel/core" : "^7.13.15" , "@babel/eslint-parser" : "^7.13.14" , "@babel/plugin-proposal-class-properties" : "^7.13.0" , "@babel/plugin-transform-arrow-functions" : "^7.13.0" , "@babel/plugin-transform-async-to-generator" : "^7.13.0" , "@babel/plugin-transform-runtime" : "^7.13.15" , "@babel/polyfill" : "^7.12.1" , "@babel/preset-env" : "^7.13.15" , "@babel/runtime" : "^7.13.10" , "babel-eslint" : "^10.1.0" , "babel-loader" : "^8.2.2" , "babel-plugin-istanbul" : "^6.0.0" , "chai" : "^4.3.4" , "codecov" : "^3.8.1" , "core-js" : "^3.11.0" , "eslint" : "^7.24.0" , "eslint-config-prettier" : "^8.1.0" , "eslint-plugin-prettier" : "^3.3.1" , "husky" : "^6.0.0" , "karma" : "^6.3.2" , "karma-chai" : "^0.1.0" , "karma-chrome-launcher" : "^3.1.0" , "karma-coverage" : "^2.0.3" , "karma-mocha" : "^2.0.1" , "karma-mocha-reporter" : "^2.2.5" , "karma-webpack" : "^5.0.0" , "lint-staged" : "^10.5.4" , "mocha" : "^8.3.2" , "prettier" : "^2.2.1" , "webpack" : "^5.31.2" }, "husky" : { "hooks" : { "pre-commit" : "lint-staged" } }, "lint-staged" : { "*.{js,ts,jsx,tsx}" : [ "eslint . --fix" , "prettier --config .prettierrc --write ." ] } }
查詢npm
的相關(guān)命令是npm --help
, 比如我不知道npm init
后面可以跟什么,那么執(zhí)行npm init --help
就可以羅列出相關(guān)信息。
? ~ npm init --help npm init [--force|-f|--yes|-y|--scope] npm init <@scope> (same as `npx <@scope>/create`) npm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`) aliases: create, innit ? ~
如果你發(fā)現(xiàn)npm install
很慢,多半是長(zhǎng)城的問題,建議你改成國(guó)內(nèi)的淘寶源npm install --registry=https://registry.npm.
, 如果發(fā)現(xiàn)也還是不好使,終極解決方案: 科X學(xué)X上X網(wǎng),逃~。
代碼規(guī)范 努力做好六件事:
提交代碼前確認(rèn)所修改文件或者整個(gè)項(xiàng)目代碼規(guī)范 EditorConfig
這個(gè)對(duì)應(yīng)我們上面努力做好的第一件事 - 不同編輯器下的代碼規(guī)范。在現(xiàn)實(shí)多人開發(fā)中,由于開發(fā)者的行為習(xí)慣不同可以會(huì)導(dǎo)致代碼的風(fēng)格有所不同,有些人喜歡用vscode,有些人喜歡用webstorm,也許他們用的編輯器是一樣的,但是由于開發(fā)者在全局配置了一些設(shè)置,會(huì)導(dǎo)致整個(gè)項(xiàng)目代碼不符合預(yù)期,所以,我們需要一個(gè)在編輯器層面去協(xié)調(diào)各個(gè)編輯器環(huán)境下的代碼風(fēng)格,EditorConfig是一個(gè)不錯(cuò)的選擇,這個(gè)是本項(xiàng)目用到的關(guān)于EditorConfig的一些配置。
配置說明:
root=true
: 表示是最頂層的配置文件,發(fā)現(xiàn)設(shè)為true時(shí),才會(huì)停止查找.editorconfig文件When opening a file, EditorConfig plugins look for a file named .editorconfig in the directory of the opened file and in every parent directory. A search for .editorconfig files will stop if the root filepath is reached or an EditorConfig file with root=true is found.
EditorConfig files are read top to bottom and the most recent rules found take precedence. Properties from matching EditorConfig sections are applied in the order they were read, so properties in closer files take precedence.
原文地址:https://github.com/editorconfig/editorconfig/issues/376
end_of_line = lf
注意這個(gè)不是if
,而是lf
, 表示換行符,它有lf
、crlf
、cr
等等,跟系統(tǒng)關(guān)系比較大,反正大家都統(tǒng)一一下用lf
,可以看下這個(gè)有名的故事-GitHub 第一坑:換行符自動(dòng)轉(zhuǎn)換insert_final_newline = true
: 表示在末尾插入新行[*.{js,py}]
: 表示js和python文件charset = utf-8
: 表示字符集為utf-8
indent_style = space
: 表示代碼鎖進(jìn)格式用空格indent_size = 2
: 表示一個(gè)縮進(jìn)大小兩個(gè)空格quote_type = single
: 字符串設(shè)置為單引號(hào)trim_trailing_whitespace = true
: 表示是否在行尾修剪空白# This file is for unifying the coding style for different editors and IDEs # editorconfig.org # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true # Denotes whether to trim whitespace at the end of lines trim_trailing_whitespace = true # Matches multiple files with brace expansion notation [*.{js}] charset = utf-8 quote_type = single indent_style = space indent_size = 2 [{package.json,.travis.yml}] indent_style = space indent_size = 2
額,我覺得學(xué)這部分是有捷徑的,就是去嫖名項(xiàng)目它們的配置,然后把它們搞懂再應(yīng)用到自己或者團(tuán)隊(duì)的項(xiàng)目中。比如JQuery, Bootstrap的,跟對(duì)項(xiàng)目,做對(duì)事可以少走很多彎路的。
ESLint
這個(gè)對(duì)應(yīng)第二件事 -eslint作語法規(guī)范。eslint用來做一些js語法規(guī)范,避免一些語法上的錯(cuò)誤,當(dāng)然也可以做格式上的規(guī)范。這個(gè)是本項(xiàng)目用到的關(guān)于eslint的一些配置。
配置說明:
extends
: 繼承,表示它繼承了某些配置, 比如eslint:recommended
表示繼承了其推薦的配置,可以繼承多個(gè)的,用數(shù)組表示
plugins
: 表示安裝的插件, 寫配置的時(shí)候可以省略前面的前綴eslint-plugin-
parserOptions
: 表示解析選項(xiàng)
ecmaVersion
: 表示es語法的版本, 默認(rèn)為 3, 5。2015表示es6, 后面可自推sourceType
: 默認(rèn)是scirpt
,如果是ES模塊用module
parser
: 解析器,比如babel-eslint
, 表示一個(gè)對(duì)Babel解析器的包裝,使其能夠與 ESLint 兼容
rules
: 表示 啟用的規(guī)則及其各自的錯(cuò)誤級(jí)別, 0, 1,2
分別對(duì)應(yīng)off, warn, error
no-console
: 表示禁止調(diào)用console
對(duì)象的方法func-names
: 禁止命名的 function 表達(dá)式no-unused-vars
: 表示禁止未使用的變量object-shorthand
: 要求變量自變量簡(jiǎn)寫prettier/prettier
: 表示eslint下prettier的規(guī)則兼容arrow-body-style
: 要求箭頭函數(shù)使用大括號(hào)prefer-arrow-callback
: 要求使用箭頭函數(shù)作為回調(diào)space-before-function-paren
: 禁止函數(shù)圓括號(hào)之前有空格env
: 指定腳本的運(yùn)行環(huán)境,比如在其里面寫"es6": true
, 表示自動(dòng)啟動(dòng)es6語法, "browser": true
表示支持瀏覽器環(huán)境
{ "extends" : ["prettier" , "plugin:prettier/recommended" ], "plugins" : ["prettier" ], "parserOptions" : { "ecmaVersion" : 2015 , "sourceType" : "module" , "ecmaFeatures" : { "jsx" : true , "globalReturn" : true , "impliedStrict" : true } }, "parser" : "babel-eslint" , "rules" : { "no-console" : "off" , "func-names" : "off" , "no-unused-vars" : "warn" , "object-shorthand" : "off" , "prettier/prettier" : [ "error" , { "endOfLine" : "auto" , "singleQuote" : true , "trailingComma" : "es5" } ], "arrow-body-style" : "off" , "prefer-arrow-callback" : "off" , "camelcase" : "off" , "no-new" : "off" , "space-before-function-paren" : "off" }, "env" : { "es6" : true , "browser" : true } }
Prettier
這個(gè)對(duì)應(yīng)第三件事 - prettier作代碼的格式規(guī)范, 這個(gè)是本項(xiàng)目關(guān)于prettier的配置
配置說明:
tabWidth
: 縮進(jìn)字節(jié)數(shù)singleQuote
: 使用單引號(hào)代替雙引號(hào)endOfLine
: 結(jié)尾是 \n \r \n\r auto
trailingComma
: 在對(duì)象或數(shù)組最后一個(gè)元素后面是否加逗號(hào)bracketSpacing
:在對(duì)象,數(shù)組括號(hào)與文字之間加空格 "{ foo: bar }"alwaysParens
:(x) => {}
箭頭函數(shù)參數(shù)只有一個(gè)時(shí)是否要有小括號(hào)。avoid:省略括號(hào)eslintIntegration
: 不讓prettier使用eslint的代碼格式進(jìn)行校驗(yàn)jsxSingleQuote
: 在jsx中使用單引號(hào)代替雙引號(hào){ "semi" : true , "tabWidth" : 2 , "singleQuote" : true , "endOfLine" : "lf" , "trailingComma" : "es5" , "bracketSpacing" : true , "alwaysParens" : "always" , "eslintIntegration" : true , "jsxSingleQuote" : true }
看到這里,我們先停一停思考下,這么多配置,它們會(huì)不會(huì)產(chǎn)生沖突呢?那我要怎么去避免沖突,或者解決沖突呢?其實(shí)樓上已經(jīng)提到了用eslintIntegration
不讓prettier
使用eslint
的代碼風(fēng)格校驗(yàn)。然后在之前的eslint學(xué)習(xí)中,也可以通過在rule下新增規(guī)則作為補(bǔ)充。
babel
這個(gè)對(duì)應(yīng)第四件事 -做好代碼兼容性處理。babel是一個(gè)Javascript編譯器,可以將高版本的es語法,轉(zhuǎn)換成低版本的,以便能夠運(yùn)行在低版本瀏覽器或者其他環(huán)境,樓下是這個(gè)項(xiàng)目的babel
的配置文件
配置說明:
presets
: 預(yù)設(shè),進(jìn)行相關(guān)語法轉(zhuǎn)義plugins
:插件,補(bǔ)丁轉(zhuǎn)義器,彌補(bǔ)樓上先天不足{ "presets" : [ [ "@babel/preset-env" , { "targets" : { "browsers" : [">0.25%" , "not ie 11" , "not op_mini all" ] }, "exclude" : [ "@babel/plugin-transform-async-to-generator" , "@babel/plugin-transform-arrow-functions" ], "corejs" : { "version" : "3.8" , "proposals" : true }, "useBuiltIns" : "usage" } ] ], "plugins" : [ "@babel/transform-runtime" , "@babel/plugin-proposal-class-properties" ], "env" : { "test" : { "plugins" : ["istanbul" ] } } }
可以看下我之前寫的關(guān)于babel的一篇文章- Babel:下一代Javascript語法編譯器
一般來講有其配置文件,也會(huì)有其配置忽略文件, 比如``.prettierrc和
.prettierignore`, 其它的讀者自行觸類旁通,然后配置的文件格式也有很多種,比如說json文件,js文件,rc結(jié)尾的文件等等, 這里純粹是個(gè)人習(xí)慣, 筆者一般是用 .xxxrc
手動(dòng)擋控制單文件格式化 這里筆者以手動(dòng)擋開頭,我覺得非常應(yīng)景和帶感。與之對(duì)應(yīng)的便是自動(dòng)擋智能格式化。舉個(gè)例子吧,比如你選擇邊打邊格式化,未免也太浪費(fèi)資源了,而且可能它格式化的會(huì)和你當(dāng)時(shí)的想法有沖突。所以每次按下CTRL + S
進(jìn)行格式化的話,是一個(gè)很好的方案。它就好比開車,停車的話,掛空擋,拉手剎,下車干飯,是一氣呵成的,那個(gè)CTRL + S
就好比駕駛員手握的掛擋器,帶感。什么?剎車失靈?不存在的,阿Sir?。。?/p>
來看一下效果:
vscode-code-format 提交代碼前確認(rèn)所修改文件或者整個(gè)項(xiàng)目代碼規(guī)范 前面我們提到的是我們?cè)谄綍r(shí)開發(fā)中,對(duì)于單個(gè)文件的代碼規(guī)范手段,那么對(duì)于整個(gè)項(xiàng)目,我們應(yīng)該在每次提交前再去檢查確認(rèn)一遍,這樣子我們提交到遠(yuǎn)程的代碼才有保障。細(xì)心的同學(xué)可能已經(jīng)發(fā)現(xiàn)了,是的,在文章開頭講到的package.json
中可以配置husky
和lint-staged
去做這件事。husky
做提交前的檢查, 而lint-staged
則優(yōu)化了檢查的范圍是要提交檢查的,從而加快速度提高效率。
由于husky和lint-staged的版本不同配置也不同,這里筆者用的是最新的配置,具體的參考了這位國(guó)際友人的文章https:///sprout2000/items/29e8a637dda259bab26d
我這里的話, 就是在每次提交的時(shí)候?qū)s、ts等文件進(jìn)行eslint和prettier格式化,配置如下:
"husky" : { "hooks" : { "pre-commit" : "lint-staged" } }, "lint-staged" : { "*.{js,ts,jsx,tsx}" : [ "eslint . --fix" , ] }
來看一下效果:
husky-lint-staged 這里為了讓大家更明顯直觀看到效果,筆者沒有加prettier格式化那一句在lint-staged里面,后續(xù)加上后,關(guān)于格式的問題會(huì)被自動(dòng)修復(fù), 如下
"husky" : { "hooks" : { "pre-commit" : "lint-staged" } }, "lint-staged" : { "*.{js,ts,jsx,tsx}" : [ "eslint . --fix" , "prettier --config .prettierrc --write ." ] }
效果如下
root@ccb5f768c839:/home/coder/utils# git commit -m "test husky and lint-staged" > @ataola/utils@0.1.5 lint-staged > lint-staged ? Preparing... ? Running tasks... ? Applying modifications... ? Cleaning up... [main 0e5f4d3] sadasd 1 file changed, 2 insertions(+), 1 deletion(-) root@ccb5f768c839:/home/coder/utils#
項(xiàng)目目錄 目錄說明:
node_modules
: 安裝的npm依賴文件夾
package-lock.json
: npm的配置文件鎖
pull
: 拉取遠(yuǎn)程github倉(cāng)庫(kù)的腳本
push
: 上傳遠(yuǎn)程github倉(cāng)庫(kù)以及npm發(fā)包的腳本
? utils git:(main) tree -L 1 . ├── LICENSE ├── README.md ├── coverage ├── docs ├── img ├── index.js ├── karma.conf.js ├── lib ├── logs ├── node_modules ├── package-lock.json ├── package.json ├── pull ├── push └── test 7 directories, 8 files ? utils git:(main)
項(xiàng)目命名規(guī)范建議:
應(yīng)該使其文件或文件名命名具有語意,不會(huì)你就翻字典
要么用命名縮寫,要么用全名,建議全名用復(fù)數(shù)形式
因?yàn)閕mg是image的縮寫,你再加個(gè)s就沒有啥語意了,而全名的images表示圖片,這里可能有讀者會(huì)鉆牛角尖,你那個(gè)docs
不是和樓上沖突了嗎?不是的, doc英文單詞是文檔,docs是其復(fù)數(shù)形式, 這要和document區(qū)分開。
我們可以通過tree
命令去查看項(xiàng)目文件結(jié)構(gòu),-L
表示深度層數(shù), mac用戶可以通過brew install tree
安裝,ubuntu用戶可以通過apt-get install tree -y
安裝,centos用戶可以通過yum install tree -y
安裝,window用戶請(qǐng)下載相關(guān)tree包并配置到path環(huán)境變量里去, 或者去搜下window下的包管理命令`
git工作流和npm 努力做兩件事:
用腳本偷懶代替一行一行的敲命令,或者IDE點(diǎn)點(diǎn)點(diǎn) 腳本一把梭,梭,梭哈 我們先思考下,在git工作流中,有這樣三個(gè)概念, 萌萌噠的我, 遠(yuǎn)程倉(cāng)庫(kù),本地倉(cāng)庫(kù)。那,以這三個(gè)概念造句子,可以這么玩。萌萌噠的我爽朗地把本地倉(cāng)庫(kù)推向了遠(yuǎn)程倉(cāng)庫(kù),遠(yuǎn)程倉(cāng)庫(kù)被萌萌噠的我瀟灑地拉到了本地倉(cāng)庫(kù)。是的,這個(gè)在生活中有很形象的例子,還是萌萌噠的我饑饞碌碌地推開肯德基的大門去干飯,半個(gè)小時(shí)過去了,滿懷滋潤(rùn)的我拉開了肯德基的大門揚(yáng)長(zhǎng)而去。綜上所述,我們大致可以概括出兩個(gè)行為,推(push)和拉(pull),好上腳本。
push # !/usr/bin/env bash set -e function git_branch_name() { git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' } function e() { echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓" printf "┃$(tput bold) %-40s $(tput sgr0)┃\n" "$*" echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛" "$@" } CURRENT_BRANCH=$(git_branch_name) if [[ $CURRENT_BRANCH = feature/* ]]; then e git stash e git checkout main e git pull e git checkout "$CURRENT_BRANCH" e git merge main e git push e git stash pop || true elif [[ $CURRENT_BRANCH = main ]]; then e git stash e git push e nrm use npm e npm publish --access public e nrm use taobao e git stash pop || true fi
在push這個(gè)行為上,我們需要考慮兩點(diǎn)。第一,遠(yuǎn)程代碼有更新嗎?跟我本地會(huì)有沖突嗎?第二,我當(dāng)前是在哪個(gè)分支,我代碼才剛寫到一半,我不想提交這么辦?git stash
就是將你當(dāng)前的代碼改動(dòng)存入暫緩區(qū),使得其恢復(fù)上一次提交的狀態(tài),這個(gè)時(shí)候你從遠(yuǎn)程拉下來代碼,再去merge
下,然后你執(zhí)行git stash pop
,git checkout
是切換分支。
上面代碼的意思是,如果我是在某個(gè)特性分支,那么就先把我目前的改動(dòng)存入暫緩區(qū),然后切到主分支main,去拉取遠(yuǎn)程代碼,然后切回我當(dāng)前的分支,再去對(duì)主分支進(jìn)行merge
,然后執(zhí)行push
,最后再把我的改動(dòng)從暫緩區(qū)拿出來,然后就可以繼續(xù)開發(fā)了。如果我當(dāng)前是主分支,那太開心了,先把當(dāng)前改動(dòng)存入暫緩區(qū),然后直接push,再來個(gè)npm發(fā)包,然后把當(dāng)前改動(dòng)彈出來。
pull # !/usr/bin/env bash set -e function git_branch_name() { git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' } function e() { echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓" printf "┃$(tput bold) %-40s $(tput sgr0)┃\n" "$*" echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛" "$@" } CURRENT_BRANCH=$(git_branch_name) if [[ $CURRENT_BRANCH = feature/* ]]; then e git stash e git checkout main e git pull e git checkout "$CURRENT_BRANCH" e git merge main e git stash pop || true elif [[ $CURRENT_BRANCH = main ]]; then e git stash e git pull e git stash pop || true fi
這個(gè)pull和上面的push類似的,就不贅述了,讀者照著樓上的push去理解下pull吧。
多個(gè)remote的真香定律 為什么會(huì)有這個(gè)想法呢?由于不可描述的原因,墻對(duì)于天朝開發(fā)者來說始終是一個(gè)神秘的存在,當(dāng)我們?cè)谑褂肎itHub的時(shí)候,有時(shí)會(huì)遇到DNS污染,有時(shí)可能是墻的問題,總之就是提交也很難提交上去,拉也拉不下來。特別是在自己的云服務(wù)器上去拉GitHub上的代碼,等的花兒都謝了,算了放棄吧,先走為敬。這個(gè)時(shí)候碼云是個(gè)神奇的存在,用它拉取代碼速度是相當(dāng)?shù)目?,于是我蠢蠢欲?dòng)地加了一個(gè)碼云的remote,直接提交到碼云了,這樣子一個(gè)好處是,我本地就起一份代碼就好了,用不著同一個(gè)項(xiàng)目搞兩份代碼。嗯,remote真香?。?!
下面是我添加碼云的remote地址,然后把它上傳到碼云的步驟:
git remote add gitee https:///taoge2021/utils.git git fetch gitee git checkout -b gitee-main gitee/main git merge main git push gitee main
npm 這個(gè)其實(shí)在樓上代碼已經(jīng)有所體現(xiàn)了,這里簡(jiǎn)單講下就是,你先去https://www./
去注冊(cè)一個(gè)賬號(hào),然后本地npm login
去登陸這個(gè)賬號(hào),如果你想發(fā)布一個(gè)形如@ataola/utils
的包,那么執(zhí)行npm publish --access public
, 如果你不想的話npm publish
就可以了。
注意:發(fā)包的時(shí)候不要切到淘寶源,是在npm源上提交,可以通過 npm config set registry作轉(zhuǎn)化, 也可以用nrm這個(gè)包作源的管理
測(cè)試、持續(xù)集成和代碼覆蓋率 努力做三件事:
karma + mocha + chai 做測(cè)試的技術(shù)選型搭配其實(shí)有很多,我這里用到樓上這三位。是這樣子的,因?yàn)槲疫@個(gè)庫(kù)定義是給前端用的,后續(xù)會(huì)涉及到一些DOM,BOM等等的相關(guān)測(cè)試,我期望它是真的開了個(gè)瀏覽器去測(cè)試我的代碼。而Karma這個(gè)測(cè)試運(yùn)行器它可以做到這點(diǎn),而且它還是開源的。mocha是比較有名的測(cè)試框架,后面的chai是用來作斷言的。
karam的配置創(chuàng)建可以看下package.json
里面我配置的script腳本
"karma:init" : "karma init ./karma.conf.js" , "karma:test" : "karma start ./karma.conf.js" ,
npm run karma:init
表示創(chuàng)建一個(gè)karma的配置文件,而npm run karma:test
表示啟動(dòng)karma相關(guān)測(cè)試。
附上一份karma.conf.js
, 由于配置較多,這里如果默認(rèn)生成的話,大部分都不需要你動(dòng),就挑幾個(gè)講下,具體的還是要去看官方文檔的http://karma-runner./6.3/config/configuration-file.html
preprocessors
: 一些預(yù)處理操作mochaReporter
:暴露的mocha配置接口// Karma configuration // Generated on Sat Apr 10 2021 00:13:46 GMT+0800 (中國(guó)標(biāo)準(zhǔn)時(shí)間) module .exports = function (config ) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath : '' , // frameworks to use // available frameworks: https:///browse/keyword/karma-adapter frameworks : ['mocha' , 'chai' , 'webpack' ], plugins : [ 'karma-chrome-launcher' , 'karma-mocha' , 'karma-mocha-reporter' , 'karma-chai' , 'karma-webpack' , 'karma-coverage' , ], // list of files / patterns to load in the browser // test all files : ['lib/**/*.js' , 'test/**/*.js' ], // test single file // files: ['test/**/judge.test.js'], // list of files / patterns to exclude exclude : [], // preprocess matching files before serving them to the browser // available preprocessors: https:///browse/keyword/karma-preprocessor // test all preprocessors : { 'lib/**/*.js' : ['webpack' , 'coverage' ], 'test/**/*.js' : ['webpack' ], }, // test single file // preprocessors: { // 'test/**/judge.test.js': ['webpack'], // }, // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https:///browse/keyword/karma-reporter reporters : ['progress' ], // https://github.com/litixsoft/karma-mocha-reporter reporters : ['mocha' , 'coverage' ], mochaReporter : { colors : { success : 'blue' , info : 'bgGreen' , warning : 'cyan' , error : 'bgRed' , }, symbols : { success : '+' , info : '#' , warning : '!' , error : 'x' , }, output : 'autowatch' , showDiff : true , divider : '' , }, coverageReporter : { dir : 'coverage/' , reporters : [ { type : 'lcov' , subdir : '.' }, { type : 'text' , subdir : '.' , file : 'text.txt' }, { type : 'text-summary' , subdir : '.' , file : 'text-summary.txt' }, ], }, // web server port port : 9876 , // enable / disable colors in the output (reporters and logs) colors : true , // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel : config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes autoWatch : true , // start these browsers // available browser launchers: https:///browse/keyword/karma-launcher browsers : ['Chrome' , 'ChromeHeadless' , 'ChromeHeadlessNoSandbox' ], // you can define custom flags customLaunchers : { ChromeHeadlessNoSandbox : { base : 'ChromeHeadless' , flags : ['--no-sandbox' ], }, }, // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits singleRun : !!process.env.CI, // Concurrency level // how many browser should be started simultaneous concurrency : Infinity , webpack : { mode : 'development' , // entry: ['@babel/polyfill'], // entry: ['./index.js'], module : { rules : [ { test : /\.js$/ , exclude : /node_modules/ , use : { loader : 'babel-loader' , options : { presets : [ [ '@babel/preset-env' , { corejs : { version : '3.8' , proposals : true }, useBuiltIns : 'usage' , }, ], ], plugins : ['istanbul' ], }, }, }, ], }, }, }); };
這里如果我是寫Node的話,我會(huì)用jest,因?yàn)榕渲脮?huì)簡(jiǎn)單些。具體的讀者可以閱讀下我之前寫的文章使用jest進(jìn)行單元測(cè)試, 附上一個(gè)完整實(shí)戰(zhàn)的例子,這個(gè)是我刷leetcode做的單元測(cè)試的項(xiàng)目地址,https://github.com/ataola/coding
travis travis是做持續(xù)集成的,貼一份筆者的配置,需要注意的是,版本的不同可能配置也不太一樣,具體的還是要去看官方文檔https://docs./
language: node_js node_js: stable notifications: email: recipients: - zjt613@gmail.com on_success: change on_failure: always branches: only: - main cache: apt: true directories: - node_modules os: linux # https://docs./user/reference/overview/ dist: xenial addons: chrome: stable services: - xvfb sudo: required # turn off the clone of submodules for change the SSH to HTTPS in .gitmodules to avoid the error git: submodules: false before_install: - 'export DISPLAY=:99.0' - sleep 3 # give xvfb some time to start - '/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16' - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost & install: - npm set progress=false - npm install script: - npm run karma:test after_script: - npm run codecov
codecov codecov是做代碼覆蓋率測(cè)試的, 執(zhí)行npm install codecov -D
去安裝它,然后在packge.json
里面配置好script就好了"codecov": "codecov"
, 我們?cè)谧龀掷m(xù)集成的時(shí)候,最下面在執(zhí)行完相關(guān)karma測(cè)試后,最后會(huì)執(zhí)行npm run codecov
去讀取 coverage
目錄中的 lcov.info
文件,然后上傳到 Codecov 網(wǎng)站
測(cè)試這塊做了這么多工作,其實(shí)就是當(dāng)了一回場(chǎng)面人,在倉(cāng)庫(kù)首頁給它一個(gè)特寫,這里加了travis持續(xù)集成的構(gòu)建結(jié)果和codecov的代碼覆蓋率以增加項(xiàng)目的可信度和逼格。
unit test VSCode 開發(fā)環(huán)境 思考兩件事:
如何配置不同的開發(fā)環(huán)境,區(qū)分開發(fā)環(huán)境的共性和不同,以及其引起的不同(權(quán)衡不同項(xiàng)目利弊) 最小化插件原則,提高電腦運(yùn)行效率,不搞花里胡哨,不裝逼,把電腦當(dāng)朋友 環(huán)境的共性和不同 為什么會(huì)有這個(gè)問題,也還是源自生活中遇到的事。筆者最開始為了一步到位,將相關(guān)的prettier、eslint等等的相關(guān)配置都寫到了全局的,也就是user下面,后來在拉取項(xiàng)目的時(shí)候發(fā)現(xiàn),很多時(shí)候特別是多人開發(fā),由于eslint和prettier的配置不一樣,或者根本就沒有這塊的配置,導(dǎo)致代碼堆積如屎山難以維護(hù),這促使我有了進(jìn)一步的思考是,區(qū)分編輯器的共性和不同。舉個(gè)例子,比如說terminal這個(gè)插件,它其實(shí)可以配置調(diào)節(jié)在終端光標(biāo)的粗細(xì),我就不是很喜歡那種肥肥的光標(biāo),就把它改成line
,這種是屬于不同,是你的個(gè)性,不會(huì)因?yàn)檎f你設(shè)置了這個(gè)會(huì)影響到整個(gè)項(xiàng)目,別人電腦里沒設(shè)置還是肥肥的光標(biāo)。那么什么是共性,就比如最開始筆者說的將prettier、eslint配置到全局的做法,違背了共性,這里需要說明的一點(diǎn)是,違背不代表我是錯(cuò)的,在這件事情上沒有對(duì)錯(cuò),大環(huán)境決定的,如果一只隊(duì)伍里大家都認(rèn)為 1 + 1 =3,那么即使你認(rèn)為1 + 1 = 2,從大局上考慮,這里就姑且遷就下1 + 1 = 3吧,你可以沉默不說話,但你心里要有你堅(jiān)定的真理的答案,這個(gè)叫站隊(duì)。
具體的解決方案我認(rèn)為是,你可以在全局里去配置以那種方式去做一件事,但是具體的規(guī)則和形式需要單獨(dú)拎出來,不能寫全局里面??梢孕陆ㄒ粋€(gè).vscode
文件夾,然后在這個(gè)項(xiàng)目里面單獨(dú)配置,結(jié)合.prettierrc
、.eslintrc
等,可以參考下這個(gè)項(xiàng)目https://github.com/ataola/coding
vscode 推薦插件 gi t-commit-plugin:規(guī)范git提交信息 筆者以前也是個(gè)使用插件狂魔,總喜歡去試試倒騰這個(gè)插件那個(gè)插件好不好使好不好玩,再后來我那個(gè) 多年前買的window不堪重負(fù)萎靡不振,我就沒有這個(gè)想法了,插件只是個(gè)輔助工具,根據(jù)使用頻繁度和實(shí)用性去考量吧,老羅有句話說得好, 又不是不能用?
又不是不能用 示例講解 關(guān)于處理url參數(shù)轉(zhuǎn)成對(duì)象的格式,這個(gè)是前端開發(fā)面試的??碱},因?yàn)樗鼘?shí)用性強(qiáng),涉及基礎(chǔ)的數(shù)組字符串處理,答案還不唯一,所以這里筆者拋磚引玉,就以它為例子去講吧。
getQueryParameters
如果對(duì)正則不熟悉的話,這里可以用字符串分割分割再分割來做,具體的如下
/** * * @param {string} url * @returns {object} */ function getQueryParameters (url ) { const paramStr = decodeURIComponent (url).split('?' )[1 ]; if (!paramStr) { return {}; } const paramArr = paramStr.split('&' ); const res = {}; paramArr.forEach((param ) => { const [key, value] = param.split('=' ); res[key] = value; }); return res; }
相關(guān)測(cè)試
import { expect } from 'chai' ;import { getQueryParameters } from '../lib/url' ; describe('lib: url test' , function ( ) { it('getQueryParameters: expect { name: "ataola", age: "24" } when call function with params "https://?name=ataola&age=24"' , function ( ) { expect( getQueryParameters('https://?name=ataola&age=24' ) ).to.deep.equals({ name : 'ataola' , age : '24' }); }); }); });
最后 至此,筆者已經(jīng)向讀者們介紹了一個(gè)前端項(xiàng)目從有想法到去實(shí)踐再到總結(jié)分享的心路歷程。謝謝大家的賞臉閱讀,談起為什么寫這個(gè)項(xiàng)目,第一是項(xiàng)目做多了,自然而然就會(huì)有些想法,明人不說暗話我想偷點(diǎn)懶劃水,想早點(diǎn)下班哇,所以工作之余就勤快點(diǎn)把平時(shí)工作或者刷題常用到的總結(jié)整理下,打磨成一把瑞士軍刀,提高戰(zhàn)斗力;第二是像我們搞程序的,都挺單純的,有句話說得好”no BB, show me the code!“,可能不是很會(huì)表達(dá)自己吧,那就上代碼吧,希望面試官看了能夠加點(diǎn)印象分或者綜合得分,哈哈。
項(xiàng)目地址: https://github.com/ataola/utils