鴻蒙應用開發,比 React 體驗更好
一、pre
在閱讀這篇文章之前,必須要同步一個觀念就是:重視語義化是一個頂尖程序員的必備素養,因此很多時候你會聽到別人吐槽命名太難,其實是因為他的習慣非常好,更希望找到完全契合場景的命名,這本身就是一件費腦子的事情。如果這個觀點我們無法達成一致,那么本文的所有說法可能對你來說就顯得比較可笑。
二、痛點
一直以來,使用 HTML + CSS 來表達 UI 結構,都有一個若隱若現的痛點。痛點來源主要體現在 DOM 結構的語義表現力不足。
例如這樣一段代碼,我們能夠很清晰的知道 DOM 結構是怎么樣的,但是其具體的布局結構方式和特性就不知道了。
<div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
盡管 HTML 也新增了許多語義化標簽來彌補這種語義表現力缺失的問題,但是由于大家的應用場景是在是很難和這些語義化標簽非常契合的對應上,從而導致了語義化標簽的使用并不是很廣泛,到目前為止,語義化標簽都處于一個很尷尬的境地。
<div>
<header></header>
<nav></nav>
<section></section>
<footer></footer>
</div>
導致語義化標簽被廣大開發者冷淡的很大一部分原因,是因為 HTML 設計的這幾個語義化標簽確實是不太匹配日常開發的大多數具體場景。
好在我們在使用 React/Vue/Angular 開發項目時,可以通過自定義組件來增強某一個部分的語義表達。
例如在 React 中,我們編寫一個分頁列表,你一眼就能看出來我的頁面結構長什么樣。
<RefreshScrollView>
<Filter />
<Pagination />
</RefreshScrollView>
這已經是 UI 表述的最佳實踐。但是我們也不是只看最外面這一層,當你深入到更底層的邏輯時,最后看到的還是 div,語義表現力不足的事實總是存在的。
因此為了解決這個問題,在 antd 等優秀的開源框架中,為了增強組件的語義表現力,會提供 Row Cloumn Flex Grid 等容器組件供開發者使用。
這其實是 UI 表達的最佳實踐。但是 antd 等框架對于這種思路的踐行并不是非常徹底。他沒有非常明確的建議說,我們不要使用 div 了,所以許多開發者就算使用了這種方式,也不是把它當成最佳實踐來使用,很多時候只是為了少些兩行 css 代碼才使用他們。
很多時候也源自于 UI 組件庫本身也沒有想要去做一個大而全的布局思維重構,從而導致 antd 雖然提出了這個方向和構思,但是普通開發者也不一定能領會到。
HTML + CSS 語義表現力缺失還體現在結構和樣式分離。有很大一部分開發者并不喜歡寫完結構之后,還要重新去另外的文件給他補充樣式。并且這樣方式在維護的時候也并不友好,隱性的規則讓樣式的最終結果變得撲朔迷離。
最明顯的一點就是默認的樣式繼承規則和樣式優先級,這東西讓許多人在調樣式的時候非常難受。
所以很多后端來寫前端,可能他什么都能搞定,就是搞不定 css 樣式,這可太難了,哈哈哈哈。
許多開發者都意識到了這種痛點。因此 css in js 的方案層出不窮。原子 css 又再次重新火了起來。不過在根源上由于 HTML 文檔流的設計不夠簡潔,視覺格式化模型中涉及到的概念太多,因此最終在使用上依然會有不小的理解成本與麻煩。
例如大家在使用原子化 css 的時候,很容易會出現一個元素上套用了太多 class 的情況,反而導致可讀性極大的降低。
<!--隨意搞的一個簡易案例來說明這個問題-->
<button
class="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600 text-sm text-white font-mono font-light py-2 px-4 border-2 rounded border-blue-200"
>
Button
</button>
因此我們需要思考新的方案來解決這種場景,例如使用一個變量名來復用這些樣式。
const btn = 'blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600 text-sm text-white font-mono font-light py-2 px-4 border-2 rounded border-blue-200'
<button class={'btn'}>
Button
</button>
這些理念都非常好。但是原子化 CSS 畢竟不是直接編寫樣式,他的適用場景會受到很大的限制。
并且當我們在思考如何復用原子化 CSS 的時候,其實就表示,開發者確實在期待一套完整的,全新的 UI 布局表述方式。
這就是鴻蒙應用開發中, ArkUI 的布局思路。
在設計上,arkUI 充分吸收了 HTML 等客戶端方案發展這么多年的經驗教訓,在設計上完全摒棄了文檔流的概念,轉而強調容器的概念。為了應對不同的場景,arkUI 目前已經支持了 26 種容器組件。因為其明確的語義化,學習成本也非常低,例如如下容器組件,我們一看就能知道這是用來干嘛的。
Row
Column
Flex
Grid
List
Scroll
Swiper
Tabs
Refresh
...
并且布局方式到底是什么情況,由容器組件說了算,而不是子元素的類型說了算。
// 表示從左到右布局
Row() {
Text('hello world')
Text('hello world')
Text('hello world')
}
// 表示從上到下布局
Column() {
Text('hello world')
Text('hello world')
Text('hello world')
}
除了在語義化上非常重視之外,arkUI 并不支持結構與樣式分離,而是把設置樣式的行為當成一個 set
方法,支持一種鏈式調用的方式來做到樣式與結構合并的最終結果。
Text('width: 10px')
.fontSize('12fp')
.color('#333')
.border({
width: '10px',
color: Color.Red,
style: BorderStyle.Dotted,
radius: 15
})
這其實是原子化 css 的進化版。如果你覺得原子化 CSS 真香,那么這種方式的好處你也一定能夠快速理解到。
而且他比原子化 CSS 學習和記憶成本更低,更靈活,可以說是原子化 CSS 的理想化實現。
ArkUI 在設計上,還引入了一個風險較大的設定:樣式后置。
這個最開始是在 Swift UI 中出現,可能許多前端開發都沒見過。
Column() {
Text('hello text')
.fontSize('12fp')
.fontColor(Color.Black)
.fontStyle(FontStyle.Italic)
}
.width(20)
.height(20)
.border({
width: 10,
color: Color.Blue
})
之所以說他風大,是因為這種書寫方式大家都沒見過,可能會容易給人的第一感覺就是:什么玩意兒 ...
哪怕是在 Flutter 的設計中,也是可前置可后置,然后不管是文檔案例,還是大家在開發中,其實也是讓樣式前置。
// 偽代碼
Widget build(BuildContext context) {
return new Container(
width: 20,
height: 20,
...
child: new Text()
)
}
我剛開始在學習 Swift 使用的時候也會擔心這種樣式后置的方式會讓樣式堆在一起比較難受,但是用了一段時間之后發現,真香!
我們來看一下這樣一段代碼。
Column() {
Text(`最新值:${this.counter}`)
Column().block()
Column().block()
Column().block()
.onClick(() => {
this.counter ++
})
}
.margin(10)
.border({width: 4})
.width('50%')
.height('400lpx')
.justifyContent(FlexAlign.SpaceEvenly)
.block 是樣式的復用。
@Extend(Column) function block() {
.width(40)
.height(40)
.backgroundColor(Color.Orange)
.border({
width: 2,
color: Color.Red
})
}
之所以我覺得真香的原因是因為我們在開發過程中,其實子元素的樣式重復非常多,因此我們會考慮將子元素的樣式封裝起來,用一些方式來復用它。
這樣,當我們將樣式后置之后,雖然我們依然對父元素添加了一串樣式,但是前面一部分的代碼結構就依然非常簡潔。
以前在剛開始接觸學習 Flutter 的時候,也覺得 Flutter 的 UI 表現形式太糟糕太復雜了,為什么不學著 JSX 那樣搞簡單一點,并且其他人的這個類似想法還在 github 上有非常激烈的探討。
直到后來我才理解到,這種注重語義化和容器的 UI 表達方式,可能比 JSX 更好,這才是最佳實踐。
除此之外,這種聲明式語法的編譯速度會比 JSX 更快,性能上會更好。
三、總結
鴻蒙應用開發的 ArkUI,和基于 HTML + CSS 的 React 相比,能夠更方便的使用語義化,提倡樣式與結構合并,并在 UI 設計上,簡化了視覺格式化模型,注重容器特性,學習理解成本得到了極大的降低,并且基于 set 的思維方式鏈式調用樣式,大膽的將樣式后置,在我個人的主觀感受里,這是一種比 React,比 Flutter 更舒適的開發體驗。
大多數前端開發多半都有一個壞習慣,寫點代碼就想看看布局現在已經長什么樣了,這樣其實挺影響開發效率的。你可能需要頻繁的切換或者必須要一個外接顯示器隨時預覽,反正我就是這樣。也不知道大家有沒有... 想想這是什么原因造成的吧。