[Angular-Day 3] Cấu trúc dự án Angular và Typescript compilation

0
Dịch vụ dạy kèm gia sư lập trình
Bài này thuộc phần 2 của 3 phần trong series Học lập trình Angular từ A-Z

Ở phần trước, chúng ta đã biết cách tạo một mới một dự án Angular bằng công cụ Angular CLI. Hôm nay, mời bạn tìm hiểu kỹ hơn về cấu trúc thư mục, chức năng của chúng trong dự án. Sau đó thì tìm hiểu thêm về cơ chế compile mã Typescript trong dự án Angular nhé.

Trước khi bắt đầu, bạn có thể tải mã nguồn mà chúng ta đã tạo ở phần trước tại đây:

Bắt đầu thôi nhỉ!

Cấu trúc dự án Angular

Bạn có nhớ lúc trước, để tạo dự án, chúng ta đã làm bằng cách nào không?

Gợi ý: đó là sử dụng công cụ Angular CLI với câu lệnh: ng new MySpotify

Chúng ta cùng ngó qua một chút cấu trúc thư mục dự án mà công cụ Angular CLI đã tạo sẵn.

Angular-folder-structure

Bạn mở dự án bằng Visual Studio Code sẽ thấy dự án có cấu trúc như hình trên. Mình sẽ không giải thích chi tiết tất cả các tệp vì trọng tâm của chúng ta nhanh chóng bắt đầu Angualar, biết nhiều thứ quá lúc này dễ khiến bạn bị “tẩu hỏa nhập ma” .

Có một số file quan trọng mà bạn cần phải biết như: package.json, tsconfig.json, typings.json, index.html và thư mục app (nơi mà bạn dành 99% thời gian để làm việc với nó)

Trong thư mục src, bạn sẽ thấy tệp main.ts là tệp chính sẽ được gọi khi ứng dụng bắt đầu. Phần mở rộng là .ts là bởi vì nó được viết bằng ngôn ngữ Typescript.

tsconfig.json

Tệp tsconfig.json là nơi bạn cấu hình cho trình Typescript compiler, cấu hình cách compile từ Typescript sang Javascript.

Đây là nội dung mặc định của tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "",
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "mapRoot": "./",
    "module": "es6",
    "moduleResolution": "node",
    "outDir": "../dist/out-tsc",
    "sourceMap": true,
    "target": "es5",
    "typeRoots": [
      "../node_modules/@types"
    ]
  }
}

Ví dụ như cấu hình trên, phần mình bôi đậm chính là phiên bản Javascript mà trình Typescipt Compiler sẽ biên dịch ra. Kể ra thì trình Angular CLI cũng hơi lạc hậu nhỉ!  Giờ Javascript đã lên phiên bản ES11 rồi mà mặc định Angular CLI vẫn để ES5. Cũng có thể họ muốn tương thích ngược.

Để tìm hiểu thêm nhiều thông số cấu hình trong tsconfig.json, mình nghĩ bạn nên dành thời gian đọc tài liệu hướng dẫn chính thức trên trang chủ: Angular Compiler Options.

package.json

Đây là tệp cấu hình chuẩn của hầu hết các framework sử dụng NPM làm trình quản lý dependencies. Thế nên bạn sẽ gặp tệp này trong các dự án NodeJS, React, Vue… và cả Angular nữa.

Nhiệm vụ chính của tệp này là để bạn quản lý các dependencies (thư viện dùng trong dự án), tự động tải các dependencies từ các remote repository khi bạn gõ câu lệnh: npm install. Tệp này làm mình liên tưởng tới tệp build.gradle trong lập trình Android, nhiệm vụ và chức năng cũng tương tự.

{
  "name": "my-spotify",
  "version": "0.0.0",
  "license": "MIT",
  "angular-cli": {},
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "test": "ng test",
    "pree2e": "webdriver-manager update --standalone false --gecko false",
    "e2e": "protractor"
  },
  "private": true,
  "dependencies": {
    "@angular/common": "^2.3.1",
    "@angular/compiler": "^2.3.1",
    "@angular/core": "^2.3.1",
    "@angular/forms": "^2.3.1",
    "@angular/http": "^2.3.1",
    "@angular/platform-browser": "^2.3.1",
    "@angular/platform-browser-dynamic": "^2.3.1",
    "@angular/router": "^3.3.1",
    "core-js": "^2.4.1",
    "rxjs": "^5.0.1",
    "ts-helpers": "^1.1.1",
    "zone.js": "^0.7.2"
  },
  "devDependencies": {
    "@angular/compiler-cli": "^2.3.1",
    "@types/jasmine": "2.5.38",
    "@types/node": "^7.0.7",
    "angular-cli": "1.0.0-beta.28.3",
    "codelyzer": "~2.0.0-beta.1",
    "jasmine-core": "2.5.2",
    "jasmine-spec-reporter": "2.5.0",
    "karma": "1.2.0",
    "karma-chrome-launcher": "^2.0.0",
    "karma-cli": "^1.0.1",
    "karma-jasmine": "^1.0.2",
    "karma-remap-istanbul": "^0.2.1",
    "protractor": "~4.0.13",
    "ts-node": "1.2.1",
    "tslint": "^4.3.0",
    "typescript": "^3.9.7"
  }
}

Đây là nội dung mặc định của package.json mà trình Angular CLI đã tạo. Phần mình bôi đậm là tên ứng dụng và phiên bản.

Trong thẻ dependencies, chúng ta khai báo danh sách dependencies dùng trong dự án. Sau này, khi bạn cài thêm thư viện nào thì tên thư viện sẽ được thêm vào trong thẻ này.

Thẻ scripts là nơi bạn khai báo các câu lệnh để build, test… Trong ví dụ trên, nếu bạn khai báo: “start“: “ng serve” thì câu lệnh tương ứng khi gõ trong terminal là: npm run start. Lúc này terminal sẽ gọi câu lệnh: ng serve để thực thi.

Ý của mình là: bạn cứ khai báo câu lệnh nào thì khi gõ lệnh trong terminal sẽ tương ứng là: npm run <thẻ khai báo trong scripts>

Như nội dung thẻ scripts trên, chúng ta có các câu lệnh như: npm run ng, npm run start, npm run test…

Mình sẽ giải thích một chút về câu lệnh: ng serve.

Câu lệnh này sẽ chạy trình Typescript Compiler ở chế độ “watch” mode, nên mỗi khi bạn thay đổi code và lưu lại, trình Typescript compiler sẽ tự động theo dõi những thay đổi và load vào trình duyệt. Điều này giúp bạn không phải build lại toàn bộ dự án. Mình sẽ minh họa điều này ở phần Typescript complilation bên dưới bài viết.

app.module.ts

app.module.ts là entry point, tệp bắt đầu của ứng dụng. Một ứng dụng Angular sẽ được chia thành nhiều module riêng biệt, mỗi module sẽ thực hiện một chức năng nhiệm vụ riêng. Và chúng sẽ được lắp ráp lại với nhau thông qua tệp app.module.ts này.

Mặc định thì ứng dụng Angular sẽ có ít nhất một module, gọi là root module, nó có tên là AppModule.

Về cơ bản, với các ứng dụng nhỏ nhỏ, bạn chẳng cần chia thành nhiều module làm gì phức tạp, đau cả đầu, chỉ cần một module là đủ. Nhưng với các ứng dụng lớn thì việc chia thành nhiều module là vấn đề bắt buộc, nếu không muốn ứng dụng không có khả năng mở rộng, maintain sau này. Cách chia ứng dụng thành nhiều module với các ứng dụng lớn, mình sẽ trình bày riêng biệt ở bài viết sau, các bạn chờ nhé.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

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

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

Một module là một class Typescript được khai báo với thẻ @NgModule decorator. Decorator này sẽ thông báo với Angular rằng, class này là một module. Tất cả các module, component trong Angular đều là các class.

Trong tệp này, chúng ta sẽ khai báo các component mà Module này sử dụng, hoặc chỉ định các module khác mà sử dụng, ví dụ: BrowserModule, FormsModule

Sau này, bạn tạo thêm component thì nhớ phải khai báo trong này nữa nhé.

Để hiểu rõ hơn các thẻ sử dụng trong tệp này, bạn có thể đọc lại bài viết trước: Kiến trúc ứng dụng Angular để biết các định nghĩa Component là gì nhé.

app.component.ts

Mọi ứng dụng Angular đều có ít nhất một component, đó là AppComponent, tương ứng là app.component.ts

AppComponent là root component của ứng dụng. Tất cả các component mới tạo ra sau này đều là component con của AppComponent.

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

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

Các component là những “viên gạch” để xây dựng lên ứng dụng Angular. Do đó, dù ngôi nhà có “rẻ tiền” đến đâu thì cũng ít nhất phải có một viên gạch, đúng không?

Sau này, để tạo thêm mới một component, bạn có thể sử dụng câu lệnh của Angular CLI: ng generate component <component-name>

Mặc định, ứng dụng do Angular CLI tạo có AppComponent chưa có gì nhiều, ngoài một class Typescript với một title.

Tương tự với module, chúng ta thêm các metadata thông qua decorator, với component thì là decorator @Component.

TypeScript Compilation

Khi bạn chạy ứng dụng ở chế độ development, lúc này trình Typescript compiler sẽ chạy ở chế độ “watch”. Tức là ứng dụng sẽ tự động load lại nếu bạn thay đổi bất kỳ nội dung mã nguồn nào. Để minh họa cơ chế này, chúng ta hãy thử thay đổi nội dung title trong AppComponent nhé:

export class AppComponent {
  title = 'VNTALKING xin chào cả nhà';
}

Sau khi lưu file lại, bạn quay lại trình duyệt, nó đã tự động cập nhật nội dung mới mà không cần phải đợi bạn phải F5 trình duyệt. Ngạc nhiên chưa ^_^

Angular-app-title

Bạn có thắc mắc tại sao lại “vi diệu” như vậy không?

Chúng ta mở tệp app.component.html, bạn sẽ thấy nội dung sau:

<h1>
  {{title}}
</h1>

Với cú pháp {{title}}, chúng ta đang sử dụng string interpolation, một dạng html template (chúng ta sẽ khám phá kỹ hơn ở phần sau của series). Về cơ bản, điều này cho phép bạn render bất kỳ thuộc tính nào của component vào html. Do đó, {{title}} ở đây thực ra là đang refer tới thuộc tính title trong app.component.ts. Nên khi bạn thay đổi nội dung trong app.component.ts thì nội dung html cũng thay đổi theo.

Tổng kết bài học

Trong bài viết này, chúng ta đã được khám phá Cấu trúc dự án Angular cơ bản của một ứng dụng Angular. Sau này, khi bạn đã trở nên “pro” hơn, bạn có thể tham khảo và cải tiến cấu trúc mặc định này để phù hợp với từng dự án, cũng như nâng tầm mức độ clean code hơn.

Trong phần tiếp theo của series, chúng ta sẽ bắt tay vào thực hiện code chương trình nhé, vừa code vừa học. Hẹn gặp lại nhé.

Xem tiếp các bài trong Series
Phần trước: [Angular-Day 2] Cài đặt và tạo ứng dụng Angular đầu tiênPhần kế tiếp: [Angular-Day 4] Thực hành component, directive và service
Dịch vụ phát triển ứng dụng mobile giá rẻ - chất lượng
Bài trước[Angular-Day 2] Cài đặt và tạo ứng dụng Angular đầu tiên
Bài tiếp theoGiới thiệu Async Iterators trong NodeJS
Tên đầy đủ là Dương Anh Sơn. Tốt nghiệp ĐH Bách Khoa Hà Nội. Mình bắt đầu nghiệp coder khi mà ra trường chẳng xin được việc đúng chuyên ngành. Mình tin rằng chỉ có chia sẻ kiến thức mới là cách học tập nhanh nhất. Các bạn góp ý bài viết của mình bằng cách comment bên dưới nhé !

Bình luận. Cùng nhau thảo luận nhé!

avatar
  Theo dõi bình luận  
Thông báo