一直以來(lái),前端工程中的配置大多都是 .js 文件或者 .json 文件,最常見(jiàn)的比如:
這些配置對(duì)前端非常友好,因?yàn)槎际俏覀兪煜さ?JS 對(duì)象結(jié)構(gòu)。一般靜態(tài)化的配置會(huì)選擇 json 文件,而動(dòng)態(tài)化的配置,涉及到引入其他模塊,因此會(huì)選擇 js 文件。
還有現(xiàn)在許多新工具同時(shí)支持多種配置,比如 Eslint,兩種格式的配置任你選擇:
后來(lái)不知道什么時(shí)候,突然出現(xiàn)了一種以 .yaml 或 .yml 為后綴的配置文件。一開(kāi)始以為是某個(gè)程序的專有配置,后來(lái)發(fā)現(xiàn)這個(gè)后綴的文件出現(xiàn)的頻率越來(lái)越高,甚至 Eslint 也支持了第三種格式的配置 .eslintrc.yml。
既然遇到了,那就去探索它!
下面我們從 YAML 的出現(xiàn)背景,使用場(chǎng)景,具體用法,高級(jí)操作四個(gè)方面,看一下這個(gè)流行的現(xiàn)代化配置的神秘之處。
一個(gè)新工具的出現(xiàn)避免不了有兩個(gè)原因:
YAML 這種新工具就屬于后者。其實(shí)在 yaml 出現(xiàn)之前 js+json 用得也不錯(cuò),也沒(méi)什么特別難以處理的問(wèn)題;但是 yaml 出現(xiàn)以后,開(kāi)始覺(jué)得它好亂呀什么東西,后來(lái)了解它后,越用越喜歡,一個(gè)字就是優(yōu)雅。
很多文章說(shuō)選擇 yaml 是因?yàn)?json 的各種問(wèn)題,json 不適合做配置文件,這我覺(jué)得有些言過(guò)其實(shí)了。我更愿意將 yaml 看做是 json 的升級(jí),因?yàn)?yaml 在格式簡(jiǎn)化和體驗(yàn)上表現(xiàn)確實(shí)不錯(cuò),這個(gè)得承認(rèn)。
下面我們對(duì)比 YAML 和 JSON,從兩方面分析:
JSON 比較繁瑣的地方是它嚴(yán)格的格式要求。比如這個(gè)對(duì)象:
{ name: 'ruims'}
在 JSON 中以下寫(xiě)法通通都是錯(cuò)的:
// key 沒(méi)引號(hào)不行{ name: 'ruims'}// key 不是 '' 號(hào)不行{ 'name': 'ruims'}// value 不是 '' 號(hào)不行{ 'name': 'ruims'}
字符串的值必須 k->v 都是 '' 才行:
// 只能這樣{ 'name': 'ruims'}
雖然是統(tǒng)一格式,但是使用上確實(shí)有不便利的地方。比如我在瀏覽器上測(cè)出了接口錯(cuò)誤。然后把參數(shù)拷貝到 Postman 里調(diào)試,這時(shí)就我要手動(dòng)給每個(gè)屬性和值加 '' 號(hào),非常繁瑣。
YAML 則是另辟蹊徑,直接把字符串符號(hào)干掉了。上面對(duì)象的同等 yaml 配置如下:
name: ruims
沒(méi)錯(cuò),就這么簡(jiǎn)單!
除了 '' 號(hào),yaml 覺(jué)得 {} 和 [] 這種符號(hào)也是多余的,不如一起干掉。
于是呢,以這個(gè)對(duì)象數(shù)組為例:
{ 'names': [{ 'name': 'ruims' }, { 'name': 'ruidoc' }]}
轉(zhuǎn)換成 yaml 是這樣的:
names: - name: ruims - name: ruidoc
對(duì)比一下這個(gè)精簡(jiǎn)程度,有什么理由不愛(ài)它?
說(shuō)起增加的部分,最值得一提的,是 YAML 支持了 注釋。
用 JSON 寫(xiě)配置是不能有注釋的,這就意味著我們的配置不會(huì)有備注,配置多了會(huì)非常凌亂,這是最不人性化的地方。
現(xiàn)在 yaml 支持了備注,以后配置可以是這樣的:
# 應(yīng)用名稱name: my_app# 應(yīng)用端口port: 8080
把這種配置丟給新同事,還怕他看不懂配了啥嗎?
除注釋外,還支持配置復(fù)用的相關(guān)功能,這個(gè)后面說(shuō)。
我接觸的第一個(gè) yaml 配置是 Flutter 項(xiàng)目的包管理文件 pubspec.yaml,這個(gè)文件的作用和前端項(xiàng)目中的 package.json 一樣,用于存放一些全局配置和應(yīng)用依賴的包和版本。
看一下它的基本結(jié)構(gòu):
name: flutter_demodescription: A new Flutter project.publish_to: 'none'version: 1.0.0dependencies: cupertino_icons: ^1.0.2dev_dependencies: flutter_lints: ^1.0.0
你看這個(gè)結(jié)構(gòu)和 package.json 是不是基本一致?dependencies 下列出應(yīng)用依賴和版本,dev_dependencies 下的則是開(kāi)發(fā)依賴。
后來(lái)在做 CI/CD 自動(dòng)化部署的時(shí)候,我們用到了 GitHub Action。它需要多個(gè) yaml 文件來(lái)定義不同的工作流,這個(gè)配置可比 flutter 復(fù)雜得多。
其實(shí)不光 GitHub Action,其他流行的類似的構(gòu)建工具如 GitLab CI/CD,circleci,全部都是齊刷刷的 yaml 配置,因此如果你的項(xiàng)目要做 CI/CD 持續(xù)集成,不懂 yaml 語(yǔ)法肯定是不行的。
還有,接觸過(guò) Docker 的同學(xué)肯定知道 Docker Compose,它是 Docker 官方的單機(jī)編排工具,其配置文件 docker-compose.yml 也是妥妥的 yaml 格式?,F(xiàn)在 Docker 正是如日中天的時(shí)候,使用 Docker 必然免不了編排,因此 yaml 語(yǔ)法早晚也要攻克。
上面說(shuō)的這 3 個(gè)案例,幾乎都是現(xiàn)代最新最流行的框架/工具。從它們身上可以看出來(lái),yaml 必然是下一代配置文件的標(biāo)準(zhǔn),并且是前端-后端-運(yùn)維的通用標(biāo)準(zhǔn)。
說(shuō)了這么多,你躍躍欲試了嗎?下面我們?cè)敿?xì)介紹 yaml 語(yǔ)法。
介紹 yaml 語(yǔ)法會(huì)對(duì)比 json 解釋,以便我們快速理解。
先看一下 yaml 的幾個(gè)特點(diǎn):
相比于 JSON 來(lái)說(shuō),最大的區(qū)別是用 縮進(jìn) 來(lái)表示層級(jí),這個(gè)和 Python 非常接近。還有強(qiáng)化的一點(diǎn)是支持了注釋,JSON 默認(rèn)是不支持的(雖然 TS 支持),這也對(duì)配置文件非常重要。
YAML 支持以下幾種數(shù)據(jù)結(jié)構(gòu):
先看對(duì)象,上一個(gè) json 例子:
{ 'id': 1, 'name': '楊成功', 'isman': true}
轉(zhuǎn)換成 yaml:
id: 1name: 楊成功isman: true
對(duì)象是最核心的結(jié)構(gòu),key 值的表示方法是 [key]: ,注意這里冒號(hào)后面有個(gè)空格,一定不能少。value 的值就是一個(gè)純量,且默認(rèn)不需要引號(hào)。
數(shù)組和對(duì)象的結(jié)構(gòu)差不多,區(qū)別是在 key 前用一個(gè) - 符號(hào)標(biāo)識(shí)這個(gè)是數(shù)組項(xiàng)。注意這里也有一個(gè)空格,同樣也不能少。
- hello- world
轉(zhuǎn)換成 JSON 格式如下:
['hello', 'world']
了解了基本的對(duì)象和數(shù)組,我們?cè)賮?lái)看一個(gè)復(fù)雜的結(jié)構(gòu)。
眾所周知,在實(shí)際項(xiàng)目配置中很少有簡(jiǎn)單的對(duì)象或數(shù)組,大多都是對(duì)象和數(shù)組相互嵌套而成。在 js 中我們稱之為對(duì)象數(shù)組,而在 yaml 中我們叫 復(fù)合結(jié)構(gòu)。
比如這樣一個(gè)稍復(fù)雜的 JSON:
{ 'name': '楊成功', 'isman': true, 'age': 25, 'tag': ['陽(yáng)光', '帥氣'], 'address': [ { 'c': '北京', 'a': '海淀區(qū)' }, { 'c': '天津', 'a': '濱海新區(qū)' } ]}
轉(zhuǎn)換成復(fù)合結(jié)構(gòu)的 YAML:
name: 楊成功isman: trueage: 25tag: - 陽(yáng)光 - 帥氣address: - c: 北京 a: 海淀區(qū) - c: 天津 a: 濱海新區(qū)
若你想嘗試更復(fù)雜結(jié)構(gòu)的轉(zhuǎn)換,可以在 這個(gè) 網(wǎng)頁(yè)中在線實(shí)踐。
純量比較簡(jiǎn)單,對(duì)應(yīng)的就是 js 的基本數(shù)據(jù)類型,支持如下:
比較特殊的兩個(gè),null 用 ~ 符號(hào)表示,時(shí)間大多用 2021-12-21 這種格式表示,如:
who: ~date: 2019-09-10
轉(zhuǎn)換成 JS 后:
{ who: null, date: new Date('2019-09-10')}
在 yaml 實(shí)戰(zhàn)過(guò)程中,遇到過(guò)一些特殊場(chǎng)景,可能需要一些特殊的處理。
在 shell 中我們常見(jiàn)到一些參數(shù)很多,然后特別長(zhǎng)的命令,如果命令都寫(xiě)在一行的話可讀性會(huì)非常差。
假設(shè)下面的是一條長(zhǎng)命令:
$ docker run --name my-nginx -d nginx
在 linux 中可以這樣處理:
$ docker run \ --name my-nginx \ -d nginx
就是在每行后加 \ 符號(hào)標(biāo)識(shí)換行。然而在 YAML 中更簡(jiǎn)單,不需要加任何符號(hào),直接換行即可:
cmd: docker run --name my-nginx -d nginx
YAML 默認(rèn)會(huì)把換行符轉(zhuǎn)換成空格,因此轉(zhuǎn)換后 JSON 如下,正是我們需要的:
{ 'cmd': 'docker run --name my-nginx -d nginx' }
然而有時(shí)候,我們的需求是保留換行符,并不是把它轉(zhuǎn)換成空格,又該怎么辦呢?
這個(gè)也簡(jiǎn)單,只需要在首行加一個(gè) | 符號(hào):
cmd: | docker run --name my-nginx -d nginx
轉(zhuǎn)換成 JSON 變成了這樣:
{ 'cmd': 'docker run\n--name my-nginx\n-d nginx' }
獲取配置是指,在 YAML 文件中定義的某個(gè)配置,如何在代碼(JS)里獲???
比如前端在 package.json 里有一個(gè) version 的配置項(xiàng)表示應(yīng)用版本,我們要在代碼中獲取版本,可以這么寫(xiě):
import pack from './package.json'console.log(pack.version)
JSON 是可以直接導(dǎo)入的,YAML 可就不行了,那怎么辦呢?我們分環(huán)境解析:
在瀏覽器中
瀏覽器中代碼用 webapck 打包,因此加一個(gè) loader 即可:
$ yarn add -D yaml-loader
然后配置 loader:
// webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.ya?ml$/, type: 'json', // Required by Webpack v4 use: 'yaml-loader' } ] }}
在組件中使用:
import pack from './package.yaml'console.log(pack.version)
在 Node.js 中
Node.js 環(huán)境下沒(méi)有 Webpack,因此讀取 yaml 配置的方法也不一樣。
首先安裝一個(gè) js-yaml 模塊:
$ yarn add js-yaml
然后通過(guò)模塊提供的方法獲?。?/span>
const yaml = require('js-yaml')const fs = require('fs')const doc = yaml.load(fs.readFileSync('./package.yaml', 'utf8'))console.log(doc.version)
配置項(xiàng)復(fù)用的意思是,對(duì)于定義過(guò)的配置,在后面的配置直接引用,而不是再寫(xiě)一遍,從而達(dá)到復(fù)用的目的。
YAML 中將定義的復(fù)用項(xiàng)稱為錨點(diǎn),用& 標(biāo)識(shí);引用錨點(diǎn)則用 * 標(biāo)識(shí)。
name: &name my_configenv: &env version: 1.0compose: key1: *name key2: *env
對(duì)應(yīng)的 JSON 如下:
{ 'name': 'my_config', 'env': { 'version': 1 }, 'compose': { 'key1': 'my_config', 'key2': { 'version': 1 } }}
但是錨點(diǎn)有個(gè)弊端,就是不能作為 變量 在字符串中使用。比如:
name: &name my_configcompose: key1: *name key2: my name is *name
此時(shí) key2 的值就是普通字符串 _my name is *name_,引用變得無(wú)效了。
其實(shí)在實(shí)際開(kāi)發(fā)中,字符串中使用變量還是很常見(jiàn)的。比如在復(fù)雜的命令中多次使用某個(gè)路徑,這個(gè)時(shí)候這個(gè)路徑就應(yīng)該是一個(gè)變量,在多個(gè)命令中復(fù)用。
GitHub Action 中有這樣的支持,定義一個(gè)環(huán)境變量,然后在其他的地方復(fù)用:
env: NAME: testdescribe: This app is called ${NAME}
這種實(shí)現(xiàn)方式與 webpack 中使用環(huán)境變量類似,在構(gòu)建的時(shí)候?qū)⒆兞刻鎿Q成對(duì)應(yīng)的字符串。
如果本文對(duì)你有啟發(fā),請(qǐng)甩手一個(gè)贊
聯(lián)系客服