วิธีพัฒนา Angular Library ในเครื่อง

Nov 25 2022
สวัสดี ! ในเรื่องนี้ ฉันอยากจะแสดงวิธีทำงานกับ Angular libraries ในเครื่อง และพัฒนาโดยไม่จำเป็นต้องส่งไปยัง repository ระยะไกลตลอดเวลา :) DRY - วิธีหลีกเลี่ยงการทำซ้ำโค้ด หากคุณทำงานในโครงการ Angular ขนาดใหญ่และซับซ้อน ฉันแน่ใจว่าคุณจะประสบปัญหาการทำซ้ำรหัส ตัวอย่างเช่น เรามาเน้นที่ "รูปลักษณ์" ของแอปพลิเคชันของคุณ หน้าทั้งหมดควรมีสไตล์ที่สอดคล้องกัน (คุณไม่ต้องการทำให้ผู้ใช้สับสนและมีปุ่มที่ดูแตกต่างกันในแต่ละหน้า)
ภาพถ่ายโดย Ryunosuke Kikuno บน Unsplash

สวัสดี ! ในเรื่องนี้ ฉันอยากจะแสดงให้คุณเห็นวิธีการทำงานกับ Angular libraries ในเครื่อง และพัฒนาโดยไม่จำเป็นต้องส่งไปยัง repository ระยะไกลตลอดเวลา :)

DRY - วิธีหลีกเลี่ยงการใช้รหัสซ้ำ

หากคุณทำงานในโครงการเชิงมุมขนาดใหญ่และซับซ้อน ฉันแน่ใจว่าคุณจะต้องประสบปัญหาการทำซ้ำโค้ด ตัวอย่างเช่น เรามาเน้นที่ "รูปลักษณ์" ของแอปพลิเคชันของคุณ หน้าทั้งหมดควรมีสไตล์ที่สอดคล้องกัน (คุณไม่ต้องการทำให้ผู้ใช้สับสนและมีปุ่มที่ดูแตกต่างกันในแต่ละหน้า) โอเค ง่ายมาก คุณสามารถย้ายสไตล์ทั่วไปเหล่านั้นไปยังstyles.scssไฟล์ หรือใช้รูปแบบ 7–1เพื่อออกแบบอย่างหรูหรา

แต่ถ้าคุณมีส่วนประกอบ ทั่วไปบางอย่าง ซึ่งไม่ควรดูเท่านั้น แต่ยังทำงานเหมือนกันด้วย มัน… ยังง่าย! เพียงสร้าง โมดูลที่ ใช้ร่วมกันในแอปพลิเคชันของคุณ กำหนดส่วนประกอบของคุณที่นั่น และส่งออกในข้อกำหนดของโมดูล ยอดเยี่ยม !

ตอนนี้เรามาเน้นที่ปัญหาที่ซับซ้อนมากขึ้น จะเกิดอะไรขึ้นหากบริษัทของคุณมีแอปพลิเคชันเชิงมุมมากกว่าหนึ่งรายการ และแต่ละแอปพลิเคชันควรมีลักษณะคล้ายกัน เช่น

  • มีส่วนหัวและส่วนท้ายเหมือนกัน
  • มีสไตล์ CSS ขององค์กรเหมือนกัน
  • มีวิธีเดียวกันในการแสดงแบบฟอร์ม ข้อผิดพลาดในการตรวจสอบ และข้อความอื่นๆ

การสร้างห้องสมุด

คุณสามารถค้นหาหน้าเอกสารอย่างเป็นทางการเกี่ยวกับการสร้างไลบรารีได้ที่นี่ ทำตามขั้นตอนที่อธิบายไว้และสร้างห้องสมุดของเรา:

ng new my-shared-workspace --no-create-application
cd my-shared-workspace
ng generate library my-buttons

  • my-shared-workspaceคือพื้นที่ทำงานทั่วไปสำหรับไลบรารีของคุณ
  • my-buttonsเป็นไลบรารีตัวอย่างที่คุณสามารถนำไปใช้ซ้ำในโครงการอื่นๆ ได้

{
  "name": "@my-company/my-buttons",
  "version": "0.0.1",
  "peerDependencies": {
    "@angular/common": "^14.2.0",
    "@angular/core": "^14.2.0"
  },
  "dependencies": {
    "tslib": "^2.3.0"
  }
}

my-shared-workspace/projects/my-buttons/src/lib> ng g c fancy-button

สร้างคอมโพเนนต์แล้ว ดังนั้นเราจึงสามารถอัปเดตเทมเพลตเพื่อแสดงปุ่มได้

<button>fancy-button works!</button>

@NgModule({
  declarations: [
    MyButtonsComponent,
    FancyButtonComponent
  ],
  imports: [
  ],
  exports: [
    MyButtonsComponent,
    FancyButtonComponent // <-- here
  ]
})
export class MyButtonsModule { }

/*
 * Public API Surface of my-buttons
 */

export * from './lib/my-buttons.service';
export * from './lib/my-buttons.component';
export * from './lib/my-buttons.module';
export * from './lib/fancy-button/fancy-button.component'; // <-- here

เราสร้างห้องสมุดของเราแล้ว ดังนั้นตอนนี้ถึงเวลาสำหรับแอปพลิเคชันไคลเอ็นต์แล้ว ฉันจะใช้ Angular CLI เพื่อสร้าง: ng new my-applicationและฉันจะเพิ่ม@my-company/my-buttonsเป็นการพึ่งพาในแอปพลิเคชันpackage.json(การพึ่งพาล่าสุดหลังจากzone.js)

{
  "name": "my-application",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^14.2.0",
    "@angular/common": "^14.2.0",
    "@angular/compiler": "^14.2.0",
    "@angular/core": "^14.2.0",
    "@angular/forms": "^14.2.0",
    "@angular/platform-browser": "^14.2.0",
    "@angular/platform-browser-dynamic": "^14.2.0",
    "@angular/router": "^14.2.0",
    "rxjs": "~7.5.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.11.4",
    "@my-company/my-buttons": "0.0.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^14.2.6",
    "@angular/cli": "~14.2.6",
    "@angular/compiler-cli": "^14.2.0",
    "@types/jasmine": "~4.0.0",
    "jasmine-core": "~4.3.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.0.0",
    "typescript": "~4.7.2"
  }
}

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyButtonsModule } from '@my-company/my-buttons';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    MyButtonsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

<lib-fancy-button></lib-fancy-button>

Error: src/app/app.component.html:1:1 - error NG8001: 'lib-fancy-button' is not a known element:
1. If 'lib-fancy-button' is an Angular component, then verify that it is part of this module.
2. If 'lib-fancy-button' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.

1 <lib-fancy-button></lib-fancy-button>
  ~~~~~~~~~~~~~~~~~~

  src/app/app.component.ts:5:16
    5   templateUrl: './app.component.html',
                     ~~~~~~~~~~~~~~~~~~~~~~
    Error occurs in the template of component AppComponent.




** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

คุณจะเห็นข้อผิดพลาดเพิ่มเติม เช่น:

Error: src/app/app.module.ts:3:33 - error TS2307: Cannot find module '@my-company/my-buttons' or its corresponding type declarations.

3 import { MyButtonsModule } from '@my-company/my-buttons';

npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@my-company%2fmy-buttons - Not found
npm ERR! 404
npm ERR! 404  '@my-company/[email protected]' is not in this registry.
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
Npm link to the rescue

คุณสามารถค้นหาเอกสารประกอบคำสั่งลิงค์ npm ที่ สมบูรณ์ได้ ที่นี่ ฉันจะแสดงวิธีใช้คำสั่งนี้เพื่อพัฒนาไลบรารีที่ใช้ร่วมกันในเครื่องด้วยการรีโหลดแบบสด

กลับไปที่my-shared-workspaceไดเร็กทอรี สร้างmy-buttonsไลบรารี (แฟล็--watchกมีหน้าที่รับผิดชอบในการรีโหลดสด) และไปที่ไดเร็กทอรีของบิลด์

my-shared-workspace> ng build my-buttons --configuration development --watch
Building Angular Package

------------------------------------------------------------------------------
Building entry point '@my-company/my-buttons'
------------------------------------------------------------------------------
✔ Compiling with Angular sources in Ivy full compilation mode.
✔ Writing FESM bundles
✔ Copying assets
✔ Writing package manifest
✔ Built @my-company/my-buttons

------------------------------------------------------------------------------
Built Angular Package
 - from: /Users/<user>/<path>/my-shared-workspace/projects/my-buttons
 - to:   /Users/<user>/<path>/my-shared-workspace/dist/my-buttons
------------------------------------------------------------------------------

my-shared-workspace> cd dist/my-buttons
my-shared-workspace/dist/my-buttons> npm link

added 1 package, and audited 3 packages in 1s

found 0 vulnerabilities

ในmy-applicationไดเร็กทอรี ให้angular.jsonเพิ่มอีกหนึ่งตัวเลือก - รักษาSymlinksซึ่งจะช่วยให้เราสามารถใช้ไลบรารีที่เชื่อมโยงนี้ได้

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "my-application": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/my-application",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "preserveSymlinks": true,
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "500kb",
                  "maximumError": "1mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2kb",
                  "maximumError": "4kb"
                }
              ],
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true
            }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "browserTarget": "my-application:build:production"
            },
            "development": {
              "browserTarget": "my-application:build:development"
            }
          },
          "defaultConfiguration": "development"
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "my-application:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "inlineStyleLanguage": "scss",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          }
        }
      }
    }
  }
}

my-application> npm link @my-company/my-buttons

added 1 package, removed 1 package, and audited 918 packages in 2s

121 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

หลังจากนั้นให้รันng serveอีกครั้ง:

my-application> ng serve
✔ Browser application bundle generation complete.

Initial Chunk Files   | Names         |  Raw Size
vendor.js             | vendor        |   3.30 MB |
polyfills.js          | polyfills     | 318.02 kB |
styles.css, styles.js | styles        | 210.56 kB |
main.js               | main          |  12.32 kB |
runtime.js            | runtime       |   6.53 kB |

                      | Initial Total |   3.84 MB

Build at: 2022-11-24T18:27:00.803Z - Hash: 7669daf65ca2f665 - Time: 4598ms

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **


✔ Compiled successfully.

มาดูผลลัพธ์กัน:

ยอดเยี่ยม ! ตอนนี้ลองเปลี่ยนบางอย่างใน fancy-button-component.html

<button>fancy-button works - live reload!</button>

ตอนนี้คุณสามารถทำงานได้ทั้งไลบรารีและแอปพลิเคชันโดยไม่มีปัญหา :)

การยืนยัน

หากต้องการตรวจสอบเวอร์ชันของไลบรารีที่คุณใช้ คุณสามารถเรียกใช้คำสั่งดังต่อไปนี้:

npm list -g
/usr/local/lib
├── @angular/[email protected]
├── @my-company/[email protected]+1669314745555 -> ./../../../Users/<user>/<path>/my-shared-workspace/dist/my-buttons

หลังจากที่คุณทำงานเปลี่ยนแปลงภายในเครื่องเสร็จแล้ว คุณสามารถหยุดใช้ไลบรารีที่เชื่อมโยงในแอปพลิเคชันไคลเอนต์ด้วยวิธีต่อไปนี้

my-application> npm unlink @my-company/my-buttons --no-save

my-shared-workspace/dist/my-buttons> npm rm -g @my-company/my-buttons

วัสดุ