校麗麗 楊 雷 吳 玨
(西南科技大學計算機科學與技術(shù)學院 四川綿陽 621010)
隨著Web技術(shù)的快速發(fā)展和移動互聯(lián)網(wǎng)時代的到來[1],越來越多的數(shù)據(jù)處理和業(yè)務邏輯處理開始偏向于前端,使得前端對性能的要求越來越高,傳統(tǒng)的Web技術(shù)已經(jīng)不能滿足用戶需求。傳統(tǒng)的Web技術(shù)操作對象是DOM(Document Object Model)[2-3],即文檔對象模型。DOM以一種獨立于平臺和語言的方式訪問和修改一個文檔的內(nèi)容和結(jié)構(gòu),是表示和處理一個HTML或XML文檔的常用方法[4]。DOM樹是由頁面中的標簽創(chuàng)建而成[5],其結(jié)構(gòu)在一定程度上反映了頁面的內(nèi)容及內(nèi)容之間的關(guān)系[6]。DOM操作繁瑣復雜,當DOM樹結(jié)構(gòu)、元素的幾何屬性、窗口大小以及元素內(nèi)容發(fā)生變化時,會引起DOM的重繪和回流,并且在操作過程中,也會出現(xiàn)以下幾個問題:
(1) 操作復雜、易出錯。DOM樹操作需復雜選擇器,每次操作DOM樹,需清楚記得DOM節(jié)點的id或者class。
(2) 兼容性較差。在動態(tài)網(wǎng)頁中,元素綁定和刪除事件的方法不同,在IE中綁定和刪除事件的方法分別是attachEvent()和detachEvent();而在FireFox中綁定和刪除事件分別為addEventListener()和removeEventListener()。
(3) 渲染速度緩慢,浪費資源。對于一個數(shù)據(jù)很大的列表,若只有一條數(shù)據(jù)變化或一個DOM節(jié)點的尺寸發(fā)生變化的情況,整個DOM樹需重新編譯和渲染,降低其渲染速度,浪費大量資源。
(4) 擴展DOM屬性和數(shù)據(jù)處理方法存在較大的局限性。
針對以上問題,本文提出一種基于數(shù)據(jù)驅(qū)動模型[7]的VirtualDOM樹的構(gòu)建方法,此方法操作對象不是DOM節(jié)點,而是數(shù)據(jù),通過數(shù)據(jù)來控制渲染、指令和表達式擴展DOM功能,增強數(shù)據(jù)驅(qū)動的能力,提高DOM節(jié)點靈活性、開發(fā)效率和應用性能。
數(shù)據(jù)是程序的核心,代碼的實現(xiàn)總是和數(shù)據(jù)結(jié)構(gòu)緊密相關(guān)。在軟件開發(fā)過程中,數(shù)據(jù)驅(qū)動是盡可能將復雜的設計從程序代碼轉(zhuǎn)移至數(shù)據(jù),從而降低程序設計的復雜度。數(shù)據(jù)驅(qū)動模型以數(shù)據(jù)為中心,使視圖處于從屬地位。數(shù)據(jù)驅(qū)動模型要求在編程時將模板代碼和數(shù)據(jù)結(jié)構(gòu)劃分清楚。在改變程序邏輯的時候,只需修改數(shù)據(jù),而不需修改模板代碼,實時反映數(shù)據(jù)的真實變化。相對于傳統(tǒng)的程序邏輯解決問題,使用數(shù)據(jù)驅(qū)動模型具有以下優(yōu)點:
(1) 易修改。當增加新的邏輯時,只需修改數(shù)據(jù),不需修改模板代碼。
(2) 控制復雜度。通過把程序邏輯的復雜度轉(zhuǎn)移至更容易處理的數(shù)據(jù)中來,從而達到控制復雜度的目標。
(3) 隔離變化。當程序邏輯發(fā)生變化,只需修改邏輯代碼,并不影響模板代碼,從而實現(xiàn)隔離。
(4) 實現(xiàn)重用。在開發(fā)過程中,會出現(xiàn)部分邏輯相同,只是處理函數(shù)不同的情況,對相同代碼進行提取,實現(xiàn)代碼重用。
指令的作用是操作DOM元素節(jié)點。將指令綁定在DOM元素節(jié)點上時,會給綁定的DOM元素節(jié)點添加一些特殊行為,因此可將其視作特殊的HTML屬性。表達式是使用雙重大括號(“{{}}”)綁定的JavaScript[8]代碼塊,當表達式的值改變時,將其產(chǎn)生的連帶影響響應式地作用于DOM。
指令一般分為內(nèi)置指令和自定義指令兩種。內(nèi)置指令是前端框架定義好的指令,可直接使用。自定義指令即用戶自己定義的指令,其作用是擴展元素屬性、操作DOM更加簡單。常見的內(nèi)置指令有以下幾種:
(1) 條件判斷指令。根據(jù)其后表達式bool值來判斷插入或刪除元素。
(2) 條件渲染指令。與條件判斷指令類似,都是判斷元素是否顯示。不同的是,條件渲染指令無論其后表達式的值為true或false,元素都會存在于HTML代碼中,而條件判斷指令只有值為true時,其元素才會存在于HTML代碼中。
(3) 循環(huán)指令?;谝粋€數(shù)組渲染一個列表,類似于JavaScript遍歷。
為解決傳統(tǒng)DOM所存在的問題,采用基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹提高編譯、渲染性能以及擴展更多DOM屬性和數(shù)據(jù)處理方法。VirtualDOM是輕量級JavaScript對象,其執(zhí)行在JavaScript引擎[9]中。VirtualDOM的生成給DOM帶來以下幾個優(yōu)點:
(1) 標簽之間的空格在VirtualDOM創(chuàng)建時就被排除,不會帶來意外的空白。
(2) 元素的字符串被自動轉(zhuǎn)義,不會容易導致代碼注入。
(3) 模塊化易實現(xiàn),從而實現(xiàn)代碼共享。
VirtualDOM相對傳統(tǒng)DOM操作有以下幾個優(yōu)點:
(1) 資源浪費少、操作簡單,處理速度高。
(2) 無需擔心兼容性問題。
(3) 通過擴展屬性(指令)和數(shù)據(jù)處理方法(表達式)擴展DOM功能,提高DOM靈活性。
對復雜的DOM結(jié)構(gòu),提供一種方便的工具,進而最小化地操作DOM,是VirtualDOM的核心思想。VirtualDOM樹是在加載數(shù)據(jù)時進行創(chuàng)建,在VirtualDOM樹創(chuàng)建過程中,需要解決以下關(guān)鍵技術(shù):
(1) 初始化數(shù)據(jù)initData(data),其中data表示模型的數(shù)據(jù)。
(2) 判斷視圖類型。若為子視圖,通過get(args)獲取VirtualDOM的父容器,并將其子節(jié)點轉(zhuǎn)移至VirtualDOM;若為父視圖,則獲取body容器,轉(zhuǎn)移其子節(jié)點。
get(args)定義如下:
get(selector,findAll,pView)
其中,selector表示選擇器,findAll表示是否獲取所有,默認為false,pView表示的是父對象。
VirtualDOM樹具體創(chuàng)建步驟如下:
(1) 創(chuàng)建VirtualDOM根節(jié)點;
(2) 檢查是否需要加載模板文件,若存在則加載模板文件;
(3) 把模塊對應的DOM節(jié)點及其子節(jié)點轉(zhuǎn)移到VirtualDOM中;
(4) 把模板文件或模板字符串內(nèi)容統(tǒng)一追加至VirtualDOM中;
(5) 將VirtualDOM綁定到對應模塊。
VirtualDOM樹的編譯和渲染是可視化呈現(xiàn)最重要的一步,其速度直接影響應用性能及用戶體驗。
編譯是利用編譯程序從源語言編寫的源程序產(chǎn)生目標程序的過程。但本文所提出的編譯與C,Java編譯不同,本文所提出的編譯是將JavaScript代碼編譯成前端框架能夠識別的代碼。由上述可知VirtualDOM樹的創(chuàng)建步驟,但是前端框架代碼并不識別,因此需將VirtualDOM進行編譯成前端框架能夠識別的代碼。
在編譯階段,提供統(tǒng)一編譯接口compile,所有的VirtualDOM編譯都需要調(diào)用接口compile。編譯是一個迭代的過程,compile第一步檢查VirtualDOM是否已編譯,若已編譯,則不再編譯,只需進行渲染。
在編譯階段,需要解決以下關(guān)鍵技術(shù):
(1) 處理屬性值為表達式的指令。使用getAttrsByValue(args)獲取屬性列表,將帶有表達式的屬性保存至數(shù)組。
(2) 初始化指令集。初始化指令集接口為initViewDirectives(el),el表示視圖view。
(3) 遍歷待編譯的Element的孩子節(jié)點,進行文本節(jié)點、注釋以及元素節(jié)點的處理。
VirtualDOM編譯流程如圖1所示。
圖1 VirtualDOM編譯流程Fig.1 The compiling process of VirtualDOM
傳統(tǒng)的DOM樹修改數(shù)據(jù)之后都需重新編譯,不僅浪費資源,還會出現(xiàn)卡頓現(xiàn)象,降低用戶體驗效果。采用數(shù)據(jù)驅(qū)動模型以及VirtualDOM,實時反映數(shù)據(jù)的真實變化,簡化開發(fā)流程,提高開發(fā)效率和用戶體驗度。
渲染不僅是將DOM樹中的節(jié)點進行繪制[10],也是頁面進行可視化呈現(xiàn)的最重要的一步,其速度直接影響頁面的可視效果。在渲染時,使用DocumentFragment進行緩存操作,使操作元素進行了“離線”處理,減少頁面回流和重繪。對一個HTML文件,渲染主要分為以下幾步:
(1) 解析HTML并構(gòu)建VirtualDOM樹。瀏覽器采用自上而下的方式解析HTML,在解析的過程中構(gòu)建VirtualDOM樹。
(2) 將CSS樣式應用至VirtualDOM節(jié)點。
(3) 布局。布局是一個遞歸的過程,從根節(jié)點開始,遞歸遍歷子節(jié)點,并計算每個節(jié)點的大小和位置信息。
(4) 繪制。將樹繪制在顯示屏上。對于每個節(jié)點,繪制順序如下:①背景顏色;②背景圖片;③邊框;④子節(jié)點;⑤輪廓。
不同的渲染引擎,其渲染流程不同,以Webkit為例,其主要過程如圖2所示。
圖2 Webkit渲染主要過程Fig.2 The main process of webkit rendering
render(container,data)是渲染提供的接口,其中container表示容器,data是數(shù)據(jù)。在渲染VirtualDOM過程中,需解決以下幾個問題:
(1) 判斷視圖是否存在。若不存在,調(diào)用getView(args)獲取視圖。
(2) 判斷是否可以找到視圖。若找不到視圖,直接返回false,不再進行VirtualDOM的渲染。
(3) 若視圖存在,判斷其孩子節(jié)點的長度是否為0(為0表示沒有被渲染)。若為0,克隆VirtualDOM,調(diào)用renderDOM(args)將克隆后的所有節(jié)點渲染至視圖和所有的子節(jié)點復制到視圖,并設置已渲染的標志。若孩子節(jié)點不為0,則從視圖開始渲染,即發(fā)生變化的數(shù)據(jù)對象對應的DOM節(jié)點及其孩子節(jié)點。數(shù)據(jù)發(fā)生變化主要有兩種情況:①表達式計算;②指令值發(fā)生變化。render渲染流程如圖3所示。
為驗證基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹構(gòu)建方法的高效性,實驗基于傳統(tǒng)DOM操作與基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹的構(gòu)建,并通過不同的DOM節(jié)點數(shù),對頁面的代碼行和編譯、渲染速度進行統(tǒng)計,對比它們的差異。實驗中使用的2款不同渲染引擎的測試瀏覽器詳細配置見表1。
表1 測試瀏覽器配置Table 1 The settings of testing browser
圖3 VirtualDOM渲染流程Fig.3 The rendering process of VirtualDOM
代碼行:實現(xiàn)相同的應用和效果,代碼行越少,開發(fā)者需要做的工作越少,開發(fā)速度越快。
編譯和渲染速度:在不同開發(fā)模式下,對于相同的數(shù)據(jù)源,相同頁面數(shù)據(jù)編譯和渲染完成所需要的時間。速度越快,用戶體驗感越好。
在驗證過程中,實現(xiàn)了注冊(register)、商品列表(commodity)的頁面。為排除網(wǎng)絡因素對應用性能表現(xiàn)的影響,實驗過程中所用的數(shù)據(jù)均為本地JSON[11]格式數(shù)據(jù),圖片和文字信息均保存在本地。圖4、圖5是應用運行在Chrome上的效果截圖,其中圖4是注冊頁面,圖5是商品列表頁面。
圖6是實現(xiàn)2個頁面,采用不同開發(fā)模式,開發(fā)者需要實現(xiàn)的javaScript代碼行數(shù),只包括開發(fā)時需要開發(fā)者自己編寫的代碼,不包含基礎庫的引用。
圖4 注冊頁面運行效果Fig.4 The running effect of registration page
圖5 商品列表頁面運行效果Fig.5 The running effect of commodity page
圖6 開發(fā)代碼量統(tǒng)計Fig.6 The statistics of development code
從圖6可以看出,基于數(shù)據(jù)驅(qū)動模型,開發(fā)者需要實現(xiàn)的代碼量均比原生javaScript少。主要原因在于基于數(shù)據(jù)驅(qū)動模型開發(fā)的應用可以擴展DOM屬性和數(shù)據(jù)處理方法,提高DOM節(jié)點靈活性。對于注冊頁面,需要大量的驗證工作,比如郵箱、電話等。使用validity指令可對其進行直接校驗,無需復雜的javaScript代碼進行選擇并操作。在商品列表頁面,使用repeat指令對數(shù)據(jù)進行循環(huán),而無需使用for,forEach等,減少javaScript代碼量,提高開發(fā)效率。
圖7-圖9顯示了傳統(tǒng)DOM和基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹構(gòu)建兩種模式下開發(fā)的應用,運行在Chrome,加載不同數(shù)據(jù)量的頁面,第一次加載、修改一條數(shù)據(jù)、修改全部數(shù)據(jù)時渲染時間的累積統(tǒng)計對比,數(shù)據(jù)量分別為50條、100條、500條和1 000條。
圖7 第一次加載時間統(tǒng)計Fig.7 The time statistics of first loading
圖8 修改一條數(shù)據(jù)時間統(tǒng)計Fig.8 The time statistics of modifying a piece of data
圖9 數(shù)據(jù)全部修改時間統(tǒng)計Fig.9 The time statistics of modifying all of data
從圖中可以看出,基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹構(gòu)建方法搭建的應用,其渲染時間比原生DOM渲染時間短,其原因是在第一次加載時,VirtualDOM的執(zhí)行在javaScript引擎中,開銷較?。恍薷臄?shù)據(jù)時,使用數(shù)據(jù)驅(qū)動模型,將復雜的邏輯轉(zhuǎn)移至數(shù)據(jù),控制其復雜度,實時反映數(shù)據(jù)的真實變化?;谠膽?,每次進行數(shù)據(jù)修改時,都需要進行選擇、綁定事件、數(shù)據(jù)處理等步驟,所以其修改數(shù)據(jù)的渲染時間都比較長。圖10是基于Chrome,Safari兩款不同渲染引擎對原生DOM和基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹構(gòu)建應用進行渲染所需時間的結(jié)果對比,數(shù)據(jù)量為100條。
圖10 基于Chrome,Safari時間統(tǒng)計Fig.10 The time statistics based on Chrome and Safari
從圖10可以看出,基于同一種方法開發(fā)的應用,在不同渲染引擎的瀏覽器上運行,頁面的渲染所需時間不同,即瀏覽器本事的渲染引擎性能對應用性能有影響。
根據(jù)上述實驗可以得出以下兩點:第一,實現(xiàn)相同的功能,基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹的構(gòu)建方法的開發(fā)代碼量比原生應用少;第二,在相同配置的設備下,基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹構(gòu)建方法的編譯和渲染速度比原生DOM快。在數(shù)據(jù)量較小的情況下,兩者的差別較小。隨著數(shù)據(jù)量的增加,兩者之間的差距逐漸增大。
為降低Web應用開發(fā)難度、提高開發(fā)效率,本文提出一種基于數(shù)據(jù)驅(qū)動模型的VirtualDOM樹的構(gòu)建方法。為實現(xiàn)數(shù)據(jù)雙向綁定,采用數(shù)據(jù)驅(qū)動模型,以實現(xiàn)實時顯示數(shù)據(jù)真實變化。采用輕量級JavaScript對象——VirtualDOM模擬真實DOM,提高頁面編譯和渲染速度,加快開發(fā)速度,縮短項目周期。通過擴展DOM屬性和數(shù)據(jù)處理方法,擴展DOM功能,提高DOM節(jié)點靈活性。實驗結(jié)果表明,與傳統(tǒng)DOM樹相比,基于數(shù)據(jù)模型的VirtualDOM構(gòu)建方法,其渲染速度更快、應用性能更高。但是VirtualDOM的執(zhí)行在JavaScript引擎中會占用部分內(nèi)存,因此,優(yōu)化VirtualDOM對內(nèi)存的占用是下一步研究的重點。
[1]SHAHZAD F. Modern and responsive mobile-enabled web applications [J]. Procedia Computer Science, 2017, 110:410-415.
[2]KNOTT S D, LAIRD J A, WALTERS R J. Dynamic rendering of a document object model, EP2642718[P]. 2013.
[3]FROLIN S O, GUANPENG L, KARTHIK P,et al. Automatic fault localization for client-side JavaScript [J]. Softw. Test. Verif. Reliab.,2016,26(1):28-30.
[4]李景. 基于DOM樹信息抽取的移動網(wǎng)站開發(fā)研究[D]. 山東青島:中國海洋大學, 2011.
[5]丁寶瓊, 謝遠平, 吳瓊. 基于改進DOM樹的網(wǎng)頁去噪聲方法[J]. 計算機應用, 2009, 29(b06):175-177.
[6]羅明宇, 凌捷. 基于DOM樹序列值比對的SQL注入漏洞檢測[J]. 計算機工程與設計, 2015,(2):350-354.
[7]LIJUAN HUANG. Analysis on e-consumers’ purchasing behavior based on data-driving model[J]. Journal of Networks, 2011, 6(12):1713-1718.
[8]HAFIZ M, HASAN S, KING Z, et al. Growing a language: An empirical study on how (and why) developers use some recently-introduced and/or recently-evolving JavaScript features[J]. Journal of Systems & Software, 2016, 121:191-208.
[9]余啟洋, 桑楠, 郭文生. 嵌入式瀏覽器JavaScript引擎的研究與設計[J]. 計算機應用與軟件, 2014,(5):251-255.
[10] KIM D, LEE C, LEE S, et al. Parallelized sub-resource loading for web rendering engine[J]. Journal of Systems Architecture, 2013, 59(9):785-793.
[11] 高靜, 段會川. JSON數(shù)據(jù)傳輸效率研究[J]. 計算機工程與設計, 2011, 32(7):2267-2270.