Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
前面的章節(jié)中,我們創(chuàng)建了wordpress主題trans的公共模板:header.php頭部模板、sidebar.php側邊欄模板、footer.php底部模板,這樣就更加方便了trans主題的其它動態(tài)模板的開發(fā)。就比如,今天我們要開發(fā)的模板——文章列表頁模板,就不需要再去修改頭部、側邊欄、底部的代碼了。好了,閑話不多說,我們直接進入到今天的主題當中,怎樣添加trans主題的文章列表頁模板?
第一步:創(chuàng)建列表頁模板文件。
在trans主題目錄下創(chuàng)建一個列表頁模板的文件——archive.php。wordpress程序默認的文章列表頁模板的名字必須是:archive 或 categoty,也就是說,可以是archive.php,也可以是category.php,我們這里用archive.php。
第二步:引入頭部模板。
用sublime等編輯器打開trans主題的靜態(tài)模板list.html,把你的代碼復制到archive.php文件中。然后,在代碼找到</header>這句代碼,從<!doctype html>與</header>之間的所有代碼全部刪除,因為這段代碼,我們已經可以使用公共模板——header.php來代替了。
刪除后,我們再來引入header.php這個頭部模板:
< ?php get_header(); ?>
這時,我們可以正常打開列表頁了。但是有一個小問題:標題顯示的不是文章分類的名稱,仍然是網站的名稱。而我們需要的效果是,在列表頁時,我們需要顯示的是文章分類目錄的名稱。所以,我們要在header.php文件中的<title></title>標簽中修改一個調用代碼,
原代碼是:
< title>< ?php echo get_bloginfo("name"); ?></title>
修改成如下代碼:
< title>< ?php if(is_home()){echo get_bloginfo("name"); }else{if(is_category()){$cat_c = get_the_category();echo $cat_c[0]->cat_name; echo " - "; bloginfo("name");}else{the_title(); echo " - "; bloginfo("name");} } ?></title>
這段代碼的意思是:如果是網站的首頁,就顯示網站的名稱;如果是列表頁,就顯示為“分類目錄名稱 + 網站名稱”;否則就顯示“文章標題+網站名稱”。
第三步:引入右側邊欄模板。
在archive.php中找到< div class="c_right">標簽中的所有代碼,刪除掉,然后,在當前位置上,引入公共模板sidebar.php側邊欄模板,代碼如下:
< ?php get_sidebar(); ?>
這樣,我們就不需要再去修改原< div class="c_right">標簽內的代碼了,而只需直接把sidebar.php拿來用就可以了。
第四步:引入底部模板。
同上,在archive.php代碼中找到< footer>標簽,然后,把< footer>及它后面的所有代碼全部刪除掉,現(xiàn)在不需要了,因為,我們有footer.php底部公共模板了。刪除掉后,我們在原位置上直接引入這個footer.php底部模板,代碼如下:
< ?php get_footer(); ?>
在引入完頭部、側邊欄、底部的公共模板后,我們再來看看archive.php的模板代碼,如下圖:
然后,我們只需要修改archive.php模板代碼中的 < div class="c_left">標簽內部的代碼就可以了。
第五步:修改左側主體部分。
其實,我們查看trans主題的靜態(tài)代碼的效果時,我們可以看到,列表頁與首頁基本上是一樣的,只是左側的頂部多了一個面包屑導航。所以,我們只需要修改< div class="c_left">標簽里的代碼就可以了(如上圖所示)。我們可以先把archive.php代碼中的< div class="c_left">里的代碼全部刪除掉,然后把首頁模板index.php代碼中的< div class="c_left">所有代碼全部復制下來,粘貼到archive.php中。這段代碼包括了左側的文章列表以及分頁按鈕。這樣,archive.php的左側主體部分基本弄好。
第六步:添加面包屑導航。
我們在archive.php模板的< div class="c_left"> 標簽中的< div class="left_bottom">標簽的上方,添加發(fā)下代碼:
< div class="left_top">< ul>< li><span class="dashicons-before dashicons-admin-generic"></span><?php echo $cat_c[0]->cat_name; //獲取當前分類名 ?></li>< li>< a href="< ?php bloginfo("siteurl"); ?>">< span class="dashicons-before dashicons-admin-home"></span>首頁</a> > < ?php the_category(","); ?></li></ul>< ul>< ?php $cat_desc = $cat_c[0]->category_description; //獲取當前分類的描述。if($cat_desc){echo $cat_desc; }else{echo $cat_c[0]->cat_name; //獲取當前分類名}?></ul></div>
這段代碼中,我們又用到了幾個wordpress的函數:
the_category(","):獲取當前分類導航;$cat_c = get_the_category():獲取當前分類的信息,并賦值給$cat_c這個變量
面包屑導航的主要作用,就是讓用戶可以看到當前頁面所在的個體位置。而且,可以通過點擊面包屑導航中的相關鏈接,可以跳轉到鏈接頁面,如:返回首頁(如下圖)。
?通過上面的幾步,我們就完成了wordpress主題trans的文章列表頁動態(tài)模板的修改。嗯,是不是有一個小小的發(fā)現(xiàn):修改trans主題的列表頁模板要比修改首頁模板index.php所花的時間要少得多。是的,因為在修改index.php首頁模板時,我們需要所有的代碼,而修改archive.php模板時,我們只需要把公共模板拿來直接用就可以了,省去了大量的時間,這就是公共模板的魅力之所在。好了,本節(jié)就介紹到這里,如有疑問,歡迎點評。
作為一個內容類應用,看新聞讀資訊一直是頭條用戶的核心需求,頁面的打開速度直接關系到用戶使用頭條的核心體驗,在頭條中,為了更多的承載足夠豐富的樣式和邏輯下保持多端體驗的統(tǒng)一,詳情頁的內容我們是通過 WebView 來承載的,但 WebView 本身的性能相比 Native 來說比較差,因此,技術團隊一直致力于優(yōu)化詳情頁的加載速度。
經過不斷的優(yōu)化,目前中詳情頁在線上的打開體驗,從肉眼上基本已經感知不到加載過程。在接下來這篇文章里,我們會逐步拆解和介紹我們對詳情頁加載優(yōu)化的思路和實踐。
先讓我們來看看優(yōu)化前后的效果吧~
詳情頁加載體驗優(yōu)化前
詳情頁加載體驗優(yōu)化后
當我們開始著手優(yōu)化頁面加載速度之前,我們需要明確一個問題,怎樣才是用戶真正體驗到的頁面加載時間。
首先我們可以看下面這個公式:
頁面加載時間 = 頁面加載完成時間 - 頁面開始加載時間
頁面開始加載時間很好確定,當用戶點擊了 Feed 上的卡片,我們就可以認為頁面開始加載了。
問題是怎么定義頁面加載完成了呢?從客戶端的角度上看,無論是 iOS 還是 Android,WebView 都提供了一個 loadFinsih 的回調,但在實際應用中我們發(fā)現(xiàn),loadFinish 回調并不能反應用戶的真實體驗。
一般來說,WebView 渲染需要經過下面幾個步驟
而 loadFinish 實際上是在頁面加載完畢階段,而 DOM 構建完成時頁面結構就已經基本渲染完成,所以從用戶真實體驗的角度出發(fā),我們以 DOM 結構構建完成(即 domReady)的時間點作為頁面加載完成時間點。
在詳情頁瀏覽過程中,除了頁面加載速度之外,還有一個特別影響用戶體驗的問題,就是頁面的白屏,也是早期的時候用戶反饋比較多的問題,但有很多場景都可能導致詳情頁發(fā)生白屏,比如說網絡異常,WebView 異常等等,需要從用戶體驗的角度出發(fā)去檢測用戶發(fā)生白屏的情況。
目前可以想到最直觀的方案就是對 WebView 進行截圖,遍歷截圖的像素點的顏色值,如果非白屏顏色的顏色點超過一定的閾值,就可以認為不是白屏,目前需要考慮的是這個方案的性能問題和檢測時機。
iOS 中提供了 WebView 快照的接口獲取當前 WebView 渲染的內容,底層采用異步回調的實現(xiàn)方式,API 耗時 10ms 左右,用戶基本無感知。
- (void)takeSnapshotWithConfiguration:(nullable WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void (^)(UIImage * _Nullable snapshotImage, NSError * _Nullable error))completionHandler API_AVAILABLE(ios(11.0));
Android 中系統(tǒng)提供的獲取視圖內容的接口為 getDrawingCache,API 耗時在 40ms 左右,性能損耗也不是特別大。
除了截圖的性能損耗,像素點檢測也是白屏檢測中比較耗時的場景,經過實驗,我們把 WebView 截圖的圖片進行縮小到原圖的 1/6,遍歷檢測圖片的像素點,當非白色的像素點大于 5% 的時候我們就認為是非白屏的情況,可以相對高效檢測準確得出詳情頁是否發(fā)生了白屏。
確定好口徑之后,我們還有需要明確的一個問題是,什么指標可以反映用戶刷頭條時的真實體驗。
最早的時候,我們用的是詳情頁頁面的頁面平均加載時長,也就是頁面加載時長的總和/頁面 pv,在開始的時候這個指標也的確可以明確我們的加載速度。
后來隨著詳情頁的加載優(yōu)化逐漸的深入,會發(fā)現(xiàn)平均加載時長雖然也可以反映詳情頁加載速度,但是因為詳情頁的 pv 比較高,如果使用平均加載速度化很多用戶體驗問題就被平均掉了,并不能反映用戶的真實情況,后面我們又調整了口徑,將指標調整為所有用戶進入詳情頁的 80 分位值,比如說,假如頭條詳情頁加載速度 80 分位值是 1 秒,那么就說明 80% 的情況下用戶進入詳情頁都能在 1s 內加載完成,當然經過我們的不斷優(yōu)化,詳情頁加載的 80 分位值已經能夠達到 0.3s 以內,也就是說,80% 的情況下用戶都能夠在 0.3s 內完成頁面加載。
80分位優(yōu)化數據對比
再后來我們又發(fā)現(xiàn),在頭條詳情頁的量級下面,即使是 80 分位的數據也不能反應許多長尾用戶的真實情況,也為了更極致的追求詳情頁的加載性能,我們最后將詳情頁的性能口徑調整到 95 分位。到目前在我們的努力下,詳情頁的加載速度 95 分位也優(yōu)化了將近 80% 。
我們究竟做了什么呢,接下來會慢慢介紹一下。
如前所述,圖文詳情頁是通過 WebView 來承載的,而 WebView 承載頁面最簡單的做法就是直接通過 URL 去加載一個線上頁面。那么先來一道簡單的面試題,當用戶從瀏覽器輸入一個 URL 到頁面展現(xiàn)發(fā)生了什么呢?
之前已經介紹過頁面的渲染流程了,現(xiàn)在我們再簡單看看用戶從點擊到看到頁面內容需要經歷如下幾個階段:
WebView 加載流程
可以看到,通過線上頁面加載用戶每次進入詳情頁都要通過多次網絡加載,極容易受網絡波動的影響,這種情況下,也無法保證頁面加載的時長和成功率,極大的影響了用戶體驗。
于是在頭條中,我們將新聞中標題和正文內容進行拆分,把頭條詳情頁的公共樣式 CSS 和 邏輯 JS 都抽離出來,形成一個獨立而完備的詳情頁模板,這樣我們就可以把模板直接內置在客戶端中。
同時我們會與前端約定好的 JS 腳本,通過接口將正文內容數據注入頁面完成詳情頁的頁面展示,通過該這種方式我們可以將接口放到客戶端上進行請求。
這樣用戶進入詳情頁的時候只需要本地加載模板,而且加載模板的時候也可以同時并行請求詳情頁數據,再將數據注入進模板中。
那么用戶點擊到看到頁面內容只需要經歷下面的階段:
模板拆分
如上圖所示,我們只需要通過一次網絡加載就可以完成頁面渲染。
還能不能更快一點呢?當然能!
為了提高頁面的加載速度,客戶端通過一定的策略去預加載新聞數據,這樣在理想狀態(tài)下用戶進入頁面時看到頁面時就可以直接使用緩存的數據,用戶在看新聞的時候可以實現(xiàn)完全離線化,避免受到網絡的影響。
本地加載
完全脫離了網絡加載之后,還能再快一點呢?當然還是可以的!
當全流程離線化之后,頁面加載的瓶頸就變成了本地模板的加載時間,所以我們接下來要做的就是優(yōu)化模板加載時間。
對于模板來說,我們做了兩件事情
通過上面優(yōu)化,我們就已經將模板加載時間大大優(yōu)化了,但是還能不能更給力呢?還是可以的。
對于客戶端來說,當模板跟數據分離之后,由于每次用戶點擊的時候加載的都是同一個模板,所以實際上,我們并不需要在用戶進入頁面的時候才去創(chuàng)建 WebView 以及加載模板,我們只需要在合適的時機在后臺創(chuàng)建 WebView,并且提前預熱加載模板,當用戶點擊進入頁面的時候就能使用已經加載好模板的 WebView,直接將詳情頁的內容數據通過 JS 注入到頁面中,前端收到數據后進行頁面渲染即可。
此時用戶進入詳情頁實際就不再需要重新加載模板了,路徑就變成了:
模板預熱
可以看下,通過本地測試的模板預熱和數據預取的優(yōu)化效果,還是比較明顯的,基本上已經達到了上面的截圖中的驗證效果。
本地測試數據
當我們拆分完模板和數據之后,數據上優(yōu)化已經比較明顯,但我們說過,除了驗證數據,我們還需要看線上用戶的真實體驗數據,從 95 分位上看實際數據優(yōu)化卻不是很明顯,所以我們從數據上觀察,用戶預熱模板的命中率只有 53%,還有進一步的提升空間。
模板預熱率
為了盡可能的提高頁面的加載速度,我們希望用戶每次進入詳情頁的時候都能夠使用預熱好模板的 WebView,一般情況下,我們都會使用模板預創(chuàng)建池的手段來優(yōu)化用戶進入詳情頁時的預熱模板命中率。
但其實在很多情況下,WebView 的創(chuàng)建是一個性能開銷比較大的操作,如果我們使用預創(chuàng)建池的方案,那么就會在后臺頻繁創(chuàng)建 WebView,這樣對用戶在 Feed 場景的瀏覽體驗也會有一定的影響。
而且假如用戶頻繁且快速進出詳情頁時,實際場景中用戶也很容易遇到無法命中預熱模板的場景。
這個時候為了優(yōu)化用戶的體驗,如前文所述,我們每次使用的時候都是同一個模板,所以我們使用完當前 WebView 之后,只需要在用戶退出頁面的時候把正文數據清空,這樣進入下一個頁面的時候就能夠繼續(xù)復用這個 WebView 重新注入數據即可。
通過這個手段,我們既避免了頻繁在后臺預創(chuàng)建 WebView 對用戶刷 Feed 體驗的影響,把用戶進入頁面時候的預熱模板命中率從 53% 提升到 92%,優(yōu)化了用戶體驗。
預熱模板命中率
說完我們在模板 WebView 方面的優(yōu)化之后,再介紹一下我們在內容請求上的優(yōu)化。
由于頭條詳情頁請求有以下特點
所以我們將詳情頁內容數據分為靜態(tài)和動態(tài)兩部分,將正文內容、標題、作者欄等用戶主要消費的又基本不變的內容托管到了 CDN 上。
CDN 的全稱是 Content Delivery Network,即內容分發(fā)網絡。其目的是通過在現(xiàn)有的 Internet 中增加一層新的網絡架構,將網站的內容發(fā)布到最接近用戶的網絡“邊緣”,使用戶可以就近取得所需的內容,提高用戶訪問網站的響應速度。CDN 有別于鏡像,因為它比鏡像更智能,或者可以做這樣一個比喻:CDN=更智能的鏡像+緩存+流量導流。因而,CDN 可以明顯提高 Internet 網絡中信息流動的效率。從技術上全面解決由于網絡帶寬小、用戶訪問量大、網點分布不均等問題,提高用戶訪問網站的響應速度。
托管到 CDN 之后,全國各地的用戶可以直接從最佳節(jié)點就獲取到詳情頁數據,也大大節(jié)省了帶寬成本。
1. 多域名備份
為了防止某個 CDN 出現(xiàn)故障,導致服務雪崩,服務端會下發(fā)多個 CDN 鏈接,當用戶訪問當前 CDN 節(jié)點的出異常時,可以快速自動切換到下個 CDN 節(jié)點。
2. 快速超時
一般的超時策略,客戶端在請求時,會遍歷請求 CDN 1、2、3。如果這些 CDN 都請求失敗,則整個網絡請求算作失敗。
但這個方案的問題是,假設請求 CDN 的超時時間是 15s。如果 CDN 1 出現(xiàn)故障,則需要等待 15s 才能切換到 CDN 2 上,這對于詳情頁的加載時間來說是不可接受,如果用戶網絡突然變差,則需要等待 45s 才能返回失敗展示錯誤頁。
基于此我們設計了詳情頁請求的快速動態(tài)超時策略
幾個 case:
可以看到,通過多域名備份和快速超時的策略,即使用戶在網絡或者服務異常的情況下,也能快速恢復或者讓用戶能感知到自身網絡問題。
當我們在模板層和網絡層優(yōu)化到極致的時候,限制我們的就是 WebView 的渲染速度了!
正常來講,正常的內容數據可能是類似 JSON 等數據,客戶端獲取到數據之后,將數據注入給前端,前端還需要將 JSON 數據跟模板進行組裝,拼上 HTML 標簽等模板了之后再呈現(xiàn)到 WebView 渲染,導致前端渲染上耗時也比較久。
為了提高用戶的首屏效率,我們在服務端就會把所有的詳情頁正文的 HTML 數據組裝好,通過將服務端直出內容注入到頁面中時,可以直接給 WebView 進行渲染,對于其他動態(tài)下發(fā)的內容(比如相關搜索),前端再進行二次異步處理,提升用戶效率。
一般來說,我們正文中所有內容都是通過 WebView 渲染,經過上述的優(yōu)化之后,文章的文字部分渲染效率已經很高了,但是實際場景中,很多文章會包含比較多的圖片和視頻場景。
在實際場景中,WebView 渲染非文字內容會存在以下問題:
所以在詳情頁中,我們會將圖片和視頻等非文字內容通過原生組件的方式放在客戶端進行渲染,既可以提高渲染效率,也可以減少不必要的流量消耗。
原生化渲染還有一個好處,圖片越來越成為文章體驗的重要部分,對于多圖文章,我們在 Feed 頁面也可以智能加載詳情頁需要的圖片,增加用戶的文章首屏體驗。
講完了性能優(yōu)化,最后再分享一下我們對詳情頁白屏率的一些優(yōu)化,其實很多用戶反饋白屏問題大部分都可能是由于網絡等問題導致頁面加載時間過長,導致用戶從體驗上觀感是白屏了,這部分通過上面分享的性能優(yōu)化手段已經能夠解決,所以下面只是簡單介紹下一些非網絡原因的白屏問題。
我們通過白屏檢測和上報之后的數據分析之后發(fā)現(xiàn),非網絡原因導致的詳情頁的白屏問題大體是 WebView 加載的問題。
在 iOS 中,我們使用的是系統(tǒng)提供的 WKWebView,WKWebView 是運行在一個獨立進程中的組件,所以當 WKWebView 上占用內存過大時,WKWebView 所在的 WebContent Process 會被系統(tǒng) kill 掉,反映在用戶體驗上就是發(fā)生了白屏。
根據網上的做法,我們可以在 WKWebView 提供的回調 webViewWebContentProcessDidTerminate 函數中通過 reload 方法重新加載當前頁面恢復,但是這種情況只適用于通過 loadRequest 加載的請求,在詳情頁中,由于使用了模板化的 WebView 中,重新 reload 只能重新 reload 模板,并不能正常恢復整個詳情頁,需要客戶端重新加載模板之后再重新注入數據。
另外由于我們有預熱模板的邏輯,所以可能在進入詳情頁的時候使用的 WKWebView 就已經崩潰,在調用 JS 注入數據時會直接返回失敗,失敗時,我們會嘗試重新加載模板。但后來實際操作中發(fā)現(xiàn)一個問題,如果直接調用數據注入的方法,等待系統(tǒng) WebView 返回失敗的回調耗時比較久,所以后續(xù)也調整了數據注入的接口,我們提前在注入的腳本中判斷是否存在數據注入的接口,如果不存在,就說明模板存在問題,直接重試即可。
而在 Android 中,我們采用的是自研內核 WebView,也會遇到一些奇奇怪怪的坑。
當然不管是 iOS 和 Android, WebView 加載的邏輯都比較復雜,有時候怎么重試也無法成功,這個時候我們會直接降級到加載線上的詳情頁,優(yōu)先保證用戶的體驗。
限于篇幅原因,我們還做了很多其他事情,包括請求精簡,push 文章預拉取,數據注入的方式優(yōu)化等等,也做了很多其他的方向的探索,這里不做展開,希望有機會能再分享給大家。
最后總結一下我們在優(yōu)化詳情頁打開速度之后的一些想法
Android Camera 內存問題剖析
字節(jié)跳動自研線上引流回放系統(tǒng)的架構演進
iOS大解密:玄之又玄的KVO
Android '秒' 級編譯速度優(yōu)化
技術團隊不僅致力于在業(yè)務上不斷深耕挖掘,在技術上也一直在追求極致的用戶體驗。
如果你也向往在一個億級 DAU 業(yè)務里成長,也期待在技術上有突飛猛進的提升,歡迎你加入我們。
無論你是 iOS/Android/前端/后端,我們在深圳/北京/廣州等你來,一起做更有挑戰(zhàn)的事!簡歷投遞郵箱: tech@bytedance.com ;郵件標題:姓名-工作年限-頭條技術團隊。
歡迎關注字節(jié)跳動技術團隊
頁切圖過程中div+css命名規(guī)則
標簽屬性命名規(guī)范 (建議)
下劃線連接符命名法“hello_world”
中杠 連接符命名法“hello-world”
駱駝式命名法“helloWorld”
內容:content/container 導航:nav 側欄:sidebar
欄目:column 標志:logo 頁面主體:main
廣告:banner 熱點:hot 新聞:news
下載:download 子導航:subnav 菜單:menu
搜索:search 頁腳:footer 滾動:scroll
版權:copyright 友情鏈接:friendlink 子菜單:submenu
內容:content 標簽頁:tab 文章列表:list
注冊:regsiter 提示信息:msg 小技巧:tips
加入:joinus 欄目標題:title 指南:guild
服務:service 狀態(tài):status 投票:vote
尾:footer 合作伙伴:partner 登錄條:loginbar
頁面外圍控制整體布局寬度:wrapper 左右中:left right center
(二)注釋的寫法:
/* Footer */
內容區(qū)
/* End Footer */
(三)id(具有唯一性)的命名:
(1)頁面結構
容器: container 頁頭:header 內容:content/container
頁面主體:main 頁尾:footer 導航:nav
側欄:sidebar 欄目:column 左右中:left right center
頁面外圍控制整體布局寬度:wrapper
(2)導航
導航:nav
主導航:mainbav
子導航:subnav
頂導航:topnav
邊導航:sidebar
左導航:leftsidebar
右導航:rightsidebar
菜單:menu 子菜單:submenu 標題: title 摘要: summary
(3)功能
標志:logo
廣告:banner
登陸:login
登錄條:loginbar
注冊:regsiter
搜索:search
功能區(qū):shop
標題:title
加入:joinus
狀態(tài):status
按鈕:btn
滾動:scroll
標簽頁:tab
文章列表:list
提示信息:msg
當前的: current
小技巧:tips
圖標: icon
注釋:note
指南:guild
服務:service
熱點:hot
新聞:news
下載:download
投票:vote
合作伙伴:partner
友情鏈接:link
版權:copyright
(四)class的命名:
(1)顏色:使用顏色的名稱或者16進制代碼,如
.red { color: red; }
.f60 { color: #f60; }
.ff8600 { color: #ff8600; }
(2)字體大小,直接使用"font+字體大小"作為名稱,如
.font12px { font-size: 12px; }
.font9pt {font-size: 9pt; }
(3)對齊樣式,使用對齊目標的英文名稱,如
.left { float:left; }
.bottom { float:bottom; }
(4)標題欄樣式,使用"類別+功能"的方式命名,如
.barnews { }
.barproduct { }
注意事項:
1.一律小寫;
2.盡量用英文;
3.不加中杠和下劃線; (我倒是經常加)
4.盡量不縮寫,除非一看就明白的單詞. (偷懶經常縮寫)
主要的 master.css 模塊 module.css 基本共用 base.css
主題 themes.css 專欄 columns.css 打印 print.css
文字 font.css 表單 forms.css 補丁 mend.css
布局,版面 layout.css
*請認真填寫需求信息,我們會在24小時內與您取得聯(lián)系。