隨着項目的不斷迭代,咱們的css會不斷變大,但一般頁面上須要用到的樣式並無那麼多,不少樣式是無用的,而若是靠人工去剔除,吃力又容易出錯。css
有痛點就應該去想辦法解決。那麼有沒有辦法經過自動化來把這些無用的樣式剔除呢?答案是確定的,否則就沒有這篇文章了。目前我能找到的有如下兩種方案:html
這種方案很精準,經過篩選有用的選擇器來去掉那些沒有用到的選擇器。換句話來,只保留被用到的樣式,去掉沒用到的樣式。webpack
但這個方案明顯存在缺陷,若是在js裏面對dom的操做(例如對dom添加一個class樣式等操做),這些如何判斷?很顯然,若是是js中引用到了css樣式的話,這種方案是解決不了的。web
那麼怎麼肯定一個選擇器必定不會被用到?從選擇器的類型來看,至少包括下面幾種:瀏覽器
標籤選擇器緩存
class選擇器dom
ID選擇器測試
相鄰選擇器字體
父子選擇器ui
屬性選擇器
...
具體有哪些,能夠參考下面的連接
這麼多類型的選擇器,若是簡單以字符解析js,想在js中肯定用到了某個選擇器,無疑是比較困難的事情。但咱們換個角度來思考,無論選擇器類型多複雜,它們都是由單詞組成的,好比:
h1 => ["h"] .a .d => ["a", "c"] .ab-c => ["ab", "c"] #text => ["text"] .ab-c #text => ["ab", "c", "text"]
所以一個合法的選擇器,咱們能夠看做是一組單詞的集合。
接着,再想一想咱們在html或者js裏面是若是引用這些選擇器的?無非就是:
<div class="ab-c"> <span id="text"></span> </div> $("text").addClass(".a");
那麼對於一個選擇器來講,在html或者js被引用的話,那麼html或者js代碼裏面必定會出現這個選擇器的全部單詞。若是沒有出現或者沒有所有出現的話,證實這個選擇器是沒有被用到的。
好比上面例子中:
選擇器h1
的單詞集合["h"]
在html或者js中並沒出現,所以選擇器h1是無用的。
選擇器.a .d
的單詞集合["a", "d"]
在js中只出現了單詞a
,而沒有出現單詞d
,所以選擇器.a .d
也是無用的。
所以,怎麼肯定一個選擇器必定不會被用到這個問題,就轉化成,如何肯定一個選擇器的單詞集合是不是html或者js代碼中的單詞集合的子集這個問題。
那麼判斷一個選擇器的單詞集合是不是html或者js代碼中的單詞集合的子集,若是是就保留,若是不是就丟棄掉好了。
這種方案經過剔除必定不會用到的選擇器,換句話來講,它只能知道某個選擇器可能被用到,沒法肯定某個選擇器是必定會用的到。
若是有仔細注意上面的例子,就會發現若是咱們的html結構是這樣的,選擇器.ab-c
依舊會被保留下來
<div class="ab"> <span class="c"></span> </div>
也就是說這種方案也是仍是有缺陷的,不能精準地肯定某個選擇器必定會用到,會存在漏剔除的狀況(但毫不會出現誤剔除的狀況)。
在實際應用中,咱們發現這種方案仍是能剔除不少沒必要要的樣式(因此目前測試的案例很少):
所以,從原理和效果上來看,第二種方案來實現剔除css是極不錯的,在實際運用中,採用開源的purifycss (畢竟有現成的,就不必本身造輪子了)。
精簡外部css的時候,須要注意內部是否以相對路徑引用其餘的資源(圖片,字體等)
舉個例子,個人html結構和css引用是這樣的:
<link rel="stylesheet" href="//res.wx.qq.com/wechatgame/static/game/dist/css/style-notification.fb62b095.css"> <div class="bg"></div>
而style-notification.fb62b095.css
這個css是屬於外部的一個css,它的代碼裏用到一張背景:
.bg { background-image: url("../../images/bg.jpg"); }
那這時候我精簡css的話,把這個css轉化成咱們本身的cdn(//wximg.gtimg.com/wxgame/webpack/ddz/mincss/style-notification_fb62b095.css
), 那麼裏面的代碼天然也是:
.bg { background-image: "../../images/bg.jpg"; }
而此時再訪問咱們的頁面的時候,就會發現背景加載不出來了,控制檯顯示圖片404了。緣由很簡單:
../../images/bg.jpg
這個相對路徑對於//res.wx.qq.com/wechatgame/static/game/dist/css/style-notification.fb62b095.css
來講,它的真實路徑是//res.wx.qq.com/wechatgame/static/game/dist/images/bg.jpg
, 而對於//wximg.gtimg.com/wxgame/webpack/ddz/mincss/style-notification_fb62b095.css
來講,它的真實路徑是//wximg.gtimg.com/wxgame/webpack/ddz/mincss/images/bg.jpg
。而咱們在上傳cdn,並無上傳//wximg.gtimg.com/wxgame/webpack/ddz/mincss/images/bg.jpg
,因此就出現圖片加載不出來的狀況。
解決:在精簡css的時候,須要把css裏面的相對路徑替換成絕對路徑
// 將css中的引用的相對路徑轉化爲絕對路徑 function relative2absolute(cssContent, base) { return cssContent.replace(/url\([\"\']?([^\)]+)[\"\']?\)/g, function(input, _url) { if( isRemotePath(_url)) { return input; } return "url(" + url.resolve(base, _url) + ")"; }); }
精簡css後,破壞了通用模塊css的緩存
這實際上是一個取捨問題。把不少通用css放在一個通用模塊裏面,頁面加載的時候,能夠利用瀏覽器緩存加快速度。而若是精簡css的話,意味着每一個頁面所用到的通用模塊都不同,天然就用不了緩存。
解決:不精簡通用模塊css,只精簡具體業務相關的css。個人實現是給link加個標誌min-css="true"
:
<link rel="stylesheet" href="//res.wx.qq.com/wechatgame/static/game/wgui/css/wgui.min.css"> <link rel="stylesheet" href="//res.wx.qq.com/wechatgame/static/game/dist/css/style-notification.fb62b095.css" min-css='true'>
這樣子,只構建精簡帶有標誌位的css,通用模塊的css仍能夠利用緩存,而具體業務相關的css能夠儘量剔除無用的樣式,減小體積。