為 Angular 1.x 添加依賴注入 Decorator

最近在研究從 Angular 1.x 切換到 Angular 2,因為目前 Angular 2 正處于 RC 階段還會有比較多的變化谬泌,暫時還是使用 1.x 。 為了以后能比較方便的遷移殉摔,所以雖然用的是 1.x 但是在寫法上還是希望更接近于 2.0 的寫法浊仆,也就有了以下我想要實現(xiàn)的功能:使用 decorator 來處理 angular 1.x 的依賴注入。

在設計芳杏、實現(xiàn)的時候我盡量只使用 ES6 和 ES7 的新特性矩屁,因為在試用 Angular 2 的時候發(fā)現(xiàn) Typescript 編譯確實是太慢了,而且 Typescript 的靜態(tài)類型檢查雖然是我喜歡的但是使用起來對開發(fā)效率影響比較大蚜锨,綜合平衡后決定未來還是基于 ES 標準開發(fā)就好了档插,不打算切換到 Typescript 或者其它 XType。

Angular 1.x 依賴注入的現(xiàn)狀和問題

使用過 Angular 的朋友都知道依賴注入是 Angular 中非常重要的特性亚再,在 1.x 時代有3種方式可以實現(xiàn)依賴注入:

方式一:

angular.module('app', [])
    .controller('mainCtrl', [
        '$scope',
        function($scope) { ... }
    ])

方式二:

function mainCtrl($scope) { ... }
mainCtrl.$inject = ['$scope'];
angular.module('app', [])
    .controller('mainCtrl', mainCtrl);

方式三:

angular.module('app', [])
    .controller(
        'mainCtrl', 
        function($scope) { ... }
    );

方式1郭膛、2都需要寫兩次需要注入的服務并且配置服務名稱和編寫入?yún)r的順序必需一致,比較影響開發(fā)效率而且容易出錯氛悬。而方式3在框架內(nèi)部是通過分析函數(shù)的入?yún)⒚Q來確定需要注入的服務则剃,使用起來非常方便但是如果對JS代碼進行壓縮處理后耘柱,框架就無法得知真正需要注入的服務了。

所以棍现,目前這三種依賴注入的方式都存在一些問題调煎。不過基本方式三和 ng-annotate 還是可以比較完美的解決依賴注入的問題,簡單來說 ng-annotate 就是通過自動構(gòu)建工具在壓縮JS代碼前將方式三轉(zhuǎn)換成方式二己肮,而這一切都是由構(gòu)建工具自動完成我們在開發(fā)的時候并不用關(guān)心士袄。

廢話了這么多只是為了說明本文希望解決的問題:其實很簡單,就是希望在依賴注入的時候只寫一次需要注入的服務谎僻!雖然使用 ng-annotate 已經(jīng)可以實現(xiàn)娄柳,但是我希望用最新的標準來處理這個問題,以便未來更方便的向 Angular2 過渡艘绍,這種方式就是使用 Decorator赤拒。

使用 Decorator 解決 angular 1.x 的依賴注入問題

Decorator 是 ES7 的一個提案,目前通過 Babel 或者 Typescropt 已經(jīng)可以提前享用诱鞠。嘗試過 Angular2 的朋友應該知道在 Angular2 中就引入了很多的 Decorator挎挖,例如:

import { Component } from '@angular/core';

@Component({
...
})
export class MainComponent {
...
}

這里的 Component 就是一個 Decorator, 使用過 Python 的朋友應該也非常熟悉這種語法。簡單來說 Decorator 就是一個模板航夺,可以在編譯階段對被裝飾的類或類成員進行修改(目前通過Babel使用的時候貌似還是在Runtime階段起使用)蕉朵。

在使用 Decorator 解決依賴注入問題的時候我首先想到的就是 ng-annotate 的方案,在 Decorator 里去分析被裝飾類構(gòu)造函數(shù)的入?yún)⒘斜矸蟠妫缓鬄楸谎b飾類添加$inject屬性墓造,代碼如下:

function inject() {
    return function(target) {
        let services = angular.injector.$$annotate(target);
        target.$inject = services;
    }
}

@inject()
class MainComponent {
    constructor($scope) {
        ...
    }
}

其中angular.injector.$$annotate(target)就是 angular 中解析入?yún)⒚Q列表的函數(shù),之前還花了好長時間自己去實現(xiàn)锚烦,后來一想 angular 既然可以自動發(fā)現(xiàn)需要注入的服務肯定有完整的入?yún)⒎治雒倜觯痪驮谠创a的 src/auto/injector.js 里找到了。這種方式在沒有壓縮JS代碼的情況下測試運行沒有問題涮俄,$inject屬性正確添加蛉拙。但是,在壓縮JS代碼后就出現(xiàn)沒有找到aProvider這樣的錯誤了彻亲,閱讀 Babel 轉(zhuǎn)譯后的代碼也沒有發(fā)現(xiàn) MainComponent.$inject = [...] 這行代碼孕锄,所以我認為在 Babel 中 Decorator 其實是在 Runtime 中執(zhí)行而不是在編譯的時候執(zhí)行(難道我對編譯和運行時的理解有錯?還是提案里不是說的在編譯時執(zhí)行苞尝?)畸肆。

其實在 Github 中已經(jīng)有人為 Angular 1.x 的依賴注入提供了 Decorator 的實現(xiàn),大概的實現(xiàn)方式是:

function inject(...services) {
    return function(target) {
        target.$inject = services;
    }
}

@inject('$scope', '$http')
class MainComponent {
    constructor($scope, $http) {
        ...
    }
}

但是這種實現(xiàn)方式還是需要寫兩次要注入的服務并且入?yún)⒌捻樞蚺c配置的順序還必須一致宙址,無非是將MainComponent.$inject = []改成了@inject(...)轴脐,并不是我希望能達到的目的。但是基于這個思路我想到了第二種實現(xiàn)方案,通過 decorator 的入?yún)@取到需要注入的服務大咱,然后在 decorator 中去修改被裝飾類的構(gòu)造函數(shù)恬涧,在實例化被裝飾類時自動將注入的服務添加為實例的屬性。

如何在 decorator 中去修改被裝飾類的 constructor 呢碴巾?這就需要使用到 ES6 的一個新 API Proxy 了溯捆,具體實現(xiàn)如下:

function inject(...services) {
    return function(target, property, descriptor) {
        var p = new Proxy(target, {
            construct: function(target, args) {
                for(let i in services) {
                    target.prototype[services[i]] = args[i];
                }
                return new target(...args);
            }
        });
        p.$inject = services;

        return p;
    }
}

@inject('$scope', '$http')
class MainComponent {
    constructor() {
        this.$http.get(url).then(data => {
            this.$scope.data = data;
        })
    }
}

在 decorator 中我創(chuàng)建了一個 Proxy 實例來攔截被裝飾類的實例化行為,將需要注入的服務自動添加為被裝飾類的實例屬性厦瓢,從而在被裝飾類的實例中可以直接使用而不用關(guān)心依賴注入的細節(jié)(在 constructor 中不用再關(guān)心第一個入?yún)氖悄莻€服務提揍,第二個參數(shù)對應的是那個服務...)。當然旷痕,這只是一個最基礎的實現(xiàn)還可以更完善一些碳锈,例如:添加 $scope as scope 語法修改服務實例屬性名稱等。

而 Proxy 在 ES6 的文檔中有詳細說明欺抗,它可以提供一種攔截機制從而去改變被攔截對象的一些行為,配套的還有一個 Reflect 可以在修改被攔截對象后獲取對象原始的行為强重。

以上就是本文的全部绞呈,使用 decorator 可以實現(xiàn)很多有意思的功能,而從 Angular 1.x 向 2.0 遷移在 Angular 團隊內(nèi)部也在做很多的努力间景。不過我的真正目的還是脫離框架去實現(xiàn)一些常用功能模塊佃声,以便將來不管是用什么框架都能使用的到,最近在開發(fā)前后端的時候都在往這個方向努力倘要。在當前各個語言都擁有完善的包管理器的情況下圾亏,個人覺得一個大而全的框架已經(jīng)不是我需要的了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末封拧,一起剝皮案震驚了整個濱河市志鹃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泽西,老刑警劉巖曹铃,帶你破解...
    沈念sama閱讀 212,185評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捧杉,居然都是意外死亡陕见,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,445評論 3 385
  • 文/潘曉璐 我一進店門味抖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來评甜,“玉大人,你說我怎么就攤上這事仔涩∪炭溃” “怎么了?”我有些...
    開封第一講書人閱讀 157,684評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長承匣。 經(jīng)常有香客問我蓖乘,道長,這世上最難降的妖魔是什么韧骗? 我笑而不...
    開封第一講書人閱讀 56,564評論 1 284
  • 正文 為了忘掉前任嘉抒,我火速辦了婚禮,結(jié)果婚禮上袍暴,老公的妹妹穿的比我還像新娘些侍。我一直安慰自己,他們只是感情好政模,可當我...
    茶點故事閱讀 65,681評論 6 386
  • 文/花漫 我一把揭開白布岗宣。 她就那樣靜靜地躺著,像睡著了一般淋样。 火紅的嫁衣襯著肌膚如雪耗式。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,874評論 1 290
  • 那天趁猴,我揣著相機與錄音刊咳,去河邊找鬼。 笑死儡司,一個胖子當著我的面吹牛娱挨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捕犬,決...
    沈念sama閱讀 39,025評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼跷坝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了碉碉?” 一聲冷哼從身側(cè)響起柴钻,我...
    開封第一講書人閱讀 37,761評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎誉裆,沒想到半個月后顿颅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,217評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡足丢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,545評論 2 327
  • 正文 我和宋清朗相戀三年粱腻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斩跌。...
    茶點故事閱讀 38,694評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡绍些,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耀鸦,到底是詐尸還是另有隱情柬批,我是刑警寧澤啸澡,帶...
    沈念sama閱讀 34,351評論 4 332
  • 正文 年R本政府宣布,位于F島的核電站氮帐,受9級特大地震影響嗅虏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜上沐,卻給世界環(huán)境...
    茶點故事閱讀 39,988評論 3 315
  • 文/蒙蒙 一皮服、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧参咙,春花似錦龄广、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,778評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至净宵,卻和暖如春敲才,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背择葡。 一陣腳步聲響...
    開封第一講書人閱讀 32,007評論 1 266
  • 我被黑心中介騙來泰國打工归斤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刁岸。 一個月前我還...
    沈念sama閱讀 46,427評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像她我,于是被迫代替她去往敵國和親虹曙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,580評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Angular算是將后端開發(fā)工程化引入前端的先驅(qū)之一,而Dependency injection依賴注入(后面簡稱...
    點融黑幫閱讀 2,249評論 0 5
  • 1番舆、angularjs的幾大特性是什么酝碳? 雙向數(shù)據(jù)綁定、依賴注入恨狈、模板疏哗、指令、MVC/MVVM 2禾怠、列舉幾種常見的...
    秀才JaneBook閱讀 1,539評論 0 22
  • 目錄 Angular 4 依賴注入教程之一 依賴注入簡介 Angular 4 依賴注入教程之二 組件服務注入 An...
    semlinker閱讀 4,583評論 0 1
  • 版本:Angular 5.0.0-alpha 依賴注入是重要的應用設計模式返奉。它使用得非常廣泛,以至于幾乎每個人都稱...
    soojade閱讀 2,986評論 0 3
  • 今天下起了雪吗氏,干燥了一個冬季芽偏,卻在陽春三月下了一整天雪,為何這樣弦讽?春暖花開污尉,這場大雪過后應該不會再有這樣大的雪了膀哲。...
    合唱團閱讀 1,200評論 0 1