第 9 天 元件还是页面,这是个问题|page、component

前情提要

藉着重构原先从元件取得资料的方法,我们将对 http 的呼叫封装在服务里,隐藏了资料来源的细节。这使我们在元件中在可以更专注於与画面的互动。并且,如果在两个元件中都需要取得英雄列表,不必在两处撰写相同的程序码,只需要注入服务使用方法便可取得。日後如资料来源有所异动,也仅需调整 service 中的方法。

当然,当我们从元件中呼叫服务中的方法取得资料後,在资料流到订阅处之前,可以使用 RxJS 的管道(pipe)加上操作符(operators)来加工资料。因此在不同的元件需求下,可以使用服务的同一个方法,透过不同的加工让来取得符合需求的资料。

接下来,我们将实作新增英雄功能。在此之前,我们先来思考一个问题:元件(component)还是页面(page)?

什麽是页面?

一个简单的说明是:一个路由对应的就是一个页面。

打开 app-routing.module.ts 查看目前的路由设定:

const routes: Routes = [
  {
    path: '',
    redirectTo: '/heroes',
    pathMatch: 'full'
  },
  {
    path: 'heroes',
    children: [
      {
        path: '',
        component: HeroListComponent
      },
      {
        path: ':id',
        component: HeroDetailComponent,
      }
    ]
  },
]

每个 path 会呈现的画面就是页面(page),现在可以看到主要使用了两个元件 HeroListComponent 、 HeroDetailComponent 来呈现页面。如果在这个情境下,思考「元件还是页面」似乎有些匪夷所思。而且,这重要吗?

我只能说细思极恐。的确,我们现在直接将两个元件用作页面,但请记得元件的特点是可复用性。在没有特别的规范下,元件是可以在同一个 module 里面被任意使用的;如果你将这个元件汇出(export),那有汇入(import)此 module 的地方都可以使用。

当我们在规划页面时,它往往具备特殊性,并非可以任意当作组件放入其他元件中。试想,如果刚加入一个已启动一阵子的案子,或是接手维护一个专案,当你调整了某个画面上使用的元件,会预期某个页面会因为这个调整发生改变吗——甚至是产生 bug。

在 Angular 里,我们可以透过元件@Component装饰器的元资料中不提供 selector,自然,其他地方就无法使用这个元件。使用 Angular CLI 的话,我们可以在新增元件的时候加入参数 --skip-selector 来自动完成这个改动:

ng g c add-hero --skip-selector // g for generate, c for component

这样新增元件的 @Component 装饰器内的元资料就不会有 selector:

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

@Component({
  templateUrl: './add-hero.component.html',
  styleUrls: ['./add-hero.component.css']
})
export class AddHeroComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}

如果你安装了保哥的 Angular Extension Pack,你可以这样进行操作,一个指令都不用背:

https://ithelp.ithome.com.tw/upload/images/20210924/20128395u3p80kyUGT.png

  1. 从 sidebar 点击 Angular 图示打开命令清单。
  2. 在清单选择 component。
  3. 输入欲新增的 component 名称。

输入 component 并点击 enter 後,就会出现是否添加参数的清单:

https://ithelp.ithome.com.tw/upload/images/20210924/20128395k0bSXNHq3H.png

选择「Page --skip selector」就会自动新增一个页面,也就是不提供 selector 的元件

那麽,元件还是页面?

除了防止页面层级的元件遭到误用之外,我们还可以透过思考「元件还是页面」,来不断优化自己的专案设计。

以即将开始实作的「新增英雄」功能(AddHeroComponent)为例,我们来思考「元件还是页面」?

可以想见,「新增英雄」功能的画面上,我们会有一个输入英雄资料的表单,当使用者填写完成後就可以点击按钮将资料送出,完成英雄的新增。这个表单,我们应该将它制作成元件,例如 HeroDataFormComponent。你马上可以想到,我们可能会提供另外一个「编辑英雄」功能,届时,很大的机率会使用类似、甚至是相同的表单。

如果我们直接将 HeroDataFormComponent 配置路由,也就是将它作为页面使用,那会有什麽问题?一个可能是,在「新增英雄」功能与「编辑英雄」功能的画面其实不尽相同,新增英雄的部分也许会额外显示同一种类型(刺客、坦克、斗士...)的英雄作为参考,而编辑英雄并不需要显示这些资讯。

在这种需求下,如果我们直接两个功能的路由导向表单元件,那我们可能需要依据身处的路由,在表单元件里动态生产许多不同的东西,例如引用其他元件。当需要判断的情形越多,这个元件就会越难管理。想用使用这个元件的其他成员,要了解这个元件的逻辑成本也增加了。想想,当你使用一个包好的元件,而元件的 API 非常复杂的时候...。能够达成目标是好事,但人生是不是可以更简单一些呢?

在专案结构的设计上,你也可以特别在资料夹或是档案名称来区分它们。例如在每个功能模组中(feature module),建立pages 资料夹放置页面元件。或是在新增不带 selecor 的元件时,将其命名为 AddHeroPageComponent 诸如此类。

所以,在实作功能时,你会怎麽规划它的页面及元件呢?


<<:  Day9 Micro LED对LCD和OLED来说就是一个高富帅和两个矮穷丑的比较?简单介绍首款使用Micro LED的AR眼镜

>>:  [第09天]理财达人Mx. Ada-订阅盘中交易

Day 01:什麽是演算法?

演算法这个名字给人一种充满艰深、繁复计算的感觉,不像我们生活中可以或需要学会的东西。 但其实演算法在...

Vue.js 从零开始:Hoisting , undefined,not defined

续上一篇文章,写到後面还有一些观念没有讲得很清楚,像是var有Hoisting的特性,coding的...

day 5 knock, knock我要开始coroutine

coroutine神奇又好用,那我要怎麽开始呢? 官方提供了两种方法,launch和 async l...

Day2 参加职训(机器学习与资料分析工程师培训班),记录学习内容(6/30-8/20)

人工智慧与资料分析专题 今天课程主要在说明专题的制作,研究过程分为4个阶段: 研究动机: 1.研究目...

系统分析师的养成之路—案例分享(2)

看完第一个案例不知道大家有什麽收获?欢迎给我一些回馈哦!接着,跟大家分享第二个案例罗! 欢迎有兴趣的...