我的服務端JS文件合並工具


  靜態資源合並,老生常談的話題了,目的就是減少http請求數,至於為什么要減少http請求大家應該都有所了解,這里就不多贅述,近期由於項目需要,自行開發了一個文件合並工具,由於源碼在公司研發網絡拿不出來,見諒!這里介紹一下工具的開發過程和大概實現思路,第一次發文到首頁,請兄弟們多多指點。

最終效果,先睹為快^_^

最終合並后的效果 

  

  項目編譯時自動合並所有js文件

  經過測試:解析488個jsp文件,最終將314個js文件合並為77個,用時在一秒以內,因此打包時不會耗費太長時間。

對現有幾個開源合並工具的分析

  起初,本着不重復造輪子的想法,我也曾對現有的一些比較成熟的工具進行過分析:

1、minify:相關介紹

  Minify 是用PHP5開發的應用,它會合並多個CSS或者JavaScript文件,移除一些不必要的空格和注釋,進行gzip壓縮,並且會設置瀏覽器的緩存頭。
合並、壓縮、緩存都有了,功能上基本已經能夠滿足現有要求,但是比較麻煩的是它是用php開發的,我們項目使用的是java,因此還需專門為其搭建一個php運行環境,再分配一個子域名用於內部請求。

2、nginx_concat_module:相關介紹

  淘寶的開源nginx模塊,在url中加入需要合並的文件路徑以逗號隔開,使用兩個問號“??”來觸發文件的合並功能。
  優點:只需要在nginx安裝相關模塊即可,不用對運行環境大動干戈,配置和使用方式簡單。
  缺點:沒有使用nginx的項目就只能干瞪眼了,雖然我們項目有使用nginx,但是服務器屬於運維組維護,要折騰服務器還得通過他們,麻煩;多個文件路徑放到一個url當中,會導致url特別長。
  以上兩個工具還有一個共同點就是,在用戶第一次訪問的時候才做第一次合並,然后才會進行緩存處理,這樣所有頁面的第一個訪問者會有一定的等待時間,並且如果第一次訪問是並發訪問,可能還要加鎖去保證相同的文件只合並一次,這樣又會導致更多用戶等待第一次合並。
  鑒於以上種種原因,我決定自行開發一個基於java環境、預合並與在線合並同步、可壓縮、可緩存、最終訪問的url較為簡短的合並工具。 

設計過程中的一些思考

1)是否所有的文件都需要合並

  對於這個問題我是這么考慮的
  首先,對於大多數頁面都使用並且不易發生變化的公共資源不應當加入合並,比如jquery或者一些全站公共的js等,這些文件訪問率很高,與頁面上的其它資源合並后反而會導致內容的重復下載,不如讓它獨立並使用緩存,減少傳輸量(這里我之前看到網上看到許多說法跟我的更好相反,有些不解,有知道的童鞋能否給本人解惑一下)。
  其次,對於某些本身文件就很大的資源也不應該加入合並,因為與這些文件合並后的最終文件會很大,傳輸時間會很長,很可能反而不如多幾個請求來的更快一些,並且在合並序列中比較大的文件靠后的話還會導致前面一些較小文件代碼延遲執行;經過一定量的測試后發現js文件盡量保持在100K以內比較合適,至於具體的文件判斷沒有用程序去智能判斷,這個需要在開發過程中人工去控制了。
  基於以上考慮,在合並工具中加入了黑名單,在合並過程中遇到黑名單中的時候將其過濾,不進行合並。但是需要注意的一點就是在過濾黑名單時要考慮到文件的依賴順序,如果過濾的文件在合並序列中間的話就需要將合並被切為三段,合並后也是三個文件;在頭或尾的話就是兩個了,使用者可以自行配置,黑名單列表一個需要權衡的問題,還是交給使用者決定吧。

2)關於壓縮

  起初我把壓縮功能加到了合並過程中去,但是后來發現這樣不利於壓縮更細粒度的控制,因此就把它取消了,壓縮功能可以使用YUI的maven插件,在執行完合並以后調用壓縮功能,這樣在壓縮的時候可以更精確的控制哪些文件要壓縮(一些本身不會發生變化並且較大的文件可以人工壓縮好,這樣可以節省打包時的壓縮時間)、是否混淆等等

3)關於緩存與版本控制

  合並后的文件名是根據被合並的文件路徑通過md5生成的字符串, 如果參與合並的文件路徑發生變化,那么合並后的文件名也會隨之變化。  
  對於文件內容變化的識別,之前設計時是考慮是在文件名中加入版本號以及文件的MD5進行處理,這樣內容被修改后再合並的文件名就會發生變化,客戶端會重新加載,后來考慮項目中使用了E-tag,內容變化了以后E-tag可以識別到,無需去做版本控制。如果不使用etag的話就要考慮一下緩存的問題了。 

合並的工作過程

  1. jsp文件中使用自定義標簽配置好需要合並的資源列表(如<focus:static type="text/javascript" src="/script/a.js,/script/b.js,/script/c.js"></focus:static>)  
  2. 項目打包時調用maven插件titan-file-combiner-maven,根據相關配置解析目標路徑下的jsp文件,獲取所有需要合並的資源相對路徑,文件解析工具返回的是一個字符串List,每一條記錄中包含需要合並的多個資源的路徑,以逗號隔開。
  3. 遍歷上一步返回的List,調用合並工具titan-file-combiner進行合並操作,並將合並后的文件輸出到對應的目標路徑下,合並后的文件名根據所合並的文件路徑經過一定的處理后生成。
  4. 在合並完成后還可以調用yahoo的YUIcompressor插件進行壓縮並輸出到項目輸出路徑。
  5. 在用戶訪問相關頁面(即某個jsp)時,自定義標簽<focus:static>再次調用titan-file-combiner(若之前的解析有遺漏,這里會再次進行合並操作)根據src的內容生成合並后的url(這里的url和第2步中的url是一致的,指向的是合並后的文件路徑),然后生成最終的<script>標簽最終形成靜態的html傳送到客戶端。
  6. 頁面在通過最終的url來訪問合並后的資源。 

合並所依賴的資源介紹

        1)jericho-html-3.2.jar:html文件解析器(第三方jar包:用於編譯期解析jsp文件,獲取某個jsp頁面中需要預合並的資源路徑)
        2)titan-file-combiner-1.0.jar:資源合並工具(自主開發:用於遍歷項目路徑下所有的jsp文件,並調用文件解析器進行解析,獲取所有要合並的資源路徑列表,並提供合並功能)
        3)titan-file-combiner-maven-1.0.jar:maven插件(自主開發:用於在項目編譯打包時調用,解析並預合並相關資源)
        4)自定義標簽<focus:static>:自定義標簽(自主開發:用於渲染html時將開發者配置的資源路徑替換為合並后的資源路徑,並生成相應的html標簽)       

部分細節實現

1)對jericho-html-3.2.jar的使用

  jericho-html-3.2.jar不僅可以解析文件中的html標簽,同時還支持對部分服務端語言,如jsp,php等語言的解析,甚至還支持對某個url最終返回的頁面進行解析,以獲取到你想要的元素值(這里我用來解析jsp中自定義標簽的src屬性,以獲取在訪問這個頁面時哪些資源需要進行合並)。

2)文件合並的實現

   文件合並其實很簡單,就是根據傳入的參數去項目路徑下遍歷所有需要合並的文件通過字節流進行讀取到內存,然后根據指定編碼將字節數組轉為字符串,然后進行拼接(拼接時注意在結尾添加"\n"否則可會導致部分代碼被注釋掉),最后將最終的字符串再寫到目標路徑即可。

3)合並后文件名的生成

  這里我采用的是根據合並前傳入的參數(即所有參與合並的資源相對路徑)使用CRC32循環冗余校驗生成最終的校驗值,然后定義一個不含有重復字符的字符串,使用校驗值循環除以字符串的長度取余,使用余數在去字符串中定位獲取對應的字符,最終生成一個字符串。這樣參與合並的資源不同或順序不同最終生成的文件名也會不同,最終生成的文件名為4-6個字符的字符串。

4)使用時的參數配置

  由於從打包發布到用戶訪問可能會有兩個地方進行合並,並且也有兩個地方需要生成合並后的文件名,因此兩個地方的配置需要一致,因此這里的maven插件和自定義標簽的配置我都實現了兩套,一套是通過properties配置文件進行讀取,兩處讀同一份配置文件就可以很方便的保證一致性;另一套就是在調用時自行傳入參數配置,這樣在測試時修改各個參數更方便一些。

5)開發過程的調試問題

   開發過程中由於不需要使用插件進行打包,因此只有自定義標簽會實時進行合並,因此開發時可以在自定義標簽中配置不進行合並,就可以這樣就可以方便的打斷點調試了。
  
  css文件的合並原理與js基本一致,由於項目中css已經使用另一套機制去合並,因此這里就沒有再去實現。
 
  我的合並工具介紹至此,請大家多多指點。
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM