收藏本站 收藏本站
積木網首頁 - 軟件測試 - 常用手冊 - 站長工具 - 技術社區
首頁 > JavaScript > JavaScript技巧 > 正文

首頁 - PHP - 數據庫 - 操作系統 - 游戲開發 - JS - Android - MySql - Redis - MongoDB - Win8 - Shell編程 - DOS命令 - jQuery - CSS樣式 - Python - Perl

Access - Oracle - DB2 - SQLServer - MsSql2008 - MsSql2005 - Sqlite - PostgreSQL - node.js - extjs - JavaScript vbs - Powershell - Ruby

淺析Angular2子模塊以及異步加載

用Angular2開發一個大型的應用,我們通常都需要分模塊進行開發。例如將某一個功能的相關頁面和功能放在一個模塊里面,這樣既可以實現系統的松耦合,給開發和后期的維護帶來很大的便利。同時,對于子模塊,我們還可以使用延時加載,這樣可以減少初始加載的文件的大小。在這篇文章中,我們就來看看在Angular2框架下怎么實現子模塊及其延時加載。

可以在這里查看本文使用的實例 。該實例基于上篇文章Angular2使用Guard和Resolve進行驗證和權限控制 所用的實例,并在它基礎上添加了一個lazy的模塊,以及將現有的todo模塊配置成延時加載方式。

為了體現啟用延時加載前后的包的大小變化,以及啟用壓縮后的變化,在這個教程里面,使用了angular-cli創建項目腳手架,并用它來進行測試和打包。有關angular-cli的使用請查看 官網 。在這篇文章我們使用的angular-cli的版本是1.0.0-beta.21。如果你使用的是別的版本,可能結果就會不一樣。甚至有些錯誤,我們在最后會說明當前版本angular-cli的bug。

模塊設計

在開發Angular2應用時,像組件設計、路由設計以外,對于一個較大型的應用,我們還需要設計模塊。例如,將一個應用分成幾個功能模塊,以及有哪些公用模塊。公用模塊里面應該放公用的service類,例如權限驗證、登錄、獲取用戶信息、全局的錯誤處理、工具類等,還有封裝的指令或組件。而在某一個功能模塊里面,只處理這個模塊里面的業務,盡量不和其他模塊交互。

拿之前教程中的TodoList應用來說,只有home頁面和2個todo頁面,我們把todo相關的功能放在一個子模塊里面,為了演示,又加了一個簡單的名字叫lazy的模塊。我們將把todo模塊和lazy模塊配置成延時加載的模塊。

子模塊開發

接下來再看看子模塊的開發。其實在之前的例子中,就把todo相關的組件放在了一個模塊里面。但是卻沒有強調子模塊開發需要注意的地方,甚至有些配置可能沒有采用子模塊的方式進行配置。這里,我們就主要說明一下需要注意的地方,如果要查看完整的代碼,請參考 實例源代碼 。

子模塊路由

首先需要注意的是路由。在之前的例子中,我們把todo相關的路由定義在一個文件中,然后在app的路由定義中把所有路由合并到一起。 todo.routes.ts 的內容如下:

// 省略import
export const TodoRoutes: Route[] = [
  {
    path: 'todo',
    canActivateChild: [MyTodoGuard],
    children: [
      { path: 'list', component: TodoListComponent, resolve: { todos: MyTodoResolver } },
      { path: 'detail/:id', component: TodoDetailComponent, canDeactivate: [ CanLeaveTodoDetailGuard ] }
    ]
  }
];

然后在 app.routes.ts 中定義一個路由模塊:

const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', component: HomeComponent },
  ...TodoRoutes // 這里就是將TodoRoutes列表里的內容合并到routes
];
@NgModule({
 imports: [ RouterModule.forRoot(routes) ],
 exports: [ RouterModule ]
})
export classAppRoutingModule{ }

最后,在AppModule里面引入這個路由模塊。

這種方式實現的路由無法實現子模塊的延時加載,要實現延時加載,首先要將todo模塊的路由修改成子路由模塊,也就是要修改 todo.routes.ts :

// 省略import
export const TodoRoutes: Route[] = [
  {
    path: 'todo',
    canActivateChild: [MyTodoGuard],
    children: [
      { path: 'list', component: TodoListComponent, resolve: { todos: MyTodoResolver } },
      { path: 'detail/:id', component: TodoDetailComponent, canDeactivate: [ CanLeaveTodoDetailGuard ] }
    ]
  }
];
// 通過下面的方式定義了一個子路由模塊
@NgModule({
 imports: [ RouterModule.forChild(TodoRoutes) ],
 exports: [ RouterModule ]
})
export classTodoRoutingModule{ }

這里,我們定義了一個子路由模塊, TodoRoutingModule ,它使用 RouterModule.forChild(TodoRoutes) 來創建。跟整個App的路由模塊比較的話,主路由模塊使用 RouterModule.forRoot(routes) 來定義。

定義好了子路由模塊,我們就在子模塊里面引入它既可:

// 省略import
@NgModule({
 imports: [CommonModule, FormsModule, TodoRoutingModule ],
 declarations: [TodoListComponent, TodoDetailComponent, TodoItemComponent],
 providers: [TodoService, MyTodoResolver, MyTodoGuard, CanLeaveTodoDetailGuard]
})
export classTodoModule{}

這樣,我們就定義好了一個子模塊。當用戶打開 /todo/list 或 /todo/detail/* 時,這個子模塊里面的相關頁面就會展示,它也不會跟其他模塊有任何交互。也就是說,進入和離開這個子模塊,都是通過路由跳轉實現。這個子模塊也是完全獨立的,可以獨立開發,也可以很容易就用到其他應用里面。

延時加載子模塊

下面,我們就可以通過修改路由的配置,使得todo模塊實現延時加載。Angular的路由模塊已經提供了 loadChildren 定義可以直接幫我們實現該功能。下面就是新的app路由定義

const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', component: HomeComponent },
  { path: 'todo', loadChildren: 'app/todo/todo.module#TodoModule' },
  { path: 'lazy', loadChildren: 'app/lazy/lazy.module#LazyModule' }
];

@NgModule({
 imports: [ RouterModule.forRoot(routes) ],
 exports: [ RouterModule ]
})
export classAppRoutingModule{ }

在這里,我們對于 todo 路徑,交給 app/todo/todo.module 里面的 TodoModule 模塊處理。而在 TodoModule 模塊里,已經有一個子路由的定義。

最后,再修改 app.module.ts ,保證它里面不再引入 TodoModule 。如此一來,我們在主模塊AppModule里面,沒有引入 todo 模塊的任何組件或服務。這樣就能在完全脫離 TodoModule 模塊的情況下,運行主模塊的功能。當用戶打開 /todo 里面的url時,就加載 app/lazy/lazy.module 里面的 LazyModule 模塊,并交由它來處理響應的url。

總結一下,實現延時加載子模塊,主要是要注意下面幾點:

子模塊的路由用 RouterModule.forChild(TodoRoutes) 方式定義。 主模塊不要引入子模塊,也不要引入子模塊的任何組件或服務,否則子模塊就會被打包進主模塊里。 只有子模塊才會用到的Service在子模塊的 providers 里面定義,如果是主模塊和子模塊都會用到的Service就用公用模塊的方式定義。要注意這個Service的實例只能有一個。

運行

接下來我們來看看運行的結果。(注意根據運行環境不同,文件大小會不一樣)

不啟用延時加載

首先,我們在 app.module.ts 引入 TodoModule ,這樣 todo 模塊不是延時加載的,只有 lazy 模塊是延時加載的。我們使用 ng serve 的方式運行測試服務器,并打開頁面,打開幾個頁面以后,網絡請求如下:

查看圖片

從圖中可以看到,有一個3.4M的main的js文件,下面的 1.chunk.js 的 lazy 模塊延時加載的。打包的文件確實是非常的大,因為lazy模塊非常簡單,只是顯示了一個字符串在模板里。所以它的大小也非常小,才5.8k。

延時加載模式

下面在把 TodoModule 模塊從 app.module.ts 去掉,這樣, todo 模塊就是延時加載的,再看一下網絡請求:

查看圖片

這下main文件變成了3.1M,lazy模塊對應的js文件是 1.chunk.js ,還是5.8k,todo模塊對應的文件 0.chunk.js 是324K。可以看見一個很簡單的todo模塊,里面有service, rosolver, guards, 還有3個組件,里面分別都有模塊、css,雖然文件不少,但是他們的實現實際上都很小。只是一個模塊的文件,在未壓縮的情況下就有300多K,讓我這個Angular2的忠實粉絲都無語。

延時加載-prod模式

一般我們在部署應用的時候,都會使用壓縮、混淆、合并等方法來減少最終文件的大小。使用angular-cli工具,除了在編譯的時候提供打包的功能,甚至在測試的時候,也可以啟用壓縮選項。我們可以運行 ng serve -pro 來使用 prod 模式來啟動測試服務器。在啟動的過程中,可以看到很多類似下面的日志:

WARNING in 0.005fea95566fdabe23df.chunk.js from UglifyJs
Dropping unused function scheduleMicroTask [/Users/mavlarn/mydev/blog/angular2-tutorial/angular2-routes-lazy-module-webpack/~/@angular/forms/src/facade/lang.js:21,0]

可以看出,angular-cli的 prod 模式下編譯的時候,去除了很多不需要的代碼,這就是angular的 Tree Shaking 的功能。

運行以后,網絡請求如下:

查看圖片

這下main文件減少到了221K,lazy模塊對應的js文件是 1.chunk.js ,只有1.0k,todo模塊對應的文件 0.chunk.js 是17.9K。總共大小大概是240K左右,如果再使用GZip壓縮,應該可以到6,70K左右。在官方文檔里提到,一個Angular2的簡單實例,通過Tree Shaking、壓縮、GZip,最終下載的包大小有50K。我們這個實例畢竟稍微復雜,實現了大多數的通用功能,如路由、guard、resolver、表單,也是用到了Rxjs里的 Observable ,所以最終壓縮后能有70K左右的話,也符合官方文檔的說法。

編譯后

最后,我們再使用 ng build --prod 來看看用prod模式編譯后的大小:

查看圖片

結果出乎意外,main文件的大小比上面在prod模式下運行測試服務器大很多,達到800多K。應該是編譯過程需要某些參數,或者是當前的angular-cli有什么bug。

再使用 ng build --prod --aot 編譯,main文件的大小是446K。雖然小了一點,但是也不符合預期。

總結

先說延時加載,應該都知道可以減少第一次加載的文件的大小。特別是當某個模塊使用了一些比較大第三方的js庫,例如圖形庫等,那么,把這些模塊獨立出來,使用延時加載的方式,可以大大減少首次加載的時間。對于Angular2的應用來說,如果我們要定義 Component ,就從 @angular/core 里面引入 Component ,需要定義路由就從 @angular/router 里面引入`Router。所以,只要我們設計好了整個App的模塊、組件、路由,我們就可以利用延時加載的功能使得首頁文件盡可能的小。

使用模塊化的開發,也能給我們的開發和維護帶來很大的便利,項目越大越大,模塊化和組件化帶來的便利就越明顯。

目前Angular2的天坑

在網上,經常可以看到一些文章說Angular1或者2的一些坑。實際上,大部分都是因為使用不當,或者沒有按照最佳實踐去使用,特別是Angular1。雖然Angular1有本質上的性能問題,但是,通過良好的整體設計、良好的 代碼規范和質量,還是可以開發出很流暢的手機web應用。

但是,在準備這篇文章中的實例時,卻遇到了幾個嚴重的問題,讓我這個Angular2的忠實粉絲也很無奈。

Angular 2.2.2及以上版本的BUG

我在實例中使用的Angular的版本是2.2.1,如果用的版本是2.2.2 ~ 2.3.0之間,在運行或編譯的時候,可能會出現如下的錯誤:

ngCompiler.ReflectorHost is not a constructor
TypeError: ngCompiler.ReflectorHost is not a constructor

可以上Github查看該 issue 的情況。如果遇到這種問題,只能先使用2.2.1的版本。

Angular-Router

在這個實例中,延時加載的todo模塊里面有一個service,我們使用Angular的依賴注入的功能自動初始化以及諸如這個服務的實例。但是,在3.1.2及以上的版本里面,這個服務會被創建多次,每次激活相關路由的時候,就會創建一次。而且,只有在延時加載的模式才會發生這種錯誤。相關 issue

TypeScript

在我之前的教程里,判斷用戶是否具有某種權限,使用了如下的方法:

hasRole(role: string): boolean {
  return this.account && this.account.roles.includes(role);
}

但是,更新了TypeScript以后,該方法就不存在了,原因可以查看 這個 .

所以改成了用 indexOf(role) > 0 來判斷列表里是否存在一個字符串。

雖然目前Angular還不是十分穩定,有一些Bug,甚至TypeScript也不穩定,但是,相信這些問題都能夠很快解決。而且隨著框架越來越成熟,也會越來越穩定。

而且,Angular2+Typescript的開發方式也十分便利,Typescript的強類型檢查能夠幫助我們減少編碼的錯誤,提高效率。而且,我們也可以很方便的查看框架的API,能省去很多查資料的時間。

Angular2的很多思想非常適用于開發大型的應用。如果開發過大型的Java項目,就會發現學習Angular2是一件非常容易的事情。Angular2引入了很多面向對象的框架的思想,而這些,都是在面向對象領域開發大型項目的多年開發經驗。這些經驗應用到前端開發,也能幫助我們更方便的開發和維護大型的前端項目。

雖然,Angular2的應用最終的打包文件非常大(我們這個實例即使壓縮完后也有70K左右,但是如果用VUE的話會比這個小很多),但是隨著Angular2的越來越穩定,各種開發工具越來越成熟,相信文件大小的問題也能夠有一個比較好的解決方案。因為Angular2的AOT、Tree Shaking的特性,為解決大小的問題提供了前提。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持積木網。

利用Vue.js+Node.js+MongoDB實現一個博客系統(附源碼)
前言這篇文章實現的博客系統使用Vue做前端框架,Node+express做后端,數據庫使用的是MongoDB。實現了用戶注冊、用戶登錄、博客管理(文章的修改和刪除

Vue.js 2.0學習教程之從基礎到組件詳解
前言最近這段時間里不停的做著Vue的技術分享,雖然不是什么深層次的代碼底能架構,如果底層架構真說出來,我就不會做Vue.js2.0從基礎到組件了,就

vue.js父組件使用外部對象的方法示例
最近在碰到有同學問我,vue父組件怎么使用外部對象,具體例子如下:有組件a:[email protected]="onClick"componenta/div//componeta...methods:{onClick(evt){//doSomething這里只能

本周排行

更新排行

強悍的草根IT技術社區,這里應該有您想要的! 友情鏈接:b2b電子商務
Copyright © 2010 Gimoo.Net. All Rights Rreserved  京ICP備05050695號
手游棋牌游戏运营
江苏骰宝平台 亚洲兴发pt第一老虎机官网 腾讯分分彩计划软件 手机版 双色球复式投注多少钱 重庆福彩欢乐生肖走势图 云尚娱乐云搜片 时时彩微信群 极速时时开奖统计 11码二中二多少组 棋牌代理加盟 时时彩手机计划软件 财神28捕鱼官方下载 下载app领彩金37 欢乐生肖免费计划 秒速时时软件 时时彩预测家app下载