沒穿方服

首頁

工程告示牌,跟內文沒有直接關係,有一天角落鬆脫了,也開始面子掛不住

使用 cloudflare/next-on-pages 的專案 feeders,上線快 5 個月了。

功能需求不複雜,所以想找低價、好開發、又能達到足夠品質的方案。但實際做下去,開發的不便還是有感,也在網站乏人問津的狀況下就超出了免費額度。

首先談 Next.js 14 App router

我算第一次用,小小意外:

  • 點連結後有一段無反應的時間

    在 server 初始回應之前,瀏覽器是全無反應的,也就是比點擊原生 <a> 回饋更差,這個我很難接受。

    參考 Next.js App Router navigation indicator and the delay after onclick - DEV Community

    文章有一些對策,我後來乾脆裝一個進度條元件,它今年轉生成新專案 BProgress。 這種 UI 本是多此一舉,現在為了遮醜不得不用。

  • 使用 hash 的客戶端導覽 (client side navigation) 沒有處理

    一樣是基於 server 為重的設計,Next.js 對於使用 URL hash(#xxx 部分)做的導覽沒有理會。它不是一個 route,處理上一頁、下一頁時也會漏掉而出問題。框架和原生功能打架。

    類似這個 issue 說的情形:#56112: Server rendered routes with hashes do not work with browser back/forward navigation

    解法是自己監測 hashchange 做反應;另外也要監測 popstate 事件(會發生於上一頁、下一頁),自己比對 URL pathname,需要時補做 router navigation,見 commit bootleq/feeders@c211ff7

然後是 next-on-pages

  • Runtime 與 API、產品線(D1、R2 等)的先天限制

    next-on-pages 只能用 edge runtime,一些 API 也跟 Next.js 的規格不太一樣

    實際障礙例如不能做 ISR、考慮 cache 時要重新認識當時的 cache API(現在看,又不同了)。

    再說到 D1 等 edge 特化的產品(註:沒有強迫一定要用),有些限制像是不支援 transaction 如果在遇到時才發現,恐怕真的無解。

    註:後來出現了 OpenNext 可以在 worker 使用 node.js runtime。

  • worker 限額

    壓縮後超過 1MB 就會超出免費額度(worker 部分,靜態檔不算),我一開始沒注意到,後來要減肥就來不及了(我真的努力過)。
    後來有人提報 1MB 連 starter 專案都不夠用,就在 11 月放寬到 3MB 了。

    CPU time 免費額度只有 10ms,付費則有 30s 也差太多了吧。
    看一些討論是只要有密碼學運算、解析 DOM、載入較大的資料,隨便就會超過 10ms。 我的 app 有做身分認證和載入大顆 JSON,峰值遠遠超過 10ms,或許努力調校可以壓下去,但要長期活在這條線下實在削足適履。

  • git push 部署和 wrangler 上傳只能二選一

    假如想先用 wrangler pages deploy 指令部署,後期再換成跟 github 整合,照文件說法是只能開新的專案。
    Doc: Git integration | Direct Upload

  • 突然不能 deploy 的事件

    曾經發生過 vercel 上游變動,Pages 專案就突然無法 build,也就無法部署新版本的事件,issue #908
    看 PR #909 勉強可以算 vercel 的問題,但確實需要等 cloudflare 這邊救火。

結論

速度和費用表現是真的不錯(雖然無法免費),但底層相容性的東西會帶來多餘工作,也有心理負擔。 適合真正 edge 特化的輕量功能,或小專案以積極冒險的心態進入。

去年十月普及的瀏覽器 text fragment(文字片段)功能,用 #:~:text=文字寫法深度連結到網頁特定位置,很實用,即使網站有轉址也能通,例如 立法院法律條文 https://www.ly.gov.tw/Pages/ashx/LawRedirect.ashx?CODE=03134#:~:text=中華民國104年2月4日公布
點進去會發現網址變了,但還是能把左邊欄「中華民國104年2月4日公布」這段字標亮。

:~: 規格: web.devMDN

不過也發現有些網站用上去沒效果,有些自己寫的網頁竟然也不行!
要是被人發現網頁不友善卻渾然不知,那多丟臉啊。

簡單記一些踩點

  • 目標文字在初次 render 就要出現,所以需要 SSR
    如果是轉圈圈載入資料再顯示的架構大概就沒救了。

  • 顯示之後重繪也會清掉效果,例如 react 能用卻沒用 useMemo 碰上多餘重繪,這關就不過。

  • 主要內容的 HTML 節點最好提早出現,例如我有一些主文放在 <dialog>,原始碼卻放在靠近底部 </body> 的位置,這樣如果目標文字也出現在其他地方就會有問題,會優先抓到別處(較不重要)。

  • 真的改不了的時候,想換條路自己解析網址再模擬「尋找、捲動、標亮」也做不到,因為安全隱私考量,# 開始的部分會被瀏覽器拆掉,不會暴露給程式,例如 location.href 就抓不到。

  • 操作瀏覽器寫自動測試時,我找不到方法讓 headless chrome 啟用這個功能,最後只能用頭跑,或改用 Firefox。

  • 雖然可以用 CSS ::target-text 改標亮部分的樣式,但實測只能動前景、背景色,也不能用漸層之類的值。
    那要改成怎樣才符合使用者預期?我想一般答案是先不要改吧。

開啟方式

  1. 確認 Firefox 版本在 135.0 以上(2025/2/4 釋出)
  2. 進入 about:config,找出 accessibility.typeaheadfind.wrappedSoundURL, 改成「beep」或一個指向 wav 檔案的 file:// URI(例如 file:///C:/%E9%9F%B3%E6%95%88/dorya.wav)即可

說明

Ctrl + F 文字尋找功能「已達頁尾,從頁首重新搜尋」這個狀態叫做 wrapped,目前各瀏覽器處理 wrap 的行為大致一樣,就是直接跳到另一端,缺點是不容易注意到。 相關討論 20 年前就存在,例如:

增加音效或許不是完善解方,但在很多情況下也夠用了。

可以用的值

新增的 accessibility.typeaheadfind.wrappedSoundURL 設定和 Accessibility.typeaheadfind.soundURL 類似,具體可用的值為:

  • 空字串(預設值)
    不會出聲,跟舊版行為相同
  • 字串 beep
    發出系統嗶聲,跟舊版「找不到指定文字」的音效相同
  • 字串 default
    發出古代 Firefox 的預設音效,很像屁聲,這不是我亂加的,是原始行為
  • 指向 WAV 聲音檔的路徑
    因為底層是用 nsISound 實作所以只支援 wav。 格式必須是 file URI,也就是以 file:// 開頭,不確定的話可以把音檔拖進瀏覽器,再複製網址列的文字即可

這個功能我等了七年

其實最早在 2008 年寫的擴充元件 Noise 裡就有實作這個功能,但隨著 Firefox 57(2017/11/14)改變架構讓 XPCOM 套件全滅,接下來就一直沒有替代方案,在長文研究時真的很痛苦。

2024 六月處理了一個 findbar bug,其實也是為這個音效做準備,這次一個 commit 就結案,只是等待 review 推進也是拖了一個月。

可能在打 12 月商戰,搜尋商品被帶到 momo「店+」的頁面,又被手機版面傷到眼睛。
網址長得像這樣: www.momoshop.com.tw/TP/TP0003616/goodsDetail/TP00036160000057?entpCode=略

在桌面大螢幕看針對 App 做的版面,傷眼 點到行銷頁也會被 App 限定擋住

momo 不尊重桌面版(電腦版)使用者

很久以前點到手機版(.m 開頭)連結就死不做轉向或改善,經查 mo店+ 是今年三月上線的營運模式,APP 限定,現在年底了,網址散布開來了還是不改,你用桌面版點進來是你的問題,沒有要服務你。

更新 user script

更新之前寫的 MOMO 購物網「切換至桌面版」user script,現在會偵測店+ 網址,但無法導到桌面連結(因為不存在),點按鈕只會調整 <body> 寬度,讓版面正常一點點。

也幫按鈕增加一點表情。

一般按鈕 / 偵測到店+ 的按鈕 / 生氣膨脹的按鈕

Web 愛好者應該減少用 momo 買東西。

滑鼠點兩下選取一個字詞(word)的功能,在 Windows 下預設會包含字後面的空白,無視 DOM 元素邊界,還會把全形空白包進去。

平時不太會察覺,發現時往往不舒服,例如:

這個行為在 Chrome 也是一樣,但好像無解,只有 Firefox 可以用 about:config 改掉:

layout.word_select.eat_space_to_next_word 改成 false就可以了,參考


其他跟選取空白有關的選項:

  • layout.word_select.stop_at_punctuation

    不要包含標點符號,預設 true

  • layout.word_select.stop_at_underscore

    不要包含底線,預設 false

  • editor.word_select.delete_space_after_doubleclick_selection

    編輯文字時,點兩下選取後再刪除,會把字前後的空白也刪掉,若不是點兩下選取就沒有效果,預設 false
    參考 Bug 由來