透過網頁瀏覽器看到的網頁絕大部分是使用HTML進行控制編排的,由於HTML的語法結構定義較為寬鬆,網頁瀏覽器都有一定程度的容錯率,因此有可能某個100KB的HTML實際上可以等價於不到50KB的HTML,也就是說,多出來的資料量是完全不必要的,可是卻會增加更多的網路流量和時間成本。



HTML Minifier

「HTML Minifier」是筆者開發的套件,可以在產生HTML(串接字串)的同時去進行HTML的壓縮,並且支援<style><script>以及<pre><code><textarea>標籤的特殊處理。採用的HTML壓縮規則如下:

  • 永遠移除ASCII的控制字元(字元範圍0x00-0x08, 0x11-0x1F, 0x7F)。
  • 移除註解。(可選,預設會移除)
  • 移除沒作用的空格(包括空白鍵空格、TAB和換行字元)。
  • 連續的空格(包括空白鍵空格、TAB和換行字元)會被轉成單一的'\x20'或是單一的'\n',如果可以的話。
  • 空的屬性值會被化簡。(例如<input readonly="">會簡化成<input readonly>)
  • 除了以下幾個元素以外,所有元素內部的HTML都會被壓縮:
    • <pre>
    • <textarea>
    • <code>(可選,預設會壓縮)
    • <script>(如果是不支援的type屬性才不壓縮)
    • <style>(如果是不支援的type屬性才不壓縮)
  • <script><style>元素中的JS語法和CSS語法是使用minifier這個crate來壓縮的。

在早期的的版本中,這個套件會嘗試將HTML壓縮成一行(例如<a>1</a>\n /\n <a>2</a> => <a>1</a> / <a>2</a>)。不過這樣做的話,CJ字元就必須要被檢查,否則中\n文會被壓縮成中 文,就會是錯誤的結果。

在3.0.0版本之後,這個套件不再嘗試將HTML壓縮成一行了,如此一來變可以移除UTF-8計算,增進效能。而且這樣做的話,還可以讓這個套件除了能支援ASCII或是UTF-8編碼的輸入文字以外,也能支援其它任意自我同步(self-synchronizing)的編碼。

Crates.io

Cargo.toml

html-minifier = "*"

使用方法

html_minifier這個crate底下的HTMLMinifier結構實體擁有digest方法,可以多次傳入要串接至HTML中的字串,最後再使用get_html方法回傳已壓縮的HTML。也就是說,使用HTMLMinifier時,不需要先把完整的HTML先產生好,再產生出壓縮的HTML。而是可以藉由多次呼叫HTMLMinifier結構實體的digest方法,來串接字串,直接產生出壓縮的HTML。如此一來可以省下許多處理時所需記憶體,又可以加快所需的時間。

use html_minifier::HTMLMinifier;

let mut html_minifier = HTMLMinifier::new();

html_minifier.digest("<!DOCTYPE html>   <html  ").unwrap();
html_minifier.digest("lang=  en >").unwrap();
html_minifier.digest("
<head>
    <meta name=viewport>
</head>
").unwrap();
html_minifier.digest("
<body class=' container   bg-light '>
    <input type='text' value='123   456' readonly=''  />

    123456
    <b>big</b> 789
    ab
    c
    中文
    字
</body>
").unwrap();
html_minifier.digest("</html  >").unwrap();

assert_eq!("<!DOCTYPE html> <html lang=en>
<head>
<meta name=viewport>
</head>
<body class='container bg-light'>
<input type='text' value='123   456' readonly/>
123456
<b>big</b> 789
ab
c
中文
字
</body>
</html>".as_bytes(), html_minifier.get_html());
use html_minifier::HTMLMinifier;

let mut html_minifier = HTMLMinifier::new();

html_minifier.digest("<pre  >   Hello  world!   </pre  >").unwrap();

assert_eq!(b"<pre>   Hello  world!   </pre>", html_minifier.get_html());
use html_minifier::HTMLMinifier;

let mut html_minifier = HTMLMinifier::new();

html_minifier.digest("<script type='  application/javascript '>   alert('Hello!')    ;   </script>").unwrap();

assert_eq!("<script type='application/javascript'>alert('Hello!')</script>".as_bytes(), html_minifier.get_html());

如果希望產生出來的HTML資料是直接被送出,而不是存在記憶體中的話,可以使用HTMLMinifierHelper這個結構體,它提供了一個比較低階的API,允許開發者在呼叫其digest方法時,傳入一個用於輸出的實體。

use html_minifier::HTMLMinifierHelper;

use std::fs::File;
use std::io::Read;

let mut input_file = File::open("tests/data/w3schools.com_tryhow_css_example_website.htm").unwrap();
let mut output_file = File::create("tests/data/index.min.html").unwrap();

let mut buffer = [0u8; 256];

let mut html_minifier_helper = HTMLMinifierHelper::new();

loop {
    let c = input_file.read(&mut buffer).unwrap();

    if c == 0 {
        break;
    }

    html_minifier_helper.digest(&buffer[..c], &mut output_file).unwrap();
}