一個 JS 架構需要做什麼(1)
為什麼這麼說?不知道各位有沒有發現,雖然前端發展快,但一些有名的架構至少會火熱很長時間,比如 Backbone、React、Ember 。如果有心要學,肯定有足夠的時間把它學會,畢竟事實擺在面前,很多公司的上線產品就是用 React 來寫的,比如 Teambition 的簡聊,貌似它是從 Backbone 重構過來的。然而,很多同學在接手新項目時,常常會不知所措,不知道用什麼技術去做,或者說,只依賴於擅長的技術,就算在一些情境中它可能並不是最適合 的。
因為這些同學平時不夠努力嗎?不是吧。他們可能會看書到很晚,瀏覽很多部落格,就是為了去瞭解 CORS 的應用,或者是想知道為什麼 Angular 中的 scope 在某些時候不能雙向繫結了。對,時間是花了,但遇到問題還是一頭霧水。可能前端就是這麼一份工作吧,慫恿你去學遊泳,蛙泳、自由泳、蝶泳,海嘯來了照樣被 沖走……
那這篇文章要說的是什麼呢?就是假設你現在什麼都沒學,就靠基本功,去完成一個靜態頁面,當然也有商務邏輯,包括資料的 CRUD、動畫,怎麼做?有個關於 VanillaJS 的梗不知道大家看過沒,你一定會會心一笑的。
沒有 jQuery 了,沒有 Bootstrap 了,扔掉所有你引以為傲的武器,但大惡魔 IE 6 還在。具體的需求不給了,反正給了你們也不會照著去實現,真有心要做的話,可以做一個 todo app 吧。
DOM 查詢
在沒有第三方架構可以用的時候,如果真的按照功能列表,從第一條實現到最後一條,每個模組用自執行匿名函數包起來,所有代碼寫在一個檔案中,看上去十分合理,但真這麼做的話,恐怕你會瘋掉吧。哦,好處是你可以跟別人吹噓今天寫了三四百行代碼,產量很高呢!
所以,不使用第三方架構,我們可以自己寫,它的功能只要符合應用情境就可以了,不用去考慮各種不會發生的奇葩情況。
好,開始。我們最依賴的功能是通過 CSS 選取器擷取相應的 DOM 元素,這裡只使用相容性最高的方式,就是 id 和元素名選取器。
- var idRegex = /^#[\w\-]+/i,
- tagRegex = /^[a-z]+/i;
-
- function query(selector, context) {
- context = context || document;
-
- if (idRegex.test(selector)) {
- return document.getElementById(selector.substring(1));
- } else if (tagRegex.test(selector)) {
- return context.getElementsByTagName(selector);
- }
-
- return null;
- }
對了,我把所有 DOM 操作放在了 F.DOM 命名空間下,所以是這樣使用 query 方法的:
F.DOM.query('#id');
的確比 jQuery 的 $('#id') 方式麻煩很多,但“子不嫌母醜,狗不嫌家貧”,自己寫的代碼,再爛也要用下去。
另外一些必須的操作就不把代碼貼出來了,比如說 addClass、removeClass、hasClass 等。
DOM 事件
如果有同學參加過面試的話,我想“怎麼去監聽一個 DOM 事件?請儘可能考慮瀏覽器安全色性”這個問題是經常會問到吧。這兒寫一個可行方案吧。
- / 監聽 DOM 事件
- function addEventListener(el, event, handler, useCapture) {
- if (el.addEventListener) {
- el.addEventListener(event, handler, useCapture);
- } else if (el.attachEvent) {
- el.attachEvent('on' + event, handler);
- } else {
- // not support
- }
- }
-
- // 取消 DOM 事件
- function removeEventListener(el, event, handler, useCapture) {
- if (el.removeEventListener) {
- el.removeEventListener(event, handler, useCapture);
- } else if (el.detachEvent) {
- el.detachEvent('on' + event, handler);
- } else {
- // not support
- }
- }
我知道大家可能有更好的,或者更完善的方案,但抱歉這裡討論的重點不是它。
關於 DOM 事件方面,還有一些有用的方法,比如 preventDefault 和 stopPropagation 也可以自己去封裝一下。然後這兒想討論一下 DOM 載入完成的事件。jQuery 中我們會這麼用:
- $(function() {
- // ready
- });
如果我們也想封裝一個類似的方法,可能會這麼寫:
addEventListener('window', 'load', callback);
可是 load 事件是在什麼情況下觸發的呢?當頁面上的所有資源,包括圖片,載入完之後才觸發!也就是說,如果圖片很多,網速很慢,那觸發 load 要花很長時間。在本地調試時不會有這種延遲的問題,所以往往會被忽略。
那怎麼改正呢?第一,可以把 <script> 放到 <body> 中所有元素的下方,就不需要監聽任何“載入完成”的事件了。第二,監聽 DOMContentLoaded 事件,IE 9+ 支援。至於如何相容低版本瀏覽器,可以看這篇文章 (addDOMLoadEvent)。