Obs.js 是什麼?根據使用者裝置與網路自動切換網站體驗,可用來優化SEO。

網站速度不只取決於主機、快取或圖片壓縮,也取決於使用者當下的裝置狀態。

同一個網站,在桌機光纖網路上可以順暢播放影片、載入動畫與第三方插件;但在手機低電量、省流量模式或網路延遲高的情況下,這些效果反而可能拖慢網站、影響瀏覽體驗,甚至讓使用者直接離開。

這時候,就可以使用類似 Obs.js 的前端偵測方式,讓網站根據使用者的網路速度、延遲、電池狀態、記憶體與 CPU 能力,自動判斷目前應該使用:

rich:完整體驗
cautious:謹慎載入
lite:輕量模式

Obs.js是甚麼?

Obs.js 的主要功能是:

偵測使用者目前的瀏覽環境,然後把結果寫入 window.obs,並在 <html> 標籤加上對應 class,讓網站可以用 CSS 或 JavaScript 自動切換體驗。

舉例來說,如果使用者網路很好、裝置效能高,網站可以顯示影片背景、動畫、輪播、Lottie、聊天插件等完整功能。

但如果使用者網路慢、電量低、開啟省流量模式,網站就可以自動關閉重型功能,只保留主要內容,讓頁面更快、更穩定。

Obs.js 可以偵測哪些資訊?

Obs.js會使用瀏覽器提供的 API 來判斷目前環境。

常見偵測項目包含:

navigator.connection
navigator.getBattery()
navigator.deviceMemory
navigator.hardwareConcurrency

1.偵測網路狀態

透過:navigator.connection可以取得:

saveData
rtt
downlink
downlinkMax

這些欄位代表:

欄位說明
saveData使用者是否開啟省流量模式
rtt網路延遲,單位通常是毫秒
downlink預估下載速度,單位 Mbps
downlinkMax最大下載速度,部分瀏覽器才支援

2.偵測電池狀態

透過:navigator.getBattery() 可以取得:

level
charging

通常可用來判斷:

電量 <= 20%:低電量
電量 <= 5%:嚴重低電量
是否正在充電

當使用者電量很低時,網站可以減少動畫、影片、自動播放與背景特效。

3. 偵測裝置記憶體

透過:navigator.deviceMemory 可以估算裝置記憶體,例如:

navigator.deviceMemory = 4

代表裝置大約有 4GB RAM。

通常可以分成:

very-low:非常低
low:低
medium:中等
high:高

如果使用者裝置記憶體偏低,就不建議載入太多動畫、複雜前端效果或大型 JavaScript 套件。

4. 偵測 CPU 核心數

透過:navigator.hardwareConcurrency可以取得瀏覽器可用的CPU,例如:

navigator.hardwareConcurrency = 8

代表瀏覽器可使用約 8 個邏輯核心。

通常可分成:

low:低
medium:中等
high:高

如果 CPU 能力偏低,可以降低動畫頻率、減少背景運算,或關閉非必要的互動效果。

Obs.js 會在網站產生哪些結果?

Obs.js 執行後,通常會產生兩種結果。

第一種是寫入:

window.obs = {
  dataSaver: false,
  rttCategory: "low",
  downlinkCategory: "high",
  connectionCapability: "strong",
  conservationPreference: "neutral",
  deliveryMode: "rich",
  canShowRichMedia: true,
  shouldAvoidRichMedia: false,
  ramCategory: "high",
  cpuCategory: "high",
  deviceCapability: "strong"
};

第二種是把判斷結果加到 <html> 標籤上。

<html class="
  has-latency-low
  has-bandwidth-high
  has-connection-capability-strong
  has-conservation-preference-neutral
  has-delivery-mode-rich
  has-ram-high
  has-cpu-high
  has-device-capability-strong
">

這樣一來,網站就可以直接用 CSS 控制畫面。

例如:

.has-delivery-mode-lite video {
  display: none !important;
}

.has-device-capability-weak .heavy-animation {
  display: none !important;
}

可以用來控制大型圖片或動畫在低效能裝置的投放,來優化使用者體驗。

三種網站體驗模式:rich、cautious、lite

1. rich 完整模式

代表使用者目前環境良好。

通常符合:

網路速度快
延遲低
沒有開省流量
電量正常
裝置效能較好

這時候可以顯示:

影片背景
大型圖片
動畫效果
Lottie
輪播特效
聊天插件
地圖 iframe
YouTube iframe
高互動元件

2. cautious 謹慎模式

代表使用者環境普通,不算差,但也不適合載入太重。

通常可以保留主要功能,但延後載入或降低資源量。

例如:

圖片改用壓縮版本
動畫減少播放次數
第三方插件延後載入
輪播降低速度
iframe 改成點擊後才載入

3. lite 輕量模式

代表使用者目前環境不適合載入重型資源。

可能是:

網路慢
延遲高
開啟省流量
電量過低
裝置效能較差

這時候建議關閉:

影片背景
自動播放影片
大量動畫
大型輪播
Google Maps iframe
YouTube iframe
重型聊天插件
非必要追蹤工具

lite 模式的目標不是讓網站變陽春,而是讓使用者能更快看到主要內容。

如何使用

掛載JS,並確保該JS優先被載入。

<script>
  /*! Obs.js | (c) Harry Roberts, csswizardry.com | MIT */
;(()=>{const e=document.currentScript,i=window.obs&&window.obs.config||{},n=!1!==i.adaptive;if(n&&(!e||e.src||e.type&&"module"===e.type.toLowerCase())&&!1===/^(localhost|127\.0\.0\.1|::1)$/.test(location.hostname))return void console.warn("[Obs.js] Skipping: must be an inline, classic <script> in <head>.",e?e.src?"src="https://raw.githubusercontent.com/csswizardry/Obs.js/main/+e.src:"type="+e.type:"type=module");const t=document.documentElement,{connection:o}=navigator;window.obs=window.obs||{};const a=!0===i.observeChanges,r=e=>{n&&e.forEach(e=>t.classList.remove(e))},c=e=>{n&&t.classList.add(e)},s=(e,i)=>{n&&t.classList.toggle(e,i)};let l=!1;const d=()=>{const e=window.obs||{},i="number"==typeof e.downlinkBucket?e.downlinkBucket:null;e.connectionCapability="low"===e.rttCategory&&null!=i&&i>=8?"strong":"high"===e.rttCategory||null!=i&&i<=5?"weak":"moderate";const n=!0===e.dataSaver||!0===e.batteryLow||!0===e.batteryCritical;e.conservationPreference=n?"conserve":"neutral";const t="weak"===e.connectionCapability||!0===e.dataSaver||!0===e.batteryCritical;e.deliveryMode="strong"!==e.connectionCapability||t||n?t?"lite":"cautious":"rich",e.canShowRichMedia="lite"!==e.deliveryMode,e.shouldAvoidRichMedia="lite"===e.deliveryMode,r(["strong","moderate","weak"].map(e=>`has-connection-capability-${e}`)),c(`has-connection-capability-${e.connectionCapability}`),r(["conserve","neutral"].map(e=>`has-conservation-preference-${e}`)),c(`has-conservation-preference-${e.conservationPreference}`),r(["rich","cautious","lite"].map(e=>`has-delivery-mode-${e}`)),c(`has-delivery-mode-${e.deliveryMode}`)},w=()=>{if(!o)return;const{saveData:e,rtt:i,downlink:n}=o;window.obs.dataSaver=!!e,s("has-data-saver",!!e);const t=(e=>Number.isFinite(e)?25*Math.ceil(e/25):null)(i);null!=t&&(window.obs.rttBucket=t);const a=(e=>Number.isFinite(e)?e<75?"low":e<=275?"medium":"high":null)(i);a&&(window.obs.rttCategory=a,r(["low","medium","high"].map(e=>`has-latency-${e}`)),c(`has-latency-${a}`));const l=(w=n,Number.isFinite(w)?Math.ceil(w):null);var w;if(null!=l){window.obs.downlinkBucket=l;const e=l<=5?"low":l>=8?"high":"medium";window.obs.downlinkCategory=e,r(["low","medium","high"].map(e=>`has-bandwidth-${e}`)),c(`has-bandwidth-${e}`)}"downlinkMax"in o&&(window.obs.downlinkMax=o.downlinkMax),d()},u=e=>{if(!e)return;const{level:i,charging:n}=e,t=Number.isFinite(i)?i<=.05:null;window.obs.batteryCritical=t;const o=Number.isFinite(i)?i<=.2:null;window.obs.batteryLow=o,r(["critical","low"].map(e=>`has-battery-${e}`)),o&&c("has-battery-low"),t&&c("has-battery-critical");const a=!!n;window.obs.batteryCharging=a,s("has-battery-charging",a),d()},h=()=>{if(!l){if(l=!0,w(),a&&o&&"function"==typeof o.addEventListener&&o.addEventListener("change",w),"getBattery"in navigator&&navigator.getBattery().then(e=>{u(e),a&&"function"==typeof e.addEventListener&&(e.addEventListener("levelchange",()=>u(e)),e.addEventListener("chargingchange",()=>u(e)))}).catch(()=>{}),"deviceMemory"in navigator){const i=Number(navigator.deviceMemory),n=Number.isFinite(i)?i:null;window.obs.ramBucket=n;const t=(e=n,Number.isFinite(e)?e<=1?"very-low":e<=2?"low":e<=4?"medium":"high":null);t&&(window.obs.ramCategory=t,r(["very-low","low","medium","high"].map(e=>`has-ram-${e}`)),c(`has-ram-${t}`))}var e;if("hardwareConcurrency"in navigator){const e=Number(navigator.hardwareConcurrency),i=Number.isFinite(e)?e:null;window.obs.cpuBucket=i;const n=(e=>Number.isFinite(e)?e<=2?"low":e<=5?"medium":"high":null)(i);n&&(window.obs.cpuCategory=n,r(["low","medium","high"].map(e=>`has-cpu-${e}`)),c(`has-cpu-${n}`))}(()=>{const e=window.obs||{},i=e.ramCategory,n=e.cpuCategory;let t="moderate";"high"!==i&&"medium"!==i||"high"!==n?("very-low"===i||"low"===i||"low"===n)&&(t="weak"):t="strong",e.deviceCapability=t,r(["strong","moderate","weak"].map(e=>`has-device-capability-${e}`)),c(`has-device-capability-${t}`)})()}};if("prerendering"in document&&!0===document.prerendering){const e=()=>{document.removeEventListener("visibilitychange",i),h()},i=()=>{"visible"===document.visibilityState&&e()};document.addEventListener("prerenderingchange",e,{once:!0}),document.addEventListener("visibilitychange",i)}else h()})();
//# sourceURL=obs.inline.js
</script>

JavaScript 裡控制:

window.obs.deliveryMode

例如,不想在 lite 模式載入 Tawk.to 聊天插件:

if (window.obs && window.obs.deliveryMode !== "lite") {
  loadTawkTo();
}

或是只有在 rich 模式才載入大型動畫:

if (window.obs && window.obs.deliveryMode === "rich") {
loadHeavyAnimation();
}

也可以判斷是否適合載入影音內容:

if (window.obs && window.obs.canShowRichMedia) {
  console.log("可以載入影片或動畫");
} else {
  console.log("目前應該避免載入重型媒體");
}

CSS裡控制:

在 lite 模式下關閉影片、iframe、重型動畫:

.has-delivery-mode-lite video, 
.has-delivery-mode-lite iframe, 
.has-delivery-mode-lite .heavy-animation,
.has-delivery-mode-lite .lottie-box,
.has-delivery-mode-lite .hero-video {
  display: none !important;
}

低效能裝置減少動畫:

.has-device-capability-weak * {
  animation: none !important;
  transition-duration: 0.01ms !important;
}

省流量或低電量時,隱藏背景影片:

.has-conservation-preference-conserve .hero-video {
  display: none !important;
}

網路狀態良好時,顯示高品質內容:

.has-delivery-mode-rich .rich-media {
  display: block;
}

使用時要注意什麼?

1. 不是所有瀏覽器都支援完整偵測

例如:

Safari 不一定完整支援 navigator.connection
Firefox 支援度也有限
Battery API 在許多瀏覽器受到限制

所以這段程式不能保證每個瀏覽器都取得完整資訊。

但沒有取得資料時,程式會使用預設判斷,不會導致網站壞掉。


2. 不要把它當成安全判斷

這些資料都來自瀏覽器端,不能拿來做安全驗證。

適合用途是:

前端體驗優化
效能調整
資源載入判斷

不適合用途是:

會員權限判斷
付款驗證
防攻擊判斷
後端安全控制

3. 不建議拿來追蹤使用者

這段程式本身沒有把資料送出去,也沒有 fetch()XMLHttpRequestsendBeacon() 之類的傳送行為。

但它取得的裝置資訊,例如記憶體、CPU、網路狀態,仍然可能被視為瀏覽器指紋的一部分。

因此建議只拿來做網站體驗優化,不要搭配第三方追蹤做個人識別。

官網:GitHub – csswizardry/Obs.js: Context-aware web performance for everyone · GitHub

贊助商連結

發佈留言