我終於知道公司前端為啥不加班了…
摘要:如何快速、高效地構建前端組件乃至頁面是解放前端生產力的重要標誌,掌握抽象組件和頁面模型,理解前端可視化搭建思路,擺脫固有的開發模式,提高前端開發效率,是每位前端應該了解的。
本文分享自華為雲社區《【雲駐共創】前端可視化框架是怎樣煉成的?》,原文作者:華為雲EI專家胡琦。
隨著移動互聯網的迅猛發展和 5G 技術的普及,前端頁面需求量爆炸增長,用戶交互也變得越來越複雜,頁面從零開發的成本也水漲船高。如何快速、高效地構建前端組件乃至頁面是解放前端生產力的重要標誌,掌握抽象組件和頁面模型,理解前端可視化搭建思路,擺脫固有的開發模式,提高前端開發效率,是每位前端應該了解的。
從一個「栗子」說起
在我們前端開發過程中可能經常這樣會遇到這樣的場景:
(某天,產品經理找到前端。) 產品經理:簡單開發一個歡迎頁面,就展示下「歡迎訪問」。 (這沒什麼難度啊,程式碼很好寫了,於是前端不假思索就把程式碼秀出來。) 前端: ``` <template> <div> 歡迎訪問 </div> </template> ``` (一天之後,產品經理覺得這個歡迎詞不夠具體,還想加個主語,於是……) 產品經理:那個歡迎詞簡單改一下,改成「歡迎訪問我們的網站」。 前端: ``` <template> <div> 歡迎訪問我們的網站 </div> </template> ``` (經過程式碼提交、程式碼審核、程式碼合併、部署到測試環境、測試驗證、灰度發布、產品驗證、 發布上線等一系列流程,歡迎詞終於更新了…… 又一天後,產品經理覺得歡迎詞沒有展現出我們的優勢,於是加了個形容詞來描述我們的網站。) 產品經理:那個歡迎詞還是不行,改成「歡迎訪問我們帥氣的網站」。 (迫於產品經理手中40米長大刀的威懾,儘管心中滿是問號,前端還是修改了程式碼。) 前端: ``` <template> <div> 歡迎訪問我們帥氣的網站 </div> </template> ``` (又又一天後,產品經理想到一個「狂拽酷炫」的形容詞,於是……) 產品經理:歡迎詞再改下,改成「歡迎訪問我們狂拽酷炫的網站」。 (此時,前端坐不住了,想到了通過讓產品經理自己維護JSON文件,在頁面中獲取JSON中title欄位進行渲染顯示) 前端: ``` <template> <div> {{title}} </div> </template> <script> import axios from "axios"; export default { data() { return { title: '' } }, created() { this.queryTitle() }, methods: { queryTitle() { axios.get('JSON文件所在路徑').then(res=>{ if(res && res.status === 200) { this.title = res.data.title } }) } } } </script> ``` (又又又一天後,產品經理有了另一個想法,這次不是改歡迎詞了。) 產品經理:我想再加一個選擇框,用來收集用戶資訊。 (此時,一陣風吹過,前端在風中凌亂了……)
當然,既然能想到通過 JSON 去配置,那意味著整個組件、頁面都能配置,只需給到產品經理一個可視化的介面去配置,即可生成 TA 想要的頁面,大大節省了溝通成本、提升了開發效率。這時,可視化框架應運而生。
可視化框架的構成和分工
可視化框架是怎樣的?首先我們先了解一下可視化框架的構成和分工。開發作為可視化框架的維護者,可以提供一個可視化的配置平台;目標受眾,也就是可視化平台的使用者,如上文例子中的產品經理;使用者通過可視化配置平台對頁面進行配置,平台會產出一份配置文件用來描述頁面並上傳到存儲空間;配置文件通過渲染引擎解析最終生成頁面。在這個構成中,作為開發框架的維護者,我們只需要關注可視化配置平台和渲染引擎,當然可視化配置平台也只是將使用者的輸入轉化為渲染引擎能解析的配置文件並上傳,是錦上添花的一環,如果還想應用在更複雜的場景,還可以加上許可權管理,比如 A 只能編輯 A 負責的頁面,B 只能編輯 B 複製的頁面,還可以實現一個工作流的流程編排,比如審核……
可視化框架的核心在於渲染引擎,我們如何得出這個渲染引擎以及渲染引擎是如何運作並渲染最終的頁面,是我們所關心的。如何得出渲染引擎就不得不先說說頁面泛化模型。
頁面泛化模型
為什麼會有頁面泛化模型?因為我們不同的頁面其實是通過渲染引擎解析不同的配置文件渲染出來的,因此渲染引擎需要泛化的能力。比如頁面 A 是表單填寫,那麼就會要求渲染引擎能夠解析表單的配置文件;比如頁面 B 包含卡片式列表,就會要求渲染引擎能夠解析列表的配置文件。頁面泛化模型的建立,主要根據開發者自身的經驗和方法建立滿足自身業務需求的模型即可。像上文案例中,如果產品經理一直只要求修改歡迎詞,那案例中的那段 title 的渲染引擎就足夠滿足需求了。接下來,以華為雲官網頁面為例,拋磚引玉講講頁面泛化模型如何建立。
從上面截圖中我們可以看出這其實是一個從上到下的結構,可以理解為頁面是由一個個樓層構成的,樓層可以理解為body下的一個div塊,或者是一個功能塊。比如圖中從上到下是頁頭、banner、引導跳轉樓層、推薦文章、技術領域樓層。當然我們經常遇到的頁面也不全都是上下結構,還有左右結構。
其實,像這樣左右結構的頁面,您可以理解外層還有一個更大的容器組件包裹,整體形成一個樓層。容器組件只負責樣式,比如上圖中容器組件負責左右布局,如果您對布局還沒有概念,您可按下F12鍵查看上圖布局的程式碼,您會看到其實左邊兩個模組是被classname為edu-index-version-left的div包裹,右邊兩個模組是被classname為edu-index-version-right的div包裹,它們依舊是從上到下的樓層。因此,頁面 = 樓層 + 容器組件。
再來看看樓層解構,以上圖為例,圖中主體部分是一個tab功能組件,通過查看頁面源碼,我們發現兩處紅色框圈出來的文本並沒有設計在tab功能組件里,而是單獨成為文本控制項,這是因為這部分是屬於訂製化的功能,並不是每個tab功能組件都有的,就抽離出來了遵循單一職責原則,藍色框是一個容器組件。再來看看tab功能組件中的卡片,卡片也是由一個一個的控制項組成。因此,樓層=容器組件 +控制項。
可視化框架中的控制項和常見的 UI 框架中的組件一樣,都具有完善且單一的職責,比如表格組件,無論再怎麼加功能都是在表格內部,表格裡面的功能是無法拆分出去的。但可視化框架中的控制項和 UI 框架中的組件還是有區別的,主要不同點在於控制項需要用來被編輯,因此它具有統一的props,所有的控制項要遵循一樣的props,如視圖配置和數據源,在可視化中我們不知道頁面會用到哪些組件,因此需要統一去做循環渲染,不感知控制項的具體屬性。
可視化框架中的容器組件本質上也是組件,主要負責布局,分為基礎容器組件和功能容器組件。基礎容器組件中左右布局一般通過柵格或者 Flex 實現,上下布局通過正常的文本流或者定位以及控制項自身間距等實現。功能容器組件如 tab 容器、輪播組件等。
當考慮用戶交互時,事件在可視化模型中就不得不考慮。可視化模型中的事件需要關注交互的發起者、交互的作用者以及交互的影響方式,而不需要關注交互的種類和交互的具體內容。比如點擊了某個按鈕,步進器的最大最小值從 1、2 變成 3、4,其實是改變了input控制項的min和max屬性;比如表格篩選加了一定條件之後,顯示數據變少了,是因為觸發了數據事件影響了表格的數據源;比如一個開關組件,點擊之後控制控制項的顯示和隱藏……
最終,我們確定的可視化模型就是上圖中總結的點。可視化頁面由控制項 + 容器組件 + 事件組成,控制項的粒度最小,是功能的最小單位;容器組件負責布局,是樣式的集合;事件響應用戶交互傳遞控制項間依賴關係。那控制項、容器組件、事件是怎麼結合的呢?就不得不談談渲染引擎了。
渲染引擎
渲染引擎本質上也是組件,主要功能是渲染當層組件、處理當層組件交互關係、對當層組件狀態進行管理。它不關注子層組件,子層組件由子層容器的渲染引擎渲染,因為每一層組件的配置和數據源不一樣,因此渲染結果也不相同。視圖部分示例程式碼(基於Vue.js)如下:
<template> <div :class="clsPrefix"> <component :is="component._type" v-for="component in viewConfig.components" v-show="showState[component._id]" :ref="component._id" :key="component._id" :viewConfig="component" :dataSource="dataSource[component._id]" @valueChange="valueChangeHandler($event, component._id)" /> </div> </template>
最外層是由div包裹,使用自定義動態組件component的方式定義渲染引擎,帶下劃線的屬性意味著是自定義組件的內置屬性,is決定組件渲染的類型,如button,做到可以不感知當層組件具體內容渲染當層組件;v-for循環當前組件的屬性;v-show控制組件是否顯示,通過showState進行狀態管理;ref建立索引,可以用來做一些高級功能,比如父層容器調用子層容器的方法;viewConfig和dataSource就是上文中提到的控制項中所必需的;@valueChange是組件提交的統一事件。
父層容器和子層容器本質都是渲染引擎,只是樣式不同。比如說一個左右布局的容器組件,相當於在視圖中v-for循環的地方綁定一個class如柵格布局或者flex布局的樣式,如果是柵格布局的話,用戶就要先定義type是柵格組件,然後配置viewConfig中grid屬性之類的,如果是flex布局,定義的type就是flex組件,對齊方式如指定為space-between之類的。上文中示例程式碼就是一個正常的從上到下的布局。
首先外層傳遞兩個關鍵的參數–viewConfig和dataSource,當層容器就會進行配置解析和配置分發。配置解析主要包含屬性映射和生成事件,屬性映射比如控制項本身需要title屬性,而配置文件中可能是叫label屬性,這時我們要將label轉換成title,只不過轉換邏輯不包含在渲染引擎中,只是調用外部封裝好的方法;生成事件則是根據配置生成預設事件並掛載。在配置分發之前,會先將解析好的配置進行初始化父層狀態並存放在父層,主要考慮到viewConfig和dataSource不是同步賦值,需要等待都就緒了才進行分發。
在介紹控制項的時候,提到每個控制項都要提交,因為每個控制項都不能成為其他控制項的一個origin。比如Tip組件,雖然本身可能沒有交互,但可能成為別的控制項的依賴項,子層可能需要獲取內容,子層容器的提交會被收集在父層容器的valueWatch中,在子層容器提交以後,如果狀態變化的話,那父層容器就會根據functionList去循環執行的事件;如果是展示類事件,就會更新父層的showState控制顯示或隱藏,如果是數據類事件,則會改變viewConfig和dataSource,對目標進行一個重新賦值。 總得來說渲染引擎流程就包含以上四個步驟:配置解析、配置分發、收集提交、發起事件。
總結
為了減少頁面開發程式碼量,提升程式碼復用度,我們期望頁面能進行可視化編輯;為了得出一個通過的可視化框架,我們對頁面進行了可視化建模分析,得出幾乎所有的頁面都可以由控制項和容器組件構成,通過泛化的事件處理用戶交互和組件間的級聯關係;根據建模的結果建立了渲染引擎,支撐起整個流程,最終實現可視化。
本文整理自華為雲社區內容共創活動第二期之【線上直播】2.0倍起步?高效完成前端頁面。
查看活動詳情://bbs.huaweicloud.com/forum/thread-111494-1-1.html