一種渲染和增量更新網(wǎng)頁的方法
【技術(shù)領(lǐng)域】
[0001] 本發(fā)明設(shè)及網(wǎng)頁的擅染和更新方法,尤其是瀏覽器客戶端中的網(wǎng)頁的擅染和更新 方法,屬于網(wǎng)頁擅染技術(shù)領(lǐng)域。
【背景技術(shù)】
[0002] 最近十年,大量的web網(wǎng)站采用客戶端網(wǎng)頁擅染來取代部分甚至全部服務(wù)器端網(wǎng) 頁擅染,目的是減少或消除整頁刷新,提供更快速的用戶體驗(yàn)。例如,在網(wǎng)頁中嵌入的 化vaScript腳本(運(yùn)行于瀏覽器客戶端中)可W通過XMLHttpRequest接口主動向服務(wù)器請 求數(shù)據(jù),然后根據(jù)獲得的數(shù)據(jù)擅染和更新網(wǎng)頁的文檔對象模型(D0M),從而改變呈現(xiàn)給用戶 的界面。幾乎所有的網(wǎng)頁都用(X)HTML語言來實(shí)現(xiàn),所W其文檔對象模型就是HTML D0M,W 下在不至于混淆的情況下就簡稱DOM。
[0003] HTML文檔,W及HTML中可W嵌入的SVG和MathML文檔,都具有樹形的結(jié)構(gòu),樹的節(jié) 點(diǎn)主要分為元素節(jié)點(diǎn)化lement)和文本節(jié)點(diǎn)(Text),其中元素節(jié)點(diǎn)還可W有若干屬性。D0M 就是文檔的樹形結(jié)構(gòu)在內(nèi)存中的一種表示形式。D0M還提供一組應(yīng)用程序編程接口(API)來 查詢和修改文檔。在瀏覽器中,訪問D0M API的語言是化vaScript(在底層,D0M對象是瀏覽 器實(shí)現(xiàn)的對象,通常WC++實(shí)現(xiàn),并非化vaScript對象)。在瀏覽器中,修改D0M后,相應(yīng)的頁 面外觀也會立即做出相應(yīng)的變化。
[0004] 應(yīng)當(dāng)注意的是,在瀏覽器編程環(huán)境中,"屬性"一詞有多重含義:
[0005] (l化TML/XML屬性。如''<a虹ef='http://a.cn'〉"中的虹ef='http://a.cn'。
[0006] (2)CSS屬性。如 "color:reef。
[0007] (3)WebIDL屬性。指D0M API定義的,由瀏覽器實(shí)現(xiàn)的D0M對象的屬性,例如 Element.onclick。
[000引(4)化vaScript對象屬性。指化vaScript語言本身實(shí)現(xiàn)的對象的屬性。在網(wǎng)頁擅染 中,W上幾種屬性都可能設(shè)及到,本發(fā)明會在必要時加 W區(qū)分說明。
[0009] 在瀏覽器客戶端的網(wǎng)頁編程中,"擅染"是指整體地創(chuàng)建、替換D0M樹中較大的部 分,而"更新"是指局部地更新整個D0M樹中的若干較小的部分。兩者在理論上并沒有清晰的 界線,但在實(shí)踐上有重要區(qū)別。單純依賴整體擅染而缺少局部更新的后果包括性能低下、頁 面閃爍、重置滾動條的位置、丟失用戶輸入等,所W理想的網(wǎng)頁的擅染和更新方法應(yīng)該對局 部更新有很好的支持。
[0010] 對于化vaScript腳本如何擅染和更新網(wǎng)頁的D0M,業(yè)界已經(jīng)有了很多種解決方案, 但都有著各種各樣的問題。例如:
[0011] 1.服務(wù)器發(fā)送的數(shù)據(jù)中包含HTML文本片段,客戶端的化vaScript代碼通過在適當(dāng) 的HTML元素上設(shè)置Element. innerHTML屬性來完成擅染。運(yùn)樣做的問題是(1)增加了服務(wù)器 端代碼的復(fù)雜性,限制了客戶端化vaScript代碼的靈活性。(2)不規(guī)范的HTML文本片段可能 引發(fā)擅染錯誤甚至安全漏桐。(3)在HTML文本片段上添加事件監(jiān)聽器仍需通過D0M編程來實(shí) 現(xiàn)。(4)HTML文本片段是一個整體,只能整體替換頁面中的一大塊,難W做到局部更新。
[0012] 2.直接DOM編程。即直接編寫客戶端的化vaScript轉(zhuǎn)換例程,將服務(wù)器端發(fā)送的數(shù) 據(jù)轉(zhuǎn)換為D0M。服務(wù)器端通常發(fā)送JSONQavaScript Object No化tion)格式的數(shù)據(jù)(也有采 用XML的),客戶端將其解析為簡單化vaScript對象。運(yùn)需要大量使用過程式的D0M APKAPI =應(yīng)用程序編程接口),使得代碼冗長和難W維護(hù),對于局部更新就更是復(fù)雜。盡管jQuery 等化vaScript庫可W簡化部分代碼,但問題并沒有被有效地解決。
[0013] 3.使用客戶端模板引擎來擅染??蛻舳四0逡媸莻€化vaScript例程,W模板(通 常是個有格式的字符串)和模板參數(shù)(通常是個簡單化vaScript對象)為輸入,WHTML文本 片段或HTML D0M為輸出。服務(wù)器端可W發(fā)送JSONQavaScript Object No化tion)格式的數(shù) 據(jù),客戶端將其解析為簡單化vaScript對象,作為模板參數(shù)輸出。客戶端模板引擎實(shí)際上是 將前述化vaScript轉(zhuǎn)換例程的一種抽象,可W大大簡化D0M的擅染。但是模板引擎的問題 是:(1)為了提高靈活性,模板字符串通常有有自己的編程結(jié)構(gòu),如分支、循環(huán)、作用域規(guī)則 等,運(yùn)等于要求開發(fā)人員學(xué)習(xí)一種新的語言,且運(yùn)種語言缺乏編程工具的支持,難W被調(diào)試 和除錯,運(yùn)就增大了開發(fā)成本;(2)模板的輸出通常是一個整體,只能整體替換頁面中的一 大塊,難W做到局部更新。
[0014] 4.通過虛擬D0M和差異計算來擅染和更新D0M。虛擬D0M-般是用化vaScript對象 實(shí)現(xiàn)的樹形數(shù)據(jù)結(jié)構(gòu),與實(shí)際的D0M有一一對應(yīng)的關(guān)系(實(shí)際的D0M是瀏覽器實(shí)現(xiàn)的對象,通 常WC++實(shí)現(xiàn))??蛻舳说幕痸aScript代碼,每次從服務(wù)器端獲得數(shù)據(jù)(如JS0N)后,產(chǎn)生出描 述整個界面的虛擬D0M樹,交給化vaScript實(shí)現(xiàn)的擅染例程去產(chǎn)生擅染或更新D0M。初次擅 染時,擅染例程根據(jù)虛擬D0M樹創(chuàng)建實(shí)際D0M樹;后續(xù)的更新中,擅染例程先計算的更新前后 的兩個虛擬D0M樹的差異數(shù)據(jù),然后根據(jù)運(yùn)個差異數(shù)據(jù)去修改實(shí)際D0M。如果差異很小,則被 修改的實(shí)際D0M節(jié)點(diǎn)就只是頁面的一小部分,從而避免前述整體擅染的問題。虛擬D0M和差 異計算是目前比較理想的網(wǎng)頁擅染和更新方法,具體的例子有美國化cebook公司開發(fā)的 React框架化ttps :/7facebook.github. io)等。但目前的一些實(shí)現(xiàn),包括React也存在一些 問題:(1)計算兩個虛擬D0M樹的差異數(shù)據(jù)是比較復(fù)雜的,一方面可能引起性能問題,另一方 面也導(dǎo)致擅染例程的代碼量較大,而運(yùn)會導(dǎo)致頁面的初次加載較慢;(2)虛擬D0M的創(chuàng)建需 要使用特定的例程或者領(lǐng)域特定語言(Domain Specific Language,DSL,對于React就是 JSX),前者的問題是代碼的可讀性不佳,且增加了代碼量,后者的問題與客戶端模板引擎類 似:產(chǎn)生了一口新語言。
【發(fā)明內(nèi)容】
[0015] 為解決W上問題,本發(fā)明旨在提供一種避免了現(xiàn)有虛擬D0M和差異計算的缺點(diǎn)的 基于虛擬D0M的網(wǎng)頁擅染和更新方法。
[0016] 為實(shí)現(xiàn)上述目的,本發(fā)明采用的技術(shù)方案如下:
[0017] -種擅染和更新網(wǎng)頁的方法,包括W下步驟:
[0018] (1)用樹形數(shù)據(jù)結(jié)構(gòu)(稱為虛擬文檔對象模型或虛擬D0M)來表示將要被擅染出來 的網(wǎng)頁的文檔對象模型(D0M),其中D0M節(jié)點(diǎn)的屬性用虛擬D0M的節(jié)點(diǎn)的屬性表示,D0M節(jié)點(diǎn) 的子節(jié)點(diǎn)用虛擬D0M的節(jié)點(diǎn)的子節(jié)點(diǎn)線性表來表示;
[0019] (2)如果對應(yīng)的D0M樹還不存在,則遞歸遍歷上述虛擬D0M,根據(jù)其節(jié)點(diǎn)創(chuàng)建D0M節(jié) 點(diǎn),根據(jù)子節(jié)點(diǎn)線性表創(chuàng)建D0M節(jié)點(diǎn)的子節(jié)點(diǎn),并將D0M子節(jié)點(diǎn)添加到其父節(jié)點(diǎn)中;
[0020] (3)如果對應(yīng)的DOM樹已經(jīng)存在,則同時遞歸遍歷虛擬DOM和DOM樹,對于兩者中位 置對應(yīng)的節(jié)點(diǎn):
[0021] A)如果兩者的節(jié)點(diǎn)名不同,則根據(jù)虛擬D0M節(jié)點(diǎn)重新創(chuàng)建D0M節(jié)點(diǎn)并替換原來的 D0M節(jié)點(diǎn);
[0022] B)如果對應(yīng)位置上的D0M節(jié)點(diǎn)不存在而虛擬D0M節(jié)點(diǎn)存在,則根據(jù)虛擬D0M節(jié)點(diǎn)創(chuàng) 建D0M節(jié)點(diǎn)并插入該位置;
[0023] C)如果對應(yīng)位置上的虛擬D0M節(jié)點(diǎn)不存在而D0M節(jié)點(diǎn)存在,則刪除該D0M節(jié)點(diǎn);
[0024] D)如果兩者的節(jié)點(diǎn)名相同:一方面枚舉兩者的屬性,對同名且等值的屬性不做處 理;對同名不等值的屬性或D0M節(jié)點(diǎn)缺少的屬性,,將虛擬D0M節(jié)點(diǎn)的屬性值寫入D0M節(jié)點(diǎn)中; 對虛擬D0M節(jié)點(diǎn)缺少的屬性,刪除D0M節(jié)點(diǎn)的屬性;另一方面,遞歸遍歷兩者的子節(jié)點(diǎn),繼續(xù) 做上述A-D的操作。
[0025] 進(jìn)一步地,如上所述的擅染和更新網(wǎng)頁的方法,該方法是用化vaScript語言來實(shí) 現(xiàn),且虛擬D0M采用簡單化vaScr ipt對象來表示。
[0026] 進(jìn)一步地,如上所述的擅染和更新網(wǎng)頁的方法,用虛擬D0M節(jié)點(diǎn)的屬性名的前綴來 區(qū)分不同類型的屬性。
[0027] 進(jìn)一步地,如上所述的擅染和更新網(wǎng)頁的方法,為每個通過虛擬D0M創(chuàng)建的D0M元 素節(jié)點(diǎn)建有一個簿記表,記錄上述方法中添加的屬性及其對應(yīng)的值,在第(3-D)步中枚舉 D0M屬性和比較屬性值的操作通過該簿記表來進(jìn)行。
[0028] 進(jìn)一步地,如上所述的擅染和更新網(wǎng)頁的方法,根據(jù)虛擬D0M節(jié)點(diǎn)創(chuàng)建實(shí)際D0M節(jié) 點(diǎn)時,根據(jù)節(jié)點(diǎn)名和父節(jié)點(diǎn)的命名空間來推測其命名空間。
[0029] 本發(fā)明的有益效果在于:(1)沒有單獨(dú)的虛擬D0M差異計算步驟(可W認(rèn)為與D0M樹 的更新合一了),因此D0M的更新算法大大簡化,既減少了代碼量又獲得了較好的性能。(2) 可W直接利用簡單化vaScript對象來表示虛擬D0M結(jié)構(gòu),使代碼具有較好的可讀性,開發(fā)人 員的學(xué)習(xí)成本低,并減少了代碼量。(3)可W方便地處理(X)HTML內(nèi)嵌SVG和MathML的情形 (此時需要區(qū)分命名空間)。(4)使用前綴區(qū)分不同類型的屬性,即避免了屬性名沖突又簡化 了代碼。
【具體實(shí)施方式】
[0030] 下面結(jié)合實(shí)施例對本發(fā)明進(jìn)行詳細(xì)的描述。
[0031] 一、虛擬D0M的形式
[0032] 本發(fā)明中,虛擬D0M采用簡單JavaScript對象(Plain JavaScript Object)來表 示,例如虛擬DOM樹:
[0033]
[0035] 就對應(yīng)于如下運(yùn)個HTML DOM樹:
[0036]
[0037] 可見虛擬DOM樹就是一個簡單化vaScript對象,可W用對象字面量的寫法來創(chuàng)建。 每個對象對應(yīng)一個實(shí)際D0M中的元素,子節(jié)點(diǎn)則通過Kids屬性列出。虛擬D0M的各種屬性的 意義如下:
[003引 (1 )Name:對應(yīng)HTML或SVG或MathML的元素名。
[0039] (2化ids:子節(jié)點(diǎn)列表,是一個數(shù)組,其成員要么也是個D0M樹,要么是字符串;字符 串則對應(yīng)實(shí)際D0M中的文本節(jié)點(diǎn)。
[0040] (3)W'@'開頭的屬性映射到HTML/XML屬性(去掉去掉首字符,下同)。
[0041] (4) W'-'開頭的屬性開頭的屬性映射到CSS屬性。
[0042] (5) 開頭的屬性映射到HTML class屬性的片段,如果其值是"真"則該class 存在,如果其值是"假"則該class不存在。
[0043] (6)其它的屬性,如onclick、value,映射到同名的WeblDL屬性。
[0044] 二、從虛擬D0M樹擅染、更新實(shí)際D0M樹的方法
[0045] "從虛擬D0M樹更新實(shí)際D0M樹"的方法有Ξ個輸入?yún)?shù):(1)虛擬D0M節(jié)點(diǎn)(可W是 字符串或化vaScript對象)。(2)實(shí)際D0M節(jié)點(diǎn)。(3)更新后的實(shí)際父節(jié)點(diǎn);方法的返回值(即 輸出)是更新后的實(shí)際D0M節(jié)點(diǎn)。第(2)、(3)個輸入?yún)?shù)可W省略,運(yùn)樣就相當(dāng)于初次擅染實(shí) 際D0M的情形。該方法的具體過程是:
[0046] SOI:讓"當(dāng)前實(shí)際父節(jié)點(diǎn)"的值是參數(shù)"實(shí)際D0M節(jié)點(diǎn)"的父節(jié)點(diǎn),轉(zhuǎn)到S02;
[0047] S02:如果參數(shù)"虛擬D0M節(jié)點(diǎn)'的類型是字符串,則轉(zhuǎn)到S03,否則轉(zhuǎn)到S04;
[004引S03:如果參數(shù)"實(shí)際D0M節(jié)點(diǎn)'存在且類型是"文本節(jié)點(diǎn)',則設(shè)置"實(shí)際D0M節(jié)點(diǎn)'的 值為參數(shù)"虛擬D0M節(jié)點(diǎn)"的值,返回"實(shí)際D0M節(jié)點(diǎn)",結(jié)束;否則,新建一個文本節(jié)點(diǎn),名為 "新實(shí)際D0M節(jié)點(diǎn)",值為參數(shù)"虛擬D0M節(jié)點(diǎn)"的值(JavaScript代碼的例