這篇主要講如何在 Angular 的專案中手動加入 Manifest 檔案,來達成 PWA 最基本的需求之一


什麼是 Manifest

類似像 Android APP 都會有的 AndroidManifest.xml 一樣,Web APP 也希望有一個檔案來集中管理應用程式的基本資訊,Web APP 的 manifest 檔案格式為 .json

The web app manifest provides information about a web application in a JSON text file, necessary for the web app to be downloaded and be presented to the user similarly to a native app.

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"name": "HackerWeb",
"short_name": "HackerWeb",
"scope":"/"
"start_url": ".",
"display": "standalone",
"background_color": "#fff",
"description": "A simply readable Hacker News app.",
"icons": [{
"src": "homescreen48.png",
"sizes": "48x48",
"type": "image/png"
}, {
"src": "homescreen72.png",
"sizes": "72x72",
"type": "image/png"
}, {
"src": "homescreen96.png",
"sizes": "96x96",
"type": "image/png"
}, {
"src": "homescreen144.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "ihomescreen168.png",
"sizes": "168x168",
"type": "image/png"
}, {
"src": "homescreen192.png",
"sizes": "192x192",
"type": "image/png"
}],
"related_applications": [{
"platform": "play",
"url": "https://play.google.com/store/apps/details?id=cheeaun.hackerweb"
}]
}

Deploy on Angular

src/

在要啟用 PWA 的 app/src 底下新增 manifest.json 檔案,通常會跟 index.html 同個 path

html

在網頁的入口檔案比如 index.html 中的 <head> 欄位內加入

1
<link rel="manifest" href="/manifest.json">

angular.json

通常 angular.json 會由 Angular CLI 自動幫你建好,但因為 Angular build app 的時候會將檔案打包到指定的 output path 下 (比如說 dist/),會在 angular.json 中根據給定的設定做打包,有可能你 link manifest 的 index.htmlmanifest.json 最後在不一樣的路徑,看你的設定怎麼寫

所以如果希望 manifest.json build 完會在正確的相對路徑上,比如說應該要和 index.html 在同個路徑,就有可能需要修改 angular.json

angular.json 中找到 projects property,底下會列出所有包含的 application,找到對應的 application 的欄位

假設你的 application name = “my-app”,在 “my-app” 這個 property 底下找到 “architect” 欄位

1
2
3
4
5
6
7
8
"my-app": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {}
}

在 “architect” 底下找到 “build”

1
2
3
4
5
6
7
8
9
10
"architect": {
"build": { },
"serve": { },
"e2e" : { },
"test": { },
"lint": { },
"extract-i18n": { },
"server": { },
"app-shell": { }
}

在 “build” 內 找到 “options”,然後裡面會有 “assets”,在這邊加上 manifest 的 output 設定

  • “options” 內有個設定叫 “outputPath”
  • 如果 “output” 設為 “/”,會放在 “outputPath” 下,也就是 dist/practice/demo/
  • 如果 “output” 設為 “/assets/”,會放在 dist/practice/demo/assets/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

"outputPath": "dist/practice/demo/",
...
"assets": [
...
{
"glob": "favicon.ico",
"input": "projects/my-app/src",
"output": "/"
},
{
"glob": "fonts",
"input": "styles/",
"output": "/assets/"
},
{
"glob": "manifest.json",
"input": "projects/my-app/src/",
"output": "/"
}
],

可以參考 Angular workspace configuration 查詢更多這些關於 Angular workspace 屬性設置的資料

Manifest 屬性

陳列介紹一下在 Manifest 內幾個重要的設定

Splash screens

載入應用的畫面,Chrome 支援開啟應用的前置動畫,會根據 manifest 的內容自動配置一個開啟畫面

由 Manifest 中裡面的三種屬性組成

  • name
  • background_color
  • icons

以 Twitter 為例,background_color 就是白色,icons 就是 Twitter 的 icon,從手機桌面開啟 Twitter 的 PWA 會先看到左邊的畫面後,才進入右邊的入口頁面(未登入)

Display mode

1
2
3
4
"standalone"
"fullscreen"
"minimal-ui"
"browser"

Standalone

Web APP 看起來會像 Native APP 一樣,運行在一個全螢幕的視窗
Address bar 和 Navigation bar 會設成隱藏,Status bar (battery, time, etc.) 還是可見

Fullscreen

跟 Standalone 的差別就是不會顯示 Status bar

Minimal-UI

跟 Standalone 的差別是會視 browser 的類別來選擇支援最小的 UI,比如說在 Chrome,Address bar 會顯示出來,但不能被點擊

Browser

只會跟一般網頁一樣,從 Launcher 打開的模樣就像普通 Bookmark, 在 Chrome 上使用這個 mode 開啟網頁不會有 “Add to Home Screen” 的提示

start url

指定開啟 Web APP 時希望看到的網頁畫面

absolute url

1
"start_url":"https://yourwebsite.com"

relative url

根據 manifest.json 放的位置來指定想開啟的網頁路徑

1
"start_url":"../index.html"

scope

決定 Web APP 的 navigation scope,如果使用者瀏覽在指定的範圍外的網頁就會變成一般的網頁

absolute url

1
2
"scope":"https://yourwebsite.com"
"scope":"https://yourwebsite.com/subdirectory"

relative url

"scope":"/app"

後記

其實也沒有什麼好講的,就是純粹紀錄,主要是 manifest 的 build path 的地方比較需要注意,當初馬上轉上工前端專案後,沒有很了解 Angular 專案整個的脈絡,所以花了一兩天時間卡在那邊,希望這篇有幫助到人囉

至於 Manifest 在 iOS Safari 怎麼配置之後有空再來寫了,因為比較少東西需要講,而且網路上也比較多資料了