Tối ưu hoá công cụ tìm kiếm cho ứng dụng AngularJS 4
This post hasn't been updated for 3 years
Những ứng dụng Single Page (SPAs) thật tuyệt vời! Chúng load nhanh và cung cấp cho bạn nhiều kiểm soát về cách bạn muốn ứng dụng chạy. Chúng được parsed bởi trình duyệt và do đó bạn có thể kiểm soát được các DOM elements một cách thần thánh. Tuy nhiên, SPAs không thân thiện với công cụ tìm kiếm (not SEO friendly) bởi vì chúng thay đổi các meta tags và content bằng JavaScript và thay đổi này thường không đuợc bot của các công cụ tìm kiếm biết đến.
Ví dụ, các ứng dụng Angular 4 tải nội dung HTML trống đầu tiên trước khi lấy nội dung HTML cho trang được bằng XMLHttpRequest. Vì một số công cụ tìm kiếm không thể phân tích cú pháp JavaScript khi thu thập thông tin trang web, họ sẽ chỉ nhìn thấy nội dung trống đầu tiên.
Mặc dù Google cho biết bot tìm kiếm của họ có khả năng render JavaScript, điều này vẫn còn mơ hồ, hơn hết chúng ta vẫn nên thận trọng để giải quyết vấn đề này. Ngoài ra còn có các công cụ tìm kiếm khác không xử lý JavaScript. Bài viết này sẽ giới thiệu cách làm cho ứng dụng Angular 4 thân thiện với công cụ tìm kiếm giúp cho website của bạn có thứ hạng cao hơn trong kết quả tìm kiếm.
⚠️ Lưu ý: Đây không phải là Bài hướng dẫn về Angular 4 nên không bao gồm các chi tiết cụ thể về Angular 4 framework.
Bắt đầu làm cho ứng dụng Angular 4 của chúng ta thân thiện với công cụ tìm kiếm
Trước khi bắt đầu, chúng ta hãy xây dựng một ứng dụng đơn giản sử dụng Angular. Ứng dụng sẽ là một trang liệt kê một loạt chủ đề trên trang chủ. Chúng ta sẽ không kết nối với bất kỳ nguồn dữ liệu nào, thay vào đó sẽ hard code dữ liệu vào component.
Tạo một ứng dụng Angular 4 đơn giản
Chúng ta sẽ sử dụng ng-cli để tạo một ứng dụng Angular gọi là Blogist.
Tạo một ứng dụng mới sử dụng ng-cli
Chúng ta sẽ sử dụng ng new
command để tạo ứng dụng Angular 4.
$ ng new Blogist
⚠️ Lưu ý: Bạn cần phải sử dụng phiên bản Angular CLI mới nhất để ứng dụng này hoạt động đúng. Phiên bản mới nhất là 1.3.x vào thời điểm viết bài này.
Tiếp theo, chúng ta sẽ tạo ra một component mà sau đó có thể thêm code logic vào. Chúng ta sẽ sử dụng ng g component
command cho điều này:
$ ng g component ./blog/posts
Thêm mock data vào PostComponent của chúng ta
Vì lý do ngắn gọn, chúng ta sẽ không kết nối với một API bên ngoài. Thay vào đó, chúng ta sẽ chỉ tạo ra một số dữ liệu mô phỏng và sử dụng dữ liệu đó trong ứng dụng của chúng ta.
Mở file ./src/app/blog/posts.component.ts, chúng ta sẽ thêm một số logic code để đảm bảo rằng nó hoạt động như chúng ta muốn. Đầu tiên, chúng ta hãy mã hóa một số dữ liệu vào tệp. Thêm một method mới được gọi là postsData
cho component.
private postsData() {
return [
{
"title": "Making Angular.js realtime with Websockets by marble",
"pubDate": "2017-08-23 14:41:52",
"link": "https://blog.pusher.com/making-angular-js-realtime-with-pusher/#comment-10372",
"guid": "http://blog.pusher.com/?p=682#comment-10372",
"author": "marble",
"thumbnail": "",
"description": "always a big fan of linking to bloggers that I enjoy but dont get a great deal of link enjoy from",
"content": "<p>always a big fan of linking to bloggers that I enjoy but dont get a great deal of link enjoy from</p>",
"enclosure": [],
"categories": []
},
{
"title": "Making Angular.js realtime with Websockets by strapless strap on",
"pubDate": "2017-08-23 05:05:08",
"link": "https://blog.pusher.com/making-angular-js-realtime-with-pusher/#comment-10371",
"guid": "http://blog.pusher.com/?p=682#comment-10371",
"author": "strapless strap on",
"thumbnail": "",
"description": "very couple of internet websites that transpire to be detailed beneath, from our point of view are undoubtedly properly worth checking out",
"content": "<p>very couple of internet websites that transpire to be detailed beneath, from our point of view are undoubtedly properly worth checking out</p>",
"enclosure": [],
"categories": []
},
{
"title": "Making Angular.js realtime with Websockets by bondage restraints",
"pubDate": "2017-08-22 17:09:17",
"link": "https://blog.pusher.com/making-angular-js-realtime-with-pusher/#comment-10370",
"guid": "http://blog.pusher.com/?p=682#comment-10370",
"author": "bondage restraints",
"thumbnail": "",
"description": "very couple of web sites that occur to be in depth below, from our point of view are undoubtedly properly worth checking out",
"content": "<p>very couple of web sites that occur to be in depth below, from our point of view are undoubtedly properly worth checking out</p>",
"enclosure": [],
"categories": []
}
];
}
Để sử dụng mock data đã đuợc tạo phía trên, chúng ta thay constructor
method của PostsComponent
class bằng đoạn code dưới đây:
public posts;
constructor() {
this.posts = this.postsData();
}
Trong đoạn code phía trên, chúng ta chỉ đơn giản gán cho thuộc tính posts
bằng giá trị mà postsData
method trả về, đây chính là cách chúng ta giả lập API response.
Tạo View cho PostsComponent
Bây giờ chúng ta đã có mock posts data. Chúng ta sẽ tạo một view hiển thị tất cả posts từ mock data.
Mở view ./app/blog/posts.component.html
và thêm vào đoạn code dưới đây:
<div class="jumbotron">
<h1>Blogist</h1>
<p>This is the best resource for the best web development posts.</p>
</div>
<div class="row">
<div class="col-xs-12 col-md-12">
<ul class="list-group">
<li class="list-group-item" *ngFor="let post of posts">
<h4>{{post.title}}</h4>
</li>
</ul>
</div>
</div>
Đoạn code trên chỉ lấy về posts data sau đó lặp và hiển thị title của post.
Tiếp theo, mở file index.html
và trong thẻ <head>
thay nội dung với code dưới đây. Nó chỉ đơn giản sử dụng Bootstrap
và thêm navigation bar:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Blogist</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Blogist</a>
</div>
<ul class="nav navbar-nav">
<li class="active"><a href="#">Posts</a></li>
<li><a href="#">Web Development</a></li>
<li><a href="#">Graphic Design</a></li>
</ul>
</div>
</nav>
<div class="container">
<app-root>Loading...</app-root>
</div>
</body>
</html>
```
### Đăng ký PostsComponent trong module application module
Tiếp theo chúng ta sẽ đăng ký PostsComponent thành module ứng dụng
💡 Chú ý sử dụng **ng g component** command sẽ tự động đăng ký PostsComponent trong application module. Vì vậy, bạn có thể không cần phải làm lại. Nếu nó đã được thực hiện cho bạn, bạn có thể bỏ qua bước này.
Nếu nó chưa được đăng ký tự động, hãy mở tệp `./src/app/app.module.ts` và import `PostsComponent`:
```Javascript
import { PostsComponent } from './blog/posts.component';
Sau đó, trong mảng NgModule
declarations, thêm PostsComponent:
@NgModule({
declarations: [
...
PostsComponent,
],
...
})
Hiển thị ứng dụng Angular
Sau khi đăng ký Posts component, chúng ta sẽ include nó trong ./src/app/app.component.html
file để posts component được hiển thị. Mở ./src/app/app.component.html
file và thêm vào dòng code dưới đây:
<app-posts></app-posts>
Đó là tất cả!
Bây giờ bạn chạy ng serve
và mở URL đuợc cung cấp. Bạn sẽ có thể nhìn page với tất cả các posts:
Tuyệt vời, đó là chính xác những gì chúng ta mong đợi. Tuy nhiên, khi bạn view source của URL, bạn sẽ nhận thấy toàn bộ nội của page bị thiếu và chỉ <app-root>loading… </ app-root>
hiển thị.
Đó là do cách Angular hoạt động. Nó sẽ tải parent template đầu tiên, sau đó sau đó tải chính nó.
Sau đó nó sẽ bắt đầu quá trình thao tác DOM sẽ chèn nội dung của mỗi trang tiếp theo trong thẻ <app-root>
.
Do đó, khi bot công cụ tìm kiếm request trang này, nó sẽ nhận được HTML <app-root><app-root>Loading…</app-root> </ app-root>
ở trên và nội dung của trang đã khiến cho nội dung cần SEO không đến đưọc công cụ tìm kiếm.
Tối ưu hoá công cụ tìm kiếm cho ứng dụng AngularJS 4
Bây giờ chúng ta đã xây dựng ứng dụng sample, chúng ta có thể thấy ngay nó không thân thiện với SEO. Vì vậy, chúng ta sẽ sử dụng Angular universal platform để pre-render templates phía server-side sau đó serve khi page đã đuợc tải
💡 Universal Angular project bao base platform API và các công cụ xung quanh cho phép các nhà phát triển render phía server-side (hoặc pre-rendering) trong các ứng dụng Angular..
Để bắt đầu, chúng ta sẽ cài đặt angular/platform-server
package và angular/animations
package. Cả hai đều cần require để platform server hoạt động chính xác
. Platform server sẽ cung cấp server-side rendering.
Chạy command dưới đây trong terminal để cài đặt dependencies require cho server-side rendering của ứng dụng Angular:
$ npm install --save @angular/platform-server @angular/animations
Sau khi các packages đã cài đặt thành công NPM, mở ./src/app.modules.ts
và thay đổi BrowserModule declaration như dưới đây:
@NgModule({
...
imports: [
BrowserModule.withServerTransition({appId: 'blogist'})
],
...
})
Trong đoạn code phía trên, chúng ta thêm withServerTransition
method vào BrowserModule
và truyền vào appId
trùng với tên ứng dụng blogist. Thêm vào như này nhằm ‘cấu hình một a browser-based application chuyển từ một server-rendered app, nếu có hiển thị trên page’.
Tiếp theo chúng ta sẽ tạo application server module. Tạo một file mới ./src/app/app-server.module.ts
import { NgModule } from '@angular/core';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { ServerModule } from '@angular/platform-server';
@NgModule({
imports: [
ServerModule,
AppModule,
],
bootstrap: [
AppComponent
]
})
export class AppServerModule { }
Đây là một Angular module cơ bản sẽ hoạt động như server module. Điều lớn nhất cần chú ý là ở trên chúng ta import AppModule trong server module để nó sẽ là một phần của AppServerModule. Module này sẽ nơi chúng ta boostrap ứng dụng từ server.
Thêm title và meta tags vào ứng dụng Angular
Một điều cuối cùng chúng ta thêm vào ứng dụng là hỗ trợ meta tags và title trên mỗi page. Với Angular universal, làm điều này rất dễ dàng.
Mở ./src/app/blog/posts.component.ts
file và làm như duới đây:
Import Meta và Title từ @angular/platform-browser
package:
import { Meta, Title } from '@angular/platform-browser';
Bây giờ trong constructor
method, thêm những dòng code này:
constructor(meta: Meta, title: Title) {
this.posts = this.postsData();
// Sets the <title></title>
title.setTitle('Blogist');
// Sets the <meta> tag for the page
meta.addTags([
{ name: 'author', content: 'Blogist' },
{ name: 'description', content: 'This is a description.' },
]);
}
```
Đoạn code trên cho phép set title cho mỗi page bạn tạo và chúng sẽ trở nên pre-rendered sử dụng Angular Universal. Điều nay cho phép bạn kiểm soát tốt hơn meta tags và title của mỗi page khác nhau.
### Tạo một Express server để làm cho ứng dụng Angular SEO friendly
Hãy tạo mộtt Express server. Điều này đơn giản sẽ cho phép server-side rendering.
Tạo một file `./src/server.ts` và thêm vào nội dung dưới đây:
```Javascript
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { renderModuleFactory } from '@angular/platform-server'
import { enableProdMode } from '@angular/core'
import * as express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';
import { AppServerModuleNgFactory } from '../dist/ngfactory/src/app/app-server.module.ngfactory'
enableProdMode()
const PORT = process.env.PORT || 4000
const DIST_DIR = join(__dirname, '..', 'dist')
const app = express();
const template = readFileSync(join(DIST_DIR, 'index.html')).toString()
app.engine('html', (_, options, callback) => {
const newOptions = { document: template, url: options.req.url };
renderModuleFactory(AppServerModuleNgFactory, newOptions)
.then(html => callback(null, html))
})
app.set('views', 'src')
app.set('view engine', 'html')
app.get('*.*', express.static(DIST_DIR))
app.get('*', (req, res) => {
res.render('index', { req })
})
app.listen(PORT, () => {
console.log(`App listening on http://localhost:${PORT}!`)
});
```
Trong file này, chúng ta đã import tất cả các packages chúng ta cần để chạy Express server của chúng ta. Đặc biệt, chúng ta `import AppServerModuleNgFactory`, một file không tồn tại những sẽ được generate trong build process.
Tiếp theo, `enableProdMode()` đơn giản là enable production mode trên ứng dụng của chúng ta. Chúng ta cũng sử dụng [renderModuleFactory](https://angular.io/api/platform-server/renderModuleFactory) để parse HTML và render page đã được loaded trên server-side. Mọi thứ khác trong đoạn code này đều liên quan tới Express.
Tiếp theo chúng ta mở `./src/tsconfig.app.json` file và thêm `server.ts` vào exclude section.
```Javascript
"exclude": [
"server.ts",
...
]
💡 Thuộc tính exclude liệt kê một danh sách các files sẽ bị loại trừ khỏi quá trình biên dịch.
Mở file ./tsconfig.json
và thêm đoạn code bên dưới vào dưới cùng sau thuộc tính compilerOptions
:
...
"lib": [
"es2016",
"dom"
]
},
"angularCompilerOptions": {
"genDir": "./dist/ngfactory",
"entryModule": "./src/app/app.module#AppModule"
}
}
💡 genDir
là nơi chủ yếu mọi thứ được generated. entryModule
chấp nhận đường dẫn của module bootstrapped. #AppModule ở cuối đường dẫn là tên của các layers được published.
Bước cuối cùng cần làm là cập nhật thuộc tính scripts trong ./package.json file. Bạn nên thay thế hoặc bổ sung vào những keys có sẵn trong thuộc tính scripts:
{
...
"scripts": {
"prestart": "ng build --prod && ./node_modules/.bin/ngc",
"start": "ts-node src/server.ts"
},
...
}
Chúng ta có những commands đã được đăng ký cho start và prestart scripts trong ./package.json file. Bởi vì chúng ta đã thêm vào prestart
, nó sẽ chạy tự động truớc khi start
được.
Test SEO friendly trong ứng dụng Angular 4
Sau khi bạn đã thực hiện xong những thay đổi này cho ứng dụng, vào terminal và chạy lệnh sau đây:
$ npm run start
Thao tác này sẽ chạy prestart script chứa các lệnh ng build --prod
&& ./node_modules/.bin/ngc
và chạy start script ts-node src / server.ts
. Một khi các lệnh được hoàn thành, bạn sẽ thấy out gần với hình ảnh này trên terminal:
Khi bạn truy cập vào trang bây giờ bạn vẫn sẽ thấy output tương tự như bạn đã thấy trước đây. Tuy nhiên, khi bạn xem source, bạn sẽ thấy HTML hoàn chỉnh.
Kết luận
Trong bài này, chúng ta đã khám phá làm thế nào để làm cho Angular 4 Single Page Application (SPA) SEO Friendly bằng cách sử dụng Angular 4 Universal. Hy vọng rằng bạn đã học được một vài điều và nỗi lo về việc tối ưu hóa SEO xấu sẽ không ngăn bạn sử dụng Angular 4 cho các ứng dụng của bạn nữa.
Bài viết đuợc dịch từ nguồn: https://blog.pusher.com/make-angular-4-app-seo-friendly/
All Rights Reserved