:::

閒聊Blogger範本程式碼的管理 / How do I maintain Blogger's Template Code

4月 16, 2019 , 20 Comments Edit Copy Download

有人發現到最近「布丁布丁吃什麼?」的網站有些微的改變嗎?應該沒什麼人會注意到吧,因爲主要的改變都是在後端:Blogger範本程式碼的管理。這篇就些微的改變嗎?應該沒什麼人會注意到吧,因爲主要的改變都是在後端:Blogger範本程式碼的管理。這篇就來聊聊我管理「布丁布丁吃什麼?」範本程式碼的一些歷程吧。必須先說明的是,這只是我個人的做法,應該不適合大部分的使用者喔。




Blogger的網站設計管理工具 / Blogger’s template, layout and theme

在聊聊我怎麽管理Blogger的網站之前,我們要先回顧一下在Blogger中管理網站設計的各種方式。

範本 / Template

2019-0415-033808.png

Blogger最爲知名,也最爲新手詬病的特色,就是它從很早以前就開放讓Blog管理者能夠直接編輯網站呈現的程式碼。

Blogger的網站主題樣板是以既有的HTML程式碼爲基礎,再加上Blogger特有的一些語法,可以用來判斷現在所在的頁面類型 (<data:blog.pageType />)、簡單的邏輯控制<b:if>和<b:else />、以及呈現網頁小工具(Widget)。更多詳細的細節可以參考以下網頁:

版面配置 / Layout

2019-0415-033830.png

Blogger也有提供看起來像是所見即所得的「版面配置」功能。它能夠呈現網站的草圖架構,並提供一些設定可讓使用者微調。雖然看起來跟真正的「所見即得」好像還有點距離,但對於排斥HTML程式語言的使用者來說,這功能還算是堪用。

2019-0416-014545.png

Blogger厲害的地方在於,這個所見即得的「版面配置」是來自於網站主題樣板程式碼的內容,在「版面配置」修改的內容也會同步調整到網站主題樣板程式碼中。這兩者之間有着複雜的轉換過程,如果網站主題程式碼沒有寫好,「版面配置」會無法正常顯示。雖然WFU BLOG有介紹如何修改網站主題樣板程式碼中各區塊的細節,但因爲我既然已經懂得直接修改網站主題樣板程式碼,就用不上「版面配置」的功能了。

Blogger自適應網頁設計範本 / Blogger Responsive Web Design Template

2019-0415-033855.png

2017年的時候,Blogger因應行動裝置上網的風潮,推出了多種自適應網頁設計(Responsive Web Design, RWD)的版型,也提供了另一種網頁排版和主題配色的編輯器。RWD版型不僅在桌上型電腦的大螢幕上看起來非常符合現代網站設計的美感,在小螢幕的智慧型手機上也能夠正常運作,因而受到許多Blogger使用者的歡迎:

儘管Blogger提供的RWD版型非常漂亮,但卻也包含了更多特殊的語法。WFU BLOG直接建議大家不要修改版型的細節,直接使用Blogger提供的預設版面及效果就好。

使用自訂的RWD範本 / Cutomized RWD Template

2019-0415-033918.png

在Blogger正式推出RWD版型之前,網路上就已經有很多爲Blogger設計的RWD版型。我在2016年的時候就從gooyaabi Templates網站上找來了Breeze版型,改版之後讓網站瀏覽量大爲提升,着實令人驚豔(詳情請見「「布丁布丁吃什麼?」範本改版記錄」這篇)。然而這種自製的RWD版型只能透過Blogger範本編輯HTML程式碼來修改,而且這會導致無法配合「版面配置」跟「主題」的編輯工具。只要使用了自訂的RWD版型,那修改Blogger網頁設計的唯一之路,就是跟HTML、CSS與JavaScript這些最基本的程式語言打交道。

不過,這正合我意。

與其配合Blogger的特殊語法來設計網頁,我更喜歡直接操控最原始的HTML、CSS與JavaScript程式碼。而且對我來說,「布丁布丁吃什麼」就是練習寫網頁的遊樂場。不在這裏玩網頁語法,那要去哪裏玩呢?

所以「布丁布丁吃什麼」的網站設計管理,其實就是對Blogger範本的程式碼管理。


早期:在Blogger後臺中管理 / Maintain templete in Blogger

2019-0415-033928.png

一開始管理範本程式碼的時候,當然就是直接在Blogger後臺的範本畫面裡修改。爲了支援版面配置和主題的編輯器,Blogger的範本程式碼內容包山包海,造成管理上的不便。以下我從三個角度來看看這種管理方式上的問題。

編輯器的不足 / Problem of the template editor

2019-0415-033935.png

Blogger的範本編輯器並不是只是一個<textarea>標籤,而是一個能夠標亮XML語法的編輯器。整個Blogger範本必須符合XML語法格式,如果語法不符合XML格式,就沒辦法順利儲存Blogger範本。

但,它也只是個支援XML語法標亮、標籤收合和檢查XML語法正確性的編輯器而已。它並沒有提供整合式編輯器所具備的各種程式碼編輯輔助功能,特別是輸入前幾個字就能跟帶出後面完整語法的自動補完(auto complate)或程式碼的排版(format或code beautify)。這讓人很難在Blogger的範本編輯器裡面直接編輯程式碼。

CSS程式碼的管理 / CSS style

2019-0415-033950.png

另一個值得注意的問題是CSS程式碼。Blogger的CSS樣式表被放在<head>的<b:skin>標籤裡面。爲了能夠整合版面配置和主題的編輯器,Blogger將所有的CSS都直接放在網頁上,這樣就會造成網頁的肥大。

2019-0415-034024.png

(圖片來源:101 Computing.net: HTML – External CSS Stylesheet)

比較合理的做法是將CSS獨立於HTML程式碼之外,在HTML加入<link ref=”stylesheet” type=”text/css” href=”.../style.css” />的標籤來引用CSS樣式表。這樣子每個HTML網頁不需要重複包含相同的CSS樣式表,卻還是能夠套用共同的CSS樣式,因此能夠減少HTML的體積。

但問題是,獨立出來的CSS檔案要放在哪裏呢?我當時的做法是放在Google Page Creator上,請見「[日記]Google Page Creator」這篇。Google Page Creator是2006年Google實驗室推出的網頁編輯工具,直到2009年的時候轉移到Google Sites協作平臺,而Google Page Creator的檔案也就自動地轉移到Google Sites上。 Google Sites能夠讓人上傳檔案,最大容量是100MB。

我從Google Page Creator轉移到Google Sites的css程式碼依然可以正常使用,不過在PageSpeed Insights分析中,這些外連檔案是網頁讀取速度的瓶頸。主要是因爲原本連到Google Page Creator的檔案會被多次轉址到Google Sites中,造成讀取速度效率低落就是了。

因爲Google Sites管理不易,我後來就將程式碼轉移到Dropbox去。當時Dropbox還有提供外連的服務,稱爲Dropbox public link,不過這個服務也在2017年的時候關閉。再來我將檔案轉移至GitHub的時候,已經可以算是管理Blogger網頁程式碼的下一個時期了。

小裝置的管理 / Gadget

這個問題是早期Blogger使用者都會碰到的難題。

2019-0415-034035.png

Blogger能讓人在版面配置裡面管理小裝置(Gadget)。如果想要加入JavaScript程式碼,又不想要變更Blogger範本裡面複雜的內容,早期我都是使用「HTML/JavaScript小工具」來安插這些額外的程式碼。例如「Blogger側邊欄開關小工具」這篇。

2019-0415-034040.png

然而早期的Blogger範本中,其實並沒有直接包含我們在小工具裡面添增的內容。我們必須要手動開啓「展開小裝置範本」的設定,才能在範本程式碼中看到小裝置的內容。

2019-0415-034045.png

(圖片來源:長弓山的數位平台:[教學]如何備份及更換 Google Blogger 範本)

因爲展開設定之後小裝置的內容還是顯示在範本程式碼中,我就是一直以爲備份範本程式碼就能一併備份小裝置的設定。直到有一次我在嘗試修改Blogger範本時,使用了Blogger提供的「備份/還原」功能時,我才發現這功能原來並不會備份到小裝置的設定,導致我的小裝置內容全部付諸流水。

自從那個時候起,我就決定自訂的JavaScript程式碼不應放在小裝置中,應該直接寫在範本程式碼裡面。

到了2017年,Blogger在加入RWD版型時,範本程式碼也直接整合了小裝置的設定。我們不需要再「展開小裝置範本」,就能直接在範本中看到小裝置的內容。不過那個時候我就已經不使用小裝置了。


中期:範本程式碼的版本控制 / Version Control Blogger Template

2019-0415-034052.png

在學到版本控制的概念之後,我開始試着將它應用在Blogger的網頁程式碼管理上。之前許多程式碼擺放在Google Page Creator,雖然轉移到Google Sites這個以維基概念運作的協作平臺上,但這些外連檔案程式碼並沒有支援版本控制的功能。直到我把程式碼放到GitHub上,並使用GitHub Pages提供外連之後,「布丁布丁吃什麼」的Blogger網頁程式碼才開始比較正式的版本控制。(詳細請見:「「布丁布丁吃什麼?」在GitHub上開放原始碼囉!」)

關於GitHub Pages / About GitHub Pages

GitHub Pages是相當受人歡迎的網頁服務,而且大部分保存在GitHub的程式碼都以GitHub Pages架設網站。GitHub Pages只有提供靜態網頁的服務,也就是只能運作HTML、JavaScript、CSS以及提供大家下載的檔案,不能執行伺服器端的程式語言,像是PHP、Ruby或Python。

目前GitHub Pages使用上有幾個限制

  • 保存GitHub Pages的程式碼保存庫不得超過1GB。
  • GitHub Pages的存取流量爲每月100GB。
  • 在GitHub Pages變更檔案並發佈有次數限制,每小時最多10次。

除此之外,使用者也必須遵守GitHub Page的使用條款,不得發佈暴力、威脅、霸凌、釣魚假訊息、色情等內容。

雖然GitHub Pages只能保存靜態資源,而且存取流量有所限制,但它的速度快、提供了HTTP與HTTPS轉換等特點,讓我覺得非常好用。

兼容HTTP跟HTTPS / Mix HTTP and HTTPS

2019-0415-034101.png

2019-0415-034105.png

說到HTTP跟HTTPS,這個轉換也在Blogger使用者之間鬧得沸沸揚揚。一開始說Blogger自動幫大家升級成HTTPS,但是自訂網域的Blog,就像是「布丁布丁吃什麼?」使用了自訂網址「blog.pulipuli.info」,就無福消受。不過後來Blogger又幫自訂網域提供了HTTPS服務,詳情請看這篇「Blogger 官方免費幫自訂網址升級 HTTPS! 設定處理流程注意事項整理」。

2019-0415-034110.png

網址是可以從Blogger伺服器統一處理,但Blogger自訂的網頁程式碼,就得要自己想辦法了。如果是從http開啓網頁,那還可以正常載入以https連線的資源。但如果是以https開啓網頁,那就無法讀取http的不安全資源。因此所有外連的資源都必須以及經過混合內容(mixed content)的處理,才能確保在http或https兩者任意狀態下開啓網頁,都能看到正確的內容。

關於處理混合內容的做法,Blogger說明的「修正網誌的混合內容」或是WFU BLOG的這篇裡面也有詳細介紹。其中一個比較重要的技巧是將「http://」開頭的網址轉換成「//」開頭。例如原本的網址是:

去掉開頭的「http:」,就變成:

如果你是用http開啓網頁,那該網址就會用http協定連線。如果你是用https開啓網頁,那該網址就會用https協定連線。

然而要讓外部資源能夠改用混合內容來連線,提供外部資源的伺服器必須同時支援http跟https才行。還好「布丁布丁吃什麼」的兩個主要外部資源存放位置:Google Photo (前身是Google Picasa)跟GitHub Pages,兩者都能夠直接將網址從「http://」改爲「//」開頭,穩定地提供混合內容的服務。所以你也可以在「布丁布丁吃什麼」的網站中,找到以混合內容連結到GitHub Pages的網址,例如:

Blogger範本程式碼和文章使用的檔案 / Blogger template code and articles’ files

2019-0415-034131.png

自從將Blogger的相關程式碼移到GitHub Pages之後,我就越來越喜愛GitHub Pages的便利。後來陸陸續續在文章撰寫需要加上附件的時候,我都會把檔案也放到GitHub Pages上,提供大家直連下載。

GitHub Pages因爲結合了GitHub版本控制的優勢,我也能直接在GitHub上修改檔案,它就會自動發佈更新後的檔案到GitHub Pages上。例如我自行發展的Zotero中英文引用文獻樣式,如果有什麼修改,我就可以直接改GitHub中的程式碼即可。

然而,當我把所有的程式碼都放在同一個GitHub程式碼保存庫裡面的時候,問題就來了。因爲GitHub程式碼保存庫主要目的是做版本控制。如果檔案數量過多,版本控制的效率就會急劇變慢。而且我還會在上面擺大型檔案,更加拖慢GitHub的運作速度。有時候只是要改一個程式碼,整個git的commit、push速度都慢到令人難受,我才開始意識到這個問題的嚴重性。

在這之後,我就逐漸養成一個專案一個GitHub程式碼保存庫的習慣。雖然這樣的結果就變成我的程式碼保存庫有185個之多(其中也有蠻多是fork來測試用的就是),但分開保存的效率的確比較好,也比較符合Git的精神就是了。

使用IDE編輯網頁 / Edit template with IDE

2019-0415-034139.png

我將程式碼從Google Page Creator、Google Sites、Dropbox移至GitHub Pages,除了換地方擺這些JavaScript跟CSS程式碼之外,最大的好處,就是可以跟整合開發環境(integrated development environment, IDE)搭配。

我現在主要使用的IDE是NetBeans,NetBeans內建了跟GitHub搭配的功能,我也寫過一些簡單的介紹,像是「Git合作開發從NetBeans開始!NetBeans複製git專案教學」這篇。題外話,應該有不少人會主張Atom跟GitHub比較合,但其實具備一定水準的IDE都支援Git或早期的SVN等版本控制功能,其餘就是看個人喜好。Atom雖然有很多插件,能很自由地將IDE調整成自己喜好的形式,但要設定一堆插件其實還蠻麻煩的。而且插件安裝太多,也會很吃記憶體。所以大多時候我還是會直接使用NetBeans,它內建功能就很能滿足我的開發需求了。

2019-0415-034146.png

好啦,繼續說回來IDE這件事情。IDE爲什麼很重要呢?因爲它提供了許多好用的功能,其中最重要的就是自動補完(auto complate)。這個自動補完又分成兩種層級。基本的自動補完會按照現在編輯的程式語言,自動提示該程式語言常用的單字。例如在JavaScript程式語言中輸入「conso」,這時候NetBeans就會猜測我可能要輸入的是常用單字「console」,或甚至是更常用的「console.log」。

2019-0415-034150.png

另一方面,進階的自動補完會分析該程式使用的程式碼,或是分析整個專案的程式,並將可能會用到的屬性、方法都加入自動補完清單。例如我有一個放設定的程式,宣告了JSON形態的常數「CONFIG」,並爲常數底下的屬性加入說明。然後我在其他地方輸入CONFIG之後,NetBeans就會自動帶出CONFIG底下的屬性以及其說明。這樣子不僅可以省下回頭查閱其他程式內容的時間,自動補完功能更是大大地減少拼錯字所帶來的困擾。

2019-0415-034155.png

編輯Blogger網頁程式碼最重要的CSS樣式表,NetBeans也有很好的支援。像是撰寫色碼時自動顯示對應的顏色。

2019-0415-034159.png

或是分析目前的檔案,並列出條列式的大綱。

2019-0416-014806.png

最後再將編輯好的檔案遞交(commit),並推送(push)到GitHub上,一切都可以在NetBeans裡面,透過簡單的熱鍵(HotKey)達成。

如果使用Blogger後臺的XML編輯器,恐怕很難有效的提升維護的效率。然而到這邊爲止,我仍然是在編輯Blogger範本程式碼的靜態資源,IDE的協助也僅止於程式寫作上的輔助,對整體網站的維護與管理,其實依舊是亂無章法。

2019-0415-034207.png

最讓我困擾的是爲了RWD版型的CSS設定問題。在不同的螢幕尺寸下,RWD設定會套用對應的樣式表。然而這些樣式表有優先順序、影響範圍的衝突問題,牽一髮就會動全身。我常常會在某個螢幕尺寸下發現問題,試着去修改它之後,卻又會在另一種螢幕尺寸下造成問題。而且CSS的註解和結構等特性上,讓它本身就很難維護。改到最後,每次都在程式的最後面加上CSS規則來覆蓋前面的規則,但又可能帶來更多問題。

這些種種問題累積下來,就促使我採用不同的方式來管理Blogger範本程式碼。


現在:範本程式碼的編譯 / Template codes preprocess

2019-0415-034215.png

(圖片來源:webpack)

爲了解決以往只是以編輯靜態資源的方式管理Blogger範本程式碼時會遇到的問題,我將Blogger範本程式碼轉移到另一個程式碼保存庫,並開始採用「編譯」的方式來管理程式碼。新的「布丁布丁吃什麼」Blogger程式碼保存庫網址如下:

JavaScript與CSS的「編譯」 / “Compile” for JavaScript and CSS

也許有人會問:「JavaScript跟CSS不是可以直接用網頁讀取、不需要任何編譯嗎?」沒錯,JavaScript跟CSS的確不需要編譯就能夠執行運作,這也是我喜歡這些程式語言的理由。不過對JavaScriptc和CSS所進行的「編譯」,目的卻不是爲了能讓程式正常運作,而是能夠達到提升網頁運作的效率,以及改善程式碼管理的方式。

必須說在前頭的是,這邊所指的「編譯」並非如C或Java這種程式碼的編譯,而是指「預處理」(preprocess)或「組裝、包裝、打包」(bundle)。雖然這些做法在原理上跟編譯並不相同,但操作上看起來很類似,兩者都必需經過兩個步驟:

  1. 在既定的規範下寫好原始碼;

  2. 執行預處理或編譯,取得處理後的完成品。

因爲兩者操作上很類似,所以我比較習慣用「編譯」來統稱這些類似的做法就是了。

由於要編譯的是JavaScript和CSS這些網頁端的程式語言,我所選擇的編譯環境也是以JavaScript爲主的Node.js,編譯器使用的是近年來相當流行的Webpack。我的Webpack設定檔webpack.config.js網址如下:

那麼,我們就來看看Webpack能夠爲Blogger的範本程式碼管理,帶來什麼樣的好處吧。

程式碼壓縮 / Minify JavaScript and CSS

2019-0415-034222.png

(圖片來源:codeless: 5 Tips On How To Minify The CSS and JavaScript Codes On Your Website)

用過類似Webpack這種預處理工具的人,應該不太會把「程式碼壓縮」這件事情放在開頭。不過對網站來說,減少網頁程式碼的體積,對於運作速度是有直接且明確的影響,也是我對Webpack工具的首要期許。

我以前寫過使用PHP工具MinifyJava程式YUI Compessor來壓縮程式碼的介紹,我也這兩個壓縮工具整合到系統中,讓系統自動壓縮JavaScript跟CSS程式碼,再將壓縮後的結果保存到快取中。但是運作的時候赫然發現這兩個工具運都不太穩定,很容易吃光記憶體而當機,我也不太知道爲什麼會如此,最後還是捨棄了自動壓縮的做法。

我之前還會使用線上網頁的工具來壓縮JavaScript與CSS程式碼,例如JSCompressCSS Minifier。不過手動操作會讓人感到麻煩,反而會讓人減少想去降低了更新程式碼的動力。

Webpack也有提供壓縮JavaScript跟CSS功能,但是這個功能卻是我在Webpack設定上花了好一番功夫後才讓它可以運作。最後我在Webpack 4上實作的設定是用了UglifyJsPluginbabel-loader的組合。網路上有許多教學,衆多紛紜,但我想可能是因爲Webpack版本不同的緣故,每個版本的做法都有所不同。不管怎樣,經過長期摸索之後,總算能做到壓縮JavaScript跟CSS的功能了。

壓縮JavaScript跟CSS能帶來什麼好處呢?除了檔案體積減少,能夠讓人更快讀取完網頁之外,有一個很少人注意到的好處,就是「能夠撰寫更多註解」。以前撰寫註解意味著檔案體積變大,註解寫得越清楚,檔案就越長,寫註解的維護跟檔案讀取的效率,兩者互相衝突。但有了壓縮功能之後,在壓縮完的發佈版本中註解會被自動移除,因此就讓人更能放心撰寫註解。

2019-0415-034228.png

在NetBeans這種IDE中,只要遵循JSDoc第二版ScriptDoc (SDOC)ExtDoc等的規則撰寫註解,在自動補完的時候就能夠直接帶出註解的內容,非常好用!關於NetBeans在JavaScript註解與自動補完上的功能,請看「NetBeans: Editing JavaScript」這篇。

我個人最喜歡在撰寫程式的時候加入個人名字跟日期,過幾年後回來修改該檔案的時候,就會反省一下這段期間有沒有成長。

多檔案打包 / Package

2019-0415-034233.png

(圖片來源:CLOUDDOKI: Webpack Module Bundler)

再來要談到的是webpack的編譯所能帶來的第二個好處:多檔案的打包。這裡所說的打包,可以簡單地想像成多個文字程式碼合併成一個很長的程式碼檔案。光是這樣簡單的一個動作,就能爲網頁讀取效率和程式碼維護上帶來好處。

2019-0415-034237.png

(圖片來源:WebSiteOptimization.com: Chapter 10 - Website Optimization Metrics)

爲什麼合併成一個檔案來讀取,會比讀取多個檔案還來得快呢?這是因爲網頁在跟伺服器請求外部資源,包括JavaScript跟CSS檔案時,除了會下載這些檔案的實際內容之外,還需要進行DNS解析、取得IP、開啓接口建立連結、傳送HTTP或HTTPS請求等動作。這些動作會耗費額外的網路資源、延後網頁讀取完成的時間,檔案數量越多,這些額外消耗就會越明顯。

將多個檔案合併成一個檔案來讀取,就是爲了減少額外的消耗,提升網頁讀取的效率。這個原理就跟CSS Sprites要達到的目的一樣,詳細可以看看我之前寫的「Blogger範本應用CSS Sprites技術記事」。

如果可以克服檔案數量增加所帶來的問題,那檔案數量的增加,也就是將檔案分割成更多小型模組來管理,就能夠帶來程式碼維護的好處。舉例來說,以前我只會將CSS放在一個style.css檔案中,這裡面的樣式設定包含了導覽列、文章設定、側欄、底部區塊等一堆內容,每次要修改的時候都得花一番功夫找尋要改的位置。

2019-0415-034243.png

現在我在新的專案中,就能把這些程式碼按照區塊分割成更專指的的結構。

2019-0415-034247.png

在同一個區塊中,像是位於右下角的「go-top」功能,我甚至可以把外觀的CSS跟實作功能的JavaScript都擺在同一個資料夾中。未來如果要修改「go-top」,那就直接找資料夾中的檔案來修改即可,這樣就不用再開啓超級長的CSS檔案、用搜尋到處找尋要修改地方了。

透過webpack,我們不僅可以將多個JavaScript打包成一個JavaScript檔案,在方便程式碼維護的前提下、減少載入多檔案所帶來的額外消耗。令我意外的是,webpack還能夠將CSS一併打包到JavaScript中,讓本來要拆開讀取JavaScript跟CSS兩個檔案的方式,減少到真正只載入一個JavaScript的境界。

2019-0415-034252.png

最後打包完成的檔案之一item-header.js,就會像上圖一樣,成爲一個壓縮處理後的單一檔案了,看起來是不是很專業呢?

2019-0415-034255.png

除了使用webpack來打包JavaScript與CSS檔案之外,我也用手工另外將Blogger範本程式碼分割成多個檔案,以main-template.html連結各個零散的檔案,而這些檔案都放在components資料夾中。這樣子範本程式碼也就更方便分開維護了。

用更輕鬆的方式維護CSS:LESS預處理器 / Improve CSS code with LESS preprocessor

2019-0415-034259.png

(圖片來源:網站製作與網頁設計:關於 LESS CSS)

既然都可以用webpack來壓縮程式碼了,那使用其他預處理器來改善CSS的撰寫,也就不是什麼難事了。

老實說,CSS並不是一個很好維護的程式語言。雖然它在呈現網頁樣式的用途上已經存在已久,但CSS本身卻很難維護。

2019-0415-034305.png

最常見的問題,就是選取器(selector)難以管理。一般來說,我們會希望將CSS的選取器寫得越仔細、範圍越小越好,因爲這樣子設定的樣式只會影響特定範圍下的網頁元素(element),而不會擴及影響到其他的元素。但是要將選取器寫得仔細這件事情,其實相當令人困擾。過長的選取器在撰寫的時候會大幅度增加寫錯的機率,這不光是可能打錯字,最可能的是父子層的名稱搞錯了,導致最後的規則無法順利套用。也可能會有前後不一致的問題,一開始我可能很認真地寫了很長的選取器,但後來要修改的時候,會因爲不能理解當初這麼長的選取器是怎麽建構的,所以無法遵循當初的規則。另一方面,過長的選取器也難以閱讀。我得把整個讀取器一整行都看完,才能得知現在這個樣式到底是套用在誰身上。

除了選取器的問題之外,CSS只能用/* */的多行註解、缺乏變數與繼承的靈活彈性,都讓CSS維護起來難度很高。

爲了解決這些問題,我選擇採用與CSS原始語法較爲相似的LESS預處理器。在webpack上只要加上less-loader,就能讓webpack處理檔案的過程中將LESS轉換成CSS程式碼。從CSS踏入LESS,整個轉換可說是輕鬆寫意。

2019-0415-034311.png

對我來說,改用LESS語法來撰寫CSS之後,最大的好處就是能夠使用嵌套的方式來切割不同區塊的樣式表,關於LESS嵌套的介紹,可以看看「less的嵌套」這篇的例子。以「go-top.less」這隻程式來說,我只要在最外圍用「#footer .go-top { ... }」包住,裡面的選取器就會是專指「go-top」區塊底下的元素。我就不用每一個選取器前面還要加上「#footer .go-top 」這樣的宣告,可以減少程式碼維護的人力,整個程式也比較好閱讀。

使用LESS之後,會讓人有種想把以前寫得CSS全都用LESS重新整理的衝動,越寫越覺得感動呢。如果你只寫過CSS的話,我會大力推薦嘗試LESS或是SASS這種預處理器喔。

用Source Map找尋原始檔案 / Retrieve the source code via Source Map

2019-0415-034315.png

前面提到了壓縮、多檔案合併以及LESS的預處理,雖然經過webpack處理後能得到一個包裝好的JavaScript檔案是很不錯啦,但是如果裡面程式碼出錯了,那要怎麽回頭找到出現問題的地方呢?這個問題的解決方法,就是source map。

就結論來說,只要在webpack打包的設定中開啓source map的功能,我們使用Google Chrome的「檢查」來查詢CSS樣式或是在console查閱JavaScript的訊息時,Google Chrome就會可以幫你找出未壓縮、打包之前原始檔案的位置與其內容,讓我們能夠方便偵錯。

2019-0416-015042.png 

source map的原理跟運作方式頗爲複雜,詳情請看阮一峰的JavaScript Source Map 详解。我們主要要確定在Google Chrome的Developer Tools的Setting中,開啓Sources裡面的「Enable JavaScript source maps」即可。

在webpack中若要使用source map,JavaScript跟CSS兩者必須分開設定。JavaScript的部分是在devtool中設置’source-map’,但除了顯示原始碼的source-map之外,還有很多種不同的source map,詳情請看「webpack: devtool」。另一方面,CSS的設定只要在相關的loader後面加上「?sourceMap」即可,請看HaNdTriX的說明

自動編譯:watch / Rebuild while file changed with “watch”

上面雖然說了很多使用webpack編譯能帶來的好處,不過如果每次都要手動執行編譯這個指令、等待編譯的話,那不是還是很麻煩嗎?

別擔心,webpack早就想到這個問題的解法了,那就是「watch」參數。在執行webpack編譯的時候加上「watch」參數,webpack就會進入監控模式,當需要編譯檔案有所變動時,webpack就會自動執行編譯的動作。我們只要好好維護程式碼、乖乖儲存,編譯好的檔案就會自動擺在設定好的位置。

另一個問題是,當webpack需要編譯的檔案一多,而且還要加入壓縮、LESS預處理、建立source map等各種操作時,那編譯的速度不是很慢嗎?這讓我想起以前在編譯DSpace的時候,每一次編譯大概都要等個3分鐘才能看到成果。如果電腦等級不高的話,編譯更是費時。

對於這個問題,webpack採用的做法是指定模式(mode)。我們在使用webpack進行編譯時,需要告訴webpack現在的模式是「開發狀態」(development)還是「正式產品狀態」(production)。如果是「開發狀態」,那webpack就不會執行壓縮或一些優化的動作,只是讓檔案編譯成可以運作的狀態,這樣就能降低編譯所需要的時間。而當程式寫好,功能都沒問題之後,我會再以手動的方式以「正式產品狀態」來編譯程式碼,webpack就會產生經過壓縮處理的最終發佈檔。

透過webpack的監控(watch)和模式(mode)的搭配,就能將編譯所帶來的麻煩降到相當地的程度。要知道,程式設計師幾乎都是懶人,只有懶人才會想要用機器自動化來解決問題,也只有把像是編譯這種瑣碎的工作降到最低,我們才有其他的精力來處理更麻煩的工作啊。

關於webpack的watch跟mode設定,可以參考Mike的「Webpack教學 (三):永不停止的Watch」這篇說明,但其實mode設定還有很多種變化,可以根據專案的實際需求來選擇那些功能可以在「開發狀態」(development)下關閉。

編譯環境的維護:package.json的奧妙 / Maintain environment with package.json

2019-0415-034338.png

(圖片來源:How-To Geek: Java is Insecure and Awful, It’s Time to Disable It, and Here’s How,雖然該篇文章跟我要講的事情不大相關就是了)

我以前蠻排斥使用需要編譯的程式語言,最主要的原因在於要維持能夠編譯的環境是一件相當麻煩的事情。舉例來說,要編譯Java,我們除了需要安裝JDK (Java SE Development Kit)之外,通常還需要搭配打包工具或是特定IDE,並設定好環境變數,這一切才能有機會順利運作。看看「在Windows上建立JAVA編譯環境(eclipse、JDK)」這篇,你就知道有多麼複雜。而且我都還沒提到32位元、64位元、不同作業系統平臺的問題呢!我以前寫過的「eclipse開啟時發生無法載入「jvm.dll」問題的解決方法」就是因爲環境配置錯誤所造成的問題啊。

如果是要用在需要佈署的不同環境時,準備編譯所需要的環境更是令人崩潰。回想起以前安裝DSpace的時候,「DSpace系統安裝及設定指南」或是「DSpace 1.5.1安裝法」裡面所記載的Java環境配置中的每一個動作,我們都必須在任何伺服器、開發環境中各別設定,那還真的是讓人痛苦不已。

然而,上面我使用webpack打包、壓縮、甚至用LESS預處理,這些繁多且複雜的工具,難道就沒有配置環境的問題嗎?是的,多虧Git版本控制的同步,以及package.json記載了編譯環境所需要的完整資訊,這一切都不再是問題。

2019-0415-034345.png

(圖片來源:Why package.json? | npm basics)

我大概是在2017年開始撰寫Node.js。一開始,我以為Node.js的特色就是能夠使用與網頁用的JavaScript一樣的寫法,只是能用更多伺服器端才能做到的功能,像是檔案存取、突破瀏覽器的限制來爬資料,以及使用一些別人已經寫好的套件。直到最近開始正式使用Node.js以及其中的webpack來維護Blogger範本程式碼之後,我才發現原來Node.js真正的精華,就在於package.json中。

2019-0415-034349.png

以我管理Blogger範本程式碼的package.json爲例,裡面記載的不僅是這個專案的描述資料,最重要的是還包括了這個專案會用到的相依套件(dependencies),以及這個專案常用的指令(script)。只要使用Git同步這個package.json檔案,我們就能用「npm install」指令來安裝所有需要的套件,而且npm會自動幫忙處理各種不同作業系統、環境的問題。我在Windows 7的環境跟Linux Ubuntu環境使用Git同步專案程式碼,然後用「npm install」安裝相依套件,很快地這兩個不同的作業系統上的程式碼就能夠以相同的方式運作,而且完全不需要仰賴任何IDE的介入。

2019-0415-034354.png

加上IDE之後,操控整個專案就更爲方便。NetBeans能夠解讀package.json裡面的設定,我可以將常用的指令寫到「script」裡面,NetBeans就會將它加入到專案的選單中。因此像是要求webpack進行編譯的指令,我現在就不需要透過指令列,而可以直接用滑鼠點選來執行。

可以說,維護好package.json,就等於維護了整個開發所需要的運作與編譯環境。關於npm和package.json的更多介紹,請看拭心的「npm 与 package.json 快速入门教程」這篇。

題外話,至於程式語言撰寫風格的部分,大家多會採用ESLint套件下的「.eslintrc」來設定。這又是另一個複雜的議題了。

程式碼的測試 / Test

2019-0415-034400.png

(圖片來源:MochaJS)

除了上述使用webpack來打包、壓縮JavaScript跟CSS之外,我還試著加入了測試(test)功能。這個測試包括了使用MochaJS測試Node.js程式碼的單元測試,以及使用Selenium自動操作網頁的整合測試。這兩種測試都已經能夠順利運作,我寫了幾個練習程式碼放在test資料夾底下。但因爲這個Blogger範本程式碼並非獨立運作的系統,沒有很好地遵循ECMAScript 6的風格撰寫程式碼,所以不太能使用MochaJS來做單元測試。

2019-0415-034405.png

另一方面,使用Selenium來做整合測試就比較可行。原本我是參考「JavaScript(Node.js)+ Selenium自动化测试」這篇來撰寫Selenium測試,但這篇的做法是以JavaScript指令來描述Selenium要測試的項目,當要操作的步驟變多時,要建立測試文件就變得相當困難。於是我後來選擇的是使用Google Chrome的Selenium IDE來建立Selenium測試專案,副檔名爲side。Selenium IDE可以用錄製的方式建立測試,操作起來相當簡單。我以前寫過幾篇用Selenium IDE所建立的測試,像是「網頁應用測試工具:Selenium IDE」和「以Selenium IDE建立測試案例:圖片延緩載入」,當時還是使用Firefox才能使用Selenium IDE。時至今日,Google Chrome也有好用的Selenium IDE,而且它所產生的side測試文件,也能使用Node.js相對應的套件selenium-side-runner來執行。如過搭配webpack的話,就能做到編譯之後自動測試、顯示測試結果的功能。不過目前執行Selenium測試時還是太花時間,所以我還沒把這兩者綁在一起實作。

奇怪的是,網路上鮮少介紹如何使用Google Chrome的Selenium IDE來錄製side測試,再搭配Node.js執行測試的相關文章。也許未來有機會的話,我再把這整套做法寫一片介紹吧。

找尋Open Live Writer的替代品 / Alternative to Open Live Writer

2019-0415-034411.png

在Google關閉了Picasa API之後,連帶影響的就是Open Live Writer的圖片上傳功能也跟著要邁入尾聲(詳情請見WFU BLOG的「為何 Flickr 會刪除免費圖片額度?原來 Google 關閉了這個服務」),我需要另外尋求其他能夠一邊撰寫文章、一邊插入圖片的工具。再加上我現在也會使用Chromebook來工作,Open Live Writer在Linux上本來就用不了了,連WINE都開不起來,找其他替代的工具是勢在必行。

我目前的想法是想要回到用Blogger的草稿來上傳圖片,但是整個HTML網頁編輯則是用其他工具。大致上流程如下:

  1. 現在能夠編輯圖文內容的工具底下底下寫好文章內容。

  2. 取出文章中的圖片跟文章的HTML程式碼。

  3. 在Blogger草稿中上傳圖片,取得上傳後的網址。

  4. 將文章的HTML程式碼中圖片的部分提換。

  5. 設定好標題、標籤後,發佈到Blogger上。

嗯...我開始懷念Open Live Writer可以一氣呵成,做完這些瑣事的時候了。

2019-0415-034417.png

我現在使用Google Doc來撰寫文章,然後調整頁面尺寸、並使用stylebot修改Google Doc編輯器的背景,讓整個編輯器看起來稍微有點像是Open Live Writer編輯的感覺。這樣的介面調整純粹只是讓自己看起來開心一點。

Google Doc是一個蠻稱職的編輯器,可以自動儲存、同步就不說了,插入在Google Doc的圖片,還可以在輸出成網頁後取得原始檔案,這樣就能獨立取出圖片來手動上傳到Blogger。

儘管如此,最近在使用Google Doc撰寫時,赫然發現在Google Doc上打字非常的頓。特別是在一邊讀取其他網頁,一邊使用Google Doc打字時,更可能發生我輸入了好幾個字,過了十秒之後字才顯示在畫面上的問題。

2019-0415-034422.png

我本來以爲這是因爲我的使用的是低階的Chromebook,CPU跟記憶體效能不足,或是我使用的是RIME輸入法,它本身需要比較多資源來猜測我要輸入的文字,才會導致Google Doc運作起來這麼的卡。還讓我開始找起了有沒有更好的Chromebook可以買的念頭,雖然找了一下還是覺得我手上這臺仍然是一時之選。但後來跟其他人聊起這個話題時,我才發現其他人在Google Doc上編輯也會遇到類似的問題。於是我就試着用xfce4-notes-plugin的簡易筆記工具來打字看看,結果寫起來就順暢很多。

看來,這也許真的是Google Doc的問題。我可能還是需要另尋其他工具來撰寫文章吧。

唉,真懷念Open Live Writer啊。


結語 / In closing

這篇介紹了我是如何從原始的Blogger範本功能開始,逐漸將範本程式碼與原本的功能分離,再加上版本控制、打包與壓縮的編譯等做法。

雖然這篇的敘述只是用在Blogger範本程式碼的管理上,但是卻已經足以讓我有信心離開PHP,投入更多精力在Node.js的撰寫。事實上,我接下來幾個專案也就是用Node.js來寫,一樣也用webpack與package.json來維護整個環境。Node.js帶給程式開發相當大的便利性,用過之後就很難回去了。

當然,我目前對Node.js的研究並沒有十分深入,還有許多特色我還不夠瞭解。舉例來說,Promise的非同步用法,以及更進階的await與async關鍵字,都還是我所不熟悉的領域。

2019-0415-034429.png

(圖片來源:Carlos Vega: Node 7.6 + Koa 2: asynchronous flow control made right)

是的,我就是那個還活在回呼地獄(callback hell)中的原始人。


那麼這次對於「布丁布丁吃什麼?」的範本程式碼管理的閒聊就到這邊了。你也是Blogger的使用者嗎?你是如何管理Blogger的外觀呢?你是使用Blogger提供的版面配置或主題功能,還是手動修改範本程式碼呢?對於Open Live Writer不能上傳圖片的這個問題,你有什麼看法呢?歡迎在下面的留言處跟我們分享你的想法。如果你覺得我這篇教學寫的不錯的話,請幫我在AddThis分享工具按讚、將這篇分享到Facebook等社群媒體吧!感謝你的耐心閱讀,讓我們下一篇見。

總共20 則留言 ( 我要發問 , 隱藏留言 顯示留言 )

  1. 回覆刪除
  2. 回覆刪除
    回覆
    1. 回覆刪除
    2. 回覆刪除
    3. 回覆刪除
    4. 回覆刪除
  3. 回覆刪除
    回覆
    1. 回覆刪除
  4. 回覆刪除
    回覆
    1. 回覆刪除
  5. 回覆刪除
  6. 回覆刪除
    回覆
    1. 回覆刪除
  7. 回覆刪除
    回覆
    1. 回覆刪除
    2. 回覆刪除
  8. 回覆刪除
    回覆
    1. 回覆刪除