LoginSignup
5
5

More than 5 years have passed since last update.

Polymer サンプルコード (4) PWA [1]

Last updated at Posted at 2017-08-21

PWA (Progressive Web Application) [1]

PolymerでMarkdown形式のテキストをページ毎に整形して切り替える簡単なPWAを作ってみます。
ビルドツール(Polymer CLI)を使い、 Firebaseホスティングで確認します。

Polymer サンプルコード (3) SPA の続きです。

動作確認
Windows 10 : ○ Chrome60 Firefox54 Edge40 IE11
Android 7.1 : ○ Chrome60
macOS 10.12 : ○ Safari10.0
iOS 10.3 : ○ Safari10.0

画面イメージ

表示上は前回とほぼ同じです。

qiita-polymer-pwa1

:globe_with_meridians: デモサイト (Firebase)

Lighthouseの結果

PWAの要件チェックツール(Lighthouse)で100点でした。chrome60からはデベロッパーツールに統合されたのでチェックしやすくなっています。

image.png

実行ステップ

1. 環境設定(Firebase)の設定

ファイルのアップロード先となるFirebaseの管理コンソールからプロジェクトを作成します。

  • プロジェクトを追加をクリック
    image.png

  • プロジェクト名に適当な名前を入力、国 / 地域は日本を選択。プロジェクト名からプロジェクトIDが生成されるので、覚えておきます(サンプルではsample-1と入力してプロジェクトIDはsample-1-52253となりました)
    image.png

2-A. Google Cloud Shellでの実行

Google Cloud Shellにはgitやnpm、bower、firebaseが予めインストールされていて手軽に使えます。こちらから起動して下記のコマンドで実行してください。
polymer serveでの確認も「ウェブでプレビュー」から表示することができます。

CloudShell
# polymer-cliのインストール(5分程度かかります)
# Cloud Shellはグローバルへのインストールが一定時間でクリアされてしまうので、ホームディレクトリ下で動かします
$ npm install polymer-cli

# polymer-cliをコマンドとして使えるようにaliasを設定
$ alias polymer="~/node_modules/polymer-cli/bin/polymer.js"

# githubからソースをダウンロード
$ git clone -b sample-1 https://github.com/howking/polymer-sample-pwa sample-1

# 作業ディレクトリに移動
$ cd sample-1

# 利用するWebライブラリを取得
$ bower install

# サーバを立ち上げて動作確認
$ polymer serve

# アップロードするファイルを構築
$ polymer build

# Firebaseにログイン(表示されたURLをブラウザで開いて認証コードを入力してください)
$ firebase login --no-localhost

# 使用するプロジェクトを指定( $ firebase list で一覧が表示されます)
$ firebase use [プロジェクトID]

# ファイルをアップロード
$ firebase deploy

2-B. Windows等のローカルマシンでの実行

パッケージ管理ソフトの準備

Git(ソース管理ツール)とnpm(Node.jsのパッケージ管理ツール)が必要です。Windowsはダイアログに沿ってインストールしてください(途中の選択肢は特に変更しなくて大丈夫です)。

コマンドの準備

コマンドプロンプトから下記を入力してBower(Webコンポーネントのパッケージ管理ツール)、Polymer CLI(Polymer操作ツール)、Firebase CLI(Firebase操作ツール)をインストールしてください(途中エラーが表示される場合がありますが、最後まで終れば大丈夫です)。

localhost
$ npm install -g bower
$ npm install -g polymer-cli
$ npm install -g firebase-tools

ビルドとファイルのアップロード

ファイル一式をこちらからダウンロード(sample-1ブランチに切り替えて)するか、後述のサンプルコードからファイルを作成してください。
下記を入力するとビルドされ、ファイルがアップロードされます。

localhost
# githubからダウンロードする場合
$ git clone -b sample-1 https://github.com/howking/polymer-sample-pwa sample-1

# 作業ディレクトリに移動
$ cd sample-1

# 利用するWebライブラリを取得
$ bower install

# ローカルでサーバを立ち上げて動作確認 → http://localhost:8000/
$ polymer serve

# アップロードするファイルを構築
$ polymer build

# Firebaseにログイン
$ firebase login

# 使用するプロジェクトを指定
$ firebase use [プロジェクトID]

# ファイルをアップロード
$ firebase deploy

3. サイト確認

:tada: :tada: :tada: https://[プロジェクトID].firebaseapp.com/ で確認できます。

A. デバッグ方法

単純にソースを編集して更新、リロードしてもServiceWorkerによってオフラインキャッシュが使われてしまい、ページが更新されない場合があります

image.png

Chromeの場合は、

  1. 「その他のツール」→「閲覧履歴を消去」及び、
  2. ServiceWorkerの解除 image.png

か、

image.png

で再読み込みをしてください。

Firefoxはアドレスバーにabout:debugging#workersを入力するとServiceWorkerがリストされるので解除してください。

サンプルコード

files
├── README.md
├── bower.json
├── firebase.json
├── index.html
├── manifest.json
├── my-view.html
├── pages
│   ├── view1.md
│   ├── view2.md
│   └── view3.md
├── polymer.json
└── sw-precache-config.js
  • index.html トップページ、 <my-view>タグの呼出し元
index.html
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Polymer Sample PWA</title>
    <link rel="manifest" href="/manifest.json">
    <meta name="theme-color" content="#29549a">
    <script src="/bower_components/webcomponentsjs/webcomponents-loader.js"></script>
    <link rel="import" href="/my-view.html" async>
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('/service-worker.js');
        });
      }
    </script>
  </head>
  <body>
    <my-view key="view1" pages='[["view1","Page 1"], ["view2","Page 2"], ["view3","Page 3"]]'>
      <svg viewBox="0 0 24 30" fill="#eee" width="128"
           style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; margin: auto;">
        <path d="M19 4h-4L7.11 16.63 4.5 12 9 4H5L.5 12 5 20h4l7.89-12.63L19.5 12 15 20h4l4.5-8z"/>
      </svg>
      <noscript>Polymer Sample PWA</noscript>
    </my-view>
  </body>
</html>
  • pages/view[1-3].md マークダウンで書かれたテキスト
view1.md
# こんにちわ

これは1ページ目です。
view2.md
# 2ページ目

これはサンプルのテキストです。

## 見出し1

[リンク](https://www.google.com/)も書けます。

## 見出し2

**強調表示**
view3.md
# こんにちわ

これは3ページ目です。
  • my-view.html <my-view>タグを表示するHTML
my-view.html
<link rel="import" href="bower_components/polymer/polymer-element.html">
<link rel="import" href="bower_components/polymer/lib/elements/dom-repeat.html">
<link rel="import" href="bower_components/app-layout/app-header-layout/app-header-layout.html">
<link rel="import" href="bower_components/app-layout/app-drawer-layout/app-drawer-layout.html">
<link rel="import" href="bower_components/app-layout/app-header/app-header.html">
<link rel="import" href="bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel="import" href="bower_components/app-layout/app-drawer/app-drawer.html">
<link rel="import" href="bower_components/iron-icons/iron-icons.html">
<link rel="import" href="bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="bower_components/paper-listbox/paper-listbox.html">
<link rel="import" href="bower_components/paper-item/paper-item.html">
<link rel="import" href="bower_components/marked-element/marked-element.html">

<dom-module id="my-view">
  <template strip-whitespace>
    <style>
      :host {
        display: block;
        --app-primary-color: #29549a;
      }
      app-header {
        background-color: var(--app-primary-color);
        color: white;
      }
      app-header paper-icon-button {
        --paper-icon-button-ink-color: white;
      }
      div[main-title] { margin-left: 10px; }
      marked-element { margin-left: 15px; }
      paper-item:not(.iron-selected) { cursor: pointer; }
    </style>

    <app-drawer-layout force-narrow fullbleed>

      <app-drawer slot="drawer" swipe-open>

        <app-header fixed>
          <app-toolbar>
            <paper-icon-button icon="close" drawer-toggle></paper-icon-button>
          </app-toolbar>
        </app-header>

        <paper-listbox selected="{{key}}" attr-for-selected="key">
          <template is="dom-repeat" items="[[pages]]">
            <paper-item key="[[item.0]]" drawer-toggle>[[item.1]]</paper-item>
          </template>
        </paper-listbox>

      </app-drawer>

      <app-header-layout has-scrolling-region>

        <app-header slot="header" reveals>
          <app-toolbar>
            <paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
            <div main-title>My app</div>
            <paper-icon-button icon="delete"></paper-icon-button>
            <paper-icon-button icon="search"></paper-icon-button>
            <paper-icon-button icon="close"></paper-icon-button>
          </app-toolbar>
        </app-header>

        <marked-element>
          <div slot="markdown-html"></div>
          <script type="text/markdown" src="pages/[[key]].md"></script>
        </marked-element>

      </app-header-layout>

    </app-drawer-layout>
  </template>

  <script>
    class MyView extends Polymer.Element {
      static get is() { return 'my-view' }
      static get properties() { return { key: String, pages: Object } }
    }
    customElements.define(MyView.is, MyView)
  </script>
</dom-module>
sw-precache-config.js
module.exports = {
  staticFileGlobs: [
    '/index.html',
    '/manifest.json',
    '/bower_components/webcomponentsjs/*.js'
  ],
  navigateFallback: 'index.html'
};
  • manifest.json Webアプリケーションの定義ファイル
manifest.json
{
  "name": "Polymer Sample PWA",
  "short_name": "Polymer Smpl",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#fff",
  "theme_color": "#29549a",
  "icons": [{
    "src": "https://www.polymer-project.org/images/logos/p-logo-192.png",
    "sizes": "192x192",
    "type": "image/png"
  },{
    "src": "https://www.polymer-project.org/images/logos/p-logo-512.png",
    "sizes": "512x512",
    "type": "image/png"
  }]
}
  • bower.json 使用するWebコンポーネントライブラリの設定ファイル
bower.json
{
  "name": "polymer-sample-pwa-1",
  "dependencies": {
    "polymer": "Polymer/polymer",
    "webcomponentsjs": "webcomponents/webcomponentsjs",
    "app-layout": "PolymerElements/app-layout",
    "iron-icons": "PolymerElements/iron-icons",
    "paper-ui-elements": "PolymerElements/paper-ui-elements",
    "marked-element": "PolymerElements/marked-element"
  }
}
  • polymer.json Polymer CLIの設定ファイル
polymer.json
{
  "entrypoint": "index.html",
  "shell": "my-view.html",
  "extraDependencies": [
    "manifest.json",
    "bower_components/webcomponentsjs/*.js"
  ],
  "builds": [{
    "name": "default",
    "preset": "es5-bundled"
  }]
}
  • firebase.json Firebaseの設定ファイル(Hostingのみ)
firebase.json
{
  "hosting": {
    "public": "build/default"
  }
}

各行の説明

前回記載した部分は省きます。

index.html

index.html
<html lang="ja">
    ...
    <link rel="manifest" href="/manifest.json">
    <meta name="theme-color" content="#29549a">

<html>の言語指定や<meta>のテーマカラーを設定しないとLighthouseで点数が下がります

index.html
    <link rel="import" href="my-view.html" async>
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('/service-worker.js');
        });
      }
    </script>

<link async>で非同期読み込みとし、レンダリングをブロックさせないようにしますが、ページ読み込み時には一瞬素のHTMLが表示されることになります。
<script>で対応しているブラウザ向けにserviceWorkerを有効化します。

index.html
    <my-view key="view1" pages='[["view1","Page 1"], ["view2","Page 2"], ["view3","Page 3"]]'>
      <svg viewBox="0 0 24 30" fill="#eee" width="128"
           style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; margin: auto;">
        <path d="M19 4h-4L7.11 16.63 4.5 12 9 4H5L.5 12 5 20h4l7.89-12.63L19.5 12 15 20h4l4.5-8z"/>
      </svg>
      <noscript>Polymer Sample PWA</noscript>
    </my-view>

作成する<my-view>keypagesのパラメータを渡します。Arrayを渡すには'シングルクォーテーションでかこったJSON文字列を指定します。
素のHTMLが表示される部分に<svg>でPolymerのロゴ(ここからコピー)を表示し、またPolymer(Webコンポーネント)に対応していないブラウザ向けにはLighthouseで点数が下がるので<noscript>タグを入れておきます。

my-view.html

my-view.html
        <paper-listbox selected="{{key}}" attr-for-selected="key">
          <template is="dom-repeat" items="[[pages]]">
            <paper-item key="[[item.0]]" drawer-toggle>[[item.1]]</paper-item>
          </template>
        </paper-listbox>

index.htmlから渡された[[pages]]パラメータ([["view1","Page 1"], ["view2","Page 2"], ["view3","Page 3"]])をメニューに表示します。配列の各要素は.0のようにインデックスの数を指定します。

my-view.html
        <marked-element>
          <div slot="markdown-html"></div>
          <script type="text/markdown" src="pages/[[key]].md"></script>
        </marked-element>

index.htmlから渡された[[key]]パラメータ(view1)をmarked-elementにパスとして指定してMarkdownのコンテンツを読み込ませます。

my-view.html
  <script>
    class MyView extends Polymer.Element {
      static get is() { return 'my-view' }
      static get properties() { return { key: String, pages: Object } }
    }
    customElements.define(MyView.is, MyView)
  </script>

ページの一覧や内容、その切り替えは 全てHTML側に記述したので 、単純にタグ(エレメント要素)の宣言だけとなっています。

sw-precache-config.js

sw-precache-config.js
module.exports = {
  staticFileGlobs: [
    '/index.html',
    '/manifest.json',
    '/bower_components/webcomponentsjs/*.js'
  ],
  navigateFallback: 'index.html'
};

ビルド時に自動でオフラインキャッシュに必要なServiceWorker用のファイル(/serviceWorker.js)が生成されますが、いくつか明示的に指定する必要があるファイルをsw-precacheライブラリを使って指定します。

manifest.json

manifest.json
{
  "name": "Polymer Sample PWA",
  "short_name": "Polymer Smpl",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#fff",
  "theme_color": "#29549a",
  "icons": [{
    "src": "https://www.polymer-project.org/images/logos/p-logo-192.png",
    "sizes": "192x192",
    "type": "image/png"
  },{
    "src": "https://www.polymer-project.org/images/logos/p-logo-512.png",
    "sizes": "512x512",
    "type": "image/png"
  }]
}

「ホーム画面へ追加」を実現する為に使用します。
ホーム画面で表示される名前のshort_nameは12文字以内、background_colortheme_coloriconsも指定しないとLighthouseで点数が下がります

bower.json

bower.json
  "dependencies": {
    "polymer": "Polymer/polymer",
    "webcomponentsjs": "webcomponents/webcomponentsjs",
    "app-layout": "PolymerElements/app-layout",
    "iron-icons": "PolymerElements/iron-icons",
    "paper-ui-elements": "PolymerElements/paper-ui-elements",
    "marked-element": "PolymerElements/marked-element"
  }

Bowerで使用するバッケージ設定ファイルです。app-layoutパッケージで<app-drawer-layout><app-header>等が使えるようになり、paper-ui-elementsエイリアスパッケージ<paper-icon-button><paper-listbox>等が使えるようになります。

ちゃんとパッケージとバージョンを指定するには下記のようにします(2017/05にリリースされたv2.0向け)

 "dependencies": {
   "polymer": "Polymer/polymer#^2.0.0",
   "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0",
   "app-layout": "PolymerElements/app-layout#^2.0.0",
   "iron-icons": "PolymerElements/iron-icons#^2.0.0",
   "paper-icon-button": "PolymerElements/paper-icon-button#^2.0.0",
   "paper-listbox": "PolymerElements/paper-listbox#^2.0.0",
   "paper-item": "PolymerElements/paper-item#^2.0.0",
   "marked-element": "PolymerElements/marked-element#^2.0.0"
 }

polymer.json

polymer.json
  "entrypoint": "index.html",
  "shell": "my-view.html",

PWAのPRPLパターンとして、index.htmlをトップページ、my-view.htmlをApp Shellとして設定します。

polymer.json
  "extraDependencies": [
    "manifest.json",
    "bower_components/webcomponentsjs/*.js"
  ],

前述のオフラインキャッシュに必要なファイルを追加の依存関係として設定します。

polymer.json
  "builds": [{
    "name": "default",
    "preset": "es5-bundled"
  }]

Polymer CLIでビルドする種類を指定します。現状では

  • es5-bundled
  • es6-bundled
  • es6-unbundled

3種類が定義されていて、es5-bundledはES5への変換、各ファイルをまとめつつminify(圧縮)します。
FirebaseのホスティングはHTTP/2に対応しているので、IE11等に対応する必要がなければes6-unbundledが適しているのかもしれません。

firebase.json

firebase.json
    "public": "build/default",

アップロードするトップディレクトリを指定します(Polymer CLIのデフォルトはbuild/defaultにビルドされます)。

最後に

:bow: コメント、編集リクエスト歓迎
Polymer サンプルコード (5) PWA [2] へ続きます。

以上

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5