VC模式下javascript項目重構

項目現狀

項目爲單頁web應用,只針對chrome瀏覽器,無開發文檔。因爲是追求進度的項目,開發約定極少,除了jquery、LAB.js、bootstrap以及一些UI組件外,沒有使用其餘開源組件。
項目簡單封裝了ajax請求,form表單信息獲取,緩存,獲取和渲染模板函數以及一些UI組件,ajax請求沒有按照restful api進行封裝。
如今項目開發中所遇到的問題:javascript

  1. 代碼命名不規範,例如action,do_actioncss

  2. 代碼結構不清晰,沒有按照功能或mvc拆分紅獨立的文件,只能依靠dom去查找具體的業務代碼在哪裏html

  3. 模板寫的有問題,複用模板使用id來作dom索引,或混入javascript業務邏輯,也有整個模板只包含一行代碼前端

  4. 工具類封裝與引用不規範,主要是沒有註釋,沒有規範的例子可作參照,致使出現錯誤的使用工具類java

  5. 業務邏輯的流轉依靠dom操做,例如,在實現數據聯動的時候,每每須要手動點擊刷新按鈕或者$(...).click()。node

  6. 模板渲染封裝太薄,致使不一樣模塊的模板渲染方法各異,且會渲染模板中的javascript代碼jquery

  7. 沒法對代碼進行代碼測試,只能依靠人工測試確保代碼質量web

  8. css文件過大,基本上全部的css都集中在一個css文件下ajax

  9. 頁面跳轉策略是隱藏老頁面,顯示新頁面。chrome

重構項目方案

代碼書寫規範

css規範: http://www.iteye.com/news/27577
重點注意命名規範。

javascript命名規範: http://blog.sina.com.cn/s/blog_8db76d6d010143pi.html

javascript註釋規範:http://www.jetbrains.com/webstorm/webhelp/creating-jsdoc-comments.html
具體規範要待確立註釋要達到什麼效果後(例如:是否要自動生成doc)再寫

文件結構

前提:

  1. 儘可能不動現有文件夾名稱

  2. 儘可能少的移動文件

  3. 儘可能減小修改現有代碼

如何重構:

//fileStructure//注意,一個模塊可按照功能繼續往下劃分,相應的,與之對應的css和img也要在目錄上與之對應{
    src:{//開發環境,主要作業務邏輯  新增
        moduleName:{//模塊名稱
            src:[sourceJSFiles],
            style:[cssFiles]
        }
    },
    components:{//開發環境:自行開發的插件,公共組件等, 新增//組件也能夠做爲單獨項目獨立出來,便於測試,維護
        componentName:{
            src:[sourceJSFiles],
            style:[cssFiles]//可能存在的style
        }
    }
    libs:{//存放項目依賴的插件  新增
        pluginName:[plugin_files],
        ...:...
    },

    javascripts:{//生產環境的js源碼
        moduleName:[combineJSFiles],
        ...:...
        components:{
            component:[CombineJSFIles]
            ...:...
        }
    },

    stylesheets:{//生產環境的css,css文件可合併在一塊兒,但注意合併的順序配置。
        moduleName:[combineCSSFile],
        ...:...
        components:{
            component:[CombineJSFIles]
            ...:...
        }
    },

    views:{//存放模板,開發生產統一爲一個文件
        moduleName:[tempFiles],
        ...:...
    },

    images:{//存放圖片
        moduleName:[imgFiles],
        components:[imgFiles]//粒度再也不往下劃分
    },

    file:[files],//存放被下載的資源

    test:{//測試
         moduleName:[filesName]//fileName要以Spec結尾。例如 abcSpec.js   方便寫腳本
    },
    node_modules:{//存放開發環境 grunt依賴的插件
        ...:...
    },
    app.js//總的js文件,負責加載、配置以及開發和生產環境的切換。
    app.css//總的css文件,可放在stylesheets文件夾下面。
    main.html//index頁面
    grunt.js//引入grunt來作集成化測試以及代碼的合併壓縮 新增
    package.json

}

思考:
咱們真需經過文件結構去劃分去記憶去調試代碼嗎?對一個前端項目來說。雖然剛開始認爲:這個是必須去作的事情。但後來,隨着思考的加深,文件結構這方面,反而是無關緊要的。咱們直接能夠經過chrome debug 來快速鎖定具體的文件甚至是具體的代碼在哪裏,咱們還真的迫切須要文件結構嗎?

代碼模塊化

前提:

  1. 全部用到的javascript文件會在用戶登陸前加載好。

  2. 依據現有代碼,每一個業務邏輯模塊都會將其引用暴露在handler全局變量下面。

  3. 公用組件可重寫

如何重構:
業務邏輯代碼:
關於現有javascript邏輯代碼的重構,重點放在將現有js文件進行分拆,按照功能和模板細化到具體的文件夾下面去。
而後寫好grunt合併壓縮腳本,可將一個js模塊按照特定順序合併到一個js文件裏,而後,再將合併的文件壓縮後,放置在文件夾javascripts相應的模塊下面去。
css:
在修改業務邏輯代碼的時候,儘可能將相應的css歸到相應的文件夾下。
公共組件:
公共UI組件儘可能不使用width,heightcss樣式,多用margin,padding,max-width等,無特殊狀況下,不要使用id對組件進行索引,多使用class。寫公共組件的大致格式以下:

//組件可不提供命名衝突處理,儘可能人爲避免衝突,命名格式爲name+"Mod"
//例如:showDialogMod
//參數若是超過三個的話,使用{}來替代
//須要寫註釋,最好在註釋中,給個使用例子
var componentName=(function(args){
    'use strict';
    //...  
    //...
    return {//如需對組件進行操做(例如對組件中的值進行從新賦值),至少要返回dom引用,更好的方案是返回相應的操做方法
        //...:...
    }
})

注意:
寫組件的時候,不要使用jquery,用原生瀏覽器提供的方法,畢竟咱們沒法預言會不會出現更好的前端框架。

雙向綁定(更改成MVC)

是否須要實現雙向綁定

不實現
不實現也就是使用已有代碼的流轉方式:用戶經過view觸發事件>>導向至相應的controller->組裝數據,發送ajax請求->請求返回->(獲取模版->根據模板渲染數據)->改動與之有牽連view。括號內表示可能不須要的步驟。
例如:在記錄內容展現頁面刪除這條記錄,就須要更新當前模板,以及更新記錄列表內的數據。
實現
業務邏輯代碼的流轉方式會變成:用戶經過view觸發事件>>導向至相應的controller->組裝數據,發送ajax請求->請求返回->修改緩存model。
須要實現一批filter來對顯示數據和model數據進行轉換。

實現方式

採用現有框架

avalon
源碼4000,易掌控,不會破壞現有數據流轉和模板,但頁面的渲染方式會發生改變。可經過$watch的運用來減小代碼重構的難度。修改幅度相較其餘mvc框架小。
angular
須要根據angular對項目結構修改進行,還須要學習scope、directive 等概念,且渲染方式會更改,要本身實現一套router,簡單的使用ng-include沒法知足頁面緩存的現狀。
ember
不推薦。須要改動的地方太多,最致命的是,它是對dom侵入的,不易於小幅度修改現有項目。
backbone
屬於輕量級的,須要引入underscore.js。render函數會有較多的改動。編程的重心會轉移到view展現的編寫以及和服務器請求的匹配(backbone按照restful api 設計),雖然代碼量不會減小太多,但代碼結構會清晰不少,underscore.js提供script模板渲染。

本身寫
不具有短時間內開發一個較爲完善的mvc框架的技術儲備。

注意:
以上的實現,對傳輸的數據結構的規整性和方式都有必定的要求,對於現有的關於歷史方面等不須要進行監聽的數據,要作相應的處理。
以上框架,除了avalon,都須要爲服務器請求過來的數據作適配器(angular$resource要更改)。

不用框架的重構(VC重構)

模板重構

前提:

  1. 模板使用的流程:獲取模板(若是已緩存,則從緩存中獲取模板),渲染模板(String.replace的增強版),將渲染後的模板添加到dom中。

重構前提:

  1. 可大規模的合併模板

  2. 模板內能夠不容許擁有js代碼片斷(簡單一點的,例如onclick="abc()"能夠有)。即便容許有js代碼片斷,也不容許js代碼片斷可被渲染。

  3. chrome debug熟練使用

如何重構:
去除模板id
前端id的做用是用來標識dom全局惟一變量,是用來作dom結構和邏輯塊劃分的,不能拿來作數據索引轉換,並且因爲頁面的轉換方式,沒法肯定,某一個公共模板是否有多個實例存在於當前dom下,爲了解決這個問題。出現了構建模板間的溝通機制以及修改模板命名

構建模板間的溝通機制
TODO:
沒有雙向綁定所致使的額外手段
主要功能:防止公共模板(尤爲存在模板以id做爲索引)多個實例存在於dom時,操做紊亂。如今的想法是,作個當前系統運行時模板狀態集合以及模板操做的工具集。
模板集合的數據結構:

{
    tempName:[
            {        
                status:$num,//表明當前的模板是在顯示,仍是被隱藏,被銷燬的會被直接幹
                node:$document,//document引用
                path:$stirng,//格式 nodeName[id|className]  整個路徑都小寫,父div索引
            }
        ]
    }
}

模板操做的工具集的功能:

  1. 可按照模板名稱和加載當前模板(模板加載方式是$(div).html(template))的父div element或索引來查到具體模板實例。

  2. 封裝個具體查詢模板下dom的方法,方便操做,要兼容jquery。

  3. 封裝模板加載函數,用來替換當前的模板加載方式,要兼容jquery。

修改模板命名
因爲沒有比較好的文件結構,模板命名就變得尤其重要,爲此,咱們能夠適當的在模板內添加一些註釋(註釋的主要做用:經過debug頁面元素,能夠快速查找到模板文件)。

重寫模板渲染流程
TODO:
現有的模板渲染函數過於簡單,也爲了減小模板不容許寫js所帶來的麻煩,須要重寫此函數以及添加模板filter機制。
filter機制:就是在讀取模板的時候,若是遇到自定義的標籤,就將其進行替換,並在模板加載到dom後,當即加載相應的js特效(這個地方遇到的問題,是如何辨識以及保存相應的dom信息,html comments?)。
重寫後的渲染函數要達到:

/**
* 支持for循環和if斷定 。這個屬於額外的,可在實現基礎功能後,再來作這個。
* option 選項:{
*    method:'after|html|before',//模板加載方式,默認html 兼容jquery語法
*    dataFill:$fn|$string,//當遇到要渲染的數據爲undefined或null是,要作的處理,默認渲染成""。
*    interpolate: /\{\{(.+?)\}\}/g //自行改寫匹配內容   默認匹配{{...}}
*   
*}
* 
*
* @data #object 數據
* @elem #document  
* @callback #func 可選 渲染成功後的回調函數
* @option #ojbect 可選 配置
* 
* return obj 用到的filter引用,數據結構待思考
*/
function render(data,elem,callback,option){
    //...實現
    //...修改模板狀態,要考慮模板嵌套的問題
    
}

實現可參考 underscore.js的template函數。或者avalon.js的bindingHandlers. each處理。

合併模板
模板的不能太過單薄,一個頁面展現出來後僅須要用到只有6到10個模板就能夠了。像<div id='abc'></div>這種一行代碼一個模板文件的,注意刪除。

轉移模板中的javascript
爲何要轉移模板中的javascript代碼呢?很簡單,在模板中寫的javascript代碼沒法debug

view操做重構

如何重構:
TODO:
相似avalon的雙工綁定(觀察者模式)。但因爲和服務器交互的數據不會以model的形式進行緩存,就只能在dom之間封裝觀察者模式。
使用場景:兩個input的值同步。
作這個以前,要把模板操做的工具集寫好,在實現雙向的時候,要好好思考這麼一個場景,是否是有多個地方,對同一個數據源作了修改?

/**
* 實現個簡單的,先不考慮集合的問題
* source/target爲object時,數據結構爲:
* {
*   ele:node,
*  
*   attr:string,//attributeName,不填的話,根據ele是不是表單元素 默認爲 value或 text
* }
* @source object|node 
* @target object|node
*
* return object 返回一個object,包含,unwatch和fire方法,用來解除觀察和觸發觀察
*
*/
function watch(source,target){
    
}
/**
* 注意,來回觸發,形成的死循環問題。
*
*/
function duplexWatch(t1,t2){
    return {
        "":""
    }
}
相關文章
相關標籤/搜索