Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular必知八大概念 #48

Open
deepthan opened this issue Jun 16, 2020 · 2 comments
Open

Angular必知八大概念 #48

deepthan opened this issue Jun 16, 2020 · 2 comments

Comments

@deepthan
Copy link
Owner

deepthan commented Jun 16, 2020

Angular必知八大概念

  • 模块
    模块有两层含义:

    1. 框架代码以模块形式组织(物理模块)
    2. 功能单元以模块形式组织(逻辑模块)
      物理模块是TS/ES6提供的文件模块特征
      逻辑模块是对应应用内零散的组件、指令、服务按功能进行分类包装,其关系示意图如下:
      image

    首先,Angular 要能成功运行,至少需要定义一个模块,因为需要有一个模块作为应用启动的入口,这样的模块就称为根模块。
    然后,我们的应用会不断的添加新的功能。这些新增的功能可以封装到一个新的模块里。这些新增加的模块在 angular 里称为特性模块。有了特性模块之后,根模块原来承载在功能逻辑也可以抽离出来,放到某个特性模块里,使根模块保持简洁。
    接下来,我们添加的特性模块越来越多,他们之间可以抽出一些相似功能的组件或指令,这些公共的部分也可以封装成一个独立的模块,这样的模块在逻辑意义上不能称为特性模块,Angular 把他称为为共享模块。
    最后,还有核心模块,我们知道,一个应用里总有一些全局的组件或服务等,他们只需要在应用启动时候初始化一次即可,例如,维护登录信息的服务,或者是,公共的头部和尾部组件等。虽然我们可以把他们放到根模块里,但更好的设计是把这些逻辑也抽离出来,放到一个独立的模块,这个模块即为核心模块。核心模块要求只导入到根模块里,而尽量不要导入到特性模块或者共享模块里,这是为了在协同工作时候避免出现一些不可预料的结果。

    Angular 已经封装了不少常用的模块, 如:

    ApplicationModule:封装一些启动相关的工具;
    CommonModule:封装一些常用的内置指令和内置管道等;
    BrowserModule:封装在浏览器平台运行时的一些工具库,同时将 CommonModule 和 ApplicationModule 打包导出,所以通常在使用时引入 BrowserModule 就可以了;
    FormsModule 和 ReactiveFormsModule:封装表单相关的组件指令等;
    RouterModule:封装路由相关的组件指令等;
    HttpModule:封装网络请求相关的服务等。

    所以,如果你想使用 ngIf 和 ngStyle 等这些内置指令,记得先导入 CommonModule,其他的模块使用方法一致。

  • 组件

    组件由两部分组成的: @component和 类。
    image
    如果只是定义一个类,angular不知道怎么解释这个类,当往这个类里注入组件元数据后,angular才知道把这个类解释为组件。

    如果想了解元数据是如何注入到类里,可深入了解 reflect-metadata 这个 polyfill。

    数据绑定适用于层级相隔不远的组件,层级太深或者不同分支的组件通讯通常采用其他方式,例如利用服务作为中介。

  • 模板
    angular模板基于HTML,普通的html亦可作为模板输入。

@Component({
  template: `<p>deepthan</p>`
})
  • 元数据
    @component是装饰器,元数据主要以装饰器的函数参数指定。
    selector和 template都是元数据。
    装饰器实际上是一个自定义函数,angular的各种装饰器处理函数在modules/@angular/core/src/util/decorators.ts 中。

  • 数据绑定
    属性绑定和数据绑定均称为数据绑定。

    1. 插值
    <p>你好,{{data.name}}</p>
    
    1. 属性绑定
    [deep] = 'data[0]'
    
    1. 事件绑定
    (sendData) = getData(value) 
    
    1. 双向绑定
    <input [(ngModel)='value']>
    

    [()]是实现双向绑定的语法糖,ngmodel是辅助实现双向绑定的内置指令。 input框和value之间形成数据关联, input值发生变更时会自动赋值到 value,而value值被组件类改变时也可更新input的值。

  • 指令
    指令可以通过与dom进行灵活交互,改变样式或改变布局。

    1. 结构指令: 增加、删除、修改DOM,如 ngIf、 ngFor
    <p *ngIf='bol==true'></p>
    
    1. 属性指令 : 改变元素的外观或者行为,如ngStyle 、 ngClass
    html:
    <p> [ngStyle]='setStyles()' </p>
    ts:
    setStyles(){
        return {
            'color' : 'red'
        }
    }
    
  • 服务
    服务是封装单一功能的单元,类似于工具库,常被引用于组件内部,作为组件的功能拓展。它可以是一个简单的字符串或json数据,也可以是一个函数甚至是一个类,几乎所有的对象都可以封装成服务。
    一个简单的日志服务:

    // import statement
    @Injectable()
    export class LoggerService {
      private level: string;
      setLevel(level: string) { 
        this.level = level;
      }
      
      debug(msg: string) { }
      warn(msg: string) {  }
      error(msg: string) { }
    }
    
  • 依赖注入
    通过依赖注入机制,服务等模块可以被引入到任何一个组件(或模块或服务)中,而开发者无须关系这些模块是如何被初始化的。
    可以说依赖注入是一种帮助开发者管理模块依赖的设计模式。
    一个依赖注入例子:

    import {LoggerService} from './logger-service';
    // other import statement
    @Component({
      selector: 'contact',
      template: '...'
      providers: [LoggerService]
    })
    export class ContactListComponent {
      constructor(logger: LoggerService) { 
        logger.debug('xxx');
      }
    }
    

    @component 装饰器中的 providers元元数据是依赖注入操作的关键,它会为该组件创建一个注入器对象,并新建 LoggerService实例存储到这个注入器里。组件需要引入 LoggerService实例时,只需要在构造函数声明 LoggerService类型的参数即可,Angular自动地通过类型匹配,找出注入器里预先实例化好的 loggerService对象,在组件实例化时作为参数传入,这样组件便获得了 LoggerService的实例引用。

  • 其他概念:

Angular 是以适当的时机去检验对象的值是否被改动,这个适当的时机并不是以固定某个频率去执行,而通常是在用户操作事件(如点击),setTimeout 或 XHR 回调等这些异步事件触发之后。Angular 捕获这些异步事件的工作是通过 Zones 库实现的.

import { LoginModule } from './login/login.module'
const routes: Route: [
  { 
    path: 'role', 
    children: [
      { path: "login", loadChildren: () => LoginModule }
    ]
  }
]
  1. 子路由下面有个特性模块,懒加载:
const routes: Route: [
  { 
    path: 'role', 
    children: [
      { path: "login", () => import('./login/login.module').then((m) => m.LoginModule)}
    ]
  }
]
@ghost
Copy link

ghost commented Sep 28, 2021

你好,我有一个疑惑,最后懒加载这里是不是写反了

@deepthan
Copy link
Owner Author

deepthan commented Sep 29, 2021

@chenxxx1783415 LoadChildren有两种用法:

  1. 使用import(<module-path>).then(module => module.<routing-module>),Angular 将创建一个单独的 js 包,也即使懒加载
  2. 使用() => <routing-module>,Angular 将不会创建单独的 js 包,性能将与children方法相同。

loadChildren: () => LoginModule是第二种用法,直接加载。
'./reset/reset.module#ResetModule' 是第一种方法,懒加载的老版本写法,在9被废弃了。

感谢提出,我在文档中更新下新写法。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant