概述:
在重構廣告‘老程式碼’的初期發生了一個突發的需求,下面我簡單介紹一下,可能文中更多的是思路和結構上的東西。具體的業務實現也會簡單說一些,但說的不多。
僅用文章紀錄一下,儲存我於廣告專案珍貴的記憶。
突發事件:
上一篇文章——我和我的廣告前端程式碼(一):打碎重來、化零為整,中提到我在將業務線甲的廣告程式碼重構的事情,但是開發的過程中,有了一個小插曲——加標誌。由於我國新出臺的廣告法規定,所有的廣告必須註明“廣告”。要求一週內,連開發帶測試,打敗這個半路殺出來的‘程咬金’。
應急方案:
這本是一個很簡單的事情,如果在我新重構的廣告程式碼中,只要在公用的父類中加入一個函式就可以批量的加入標識。但是新廣告程式碼的開發,還需要很長一段時間,而且新廣告程式碼也只是對應業務線甲,加標識的事必須是全站廣告都加才行。
如果拖到新廣告程式碼上線,那麼這一段時間是非常危險的,而且業務線乙也照顧不到(兩條業務線之間差別大是歷史原因);如果在老程式碼中加標識,又將面臨低複用的程式碼,以及逐個新增的工作量(上篇文章中提到,“老程式碼”是由不同廣告的片段拼湊而成的,並不存在共用的部分和可控的生命週期)。公司同時希望不要因為‘加標識’的事情而影響廣告程式碼重構的進度,儘量減小‘加標識’的成本。
通過和廣告組以及兩條業務線的溝通。借鑑了應用程式開發中“補丁”這個思想,最終我們商量出一個方案——“老程式碼標識補丁”。
來個補丁:
這應該是個很小的專案,但是他也是我的‘孩子’,也很具有典型性。所以我把這麼一個“簡單的”程式碼拿出來與大家分享一下。
既然是個補丁就要明確幾個特點:成本低、體積小、依賴少、適用性強、汙染小、使用方便。下面就結合這幾個特點來說一下:
1、成本低、使用方便:
由於要快速上線且不能影響廣告程式碼重構的進度。所以一定要避免“過度設計”,力求降低實現難度。等到新的廣告程式碼一上線(2個月後),這個補丁將隨著‘老程式碼’一同下線。搞一套太複雜的工程,看來沒有必要。將‘加標識’的功能單獨壓成一個js檔案,在頁面的底部引用,不用的時候去掉。
對於樣式我們不會單獨再寫一個css檔案,所有的式樣都寫到DOM 自身的style屬性中。
2、體積小、依賴少:
由於DOM這部分要用的jquery,如果將jquery與‘加標識’的功能,打包到一個檔案,會將檔案大小增加好幾倍(可能業務程式碼還沒有js庫體積大)。好在我們公司的每一個頁面上都有全域性的$(jquery)。那麼我們引用頁面裡的jquery,顯得更加合適。
為了減小體積,就不要將太多的庫打包到js中。比如underscore,我們可以在其原始碼中挑出幾個需要的函式封裝到我們的js中。比如我這次僅僅依賴jquery(頁面)和underscore(一部分)還有自己封裝的公共方法。畢竟減少依賴可以在很大程度上減少體積。我最終生成的js(壓縮前)檔案的大小也只有不到5k,壓縮後為3k。
將式樣寫到DOM的style中的好處是不要求頁面中必須引用一個css檔案,並且css許可權較高。
3、適用性強、汙染小:
與大多數情況js服務於少量頁面(或單一頁面)不同,考慮到要在公司全站的每一個頁面上加入這個js,因此我們必須保證我們的js在任何一個頁面以及任何一個瀏覽器中穩定執行,瀏覽器相容性以及IE hack、還有在不同瀏覽器中js的那些坑,都是必須考慮的。
汙染小是雙方面的:一方面宿主頁面的其他js不要汙染我們加標籤的js;另一方面我們的js也不要影響上一個作用域的環境。一個良好的閉包是必須的。
程式碼結構怎麼組織:
1、方案一:
利用webpack自身產生的閉包,遮蔽內外環境。另外webpack還能按照commonJS的方式組織我的js程式碼,多個模組打包成一個檔案。問題是這個專案的規模過小,不打算持續維護,加之我當時對webpack並不是很熟悉。
2、方案二:
從一開始就只有一個js檔案,所有的程式碼封裝到一個立即執行函式中。最後用gulp工具壓縮一下。好處是程式碼結構簡單,沒有編譯過程中混入的程式碼。缺點是沒有模組管理。
最終我選擇了看似比較普通的方案二,原因是:方案二已經足夠處理我們的需求了,加之我們並不打算長期維護這個工程。如果需求在複雜一些我會考慮用方案一的。
邏輯結構是怎樣的:
1、拿到廣告位
既然是加標識,就要先搞清,哪些地方是需要加標識的。由於每個廣告都有一個佔位id,我只要知道了今天這個頁面都哪幾個佔位id上廣告了,這事就好辦了。問題來了,其實我還真不知道,今天頁面上都哪幾個廣告位上廣告了。找到後端和業務線甲的後端商量出兩個方案,(1)提供一個獲取id(今天當前頁面且上廣告)的介面;(2)將這個頁面的所有佔位id通過吐在頁面上。
咋一看方案(1)會比較合理。但我們採用的是方案(2),考慮到成本,為了這樣一個臨時的需求,破壞廣告後臺系統和系統運營各自的結構,會增加頁面相應的時間和耦合性方案中的佔位id是固定的,並不影響頁面的相應時間,而且兩個後端組並不需要改變自身的系統結構。但是要注意方案(2)中拿到的僅僅是當前頁面中有哪些廣告位,但是卻沒有“今天上了哪些廣告?”。
對於廣告位中是否有廣告,其實在前端其實有比較另類的方式判斷——佔位id的DOM中是否有特定結構的內容。一般情況下佔位id對應的DOM一般預設收起(高度為0),且內容為空。有廣告的佔位id一般包含圖片或flash。我可以通過判斷佔位id中是否存在特定結構來判斷今天這個廣告位是否上了廣告。這時候來了一個新問題,老程式碼中的廣告渲染是同過js片段拼裝的js檔案處理的,我必須在廣告老程式碼渲染結束後判斷廣告的有無並新增標識。但是單純的依靠DOM ready 或 簡單延時,是不能準確找到那個時間點的,各個頁面的廣告載入時間也不盡相同,何況老程式碼中各個廣告載入並沒有固定統一的生命週期(今天上的廣告位中,即使一部分載入好,另一部分也可能暫時為空)。
2、分組延時、計數停止
既然DOM ready 或 簡單延時都不能準確的判斷,我們就從原有的基於事件和時間改為基於資料結構的。
我們首先要定義一個專門記錄佔位id是否已被插入廣告的資料結構。
在DOM ready 之後開啟一個定時器(注意要等上一個的回撥執行完了,才能開啟下一個)。每次檢查資料結構中還沒被插入廣告的DOM,如果狀態變化就“加廣告標識”。
這種髒檢查的檢查範圍會越來越小。注意因為有未上廣告的廣告位存在,如果不加入限制,髒檢查會永遠下去,因此我會規定分組延時的組數。我在這裡設定了5組從DOM ready開始分別3000ms, 7000ms, 11000ms, 15000ms,25000ms 來檢查。最終被檢出的都加標識,剩下的被認為今天沒上廣告。
3、怎麼加標識
原則上是載入原有廣告的左下角就可以了。但是這裡有個坑——老頁面以及老程式碼的頁面結構不合理。導致相對於父級元素的左下角不一定是廣告的左下角。比如有些廣告是靠居中定位的,但標識卻加到了頁面左邊,甚至超出了1200px以外(頁面沒有一個大的main-wrap)。
對於這種情況,我們一方面要求維護頁面的前端修正佈局,一方面儘可能的通過js判斷廣告的有效位置。以及考慮標識插入的節點位置,不一定以站位id位標識的父級。考慮到老程式碼中的廣告形式只有img和flash,並且img和flash都有固定的組成結構。所以結構匹配是可行的。
4、標識用css還是圖片
給廣告加標識,我們統一加在廣告的左下角。但是就這麼一個小小的標識該怎麼實現呢?一般第一反應肯定是用屬性加一個span
1 2 3 4 |
<div id="站位id"> <div><!--廣告內容--></div> <span style="position:absolut;bottom: 0;left: 0;.......">廣告<span> </div> |
起初我也是這麼做的,後來發現我們的標識中文字大小過於小。超過了一些瀏覽器的最小文字大小限制。另外還要在style中書寫過多的式樣,一方面要控制樣式,另一方面要覆蓋可繼承的屬性,可讀性太差。(當然主要原因還是瀏覽器對最小文字大小的限制問題)
這是我不得不使用圖片來加標識。這時我發現主站和其他網站也都使用了圖片的方式,看來是都被坑到了。圖片方式如下:
1 2 3 4 |
<div id="站位id"> <div><!--廣告內容--></div> <span style="position:absolut;bottom: 0;left: 0; background-image: url(..);.......">廣告<span> </div> |
另外有個問題是設計同學反饋給我的,就是在設計製作這個小圖片的時候,要注意襯線字型與非襯線字型的區別,當圖片小到一定程度的時候。“廣告”兩個字能不能顯示全。
總結:
相比龐大的廣告投放系統和廣告展示系統,這個‘加標識’的專案小的不能再小了。但是作為一個“補丁”,這段js卻五臟具全,且不失簡單輕巧。這種“補丁”,不僅要適應不同業務中完成質量良莠不齊的頁面環境。還要在‘老程式碼’不提供任何介面的情況下‘摸黑’作戰。保證功能的前提下,還要儘可能的實現工程化且避免過度設計。
嚴格來說,這個小型專案很具有典型性。也趁機把公司所有的頁面情況摸了個遍。最大的收穫是在重構廣告展示程式碼前總結了現有頁面的坑。讓我的重構之路少了不少彎路。
我和廣告前端程式碼的故事,還在繼續!