欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

宁皓网 Angular:异步路由 有大用

我们可以设置一下,让应用延迟加载暂时不需要的模块,这些模块只有在用户访问了特定路由地址的时候才会被加载进来。

使用 canLoad 类型的守卫,可以控制模块的加载,也就是可以判断一些条件再决定是否要加载需要的模块。

模块也可以预加载,应用在加载了需要的模块以后,它会在后台加载剩下的所有的可以被预加载的模块。这个预加载的策略我们也可以自己去定制一个。

在这个策略里可以定制一下,哪些可以被预加载,哪些不能被预加载。

封面摄影:alfonso maseda varela

来自  https://ninghao.net/course/7719


# git commit -am "guard end"

# git push origin guard

# git branch 

# git log

# git checkout master

# git merge guard        合并分支到当前分支

# git log

# git push origin master

# git checkout -b async-route            #创建分支async-route并切换到它async-route

# git push origin async-route

# git  remote -v


# ng generate module modules/demo/admin --routing --module demo                #生成admin模块,--routing 表示同时生成路由模块,--module demo表示生成的模块放在demo下面

CREATE src/app/modules/demo/admin/admin-routing.module.ts (248 bytes)

CREATE src/app/modules/demo/admin/admin.module.ts (276 bytes)

UPDATE src/app/modules/demo/demo.module.ts (820 bytes)


# ng generate service core/services/selective-preloading-strategy                #生成一个service服务           

 CREATE src/app/core/services/selective-preloading-strategy.service.spec.ts (464 bytes)                                  

CREATE src/app/core/services/selective-preloading-strategy.service.ts (156 bytes)  






介绍与准备

1)介绍(异步路由)

应用可以按照功能分割成不同的功能模块,这种模块默认会全部被加载,就是用户在使用我们的应用的时候,浏览器会下载应用里的所有的代码,里面包含了所有的模块。

我们可以设置一下,让应用延迟加载暂时不需要的模块,这些模块只有在用户访问了特定路由地址的时候才会被加载进来。

使用 canLoad 类型的守卫,可以控制模块的加载,也就是可以判断一些条件再决定是否要加载需要的模块。

模块也可以预加载,应用在加载了需要的模块以后,它会在后台加载剩下的所有的可以被预加载的模块。这个预加载的策略我们也可以自己去定制一个。

在这个策略里可以定制一下,哪些可以被预加载,哪些不能被预加载。

来自   https://ninghao.net/video/7721#info


2)准备项目(异步路由)

检查一下项目当前的分支,显示是在 guard 这个分支上,

# git branch 

看一下这个分支上的提交,

# git log

最后一次提交是 Using resolve data。把项目当前的分支切换到 master,

# git checkout master

然后在这个分支上做一次合并,合并的是 guard 分支。

# git merge guard

合并之后再检查一下分支上的提交日志。

# git log

最后一次提交也会是 Using resolve data。

下面创建一个新的分支并且切换到这个分支上,分支的名字是 async-route 。

git checkout -b async-route

然后再推送一下本地的分支,推送到 origin 分支是 guard ,

git push origin guard

完成以后再推送一下 master 分支。

# git push origin master

 最后再推送一下刚才创建的 async-route 分支。

#  git push origin async-route

在这个课程里对项目的修改都会保存在这个 async-route 分支上,到时候你可以找到项目的远程,检查一下我在这个分支上做的一些提交。

#  git remote -v

我自己一直在 master 分支上工作

来自   https://ninghao.net/video/7722#info


异步路由

3)延迟加载模块(Lazy Loading)


随着应用里的功能越来越多,它的体积也会越来越大,加载应用的时间也就会越来越长,我们可以使用异步路由,延迟加载一些功能模块。也就是用户只有在请求了特定路由的时候,才会加载需要的模块。

比如在我们的应用里有个 Demo 模块,里面包含了一些演示,这个模块可以在用户访问 demo 路由的时候再加载这个模块。

回到项目,打开 demo 模块的路由模块,这里定义了一个 demo 路由,这个路由的 path 可以换成一个空白。

loadChildren

然后打开应用的路由模块,在这里可以添加一条新的路由,路由的 path 设置成 demo,在这条路由里再用一个 loadChildren,值是一个方法,用一下 import,先导入模块,位置是 ./modules/demo 里的 demo.module,这个 import 会返回 Promise,所以可以使用一个 then,得到的是个 module,返回 module 里的 DemoModule。

再打开 AppModule 模块。去掉模块里的 imports 里的这个 DemoModule 。这样上面导入这个模块的代码也就不再需要了,可以去掉它。

预览

回到浏览器试一下,打开浏览器的开发者工具,这里出现了一个错误,这是因为在 Demo 路由模块里有一条路由用到了 PostComponent 这个组件,回到项目,打开 DemoModule,在这个模块里可以导入 PostComponent 组件所在的模块 PostModule 。

然后再回到浏览器,现在应用还没有加载 DemoModule 模块的代码,打开 Network 选项卡,然后再访问一下 Demo ,显示的是 DemoComponent 组件 的视图。你会发现,页面加载的脚本里,会出现一个 modules-demo-demo-mdoule.js 这个脚本。

之前没有访问 demo ,并没有加载这个脚本文件,也就是没有加载这个 DemoModule 模块。因为现在我们在应用里设置了延迟加载这个模块。

来自  https://ninghao.net/video/7724#info

4)延迟加载 Admin 模块


打开 Demo,里面有一个 Admin 链接,访问它会重定向到登录页面,按一下登录,然后再打开 Admin。现在我想把 Admin 相关的东西单独放在一个模块里,然后配置路由,延迟加载这个模块。

Admin 模块

先去生成一个模块,ng generate 一个 module 放在 modules/demo/admin 目录的下面,加上一个 --routing 选项创建一个路由模块,再用 --module 选项设置一下要把这个模块放在哪个模块里,可以放在 demo 模块里。

# ng generate module modules/demo/admin --routing --module demo ( ng g m modules/demo/admin --routing --module demo )

回到项目,在 modules,demo,admin 的下面,可以找到刚才创建的这个 Admin 模块,还有一个对应的路由模块,就是这个 AdminRoutingModule,在 Admin 模块里已经导入了这个 AdminRoutingModule 模块。

另外还更新了一下 Demo 模块,在这个模块里导入了 Admin 模块。

清理 Demo 模块

下面可以去掉在 Demo 模块里导入的 PostModule,还有 declarations 里的 AdminComponent 组件,还有 AdminDashboardComponent 组件,因为这两个组件一会要把它们放在 Admin 模块里。

删除掉文件顶部不需要的导入。

清理 Demo 路由模块

然后打开 Demo 的路由模块,DemoRoutingModule,可以把这里的这个 admin 路由放在 Admin 模块的路由模块里,剪切一下这条路由。这样上面导入的这些跟这个剪切掉的路由相关的东西现在就可以去掉了。

Admin 路由

打开 Admin 模块的路由模块,把剪切的路由放在这个 routes 里面。有些东西的下面会出现红线,因为在这个文件里还没有导入它们,选中它,按一下 command + . ( command 加点 )选择这个 Add all missing imports ,这样编辑器会帮我们自动导入所有需要的东西。

Lazy loading

这个路由可以修改一下,去掉 admin,再打开 Demo 路由模块。在这里再定义一条路由,路由的 path 是 admin ,添加一个 loadChildren 属性,可以延迟加载 Admin 模块,提供一个方法,然后 import 的东西是 ./admin/admin.module ,接着用个 then, 方法有个 module ,要延迟加载的是 AdminModule 模块。

这样只有在访问 admin 这个路由地址的时候,应用才会加载需要的 AdminModule 模块。

Admin 模块

打开 Admin 模块,在这个模块的导入里需要再添加一个 PostModule,这是因为在 Admin 路由模块里定义的一条路由里面,用到了 PostComponent 组件,这个组件是在 PostMoudle 模块里,所以要在这个 Admin 模块导入这个 PostModule 模块。

在模块的 declarations 里面,还得再把模块里的组件添加进来, Admin 模块里有 AdminComponent,还有 AdminDashBoardComponent。

预览

回到浏览器再试一下,打开 Demo,会载入 DemoModule 模块。再打开 Admin,Login 之后,再打开 Admin。 你会发现并没有载入 Admin 模块。

这个是因为,在这个 Demo 模块里面,在它的 imports 里面,导入了 AdminModule ,这个模块现在要 Loay load,就是延迟加载,所以可以从模块的 imports 里面去掉 AdminModule ,再删除掉上面的没用的 import AdminModule 的代码。

然后回到浏览器再试一下。

打开 Demo,然后点击 Admin,虽然重定向到了登录页面,但是浏览器已经载入了 Admin 模块。

来自  https://ninghao.net/video/7725#info

5)canLoad:是否加载模块


打开 Demo,访问一下 Admin,访问这个 admin 路由会载入 AdminModule 模块,因为配置了这条路由,让它延迟加载 AdminModule 模块。这条路由还用了 AuthGuard,它会检查用户的登录状态,如果还没登录,就会打开这个登录页面。

用户在没登录情况下,访问 admin,也会加载对应的模块,我们可以在这个路由上再添加一个守卫,让它决定是否要加载模块。比如在访问 admin 的时候可以检查用户的登录状态,如果用户还没有登录,就不加载 AdminModule 模块。

这里需要用的是一个 canLoad 类型的 Guard,就是实施了 CanLoad 接口的守卫。

回到项目,可以打开之前我们创建的 AuthGuard 这个守卫。可以继续再让这个守卫实施一下 CanLoad 接口,这样这个 AuthGuard 里面需要添加一个 canLoad 方法,选中这个类,按一下 command + . 选择实施 CanLoad 接口。

这样会在类里面添加一个 canLoad 方法。可以调整一下这个方法的位置,把它放在最下面。

守卫里实施了这个接口以后,就可以用在路由上,用它来决定是否可以加载模块。方法如果 return true 就可以加载,如果 return false 就不能加载。

在方法里,先在控制台上输出一行文字,Auth guard canLoad.

然后做一下判断,判断的条件是 this.authService.isLoggedIn,如果用户是登录的状态,就可以 return true。如果没登录,可以用一下 this.router.navigate 方法,给它一个数组参数,地址是 /demo/login,就是把用户带到这个地址上。

我的编辑器代码高亮出了点问题,可以调一下,上面这个方法的返回的值,把这个 boolean 还有 urlTree 放在最后。

image.png

使用 canLoad

下面找个地方去用一下这个守卫,打开 Demo 模块的路由模块,找到 admin 这条路由,在这条路由里添加一个 canLoad ,要使用的守卫是 AuthGuard。

预览

回到浏览器上试一下,先先回到 Posts,刷新一下,然后再打开 Demo,按一下 Admin,注意这次访问 admin 的时候并没有加载 Admin 模块,因为现在这条路由上用了 canLoad 类型的守卫,这个守卫判断了一下用户的登录状态,发现用户没登录,所以就没加载 Admin 模块,然后把用户带到了这个 login 页面上。

看一下控制台,你会发现,控制台上输出了一个 Auth guard canLoad,说明执行了 AuthGuard 守卫里的 canLoad 方法。再回到 Network 选择卡。

按一下 Login, 然后打开 Admin,这次就正常加载了 Admin 模块。

来自   https://ninghao.net/video/7726


6)预加载模块(Preloading)


模块可以随着应用加载以后一块儿被加载,这种叫 Eager loading,比如应用的 AppModule 模块,还有应用里的 PostModule 模块。模块也可以只在需要它的时候被加载,比如用户访问了特定的路由之后才会加载指定的模块,这种加载叫 Lazy loading。

还有一种加载方式叫做 Preloading,预加载。用户打开应用之后,会加载 AppModule 模块,还有应用一开始就需要的 PostModule 模块。加载完这些模块以后,可以再去把延迟加载的模块也加载进来。比如在我们的应用里,DemoModule 就是一个延迟加载的模块,所以这个模块是可以被预加载的。

具体要预加载哪些可以被加载的模块,是预加载策略决定的,默认就是不预加载任何的模块,还有一种策略是加载所有可以被预加载的模块。

preloadingStrategy

打开项目里的 App 路由模块,在这个 RouterModule 的 forRoot 方法里面,给它添加一个第二个参数,它是一个对象,在这个对象里,添加一个 preloadingStrategy,设置一下预加载的策略,这里可以用一下 PreloadAllModules 这个预加载策略。这个东西来自 @angular/router 。

预览

回到浏览器预览一下,打开应用的首页,刷新一下页面,你会发现,加载的脚本文件里面,会包含 DemoModule 模块。这个模块之前是设置成了延迟加载的模块。就是只有用户在访问 Demo 这个路由的时候,才会加载这个模块。

但是现在我们把应用的预加载的策略设置成了 PreloadAllModules ,所以应用就会预加载所有的可以被预加载的模块。

不过,在我们的应用里,还有一个 AdminModule 模块也是一个延迟加载的模块,但是这个模块没有被预加载。这是因为在 admin 这个路由里面,用了一个 canLoad 守卫,这个守卫会阻止模块被预加载。

我们可以试一下,先去掉这个路由上用的 canLoad 守卫。然后回到浏览器再试一下,刷新一下页面,你会发现,这次在加载的脚本里面,就会出现 AdminModule 模块了。

来自  https://ninghao.net/video/7727#info

7)自定义预加载策略(Custom Preloading Strategy)


Angular 自带的 PreloadAllModules 这个预加载策略,会预加载所有可以预加载的模块。我们也可以去创建自己的预加载策略,这样可以自己控制哪些模块可以预加载,哪些模块不能预加载。

下面我们就去创建一个预加载策略,这个策略会根据路由上的 preload 数据,决定是否预加载模块。如果在路由数据里的 preload 属性的值设置成了 true,就可以预加载对应的模块。

创建自定义策略

在终端,先生成一个服务,ng generate 一个 service 放在 core/services 里面,名字是 selective-preloading-strategy 。

# ng g s core/services/selective-preloading-strategy  ( ng generate service core/services/selective-preloading-strategy )

回到项目,打开刚才创建的这个服务,这个服务要实施一下 PreloadingStrategy ,这就需要在这个类里面,添加一个 preload 方法,选中这个类的名字,按一下 command + . 选择实施 PreloadingStrategy。

编辑器会自动在这个类里面添加一个需要的方法。

这个 preload 里面,有个 route 参数,它是应用的路由,修改一下导入的方法,它的类型是 Route  。

preload 里面还有一个方法参数,方法的名字可以换成 load ,执行这个方法就可以预加载路由对应的模块。这个 preload 方法最终返回的是一个 Observable 类型的值。

可以做一下判断,检查 route ,也就是路由上面,有没有 data 属性,&& 如果有,再检查 route.data.preload ,就是 data 属性里的 preload 属性的值是 true 。满足这两个条件,先在控制台上输出点文字,输出 Preloaded ,再加上 route 的 path。

然后 return 一下 执行 load() 方法返回的结果。这样就会预加载这个路由对应的模块了。

不然的话,我们可以 return 一个 Observable ,这个 Observable 的值是 null 。 用一下 rxjs 里的 of 去创建这个 Observable。

使用自定义策略

下面再打开 App 模块的路由模块,修改一下要使用的 preloadingStrategy,换成我们自定义的这个 SelectivePreloadingStrategyService 。

预览

回到浏览器可以再预览一下。打开应用的首页,刷新一下页面。

你会发现这次要加载的模块里,没有 DemoModule 模块,也没出现 AdminModule 模块。

回到项目,打开 App 模块的路由模块,在 demo 这条路由里面,给它添加一个 data 属性,它是一个对象,里面添加一个 preload ,值设置成 true。在我们自定义的预加载策略里面,会用到路由里的这个 data 属性里的 preload 它的值,如果它是 true ,就会预加载这个模块。

再回到浏览器试一下,刷新一下应用的首页,这次应用就预加载了 DemoModule 模块,因为在 demo 这个路由里,它的 data 里面的 preload 属性的值是 true。

应用里使用的自定义的预加载策略,发现路由的这个 preload 数据的值是 true,就会预加载对应的模块。

打开开发者工作的控制台,你会发现这里输出了一个 Preloaded ,demo 。

来自  https://ninghao.net/video/7728







普通分类: