Xây dựng một Search-Engine Optimized (SEO) PWA (Progressive web application) bằng Angular – Phần 1

2769

Tác giả: Rajat S

Không quan trọng ứng dụng bạn xây dựng là gì, để nó có thể đến được tay người dùng thì nó phải đến được mắt người dùng trong các kết quả tìm kiếm trước cái đã. Đấy là lý do vì sao Search Engine Optimization (SEO) là một thứ rất quan trọng mà mỗi một lập trình viên khi xây dựng ứng dụng của mình phải chú ý đến.

Trở lại năm 2017, các cuộc khảo sát đã cho thấy rằng, khoảng 27% người dùng điện thoại bỏ qua cửa hàng ứng dụng trên điện thoại của mình, mà tìm đến những công cụ tìm kiếm khác như Google hay Bing.

Chỉ mở cửa hàng ứng dụng trên thiết bị của bạn lên và tìm kiếm một thứ gì đó đơn giản như máy tính bỏ túi chẳng hạn. Thứ bạn nhận được chính là một hằng hà sa số các ứng dụng có thể làm điều tương tự – tính toán.

Điều này chứng tỏ ứng dụng có vị trí top trong các công cụ tìm kiếm là một điều cực kì quan trọng cho mọi lập trình viên. Bởi người dùng tìm kiếm ứng dụng thường không bao giờ xem quá trang đầu hoặc trang hai của kết quả tìm kiếm.

Để hiểu được cách làm việc của công cụ tìm kiếm, hãy xem qua một bài viết khác của Rajat S, “How To Write Better Code in React”.

https://blog.bitsrc.io/how-to-write-better-code-in-react-best-practices-b8ca87d462b0

Nếu bạn vào Google và gõ “React” trên thanh tìm kiếm, thì tại thời điểm Rajat S viết bài này bạn chỉ có thể tìm ra nó ở trang thứ 5!

Nhưng nếu bạn tìm với một từ khóa khác như “better code react”,  thì bài của Rajat S sẽ nhảy ngay lên trang nhất!

Vậy là tạm thời đủ về SEO rồi. Giờ hãy cùng xem qua một PWA (Progressive Web App) là gì nhé!

Dựa theo khái niệm của Google, PWAs có một chút khác biệt với ứng dụng mobile truyền thống. Nó giống như là một trang Web được điều chỉnh để có thể chạy trên mobile vậy. Từ khóa ở đây là “Progressive”, nghĩa là đây là những những ứng dụng/websites mà có thể dần được điều chỉnh phù hợp với cài đặt của thiết bị đầu cuối.

Ở bài này, chúng ta sẽ xây dựng một PWA hoàn toàn mới sử dụng Angular. Bạn sẽ tự tay trải nghiệm làm việc với những thứ như “service workes” và “Angular Resolvers”.

BẮT ĐẦU

Xây dựng một Project Angular

Hãy bắt đầu bằng việc tạo mới một Project Angular trên hệ thống của bạn. Nếu bạn chưa cài đặt Angular CLI thì hãy gõ câu lệnh sau vào cửa sổ câu lệnh của bạn:

$ npm install -g @angular/cli

Sau đó, tạo mới một thư mục trên project vừa tạo tên là comicstore. Để có thể routing trong ứng dụng của chúng ta và sử dụng SASS thay vì CSS truyền thống, thì chúng ta cần phải thêm một số flags sau câu lệnh ng new như sau:

$ ng new comicstore --routing --style scss 
// wait for installation to complete
$ cd comicstore

Chạy câu lệnh ng serve để khởi động Angular Development Server. Server sẽ chạy một ứng dụng angular căn bản trên trình duyệt của bạn với địa chỉ mặc định là localhost:4200.

Nhưng chúng ta không cần những cái mà Angular đã cung cấp sẵn ở đây. Nên hãy loại bỏ nó bằng cách mở file src/app/app.component.html và thay thế toàn bộ code có sẵn với dòng code sau:

<h1>DC Comics Rebirth</h1>

Bootsrap

Chúng ta sẽ sử dụng Bootstrap khá nhiều ở ứng dụng này, đơn giản bởi vì nó sẽ tiết kiệm thời gian viết HTML và CSS của chúng ta.

Truy cập trang web BootstrapCDN và sao chép đường dẫn cho gói CSS hoàn chỉnh. Sau đó trong thư mục project, mở file style.scss và dán đường dẫn vừa sao chép trong import.

@import url(https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css)

Inline File HTML và Style

Khi mà tôi bắt đầu làm việc với Angular, tôi nhận thấy rằng mỗi component trong Angluar có 4 file:

  • File Component Class
  • File Component’s HTML mẫu
  • File Component’s StyleSheet
  • File Component’s Test

Tôi nhận ra rằng mọi thứ sẽ trở nên dễ dàng hơn nếu tôi có thể di chuyển file HTML và file StyleSheet từ các file riêng biệt vào trong file Component Class. Điều này sẽ giảm bớt số lượng file có trong mỗi Component và cũng không phải mở từng file để chỉnh sửa khi muốn sửa code.

Để bắt đầu, xóa file app.component.htmlapp.component.scss từ thư mục src/app. Sau đó vào file app.component.ts và đổi tên thuộc tính templateUrl thành template. Và cũng đổi nội dung của thuộc tính này như sau:

template: `
  <h1>DC Comics Rebirth</h1>
  <router-outlet></router-outlet>
`,

Tương tự, đổi tên thuộc tính styleUrls thành styles và thay đổi nội dung như sau:

styles: [`
  h1{
    color: #0476F2;
  }
`]

Chúng ta cũng ần phải cài đặt cho Angular biết rằng chúng ta sẽ viết HTML và Style CSS trong component. Để làm được thế thì chúng ta sử dụng câu lệnh ng config như sau:

$ ng config schematics.@schematics/angular.component.inlineStyle true
$ ng config schematics.@schematics/angular.component.inlineTemplate true

Sau đó chúng ta sẽ thấy vài thay đổi với object schematics trong file angular.json.

Xây dựng Application Layout

Thay vì tốn thời giờ xây dựng layout từ đầu, chúng ta có thể sử dụng câu lệnh module ui để tạo một ứng dụng đơn giản. Layout này sẽ bao gồm một component cho header, footer và body của layout.

Câu lệnh module ui được sử dụng với câu lệnh ng generate như sau:

$ ng generate module ui --module App

Câu lệnh này sẽ tạo ra một thư mục mới tên là ui bên trong thư mục src/app. Angular sẽ tạo một dòng code import cho thư mục này trong file app.module.ts.

Tiếp theo, chúng ta cần tạo component cho Header, Footer, và Layout của ứng dụng.

$ ng generate component ui/containers/layout 
$ ng generate component ui/containers/header 
$ ng generate component ui/containers/footer

Những dòng lệnh này sẽ tạo một file component chính và một file test cho mỗi component.

Tiếp theo, mở file app-routing.module.ts và thêm vào route mặc định cho LayoutComponent như bên dưới:

const routes: Routes = [{
  path: '',
  component: LayoutComponent,
  children: [],
}];

Bằng cách này, ứng dụng của chúng ta sẽ cài đặt render nội dung của LayoutComponent. Bạn cũng sẽ thấy rằng ứng dụng của chúng ta vẫn hiển thị tag h1 mà chúng ta đã viết bên trong file app.component.ts. Để cho đơn giản hơn, hãy di chuyển nó vào trong file layout.component.ts.

Đầu tiên, vào file app.component.ts, và xóa mảng styles. Và cũng xóa tag h1 trong thuộc tính template.

Bây giờ vào file layout.component.ts được đặt ở trong thư mục app/ui/containers/layout, và viết lại thuộc tính template như bên dưới:

template: `
  <app-header></app-header>
  <div class="container my-8 py-8">
    <router-outlet></router-outlet>
  </div>
  <app-footer></app-footer>
`,

Nếu bị lỗi trên trình duyệt của bạn, hẳn là Angular chưa import RouterModule vào file ui.module.ts. Nếu gặp trường hợp này, hãy xử lý như sau:

@NgModule({
  imports: [
    CommonModule,
    RouterModule
  ],
  declarations: [
    LayoutComponent, 
    HeaderComponent, 
    FooterComponent
  ]
})

Như là tên của của ứng dụng này, thì nó sẽ dùng để lưu truyện tranh. Mà tôi lại là fan của DC Comics, nên tôi sẽ lưu loại này. Vậy nên tôi sẽ vào google và kiếm logo của DC và lưu nó vào thư mục src/assets với tên là logo.svg.

Bây giờ ta sẽ thêm logo vào component header. Mở file header.component.ts và thêm vào những thuộc tính sau cho class HeaderComponent.

public logo = 'assets/logo.svg';
public title = 'DC Comics Rebirth';
public links = [{
  label: 'Comics',
  url: '/Comics',
}]

Xóa nội dung của thuộc tính template. Thay vào đó, chúng ta sẽ thêm vào tag nav như sau:

template: `
  <nav class=navbar navbar-expand navbar-blue fixed-top>
`

Bạn sẽ để ý rằng ở đây sẽ xuất hiện một dòng màu đen dày ở đầu của trình duyệt. Tiếp theo, hãy thêm vào logo và tiêu đề cho header này. Phía sau tag nav, thêm vào như sau:

<a routerLink="/" class="navbar-brand">
  <img [attr.src]="logo" [attr.alt]="title" width="30" height="30>
  {{title}}
</a>

Chúng ta cũng tạo một đường dẫn tên Comics. Vậy chúng ta cũng sẽ thêm đường dẫn này vào component header. Dưới tag a, thêm tag div mới như sau:

<div class="collapse navbar-collpase">     
  <div class="navbar-nav">         
    <a class="nav-item nav-link" *ngFor="let link of links"
    [routerLink]="link.urk" routerLinkActive="active"
    [routerLinkActiveOptions]="{ exact: true }">         
    {{ link.label }}         
    </a>     
  </div> 
</div>

Cuối cùng, header của chúng ta sẽ trông như thế này:

Hãy xử lý luôn component footer. Vào file footer.component.ts và xóa mọi thứ trong thuộc tính template. Vào thêm vào thẻ nav trong thuộc tính template với cùng class như cái ở trong header.

template: `
  <nav class="navbar navbar-expand navbar-dark bg-dark fixed-bottom">
`

Ta sẽ thấy xuất hiện viền màu đen lớn ở cuối trang. Đây sẽ là footer của ta. Bạn sẽ có thể viết bất kỳ thứ gì ở đây. Ví dụ như sau:

<div class="navbar-text m-auto text-white">
  The comics and characters and everything else presented here belongs to DC Comics.
</div>

Lấy dữ liệu trên Angular

Hãy bắt đầu bằng việc tạo mới route tên comic.

$ ng g m comic --routing

Câu lệnh này sẽ tạo một thư mục mới tên là comic bên trong thư mục app.

Mở file app-routing.module.ts và tạo 2 route ới bên trong mảng children.

children: [{
  path: '',
  pathMatch: 'full',
  redirectTo: '/comics',
  }, {
  path: 'comics',
  loadChildren: './comic/comic.module#ComicModule'
}],

Route đầu tiên sẽ dẫn người dùng từ route chưa định nghĩa sang route comics. Route thứ 2 chính là route comics thật.

Bây giờ khi tải lại trang, nó sẽ tự động dẫn vào route comics.

Tiếp theo, Hãy thêm module mới tên là comic:

$ ng g cl comic/models/comic

Bây giờ ta sẽ có một thư mục mới tên là models bên trong thư mục comic. Nó sẽ chứa một file tên comic.ts. Thêm những thuộc tính sau bên trong file này:

export class Comic {
  public id: string;
  public name: string;
  public description: string;
  public image: string;
  public price: number;
}

Tiếp theo, tạo một cặp container để giúp lấy dữ liệu và truyền dữ liệu đó vào những component khác.

$ ng g c comic/containers/comic-list
$ ng g c comic/containers/comic-detail

Những câu lệnh này sẽ tạo ra 2 container components comic-listcomic-detail.

Mở file comic-routing.module.ts và thêm một route vào ComicListComponent. Đường dẫn của route này sẽ trở thành một chuỗi rỗng. Tương tự, thêm một route vào ComicDetailComponent với đường dẫn đổi thành :id.

const routes: Routes = [{     
  path: '',     
  component: ComicListComponent, 
}, {     
  path: ':id',     
  component: ComicDetailComponent, 
}]

Mở file comic-list.component.ts và đặt thuộc tính template thành comics.

template: `
  {{comics | json}}
`,

Làm điều tương tự cho thuộc tính template của file comic-detail.component.ts.

Trong class component của comic-list.component.tscomic-detail.component.ts, thêm một thuộc tính comics mà có kiểu Comic[] và khởi tạo nó như một mảng rỗng.

public comic: Comic[] = new Comic();

Sau đó mở file comic-detail.component.ts và inject ActivatedRoute trong constructor. Việc này sẽ cho phép truyền thuộc tính id từ đường dẫn. Và cũng gán this.comic.id vào this.route.snapshot.paraMap.get('id') trong ngOnInit.

constructor(private route: ActivatedRoute) {}
ngOnInit() {
  this.comic.id = this.route.snapshot.paramMap.get('id');
}

Và để chắc chắn rằng ActivatedRoute được import vào file này:

import { ActivatedRoute } from ‘@angular/router’;

…[Còn tiếp]… 

Bài viết gốc được đăng tải tại Blog Bitsrc