国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

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

2019-11-19 16:44:58
字體:
供稿:網(wǎng)友

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

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

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

模塊設(shè)計

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

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

子模塊開發(fā)

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

子模塊路由

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

// 省略importexport 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列表里的內(nèi)容合并到routes];@NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ]})export classAppRoutingModule{ }

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

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

// 省略importexport 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) 來創(chuàng)建。跟整個App的路由模塊比較的話,主路由模塊使用 RouterModule.forRoot(routes) 來定義。

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

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

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

延時加載子模塊

下面,我們就可以通過修改路由的配置,使得todo模塊實現(xiàn)延時加載。Angular的路由模塊已經(jīng)提供了 loadChildren 定義可以直接幫我們實現(xiàn)該功能。下面就是新的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 模塊里,已經(jīng)有一個子路由的定義。

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

總結(jié)一下,實現(xiàn)延時加載子模塊,主要是要注意下面幾點:

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

運行

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

不啟用延時加載

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

 

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

延時加載模式

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

 

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

延時加載-prod模式

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

WARNING in 0.005fea95566fdabe23df.chunk.js from UglifyJsDropping 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 的功能。

運行以后,網(wǎng)絡(luò)請求如下:

 

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

編譯后

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

 

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

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

總結(jié)

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

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

目前Angular2的天坑

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

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

Angular 2.2.2及以上版本的BUG

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

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

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

Angular-Router

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

TypeScript

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

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

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

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

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

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

Angular2的很多思想非常適用于開發(fā)大型的應(yīng)用。如果開發(fā)過大型的Java項目,就會發(fā)現(xiàn)學(xué)習(xí)Angular2是一件非常容易的事情。Angular2引入了很多面向?qū)ο蟮目蚣艿乃枷耄@些,都是在面向?qū)ο箢I(lǐng)域開發(fā)大型項目的多年開發(fā)經(jīng)驗。這些經(jīng)驗應(yīng)用到前端開發(fā),也能幫助我們更方便的開發(fā)和維護大型的前端項目。

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 乌拉特后旗| 磴口县| 义乌市| 同心县| 苏尼特右旗| 北流市| 襄汾县| 古田县| 阿拉尔市| 肇州县| 万盛区| 昂仁县| 阿拉善盟| 枣庄市| 洛宁县| 平阳县| 阜平县| 河西区| 资阳市| 祁门县| 天津市| 三明市| 湾仔区| 海阳市| 沈丘县| 尉氏县| 沅陵县| 平塘县| 噶尔县| 江门市| 呼玛县| 临武县| 民丰县| 铜陵市| 延津县| 麟游县| 房山区| 房山区| 泰州市| 肇庆市| 新泰市|