[Angular 基础] - Angular 渲染过程 组件的创建

news2024/9/23 5:33:47

[Angular 基础] - Angular 渲染过程 & 组件的创建

之前的笔记为了推进度写的太笼统了(只有功能没有其他),当时学的时候知道是什么东西,但是学完后重新复习发现有些内容就记不清了,所以重新用自己的语言总结一下

安装 angular-cli 的指令为:

# 如果不确定是否有安装过,可以先卸载
npm uninstall -g angular-cli @angular/cli
# 重新安装 CLI
npm install -g @angular/cli

Angular 项目启动挂载过程

不涉及到 webpack/vite 编译,只是简单的加载过程

首先看一下 angular 项目的架构:

❯ tree --gitignore
.
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── src
│   ├── app
│   │   ├── app.component.css
│   │   ├── app.component.html
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   └── app.module.ts
│   ├── assets
│   ├── favicon.ico
│   ├── index.html
│   ├── main.ts
│   └── styles.css
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json

4 directories, 16 files

其中 src 外面的代码都是配置代码,一般来说没有什么变更的需要,目前我能想到的变动的情况只有添加额外的 CSS 库需要变动 angular.json

src 里面则是实现的代码,这里面 index.html 的作用就是一个锚点,也就是组件在没有渲染时的初始界面,内容如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>MyFirstApp</title>
    <base href="/" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />
  </head>
  <body>
    <app-root></app-root>
  </body>
</html>

这与没有被 JS 重写的 HTML 一致:

在这里插入图片描述

下面的 script 也是在编译的时候添加到 HTML 网页中,具体的配置依旧在 angular.json 中:

{
  // 省略其他
  "options": {
    "outputPath": "dist/my-first-app",
    "index": "src/index.html",
    "browser": "src/main.ts",
    "polyfills": ["zone.js"],
    "tsConfig": "tsconfig.app.json",
    "assets": ["src/favicon.ico", "src/assets"],
    "styles": [
      "node_modules/bootstrap/dist/css/bootstrap.min.css",
      "src/styles.css"
    ],
    "scripts": []
  }
}

这里 main.ts 就是项目中 JS 文件的入口,其中主要的作用就是挂在对应的组件:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

// 这个作用类似于 React 中的 <App />
import { AppModule } from './app/app.module';

// 这个作用类似于 ReactDOM.render(<App />, el);
platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch((err) => console.error(err));

AppModule 就是一个 Angular 的组件

组件

首先过一下什么是 Angular 组件(component),抛开具体的实现,单纯从概念上来说,React, Angular 和 Vue 的组件都是一样的:

  • 模块化的代码

    可复用的最小代码块

  • 对数据进行处理

    最简单的例子就是通过 AJAX 获取数据后渲染给用户看

  • 对用户交互进行反应

    这点也可以和上一点进行联动,如用户提供了数据之后,组件会对数据进行处理,并且处理过的数据重新渲染在 UI 上

核心概念虽然是一致的,不过具体的实现,如使用的模板——React 使用 JSX,Angular 使用 HTML template、生命周期、组件内传递数据的方式等则会有所不同。

组件的组成

首先看一下 Angular 项目的 src 结构:

❯ tree src
src
├── app
│   ├── app.component.css
│   ├── app.component.html
│   ├── app.component.spec.ts
│   ├── app.component.ts
│   └── app.module.ts
├── assets
├── favicon.ico
├── index.html
├── main.ts
└── styles.css

3 directories, 9 files

这个项目是通过 ng new my-first-app --no-strict --standalone false --routing false 运行的,也就是 angular cli 提供的模板文件。这里 app 就是一个组件,其中包含了 5 个文件。

其中最重要的两个核心文件为 ___.component.ts___.module.ts,而对大多数的组件来说,最核心的是前者的实现,大部分的 ___.module.ts 可能并不太会主动去修改。

下面也会解释为什么 csshtml 并不一定是核心文件的原因

css

当前 CSS 文件是空的:

cat src/app/app.component.css

目前的实现来说,每个 component 中都可以有对应的 CSS 文件,这个对应的 CSS 文件的 作用域(scope) 是当前的组件。

如添加了一个 .header 的 CSS:

.header {
  background-color: #f2f2f2;
  padding: 20px;
  text-align: center;
}

页面上的显示则是:

在这里插入图片描述

可以看到,样式并不是直接作用于 .header 上,而是作用于 .header[_ngcontent-ng-c317606715],后者则是随机生成的 Angular 元素的 属性(attribute,目前观测来看,同一个组件都会分享同一个),这样可以有效防止命名冲突的问题

这个 CSS 文件是通过 @Component 这个 decorator 导入的,使用的属性为 styleUrls,接受的参数是一个数组:

@Component({
  styleUrls: ['./my-component.component.css']
})

在要写的 CSS 比较多的情况下,一般是会创建一个单独的 ___.component.css 文件,但是在 CSS 样式比较短的情况下,则可以省略文件,直接在 decorator 中添加 styles 即可,如:

@Component({
  styles: [`
    h1 {
      color: blue;
    }
    button {
      background-color: green;
      color: white;
    }
  `]
})

之前看的教程说 stylesstyleUrls 二者只能存在一个,不过现在试了一下,如果二者不冲突的话是都会接受的,而且当前组件如果没有 CSS 的需求,也可以不用放 CSS。

html

这是对应组件的 HTML template,内容如下:

<div style="text-align: center">
  <h1 class="header">Welcome to {{ title }}!</h1>
  <img
    width="300"
    alt="Angular Logo"
    src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="
  />
</div>
<h2>Here are some links to help you start:</h2>
<ul>
  <li>
    <h2>
      <a target="_blank" rel="noopener" href="https://angular.io/tutorial"
        >Tour of Heroes</a
      >
    </h2>
  </li>
  <li>
    <h2>
      <a target="_blank" rel="noopener" href="https://angular.io/cli"
        >CLI Documentation</a
      >
    </h2>
  </li>
  <li>
    <h2>
      <a target="_blank" rel="noopener" href="https://blog.angular.io/"
        >Angular blog</a
      >
    </h2>
  </li>
</ul>

其本身存在的意义就是生成一个对应的 HTML 模板,通过 module 导入到对应的 HTML   元素中。以目前的例子来说,这个 HTML 模板就是重置 index.html 中的 app-root 元素

每个模板也可以通过 Angular 提供的其他指令与数据的处理层进行交互,并完成数据的动态渲染,这一趴可以理解成传统的 View

一般 HTML Template 通过 @Component 中的 templateUrl进行导入:

@Component({
  templateUrl: './my-component.component.html'
})

在创建的 HTML 元素比较少的情况下,也不需要单独创建一个 HTML 模板文件,而是使用 template 创建对应的元素即可:

@Component({
  template: `
    <div>
      <h1>Hello, {{ name }}!</h1>
    </div>
  `
})

⚠️:尽管和 CSS 文件一样可以用两种方式进行创建,但是每个 @Component 必须要有一个 templatetemplateUrl。之前看的教程说templatetemplateUrl 只能二选一,我试着跑了一下,目前的版本是会优先选择 templateUrl 中的内容,而不会报错

spec

测试文件,目前不打算涉及,因此跳过

ts

这个可以理解成 ViewModel 层,它主要的作用就是:

  • 创建一个组件类

  • 处理元数据(metadata)

    这个 metada 指的是创建组件所需要的元数据,也就是 @Component 这个装饰器所需要的数据

  • 处理逻辑

    即处理 HTML Template 中需要渲染的数据,并直接与其交互

一个案例代码为:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'Hello World';

  constructor() {}

  sayHello() {
    alert(this.title);
  }
}

这里的 @Component 是一个 TS decorator,说白了也是一个语法糖,大致的极简实现如下:

class Component {
  constructor(config) {
    this.selector = config.selector;
    this.template = config.template;
    this.render();
  }

  // 这样就不需要重复实现这个功能了
  render() {
    console.log(`${this.template} is mounted to ${this.selector}`);
  }
}

class SomeComponent extends Component {
  constructor(config) {
    super(config);
  }
}

const myComponent = new SomeComponent({
  selector: 'my-component',
  template: '<h1>Hello, world!</h1>',
});

这样的调用为:

node ang.js
<h1>Hello, world!</h1> is mounted to my-component

我试了一下 TS 的实现:

type ComponentProps = {
  selector: string;
  template: string;
};

function Component(config: ComponentProps) {
  return function <T extends new (...args: any[]) => any>(constructor: T) {
    return class extends constructor {
      selector = config.selector;
      template = config.template;

      constructor(...args: any[]) {
        super(...args);
        this.render();
      }

      render() {
        console.log(`${this.template} is mounted to ${this.selector}`);
      }
    };
  };
}

@Component({
  selector: 'app-server',
  template: './server.component.html',
})
class ServerComponent {}

const serverComponent = new ServerComponent();

不过 TS Playground 上有显示报错就是了,不知道不在 TS Playground 能不能跑起来

module

这个文件用来处理当前组件与其他组件的交互,并最终返回一个 NgModule 供其他的模块使用

一个案例代码为:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

@NgModule({
  // Angular 只会渲染 bootstrap 中包含的组件
  declarations: [AppComponent],
  // 这里负责导入本模块中 components 要用的模块
  // 如与表单交互的 FormsModule
  imports: [BrowserModule],
  // service,现在暂时用不到,用到了再补
  providers: [],
  // 这里是导出的组件
  // React 中只能导出一个组件,Angular 则是一个数组
  bootstrap: [AppComponent],
})
export class AppModule {}

⚠️:main.ts 中使用的就是这个 AppModule

我个人的理解就是,以乐高作对比,component就像一个个乐高积木,拼成功的一个完成品(比如说 🚗、🌲、🏠 这种)就是一个 NgModuleNgModule 又可以组成更大的 NgModule,比如说乐高的城市主题就会包括消防车、飞机、警局等模块。

因此,对于大多数项目——也就是中小型项目来说,一个应用里面存在一个 app.module.ts 就足够了

新建组件

创建组件有两种方式:使用 angular-cli 和 手动创建

手动创建
  1. 手动创建一个新的文件夹包含 __component.html__.component.ts 即可

    结构如下:

    ❯ tree src/app/
    src/app/
    ├── app.component.css
    ├── app.component.html
    ├── app.component.spec.ts
    ├── app.component.ts
    ├── app.module.ts
    └── serverï
        ├── server.component.html
        └── server.component.ts
    

    server 下面的就是新的组件,一般命名规范就是这样的

    其中 server.component.html 的内容为对应实现的模板:

    <h1>Hello from Server</h1>
    

    server.component.ts 将对应的 HTML Template 挂载到对应的 div 上——这里在这之前需要在 app.component.html 中创建对应的 HTML 元素,即 <app-server></app-server>server.component.ts 中的代码如下:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-server',
      templateUrl: './server.component.html',
    })
    export class ServerComponent {}
    
  2. 更新 App.module.ts 中的 declarations

    前文提过,Angular 只会使用 declarations 中提到的组件,因此不更新 declarations,无法正确渲染:

    // 导入,让 TS 可以找到 entry
    import { ServerComponent } from './server/server.component';
    
    @NgModule({
       // 提供信息给 Angular
       declarations: [AppComponent, ServerComponent],
       // ...其余不变...
    })
    

这个时候 server.component.tsserver.component.html 中的内容就可以正确渲染了:

在这里插入图片描述

使用 cli

这里使用指令即可:

# generate 和 component 分别可以使用 g 和 c 来代替
# component 为 cli 支持创建的类型,除此之外还有 directive、service、class 等
# servers 为对应组件的名称,想要修改也可以用 folder/component 这样的结构
❯ ng generate component servers
CREATE src/app/servers/servers.component.css (0 bytes)
CREATE src/app/servers/servers.component.html (22 bytes)
CREATE src/app/servers/servers.component.spec.ts (608 bytes)
CREATE src/app/servers/servers.component.ts (203 bytes)
UPDATE src/app/app.module.ts (458 bytes)

此时的项目结构为主:

❯ tree src/app/
src/app/
├── app.component.css
├── app.component.html
├── app.component.spec.ts
├── app.component.ts
├── app.module.ts
├── server
│   ├── server.component.html
│   └── server.component.ts
└── servers
    ├── servers.component.css
    ├── servers.component.html
    ├── servers.component.spec.ts
    └── servers.component.ts

3 directories, 11 files

正常情况下,使用 cli 创建的组件会在对应的 module 中被自动添加到 declarations 中去

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1430944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--强化学习、模仿学习、机器人

专属领域论文订阅 关注{晓理紫}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 为了答谢各位网友的支持&#xff0c;从今日起免费为3…

基于YOLOv8算法的照片角度分类项目实践

目录 一、任务概述二、YOLOv8算法简介2.1 算法改进2.2 算法特点2.3 网络结构2.4 性能比较 三、工程实践3.1 安装算法框架库ultralytics3.2 库存照片预处理3.2.1 提取所有图片3.2.2 去除冗余的相同照片3.2.3 去除无车辆照片3.2.4 随机提取指定数量的图片 3.3 照片朝向分类3.3.1 …

项目02《游戏-06-开发》Unity3D

基于 项目02《游戏-05-开发》Unity3D &#xff0c; 接下来做 背包系统的 存储框架 &#xff0c; 首先了解静态数据 与 动态数据&#xff0c;静态代表不变的数据&#xff0c;比如下图武器Icon&#xff0c; 其中&#xff0c;武器的名称&#xff0c;描述&#xff…

宠物空气净化器哪个牌子好?除猫毛好的猫用空气净化器牌子推荐

大家都知道&#xff0c;宠物掉毛的情况有多么严重。特别是在换毛的季节&#xff0c;简直就是毛发遍地飞。这给家里有小孩和老人的人带来了很多困扰&#xff0c;他们可能会流鼻涕、过敏等等。而且&#xff0c;宠物有时候也会随地大小便&#xff0c;那个味道真的很难闻。家里的人…

【揭秘】JMeter JDBC脚本实战,让你的性能测试更高效!

Jmeter使用jdbc的场景&#xff1a; 1、接口功能测试时&#xff0c;需要查询验证码 2、通过数据库查询已经注册的手机号码 3、性能测试时&#xff0c;直接对某个SQL做性能测试&#xff0c;快速的发现性能问题 添加一个jdbc的配置元件 配置jdbc连接信息 配置说明&#xff1a; 1…

如何看待敏捷

局部清晰&#xff0c;循序渐进&#xff0c;整体清晰增量型 考试要么预测&#xff08;传统&#xff0c;瀑布&#xff09;&#xff0c;要么敏捷&#xff0c;要么就用混合方法 项目生命周期两种&#xff1a;预测型、敏捷型 开发生命周期四种&#xff1a;预测型、迭代型、增量型、…

JVM工作原理与实战(三十四):解决GC问题的方法

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、常见的垃圾回收&#xff08;GC&#xff09;模式 二、解决GC问题的方法 1.优化基础JVM参数 2.更换垃圾回收器 3.优化垃圾回收器的参数 总结 前言 JVM作为Java程序的运行环境&a…

龙龙送外卖pta[代码+讲解]

题目 题解 代码 题目 龙龙是“饱了呀”外卖软件的注册骑手&#xff0c;负责送帕特小区的外卖。帕特小区的构造非常特别&#xff0c;都是双向道路且没有构成环 —— 你可以简单地认为小区的路构成了一棵树&#xff0c;根结点是外卖站&#xff0c;树上的结点就是要送餐的地址…

钓鱼攻击:深度解析与防范策略

一、引言 在当今的网络世界中&#xff0c;钓鱼攻击已经成为一种日益猖獗的威胁。这种攻击方式利用电子邮件、社交媒体或其他在线平台&#xff0c;伪装成可信赖的来源&#xff0c;诱导受害者点击恶意链接或下载恶意附件&#xff0c;进而窃取个人信息或实施其他恶意行为。本文将…

关于Clone

关于Clone 一般情况下&#xff0c;如果使用clone()方法&#xff0c;则需满足以下条件。 1、对任何对象o&#xff0c;都有o.clone() ! o。换言之&#xff0c;克隆对象与原型对象不是同一个对象。 2、对任何对象o&#xff0c;都有o.clone().getClass() o.getClass()。换言之&a…

VC++中使用OpenCV绘制直线、矩形、圆和文字

VC中使用OpenCV绘制直线、矩形、圆和文字 在VC中使用OpenCV绘制直线、矩形、圆和文字非常简单&#xff0c;分别使用OpenCV中的line、rectangle、circle、putText这四个函数即可。具体可以参考OpenCV官方文档&#xff1a;https://docs.opencv.org/4.x/index.html 下面的代码展…

9、C语言复习

目录 1、位操作 2、define宏定义关键词 3、ifdef条件编译 4、extern变量申明 5、typedef类别别名 6、结构体 7、static关键字 1、位操作 &&#xff1a;按位与 |&#xff1a;按位或 ^&#xff1a;按位异或 ~&#xff1a;取反 <<&#xff1a;左移 >>…

JAVA Web 学习(四)RabbitMQ、Zookeeper

十、消息队列服务器——RabbitMQ RabbitMQ是使用Erlang语言开发的开源消息队列系统&#xff0c;基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、 安全。AMQP协议更多用在企业系统内&#xff0c;对数据一致性、稳定性和可靠性要求…

会声会影2024破解版如何下载?

要下载会声会影2024&#xff0c;您可以按照以下步骤进行操作&#xff1a; 会声会影2024安装包下载如下: https://wm.makeding.com/iclk/?zoneid55677 1. 访问会声会影官方网站&#xff1a;在您的网络浏览器中&#xff0c;输入"会声会影2024官方网站"进行搜索&…

前后端数据校验

前端校验内容 前端开发中的必要校验&#xff0c;可以保证用户输入的数据的准确性、合法性和安全性。同时&#xff0c;这些校验也有助于提供良好的用户体验和防止不必要的错误提交到后端。 1、必填字段校验&#xff1a; 对于必填的字段&#xff0c;需确保用户输入了有效的数据…

图论练习1

内容&#xff1a;&#xff0c;拆点&#xff0c;分层&#xff0c;传递&#xff0c;带限制的最小生成树 [HNOI2015]菜肴制作 题目链接 题目大意 有个限制&#xff0c;号菜肴在号前完成在满足限制的条件下&#xff0c;按照出菜( 是为了满足的限制 ) 解题思路 由限制&#xf…

Github 2024-02-04 开源项目日报 Top9

根据Github Trendings的统计&#xff0c;今日(2024-02-04统计)共有9个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目6Ruby项目1HTML项目1C项目1Go项目1TypeScript项目1 Windows 终端、控制台和命令行存储库 创建周期…

一个 WPF + MudBlazor 的项目模板(附:多项目模板制作方法)

最近做了几个 WPF MudBlazor 的小东西&#xff0c;每次从头搭建环境比较繁琐&#xff0c;然鹅搭建过程还没啥技术含量&#xff0c;索性就直接做了个模板&#xff0c;方便以后使用。 1. 介绍 一个用来创建 .NET 8 WPF MudBlazor 的项目模板 适用于 VS2022 用法&#xff1a;…

【学习笔记】详解换根法(换根DP)

一.换根DP的概念 1.换根DP是什么&#xff1f; 换根DP&#xff0c;又叫二次扫描&#xff0c;是树形DP的一种。 2.换根DP能解决什么问题&#xff1f; 换根DP能解决不指定根结点&#xff0c;并且根节点的变化会对一些值产生影响的问题。例如子结点深度和、点权和等。如果要 暴力…

河西走廊潜在蒸散发时空格局变化与气象因素的关系_马亚丽_2023

河西走廊潜在蒸散发时空格局变化与气象因素的关系_马亚丽_2023 摘要关键词 1 数据与方法1.1 数据来源1.2 变化趋势分析1.3 定性分析方法1.3.1 主成分分析1.3.2 相关系数1.3.3 通径分析 1.4 定量分析方法1.4.1 敏感系数1.4.2 贡献率计算 2 结果与分析2.1 ET0多年变化特征2.1.1 E…