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