一、angular
1、angular组件通讯
(1)父子组件之间的交互(@Input/@Output/模板变量/@ViewChild)
(2)非父子组件(Service/借助service通过BehaviorSubject实现,可实现动态修改/localStorage)
(3)还可以利用Session等服务器端的解决方法
(4)providers、useExisting??
路由传参:(url形式如果拼接参数访问路由?)angular中的路由详解(2)—访问路由的一些姿势以及页面传参 - longkui - 博客园):
路由顺序
路由的顺序很重要,因为 Router
在匹配路由时使用“先到先得”策略,所以应该把具体的路由放置在前面。首先列出静态路径的路由,然后是一个与默认路由匹配的空路径路由。通配符路由是最后一个,因为它匹配每一个 URL,只有当其它路由都没有匹配时,Router
才会选择它。
通配符路由:
{ path: '**', component: }
path
为 **
的最后一条路由是通配符路由。如果请求的 URL 与前面列出的路径不匹配,路由器会选择这个路由,并把该用户送到指定页面。
重定向
{ path: '', redirectTo: '/first-component', pathMatch: 'full' }
pathMatch:'full'(
全路径完全匹配)
| 'prefix'(以 ' ' 开头时即可重定向)
嵌套路由
子路由放在父路由的 children
数组中
相对路由:
<a routerLink="../second-component">
this.router.navigate(['items'], { relativeTo: this.route });
this.router.navigate(['stu', id]);
this.router.navigate(['stu'], {queryParams: {id: '1',name: 'zhangsan'}]) ??not sure
可选的路由参数没有使用“?”和“&”符号分隔,因为它们将用在 URL 查询字符串中。它们是用“;”分隔的。这是矩阵 URL(Matrix URL)标记法。
绝对路由
this.router.navigateByUrl('home');
navigateByUrl()和navigate()的区别点是:navigateByUrl不是根据参数来确定路由地址的。
读取路由参数 :
1、路由参数-Route Parameters
this.route.snapshot.paramMap.get('id'); 组件实例永远不会被复用时才使用 snapshot
。
this.route.params.subscribe(params => {});??not sure
this.route.paramMap.pipe( switchMap((params: ParamMap) => params.get('id')!)) ); 路由器会复用同一个组件实例,而只是更新参数
------------------this.router.navigate(['/heroes', { id: heroId, foo: 'foo' }]);
2、查询参数及片段-query parameters:
this.route.queryParams.subscribe(queryParams => {});
const navigationExtras: NavigationExtras = { queryParamsHandling: 'preserve', preserveFragment: true };
------------------this.router.navigate([redirectUrl], navigationExtras);
惰性加载
const routes: Routes = [ {
path: 'items', loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
} ];
还要确保从 AppModule
中移除了 相应Module
路由守卫
路由器可以支持多种守卫接口:
-
用CanActivate来处理导航到某路由的情况。需要身份验证
-
用CanActivateChild来处理导航到某子路由的情况。会在任何子路由被激活之前运行。
-
用CanDeactivate来处理从当前路由离开的情况。决定如何处理未保存的更改。
-
用Resolve在路由激活之前获取路由数据。预先获取组件数据
-
用CanLoad来处理异步导航到某特性模块的情况。保护对特性模块的未授权加载
使用CanActivate,即使返回的是false,用户并没有权限访问该路由,但是相应的模块会被加载。使用CanLoad的话,用户也没有权限访问,相应的模块并不会被加载
2、angular设置style和css:
(1)[ngStyle]="{ 内联样式名称01:样式值,内联样式名称02:样式值}"
(2)[style.border-color]='level.color'
(3) [ngClass]="{ CSS类名01:布尔类型,CSS类名:布尔类型 }"
[ngClass]="styleMethod()"
坑01:styleMethod是一个自定义的方法,该方法返回的是一个对象,该对象的格式是: { CSS类名01:布尔类型,CSS类名:布尔类型 }
技巧01:对象的键最好用引号括起来,因为有的字符如果不用引号括起来就会报错,例如 _
技巧02:对象的键值如果有特殊字符就需要用引号括起来,否则会报错(PS: 最好都用引号括起来)
(4)[class.样式类名称]=“布尔类型”
3、ng-if 跟 ng-show/hide 的区别有哪些?
第一点区别是,ng-if
在后面表达式为 true
的时候才创建这个 dom
节点,控制dom节点的增删除来实现显示隐藏,ng-show/ng-hide 是初始时就创建了,用 display:block
和 display:none
来控制显示和不显示。
第二点区别是,ng-if
会(隐式地)产生新作用域,ng-switch
、 ng-include
等会动态创建一块界面的也是如此。
这样会导致,在 ng-if
中用基本变量绑定 ng-model
,并在外层 div
中把此 model
绑定给另一个显示区域,内层改变时,外层不会同步改变,因为此时已经是两个变量了。
ng-show
不存在此问题,因为它不自带一级作用域。
避免这类问题出现的办法是,始终将页面中的元素绑定到对象的属性(data.x)而不是直接绑定到基本变量(x)上。
4、angular生命周期
angular生命周期钩子 - 简书 (jianshu.com)
使用ngOnInit()有两个原因:
a:在构造函数之后马上执行复杂的初始化逻辑
b:在Angular设置完输入属性之后,对该组件进行准备。
ngAfterViewInit
在组件相应的视图初始化之后调用,它主要用于获取通过 @ViewChild 或 @ViewChildren 属性装饰器查询的视图元素。
初始化完组件视图及其子视图之后调用。第一次ngAfterContentChecked()之后调用,只调用一次。
6、angular时间格式化插件:moment,DatePipe(体积相比moment小,推荐)
7、动态创建组件:
8、angular中的三种类型指令:组件型、结构型、属性型
浅谈angular中的三种类型指令:组件型、结构型、属性型-js教程-PHP中文网
组件型指令 — 组件是特殊存在,拥有模板
结构型指令 — 通过添加和移除 DOM 元素改变 DOM 布局的指令,常用的有:*ngIf,*ngFor,ngSwitch
属性型指令 — 改变元素、组件或其它指令的外观和行为的指令,常用的有:NgClass,NgStyle
二、Location Strategy
1、hash路由模式
locaton.hash就是url中#后面的内容。
特性:
1.URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
2.hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
3.可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
4.我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。
2、history路由模式
HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
特性:
1.pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
2.我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
3.history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染);
4、history 模式在页面刷新的时候,会请求当前地址栏中完成的 url,这时需要服务器对这个 url 有处理,如果没有对应的文件,需要返回 index.html
5、history 模式因为是使用的 HTML5 的新规范,所以不能兼容低版本的浏览器
6、history 模式打包后,直接在浏览器中打开 /dist/index.html 会报错(原因是第 3 条指出的原因)
3、路由简单实现
三、vue模板语法与React JSX
React与Vue模板使用比较(一、vue模板与React JSX比较)_weixin_33862514的博客-CSDN博客
四、原理类
1、visual dom vs diff算法
visual dom:用JS模拟虚拟的DOM结构,生成虚拟的DOM,当数据更新是,对比DOM的变化,只更新需要更新的数据,从而减少"昂贵"的DOM操作;
作用:为了提高DOM重绘制的性能
【React】深入理解虚拟dom和diff算法_小小晴_的博客-CSDN博客
2、vue vs react
React、Vue和Angular框架比较_嘉婧的博客-CSDN博客
react生命周期
React生命周期分为三大周期,11个阶段,生命周期方法调用顺序分别如下。
(1)在创建期的五大阶段,调用方法的顺序如下。
-
getDetaultProps:定义默认属性数据。
-
getInitialState:初始化默认状态数据。
-
component WillMount:组件即将被构建。
-
render:渲染组件。
-
componentDidMount:组件构建完成
(2)在存在期的五大阶段,调用方法的顺序如下。
-
componentWillReceiveProps:组件即将接收新的属性数据。
-
shouldComponentUpdate:判断组件是否应该更新。
-
componnent WillUpdate:组件即将更新。
-
render:渲染组件。
-
componentDidUpdate:组件更新完成。
(3)在销毁期的一个阶段,调用方法 componentWillUnmount,表示组件即将被销毀。
3、Angular的更新机制-ChangeDetectionStrategy
angular提供了两种更新机制,default默认及onPush机制。准确的来说,应该说是一种机制。onPush是优化angular检测机制。
changeDetectionStrategy.Default默认机制
在angular执行检测机制之前,angular必须要检测到新的state值,如果不这样,那就没法玩了。因为angular的检测需要基于两个值的对比,才决定是否执行组件和试图的更新。
默认情况下,angular假设组件是不需要任何依赖的,而实际上,一个应用是由各种各样的state如胶水似的互相依赖的,所以才不得不保守的当组件的state一旦改变就执行检测,这就是脏检测的原理。更具体的来说,它在浏览器的事件、定时器、ajax和promise触发,就需要进行着这些检测。
changeDetectionStrategy.onPush机制
onPush机制其实就是在angular的检测中,上面说过,angular在检测过程中,是从root节点到每个节点组件,而onPush机制就是告诉angular说:“喂,你忙其他的吧,我这边不需要任何检测”。组件依赖于组件的输入更加pure,而且可以拥抱immutability。 但是使用这种机制的组件又是如何更新自身及视图的呢?方法具体的就是下面的几点:
1、当input的state改变的时候;
2、当前组件或其子组件触发的事件;
3、明确的执行ComponentRef.markForCheck()方法来通知angular;
4、通过在组件内部使用async pipe;
这种机制实际上是优化应用的策略之一。因为它减少了angular的检测工作量,而且让你的组件更加简洁,immutability。
Angular 变化检测详解 - 知乎
双向绑定原理??
OnPush策略的变更检测
当组件通过在@Component中添加 changeDetection: ChangeDetectionStrategy.OnPush,变更检测的变化:
#####不会触发的情况:
请求服务端数据,如xmlHttpRequest 回调中修改数据,不会触发变更检测,后续的该数据触发数据渲染也会延迟一步
定时事件,比如setTimeout,setInterval,promise等 不会触发变更检测
#####会触发的情况:
用户输入操作及事件,比如click, keyup,submit等,会触发变更检测!!
input传值发生了变化(值类型的值变化或者引用类型的引用变化或者不可变类型变化),会触发该子组件的变更检测!!
#####无法触发变更的时候需要手动触发
markForCheck() - 在组件的 metadata 中如果设置了 changeDetection:ChangeDetectionStrategy.OnPush 条件,那么变化检测不会再次执行,除非手动调用该方法, 该方法的意思是在变化监测时必须检测该组件。
detach() - 从变化检测树中分离变化检测器,该组件的变化检测器将不再执行变化检测,除非手动调用 reattach() 方法。
reattach() - 重新添加已分离的变化检测器,使得该组件及其子组件都能执行变化检测
detectChanges() - 从该组件到各个子组件执行一次变化检测
不是特殊情况,推荐使用 markForCheck 方法来触发检测,检测范围只会检测一条组件树分支上的数据。
原文链接:https://blog.csdn.net/Altaba/article/details/102942759
1、angular如何以及何时根据数据模型更新html
答:angular使用zone.js来进行变更检测,zone是一个跨异步任务持久化存在的执行上下文,同时angular提供了NgZone服务类,来在组件中使用zone的能力
a、执行时机:会在angular组件初始化、触发DOM事件、数据请求、宏任务、微任务、其他异步操作(websocket.onmessage\canvas.toBlob)时进行数据的变更检测
b、获取上下文以及生命周期:可以通过Zone.current来获取当前的上下文,可以通过Zone.current.fork()方法配置对应的生命周期钩子
c、手动触发变更检测:ngZone.run()方法的回调中执行一些异步函数,并在方法执行后的合适时间触发变更检测
d、手动指定不触发变更检测:ngZone.runOutSideAngular()方法的回调中添加一些不需要触发变更检测的函数
2、猴子补丁
答:在运行时添加或修改函数的默认行为,并且不会更改源代码的技术,而zone.js对DOM事件、HTTP请求、宏任务、微任务以及其他异步任务进行了包装,在监听到事件时会触发angular的tick()方法,并在tick()方法内部调用detectChange()方法实现变更检测
3、配置zone.js不需要监听的事件或者操作
答:定义zone-flags.ts文件,并在polyfills.ts文件中import 'zone.js';语句之前导入,zone-flags.ts文件内容定义方式如:
(window as any).__Zone_disable_requestAnimationFame = false; // 过滤需要监听的任务
(window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // 过滤需要监听的DOM事件
注意:配置文档:angular/MODULE.md at master · angular/angular · GitHub
5、angular模块详解
6、angular依赖注入
angular 依赖注入 多级注入器_yanyi24的博客-CSDN博客
js实现:
var services = { abc: 123, def: 456, ghi: 789 }, // 获取func的参数列表(依赖列表)
getFuncParams = function (func) {
var matches = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m);
if (matches && matches.length > 1)
return matches[1].replace(/\s+/, '').split(',');
return [];
}, // 根据参数列表(依赖列表)填充参数(依赖项)
setFuncParams = function (params) {
for (var i in params) {
params[i] = services[params[i]];
}
return params;
};// 激活器
function Activitor(func) {
var obj = {};
func.apply(obj, setFuncParams(getFuncParams(func)));
return obj;
}
// 定义新Service
function Service(abc, ghi) {
this.write = function () {
console.log(abc);
console.log(ghi);
}
}// 实例化Service并调用方法
var service = Activitor(Service);
console.log(service);
service.write();
单例服务
在 Angular 中有两种方式来生成单例服务:
- 把
@Injectable()
中的providedIn
属性设置为"root"
。 - 把该服务包含在
AppModule
或某个只会被AppModule
导入的模块中。
最好使用在服务自身的 @Injectable()
装饰器上设置 providedIn
属性的形式,因为 Angular 6.0 可以对这些服务进行摇树优化。
注入器冒泡
注入器冒泡的意思是:Angular查找服务的方式是优先在属于自己的注入器中查找服务,如果找不到,就向父组件、父模块的注入器中查找,直到找到根注入器,如果没有做特殊处理,此时,Angular就会报错!而Angular一旦找到需要的服务,就会立刻停止冒泡!
干预注入器冒泡
- @Self():只允许在自己的注入器中查找服务
- @Optional():使服务可选,也就是找到就用,没找到也不报错
- @SkipSelf():跳过自己的注入器,向父级注入器中查找
- @Host():在其宿主组件注入器中查找服务实例---宿主组件https://angular.cn/guide/dependency-injection-in-action#make-a-dependency-codeoptionalcode-and-limit-search-with-codehostcode
@Self装饰器规定只能在当前组件的注入器中查找被注入的对象,而@Host装饰器则允许在整个父组件树对应的注入器中查找被注入的对象。
惰性加载的模块,Angular会为每一个惰性加载模块重新生成一个注入器(模块级别)
useClass,useExisting,useValue
useClass:每次都会创建新的实例
useExisting调用多少次,只会进行一次实例化
useValue :定义和使用一个 InjectionToken 对象来为非类的依赖选择一个提供者令牌
muti
(多提供商),为一个token注册多个依赖
虽然 TypeScript 的 接口可以在类中提供类型支持,但它在依赖注入时却没有任何作用。在 TypeScript 中,接口只能作为类型检查,它没有可供 DI 框架使用的运行时表示形式或令牌。
- 当转译器把 TypeScript 转换成 JavaScript 时,接口就会消失,因为 JavaScript 没有接口。
- 由于 Angular 在运行期没有接口,所以该接口不能作为令牌,也不能注入它。
- 依赖注入时运行期间动态创建依赖
Angular 中有两个注入器层次结构:
ModuleInjector
层次结构 —— 使用@NgModule()
或@Injectable()
装饰器在此层次结构中配置ModuleInjector
。ElementInjector
层次结构 —— 在每个 DOM 元素上隐式创建。除非你在@Directive()
或@Component()
的providers
属性中进行配置,否则默认情况下,ElementInjector
为空
Angular 依赖注入 - 全面解析 - SegmentFault 思否
深入浅析Angular中怎么使用依赖注入-电脑自学网 (52zixue.com)
7、下一代编译与渲染管道Ivy
浅谈Angular工作原理 - 知乎 (zhihu.com)
Ivy编译, 即Angular 下一代编译和渲染管道的代号。 从 Angular 的版本 9 开始,这个新的编译器和运行时指令集就代替了老的编译器和运行时(即视图引擎 View Engine)成为了默认值。
可以使用通过 View Engine 编译器创建的库来构建 Ivy 应用程序。此兼容性由称为 Angular 兼容性编译器( ngcc )的工具提供。CLI 命令在执行 Angular 构建时会根据需要运行 ngcc.
The Angular ahead-of-time (AOT) compiler converts Angular HTML and TypeScript code into efficient JavaScript code during the build phase, before the browser downloads and runs that code. This is the best compilation mode for production environments, with decreased load time and increased performance compared to just-in-time (JIT) compilation.
原文链接:https://blog.csdn.net/i042416/article/details/108320922
View Engine vs ivy - kongshu - 博客园 (cnblogs.com)
关于Angular新的编译引擎Ivy - 掘金 (juejin.cn)
AST抽象语法树 :什么叫AST抽象语法树? (baidu.com)
8、rxjs
(96条消息) 彻底理解RxJS里面的Observable 、Observer 、Subject_家威Geek的博客-CSDN博客
RxJS Observables vs Promise 之简单对比 - 掘金 (juejin.cn)
switchMap、mergeMap、concatMap
switchMap: input的搜索框,当有新的输入舍去之前的请求,switchMap 同一时间只维护一个内部订阅。记忆switch切换新的。
mergeMap:同时维护多个活动的内部订阅,第二个参数传入数字,可以控制并发数量,mergeMap(1)相当于concatMap。如果需要考虑顺序性,concatMap 会是更好的选择。为防止内存泄漏,如果将 observable 映射成内部的定时器或 DOM 事件流。如果仍然想用 mergeMap 的话,应该利用另一个操作符来管理内部订阅的完成,比如 take 或 takeUntil。不像concatMap,在mergeMap的情况下,我们不必等待前一个内部Observable完成再去触发下一个内部Observable。
concatMap:合并observable,前一个内部 observable 完成后才会订阅下一个。
ReplaySubject,AsyncSubject,BehaviorSubject
BehaviorSubject会保存最新的发送数据,当被订阅时,会立即使用这个最新数据,BehaviorSubject必须设置默认值。因为它有一个最新值(当前值)的概念
ReplaySubject会保存所有值,然后回放给新的订阅者。
AsyncSubject 只有当 Observable 执行完成时(执行complete()),它才会将执行的最后一个值发送给观察者。也就是说,它只会保存流里的最后一条数据,而且只会在数据流complete时候才会发送。
9、Web Components
Web Components 入门实例教程 - 阮一峰的网络日志 (ruanyifeng.com)
是一套允许开发者创建可重用的自定义元素的技术。
Web Component 允许内部代码隐藏起来,这叫做 Shadow DOM,即这部分 DOM 默认与外部 DOM 隔离,内部任何代码都无法影响外部。
(96条消息) Vue3.2 新特性之 —— Web Components_Ardor-Zhang的博客-CSDN博客_vue webcomponent
10、:host、 ::ng-deep(Angular 已经把这个特性标记为已废弃了)
(96条消息) Angular中的: host 和 ::ng-deep_KenkoTech的博客-CSDN博客
11、angular装饰器及作用、自定义装饰器
Angular10--装饰器(注解) - 掘金 (juejin.cn)
装饰器 · TypeScript中文网 · TypeScript——JavaScript的超集
【Angular中的Decorator】- 类装饰器 (Class decorators)__老杨_的博客-CSDN博客_angular decorator 装饰器/注解就是一个函数,它返回函数的函数。它不是Angular的特性,而是TypeScript的一个特性。它将元数据添加到类、类成员(属性、方法)和函数参数。它向一个现有的对象添加新的功能,但是不改变其结构。其实是装饰器模式的运用。
类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。成员的名字。
参数装饰器表达式会在运行时当作函数被调用,
传入下列3个参数:
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。成员的名字。
参数在函数参数列表中的索引。
注意参数装饰器只能用来监视一个方法的参数是否被传入。
自己该怎么实现装饰器,实现每一类装饰器的不同点?
12、自己封装组件需要注意点(通用性、数据验证处理?)
易理解,通用性,扩展性,高性能,好维护
Ensure readability, performance, maintainability, ease of use and extensibility
13、angular响应式表单
深入了解ControlValueAccessor ?别再对 Angular Form 的ControlValueAccessor 感到迷惑 - 知乎 (zhihu.com)
有两种更新模型值的方式:
-
使用
setValue()
方法来为单个控件设置新值。setValue()
方法会严格遵循表单组的结构,并整体性替换控件的值。 -
使用
patchValue()
方法可以用对象中所定义的任何属性为表单模型进行替换。
setValue()
方法的严格检查可以帮助你捕获复杂表单嵌套中的错误,而 patchValue()
在遇到那些错误时可能会默默的失败。
优化显示校验error信息,定义公共error组件,调用公共service中定义的error{key:提示信息}的对应关系信息。
优化异步验证器的性能
默认情况下,所有验证程序在每次表单值更改后都会运行。对于同步验证器,这通常不会对应用性能产生明显的影响。但是,异步验证器通常会执行某种 HTTP 请求来验证控件。每次按键后调度一次 HTTP 请求都会给后端 API 带来压力,应该尽可能避免。
你可以把 updateOn
属性从 change
(默认值)改成 submit
或 blur
来推迟表单验证的更新时机。
使用模板驱动表单时,可以在模板中设置该属性。
content_copy<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">
使用响应式表单时,可以在 FormControl
实例中设置该属性。
content_copynew FormControl('', {updateOn: 'blur'});
与原生 HTML 表单验证器交互
默认情况下,Angular 通过在 <form>
元素上添加 novalidate
属性来禁用原生 HTML 表单验证,并使用指令将这些属性与框架中的验证器函数相匹配。如果你想将原生验证与基于 Angular 的验证结合使用,你可以使用 ngNativeValidate
指令来重新启用它
angular懒加载
要惰性加载 Angular 模块,请在 AppRoutingModule的
routes
中使用 loadChildren
代替 component
进行配置
const routes: Routes = [ {
path: 'items',
loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
} ];
在惰性加载模块的路由模块中,添加一个指向该组件的路由。
const routes: Routes = [ {
path: '', component: ItemsComponent
} ];
还要确保从 AppModule
中移除了 ItemsModule
。
基于字符串的惰性加载
在 Angular 版本 8 中,loadChildren
路由规范的字符串语法已弃用,建议改用 import()
语法。不过,你仍然可以通过在 tsconfig
文件中包含惰性加载的路由来选择使用基于字符串的惰性加载(loadChildren: './path/to/module#Module'
),这样它就会在编译时包含惰性加载的文件。
默认情况下,会用 CLI 生成项目,这些项目将更严格地包含旨在与 import()
语法一起使用的文件。
forRoot()
与 forChild()
CLI 会把 RouterModule.forRoot(routes)
添加到 AppRoutingModule
的 imports
数组中。 这会让 Angular 知道 AppRoutingModule
是一个路由模块,而 forRoot()
表示这是一个根路由模块。 它会配置你传入的所有路由、让你能访问路由器指令并注册 Router
。 forRoot()
在应用中只应该使用一次,也就是这个 AppRoutingModule
中。
CLI 还会把 RouterModule.forChild(routes)
添加到各个特性模块中。这种方式下 Angular 就会知道这个路由列表只负责提供额外的路由并且其设计意图是作为特性模块使用。你可以在多个模块中使用 forChild()
。
forRoot()
方法为路由器管理全局性的注入器配置。 forChild()
方法中没有注入器配置,只有像 RouterOutlet
和 RouterLink
这样的指令
Angular 创建惰性加载模块时会给它一个自己的注入器,它是根注入器的子注入器。 @SkipSelf()
让 Angular 在其父注入器中查找 GreetingModule
,这次,它的父注入器是根注入器(而上次的父注入器是空)。 当然,这次它找到了由根模块 AppModule
导入的实例。 该构造函数检测到存在 parentModule
,于是抛出一个错误
JavaScript 模块 vs. NgModule
JavaScript 模块 vs. NgModule
JavaScript 模块和 NgModule 都可以帮你模块化你的代码,但它们却有着本质性的不同。 Angular 应用同时依赖这两种模块。
JavaScript 模块是一个带有 JavaScript 代码的单独文件,它通常包含一个应用中特定用途的类或函数库。 JavaScript 模块让你可以跨多个文件进行工作。
每个模块都有自己的顶级作用域。换句话说,模块中的顶级变量和函数在其他脚本或模块中是看不到的。每个模块都为其中的标识符提供了一个命名空间,以防止它们与其它模块中的标识符冲突。要想使用多个模块,你可以通过创建一个全局命名空间并为它添加子模块来防止出现意外的全局变量。
Angular 框架本身就是作为一组 JavaScript 模块加载的。
NgModule:带有供编译用的元数据的类
Angular中TemplateRef,ViewContainerRef
ng-template、ng-content、