家好,我是前端西瓜哥。今天帶大家來學習 Prettier。
Prettier 是一款流行的代碼格式化工具。它支持的語言相當多。
它很純粹,就一個代碼格式化工具,并不會做代碼質量的檢查(比如聲明了一個未被使用的變量)。
Prettier 會強制使用統一的代碼風格,原理就是解析語言生成 AST 抽象語法樹,然后用自己的一套風格寫回到文件。
Prettier 的優點:
但 Prettier 堅持自己的品味,它更希望用戶使用它精心挑選出來的代碼風格,只提供較少的自定義配置規則。
比如有個 printWidth 的配置(默認值為 80),當一行代碼超過特定字符數時會對其做拆分換行。這個配置無法關閉,你必須得設置一個值。
下面我們就來上手 Prettier。
先是安裝:
yarn add --dev --exact prettier
# 或者是
npm install --save-dev --save-exact prettier
這里我們用了 exact 配置項來鎖定版本號,這是因為不同版本 prettier 的代碼風格可能有細微的不同。prettier 并不保證主版本相同的版本下風格是一致的。
使用命令對項目下所有文件進行格式:
npx prettier --write .
你也可以指定目錄,比如 /src;或是用通配符指定特定的文件,比如 app 目錄下的所有 .test.js 結尾的文件可以用 app/**/*.test.js。
另外,你可以創建 .prettierignore 文件來指定不需要格式化的文件。如:
# Ignore artifacts:
build
coverage
# Ignore all HTML files:
*.html
如果你想要在保存時格式化,一般都是要用到編輯器的插件。對于 VSCode 來說,你需要安裝一個名為 Prettier 的插件,然后再加上 VSCode 配置(項目下加一個 .vscode/setting.json 文件):
{
"editor.defaultFormatter": "esbenp.prettier-vscode", // 默認格式器改為 prettier
"editor.formatOnSave": true // 開啟 “保存自動格式化”
}
或者你不開啟保存自動格式化,可以在覺得需要的時候右鍵選擇 “Format Document“,或者用快捷鍵。
ESLint 是一種 Linter,能夠分析代碼并準確定位錯誤。它支持 代碼質量 以及 代碼風格 的檢查。
代碼質量,比如啟用 "no-unused-vars",變量如果聲明卻未被使用會被認為不正確。
代碼風格的能力類似 Prettier,比如 "semi": "error" 表示必須用分號結尾,對應 Prettier 的 "semi": true。
Prettier 不會標識哪些地方出問題,在編輯器中用波浪線標出來。我用 ESLint 寫新的功能時,因為代碼是半成品,總能看到一堆的錯誤提示,體驗確實不好。
總的來說,Prettier 只做代碼格式化;ESLint 既能做代碼質量檢查,也能做代碼風格檢查和修正。
一般來說,項目最好加上 ESLint,這對我們改善代碼質量很有幫助。對于代碼格式化,我們可以用 ESLint 或是 Prettier。
如果想用 Prettier 格式化 JS,你需要在 ESLint 配置文件中使用 eslint-config-prettier,將 eslint 中和 prettier 沖突的規則關閉。否則你會看到代碼被格式化了兩次,總是會不符合其中一方的規則。
另外,ESLint 不支持格式化 CSS,還是有必要裝上 Prettier 或 stylelint 的。
TypeScript 團隊在實現 typescript-eslint 時,認為 ESLint 不應該做代碼格式化,而應該是一個真正的只檢查錯誤的 Linter(可能他們被格式化的實現弄煩了),而像是 Prettier 這類的 Formatter 才應該做代碼格式化工作。具體可以看下面這篇文章:
https://typescript-eslint.io/docs/linting/troubleshooting/formatting/
Prettier 是一款代碼格式化工具,開箱即用,默認支持語言眾多,風格優美,可以讓我們輕裝上陣。如果你自己做一些小項目,用方便的 Prettier 是不錯的選擇。
我是前端西瓜哥,歡迎關注我,學習更多前端知識。
作者:田小波 來源:https://www.cnblogs.com/nullllun/p/8358146.html
. 背景
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。相對于另一種數據交換格式 XML,JSON 有著諸多優點。比如易讀性更好,占用空間更少等。在 web 應用開發領域內,得益于 JavaScript 對 JSON 提供的良好支持,JSON 要比 XML 更受開發人員青睞。所以作為開發人員,如果有興趣的話,還是應該深入了解一下 JSON 相關的知識。
本著探究 JSON 原理的目的,我將會在這篇文章中詳細向大家介紹一個簡單的JSON解析器的解析流程和實現細節。由于 JSON 本身比較簡單,解析起來也并不復雜。所以如果大家感興趣的話,在看完本文后,不妨自己動手實現一個 JSON 解析器。
好了,其他的話就不多說了,接下來讓我們移步到重點章節吧。
2. JSON 解析器實現原理
JSON 解析器從本質上來說就是根據 JSON 文法規則創建的狀態機,輸入是一個 JSON 字符串,輸出是一個 JSON 對象。一般來說,解析過程包括詞法分析和語法分析兩個階段。詞法分析階段的目標是按照構詞規則將 JSON 字符串解析成 Token 流,比如有如下的 JSON 字符串:
{ "name" : "小明", "age": 18 }
結果詞法分析后,得到一組 Token,如下:
{、 name、 :、 小明、 ,、 age、 :、 18、 }
詞法分析解析出 Token 序列后,接下來要進行語法分析。語法分析的目的是根據 JSON 文法檢查上面 Token 序列所構成的 JSON 結構是否合法。
比如 JSON 文法要求非空 JSON 對象以鍵值對的形式出現,形如 object={string : value}。如果傳入了一個格式錯誤的字符串,比如
{ "name", "小明" }
那么在語法分析階段,語法分析器分析完 Token name后,認為它是一個符合規則的 Token,并且認為它是一個鍵。
接下來,語法分析器讀取下一個 Token,期望這個 Token 是 :。但當它讀取了這個 Token,發現這個 Token 是,,并非其期望的:,于是文法分析器就會報錯誤。
這里簡單總結一下上面兩個流程,詞法分析是將字符串解析成一組 Token 序列,而語法分析則是檢查輸入的 Token 序列所構成的 JSON 格式是否合法。這里大家對 JSON 的解析流程有個印象就好,接下來我會詳細分析每個流程。
2.1 詞法分析
在本章開始,我說了詞法解析的目的,即按照“構詞規則”將 JSON 字符串解析成 Token 流。請注意雙引號引起來詞--構詞規則,所謂構詞規則是指詞法分析模塊在將字符串解析成 Token 時所參考的規則。
在 JSON 中,構詞規則對應于幾種數據類型,當詞法解析器讀入某個詞,且這個詞類型符合 JSON 所規定的數據類型時,詞法分析器認為這個詞符合構詞規則,就會生成相應的 Token。
這里我們可以參考http://www.json.org/對 JSON 的定義,羅列一下 JSON 所規定的數據類型:
當詞法分析器讀取的詞是上面類型中的一種時,即可將其解析成一個 Token。我們可以定義一個枚舉類來表示上面的數據類型,如下:
在解析過程中,僅有 TokenType 類型還不行。我們除了要將某個詞的類型保存起來,還需要保存這個詞的字面量。所以,所以這里還需要定義一個 Token 類。用于封裝詞類型和字面量,如下:
public class Token { private TokenType tokenType; private String value; // 省略不重要的代碼 }
定義好了 Token 類,接下來再來定義一個讀取字符串的類。如下:
有了 TokenType、Token 和 CharReader 這三個輔助類,接下來我們就可以實現詞法解析器了。
上面的代碼是詞法分析器的實現,部分代碼這里沒有貼出來,后面具體分析的時候再貼。先來看看詞法分析器的核心方法 start,這個方法代碼量不多,并不復雜。其通過一個死循環不停的讀取字符,然后再根據字符的類型,執行不同的解析邏輯。
上面說過,JSON 的解析過程比較簡單。原因在于,在解析時,只需通過每個詞第一個字符即可判斷出這個詞的 Token Type。比如:
正如上面所說,詞法分析器只需要根據每個詞的第一個字符,即可知道接下來它所期望讀取的到的內容是什么樣的。如果滿足期望了,則返回 Token,否則返回錯誤。
下面就來看看詞法解析器在碰到第一個字符是n和"時的處理過程。先看碰到字符n的處理過程:
private Token readNull() throws IOException { if (!(charReader.next()=='u' && charReader.next()=='l' && charReader.next()=='l')) { throw new JsonParseException("Invalid json string"); } return new Token(TokenType.NULL, "null"); }
上面的代碼很簡單,詞法分析器在讀取字符n后,期望后面的三個字符分別是u,l,l,與 n 組成詞 null。如果滿足期望,則返回類型為 NULL 的 Token,否則報異常。readNull 方法邏輯很簡單,不多說了。
接下來看看 string 類型的數據處理過程:
string 類型的數據解析起來要稍微復雜一些,主要是需要處理一些特殊類型的字符。JSON 所允許的特殊類型的字符如下:
\" \ \b \f \n \r \t \u four-hex-digits \/
最后一種特殊字符\/代碼中未做處理,其他字符均做了判斷,判斷邏輯在 isEscape 方法中。在傳入 JSON 字符串中,僅允許字符串包含上面所列的轉義字符。如果亂傳轉義字符,解析時會報錯。
對于 STRING 類型的詞,解析過程始于字符",也終于"。所以在解析的過程中,當再次遇到字符",readString 方法會認為本次的字符串解析過程結束,并返回相應類型的 Token。
上面說了 null 類型和 string 類型的數據解析過程,過程并不復雜,理解起來應該不難。至于 boolean 和 number 類型的數據解析過程,大家有興趣的話可以自己看源碼,這里就不在說了。
2.2 語法分析
當詞法分析結束后,且分析過程中沒有拋出錯誤,那么接下來就可以進行語法分析了。語法分析過程以詞法分析階段解析出的 Token 序列作為輸入,輸出 JSON Object 或 JSON Array。語法分析器的實現的文法如下:
object={} | { members } members=pair | pair , members pair=string : value array=[] | [ elements ] elements=value | value , elements value=string | number | object | array | true | false | null
語法分析器的實現需要借助兩個輔助類,也就是語法分析器的輸出類,分別是 JsonObject 和 JsonArray。
代碼如下:
語法解析器的核心邏輯封裝在了 parseJsonObject 和 parseJsonArray 兩個方法中,接下來我會詳細分析 parseJsonObject 方法,parseJsonArray 方法大家自己分析吧。
parseJsonObject 方法實現如下:
parseJsonObject 方法解析流程大致如下:
上面的步驟并不復雜,但有可能不好理解。這里舉個例子說明一下,有如下的 Token 序列:
{、 id、 :、 1、 }
parseJsonObject 解析完 { Token 后,接下來它將期待 STRING 類型的 Token 或者 END_OBJECT 類型的 Token 出現。于是 parseJsonObject 讀取了一個新的 Token,發現這個 Token 的類型是 STRING 類型,滿足期望。
于是 parseJsonObject 更新期望Token 類型為 SEL_COLON,即:。如此循環下去,直至 Token 序列解析結束或者拋出異常退出。
上面的解析流程雖然不是很復雜,但在具體實現的過程中,還是需要注意一些細節問題。比如:
在 JSON 中,字符串既可以作為鍵,也可以作為值。作為鍵時,語法分析器期待下一個 Token 類型為 SEP_COLON。而作為值時,則期待下一個 Token 類型為 SEP_COMMA 或 END_OBJECT。
所以這里要判斷該字符串是作為鍵還是作為值,判斷方法也比較簡單,即判斷上一個 Token 的類型即可。如果上一個 Token 是 SEP_COLON,即:,那么此處的字符串只能作為值了。否則,則只能做為鍵。
對于整數類型的 Token 進行解析時,簡單點處理,可以直接將該整數解析成 Long 類型。但考慮到空間占用問題,對于 [Integer.MIN_VALUE, Integer.MAX_VALUE]范圍內的整數來說,解析成 Integer 更為合適,所以解析的過程中也需要注意一下。
3. 測試及效果展示
為了驗證代碼的正確性,這里對代碼進行了簡單的測試。測試數據來自網易音樂,大約有4.5W個字符。為了避免每次下載數據,因數據發生變化而導致測試不通過的問題。我將某一次下載的數據保存在了 music.json 文件中,后面每次測試都會從文件中讀取數據。
關于測試部分,這里就不貼代碼和截圖了。大家有興趣的話,可以自己下載源碼測試玩玩。
測試就不多說了,接下來看看 JSON 美化效果展示。這里隨便模擬點數據,就模擬王者榮耀里的狄仁杰英雄信息吧(對,這個英雄我經常用)。如下圖:
關于 JSON 美化的代碼這里也不講解了,并非重點,只算一個彩蛋吧。
4. 寫作最后
到此,本文差不多要結束了。本文對應的代碼已經放到了 github 上,需要的話,大家可自行下載。(微信不支持外跳,可點擊文末閱讀原文直達)
傳送門:https://github.com/code4wt/JSONParser
這里需要聲明一下,本文對應的代碼實現了一個比較簡陋的 JSON 解析器,實現的目的是探究 JSON 的解析原理。JSONParser 只算是一個練習性質的項目,代碼實現的并不優美,而且缺乏充足的測試。
同時,限于本人的能力(編譯原理基礎基本可以忽略),我并無法保證本文以及對應的代碼中不出現錯誤。如果大家在閱讀代碼的過程中,發現了一些錯誤,或者寫的不好的地方,可以提出來,我來修改。如果這些錯誤對你造成了困擾,這里先說一聲很抱歉。
最后,本文及實現主要參考了一起寫一個JSON解析器和如何編寫一個JSON解析器兩篇文章及兩篇文章對應的實現代碼,在這里向著兩篇博文的作者表示感謝。好了,本文到此結束,祝大家生生活愉快!再見。
參考
一起寫一個JSON解析器
http://www.cnblogs.com/absfree/p/5502705.html
如何編寫一個JSON解析器
https://www.liaoxuefeng.com/article/994977272296736
介紹JSON
http://json.org/json-zh.html
寫一個 JSON、XML 或 YAML 的 Parser 的思路是什么?
www.zhihu.com/question/24640264/answer/80500016
在開發工作中,我們可能會碰到這樣的需求:需要將某個對象內容彈窗顯示或者保存在文件中,這時候如果你直接彈窗的話,很可能就是下面這樣的:
因為很多接口它對參數有要求,比如只能是字符串之類的。
這時候,就需要我們將對象轉換為字符串進行輸出,JSON.stringify() 方法就可以幫我們實現將對象轉為字符串的過程。
JSON.stringify() 方法將一個 JavaScript 對象或值轉換為 JSON 字符串,如果指定了一個 replacer 函數,則可以選擇性地替換值,或者指定的 replacer 是數組,則可選擇性地僅包含數組指定的屬性。
JSON.stringify(value[, replacer [, space]])
一個表示給定值的JSON字符串。
console.log(JSON.stringify({name: "obj"}))
// '{"name": "obj"}'
replacer 參數可以是一個函數或者一個數組。作為函數,它有兩個參數,鍵(key)和值(value),它們都會被序列化。
在開始時, replacer 函數會被傳入一個空字符串作為 key 值,代表著要被 stringify 的這個對象。隨后每個對象或數組上的屬性會被依次傳入。
函數應當返回JSON字符串中的value, 如下所示:
有以下對象:
const data=[
{
name: "person1",
sex: 0,
age: 18,
isStudent: true
},
{
name: "person2",
sex: 1,
age: 25,
isStudent: false
},
{
name: "person3",
sex: 0,
age: 15,
isStudent: true
}
]
接下來我們針對這個對象做各種需求實現。
const res=JSON.stringify(data, ["name", "sex"])
console.log(res);
// `[{"name":"person1","sex":0},{"name":"person2","sex":1},{"name":"person3","sex":0}]`
JSON.stringify() 提供了分離出自己需要的那部分數據。
const res=JSON.stringify(data, (key, value)=> {
if (key=='sex') {
return ["女", '男'][value];
}
return value;
})
console.log(res);
// `[{"name":"person1","sex":"女","age":18,"isStudent":true},{"name":"person2","sex":"男","age":25,"isStudent":false},{"name":"person3","sex":"女","age":15,"isStudent":true}]`
JSON.stringify() 提供了回調函數做一個映射關系。
const res=JSON.stringify(data, ["name", "sex"],4)
console.log(res);
輸出如下:
這里使用了 4 個空格作為層級縮進。
注意:使用 “\t” 得到的結果和使用 4 個空格得到的結果看起來很像,但實際不是一回事。
JSON.stringify() 方法可以通過參數控制輸出的數據和格式,靈活應用它會大大提高我們的工作效率。
學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!
我是〖編程三昧〗的作者 隱逸王,我的公眾號是『編程三昧』,歡迎關注,希望大家多多指教!
你來,懷揣期望,我有墨香相迎! 你歸,無論得失,唯以余韻相贈!
知識與技能并重,內力和外功兼修,理論和實踐兩手都要抓、兩手都要硬!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。