「オフラインファースト」は、アプリケーション開発パラダイムであり、開発者は、ネットワーク接続が一時的に失われた場合でも、アプリの機能が影響を受けないようにします。プログレッシブWebアプリケーションは、ネイティブアプリケーションのように感じられますが、Webアプリケーションのように実行されるため、このパラダイムに基づいて構築されることがよくあります。
このチュートリアルでは、Node.jsとSQLiteデータベースを使用してオフラインファーストのアプリケーションを構築する方法を説明します。プログレッシブウェブアプリの紹介から始めましょう。
PWAの概要
プログレッシブウェブアプリ(PWA)は、サービスワーカー、マニフェスト、その他のウェブプラットフォーム機能とプログレッシブエンハンスメントを使用して、ネイティブアプリに匹敵するエクスペリエンスをユーザーに提供するウェブアプリです。
PWAは、効率の点でネイティブアプリよりも優れている場合があります。それらはオンデマンドで動作し、貴重なスマートフォンのメモリやデータを消費することなく常に利用できます。同じアプリケーションのネイティブバージョンよりもPWAを選択すると、ユーザーが消費するデータが少なくなります。 PWAをホーム画面に保存することもできます。完全にダウンロードしなくてもインストールできます。
何を構築していますか?
プログレッシブウェブアプリケーションの力を示すために、簡単なブログアプリケーションを作成します。
ユーザーは、TwitterPWAなどの他のPWAと同じように操作できるようになります。さあ、始めましょう。
NodeJsアプリケーションを初期化します
手を汚しましょう。開始するには、次のコマンドを使用してプロジェクトフォルダを作成します。
mkdir PWA && cd PWA
次に、以下のコマンドを使用してNode.jsアプリケーションを初期化します。
npm init -y
上記のコマンドは、package.json
を作成します アプリケーションのファイル。
次に、プロジェクトフォルダに次のフォルダ構造を作成します。
Expressサーバーをセットアップする
アプリケーションのセットアップで、Expressをインストールして、以下のコマンドでNode.jsサーバーを作成しましょう。
npm install express
次に、パブリックフォルダにいくつかのフォルダとファイルを作成します。
- css/style.cssファイル
- js/app.jsファイル
次に、index.js
を作成します 以下のコードスニペットを含むプロジェクトルートディレクトリ内のファイル:
const express = require("express");
const path = require("path");
const app = express();
app.use(express.static(path.join(__dirname, "public")));
app.get("/", function (req, res) {
res.sendFile(path.join(__dirname, "public/index.html"));
});
app.listen(8000, () => console.log("Server is running on Port 8000"));
コードスニペットでは、 expressをインポートします サーバーとパスを作成します モジュール。 express.staticを使用して静的ファイルをレンダリングするようにアプリを構成しました 静的フォルダー(パブリック)へのパスを取得するメソッドは、アプリケーションのルートルートを作成し、 index.htmlをレンダリングしました。 ファイル。次に、ポート 8000をリッスンするようにアプリを構成しました 。
SQLiteデータベースに接続
アプリケーションのサーバー設定が完了したら、アプリケーションを作成して接続し、ブログの詳細を保存しましょう。開始するには、以下のコマンドを実行してsqlite3依存関係をインストールします。
npm install sqlite3
次に、エントリポイントでindex.js
ファイルに、以下のコードスニペットを追加して、アプリケーションを作成し、SQLiteデータベースに接続します。
const db = new sqlite3.Database("db.sqlite", (err) => {
if (err) {
// Cannot open database
console.error(err.message);
throw err;
} else {
console.log("Connected to the SQLite database.");
}
});
次に、ブログのリストを作成し、データベースに保存して、後で以下のコードスニペットを使用してクライアント側にレンダリングします。
let blogs = [
{
id: "1",
title: "How To Build A RESTAPI With Javascript",
avatar: "images/coffee2.jpg",
intro: "iste odio beatae voluptas dolor praesentium illo facere optio nobis magni, aspernatur quas.",
},
{
id: "2",
title: "How to Build an Offline-First Application with Node.js,"
avatar: "images/coffee2.jpg",
"iste odio beatae voluptas dolor praesentium illo facere optio nobis magni, aspernatur quas.",
},
{
id: "3",
title: "Building a Trello Clone with React DnD",
avatar: "images/coffee2.jpg",
intro: "iste odio beatae voluptas dolor praesentium illo facere optio nobis magni, aspernatur quas.",
},
];
アプリケーションの各ブロック投稿には、 idがあります 、タイトル 、アバター 、およびイントロ 田畑。
次に、データベーステーブル名ブログを作成します 上で作成したブログの詳細を、以下のコードスニペットで保存します。
db.run(
`CREATE TABLE blog (id INTEGER PRIMARY KEY AUTOINCREMENT, title text,avatar text,intro text)`,
(err) => {
if (err) {
// console.log(err)
// Table already created
} else {
// Table just created, creating some rows
var insert = "INSERT INTO blogs (title, avatar, intro) VALUES (?,?,?)";
blogs.map((blog) => {
db.run(insert, [
`${blog.title}`,
`${blog.avatar}`,
`${blog.intro}`,
]);
});
}
}
);
コードスニペットでは、テーブルブログを作成しました db.runを使用します。 db.run メソッドはSQLクエリをパラメーターとして受け取り、ブログの配列をループして、jsマップ関数を使用して作成したブログテーブルに挿入します。
データベースレコードの表示
次に、Arctypeを使用して作成したレコードを表示します。 Arctypeを使用してSQLiteデータベースのレコードを表示するには、次の手順に従います。
- Arctypeをインストールする
-
node index.js
を使用してアプリケーションを実行します データベースを作成するには - Arctypeを起動し、[SQLite]タブをクリックします
- SQLiteファイルの選択をクリックします ボタンをクリックし、 db.sqliteを見つけます サーバーの実行時に生成されたファイル。
- 以下のスクリーンショットに示すように、ブログテーブルと作成したレコードが表示されます。
ページをレンダリングする
この時点で、アプリケーションをSQLiteデータベースに接続し、データベースにいくつかのレコードを挿入しました。次に、 index.htmlを開きます ファイルを作成し、以下のコードスニペットを追加します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="css/style.css" />
<title>Blogger</title>
<link rel="manifest" href="manifest" />
</head>
<body>
<section>
<nav>
<h1>Blogger</h1>
<ul>
<li>Home</li>
<li class="active">Blog</li>
</ul>
</nav>
<div class="container"></div>
</section>
<script src="js/app.js"></script>
</body>
</html>
上記のファイルのマニフェストへのリンクを使用して単純なマークアップを作成しました。これは、次のセクションスタイルで作成します。 、および app.js ファイル。
次に、ブログを作成します index.jsでルーティングします ブログをクライアント側に返すためのファイル。
...
app.get("/blogs", (req, res) => {
res.status(200).json({
blogs,
});
});
...
public / js / app.js ファイルの場合、ブログエンドポイントにgetリクエストを送信して、バックエンドからブログを取得します。次に、ブログをループして、コンテナをターゲットにします。 クラスとそれらを表示します。
let result = "";
fetch("http://localhost:8000/blogs")
.then((res) => res.json())
.then(({ rows } = data) => {
rows.forEach(({ title, avatar, intro } = rows) => {
result += `
<div class="card">
<img class="card-avatar" src="/${avatar}"/>
<h1 class="card-title">${title}</h1>
<p class="intro">${intro}</p>
<a class="card-link" href="#">Read</a>
</div>
`;
});
document.querySelector(".container").innerHTML = result;
})
.catch((e) => {
console.log(e);
});
また、 public / css / style.cssのアプリケーションにスタイルを追加します 以下のコードスニペットを使用:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #fdfdfd;
font-size: 1rem;
}
section {
max-width: 900px;
margin: auto;
padding: 0.5rem;
text-align: center;
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
}
ul {
list-style: none;
display: flex;
}
li {
margin-right: 1rem;
}
h1 {
color: #0e9c95;
margin-bottom: 0.5rem;
}
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
grid-gap: 1rem;
justify-content: center;
align-items: center;
margin: auto;
padding: 1rem 0;
}
.card {
display: flex;
align-items: center;
flex-direction: column;
width: 15rem auto;
background: #fff;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
border-radius: 10px;
margin: auto;
overflow: hidden;
}
.card-avatar {
width: 100%;
height: 10rem;
object-fit: cover;
}
.card-title {
color: #222;
font-weight: 700;
text-transform: capitalize;
font-size: 1.1rem;
margin-top: 0.5rem;
}
.card-link {
text-decoration: none;
background: #16a0d6e7;
color: #fff;
padding: 0.3rem 1rem;
border-radius: 20px;
margin: 10px;
}
.intro {
color: #c2c5c5;
padding: 10px;
}
.active {
color: #16a0d6e7;
}
次に、 package.jsonを開きます ファイルを作成し、開始スクリプトを追加します。
"start": "node index.js"
この時点で、アプリケーションをセットアップしました。ただし、サーバーが実行されていない場合、または本番用のネットワーク接続がない場合は、アプリケーションを実行できません。次のセクションで設定しましょう。
アプリケーションの最適化
アプリケーションをすべての画面サイズと互換性があるようにする必要があります。また、 index.html のヘッドセクションにマークアップを追加して、テーマの色を追加します。 ファイル。
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#16a0d6e7"/>
マニフェストを作成する
アプリと、ユーザーのデバイスにインストールしたときのアプリの動作について説明する必要があります。マニフェストを作成することでこれを行うことができます。
マニフェストを作成する プロジェクトのルートディレクトリにファイルを追加し、次の構成を追加します。
{
"name": "Blogger"
"short_name": "Blogger"
"start_url": "/",
"display": "standalone",
"background_color": "#0e9c95",
"theme_color": "#16a0d6e7",
"orientation": "portrait",
"icons": []
}
マニフェストでは、次の構成を定義しました。
- 名前 :これはアプリの表示名を定義します。
- short_name :これは、インストール時にアプリアイコンの下に表示される名前を定義します。
- start_url :これは、ブラウザにアプリケーションのルートURLを通知します。
- 表示 :これは、ブラウザにアプリの表示方法を指示します。
- background_color: これにより、インストール時のアプリケーションの背景色が定義されます。
- theme_color: これにより、ステータスバーの色が定義されます。
- オリエンテーション: これにより、アプリの表示中に使用する向きが定義されます。
- アイコン: これにより、アプリのホームアイコンとして使用されるさまざまなサイズのアイコンまたは画像が定義されます。
ホーム画面のアイコンを手動で作成することは非常に複雑な作業になる可能性がありますが、心配する必要はありません。 pwa-asset-generatorと呼ばれるサードパーティのモジュールを利用して、以下のコマンドを使用して、パブリックディレクトリ内のメインアプリアイコンからさまざまなサイズのアイコンを生成します。
#change directory to the public folder
cd public
#generate icons
npx pwa-asset-generator logo.png icons
上記のコマンドは、アイコンを作成します アプリケーションの多くのアイコンといくつかのJSONを含むパブリックフォルダー内のフォルダー マニフェストのアイコン配列に貼り付ける端末上。
マニフェストのアイコン配列は次のようになります。
"icons": [
{
"src": "public/icons/manifest-icon-192.maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "public/icons/manifest-icon-192.maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "public/icons/manifest-icon-512.maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "public/icons/manifest-icon-512.maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
また、コマンドは、生成されたアイコンへのマークアップリンクを生成しました。
マークアップをコピーして、 public / index.htmlのマークアップのヘッドセクションに貼り付けます ファイル。
サービスワーカーの設定
マニフェストを作成したら、サービスワーカーを設定しましょう。 Service Workerは、ブラウザが別のスレッドでバックグラウンドで実行するJavaScriptコードの一部であり、アプリケーションのオフラインサポートを有効にするために、将来のリクエストのために保存するアセットとデータのキャッシュを処理します。
したがって、 blogger.serviceWorker.jsを作成します 公開のファイル フォルダ。 Service Workerの場合、多くのイベント(プッシュ、アクティブ化、インストール、フェッチ、メッセージ、同期)がありますが、このチュートリアルのデモでは、インストール、アクティブ化について説明します。 およびフェッチ イベント。その前に、アプリケーションで使用したすべてのアセットを格納する配列を作成する必要があります。
const assets = [
"/",
"css/style.css",
"js/app.js",
"/images/blog1.jpg",
"/images/blog2.jpg",
"/images/blog3.jpg,"
];
次に、インストールを聞きます 静的ファイルを登録してブラウザのキャッシュに保存するイベント。このプロセスは完了するまでに時間がかかります。待機をスキップするには、 skipWaiting()を使用します。
const BLOGGER_ASSETS = "blogger-assets";
self.addEventListener("install", (installEvt) => {
installEvt.waitUntil(
caches
.open(BLOGGER_ASSETS)
.then((cache) => {
cache.addAll(assets);
})
.then(self.skipWaiting())
.catch((e) => {
console.log(e);
})
);
});
...
次に、Service Workerが更新されるたびに、キャッシュをクリアして古いアセットを削除する必要があります。そのために、アクティブ化を聞きます 以下のコードスニペット:
...
self.addEventListener("activate", function (evt) {
evt.waitUntil(
caches
.keys()
.then((keysList) => {
return Promise.all(
keysList.map((key) => {
if (key === BLOGGER_ASSETS) {
console.log(`Removed old cache from ${key}`);
return caches.delete(key);
}
})
);
})
.then(() => self.clients.claim())
);
});
上記のコードスニペットでは、 waitUntilを使用しています サービスワーカーのメソッド。このメソッドは、アクションが終了するのを待ってから、クリアしようとしているアセットが現在のアプリのアセットであるかどうかを確認してから削除します。
次に、それらを使用するためにキャッシュに保存されているファイルが必要です。
self.addEventListener("fetch", function (evt) {
evt.respondWith(
fetch(evt.request).catch(() => {
return caches.open(BLOGGER_ASSETS).then((cache) => {
return cache.match(evt.request);
});
})
);
})
ページでリクエストが行われると、PWAはキャッシュをチェックし、ネットワークにアクセスするのではなく、キャッシュにデータがあるかどうかをキャッシュから読み取ります。次に、 responseWithを使用します メソッドでは、ブラウザのデフォルトを上書きし、イベントがpromiseを返すようにします。キャッシュが完了すると、evt.requestに対応するキャッシュを返すことができます。キャッシュの準備ができたら、evt.requestに一致するキャッシュを返すことができます。
サービスワーカーの設定に成功しました。それでは、アプリケーションで利用できるようにしましょう。
サービスワーカーを登録する
それでは、サービスワーカーを public / js / app.jsに登録しましょう。 以下のコードスニペットを含むファイル:
...
if ("serviceWorker" in navigator) {
window.addEventListener("load", function () {
navigator.serviceWorker
.register("/blogger.serviceWorker.js")
.then((res) => console.log("service worker registered"))
.catch((err) => console.log("service worker not registered", err));
});
}
ここでは、アプリケーションのブラウザがService Workerをサポートしているかどうかを確認し(もちろん、すべてのブラウザがService Workerをサポートしているわけではありません)、ServiceWorkerファイルを登録します。
次に、以下のコマンドを使用してアプリケーションを実行します。
npm start
ブラウザでlocalhost:8000に移動して、アプリにアクセスします。
Google灯台チェック
次に、GoogleLighthouseチェックを使用してPWAを適切に設定したかどうかを確認しましょう。ブラウザを右クリックして、「検査」を選択します。 [検査]タブで、灯台を選択し、[レポートの生成]をクリックします。アプリケーションですべてがうまくいった場合は、下のスクリーンショットのような出力が表示されるはずです。
最初のアプリケーションを正常に作成しました。サーバーを停止して、オフラインモードでアプリケーションをテストすることもできます。
結論
プログレッシブウェブアプリ(PWA)は、最新のAPIを使用して、単一のコードベースで拡張機能、信頼性、およびインストール性を提供します。エンドユーザーは、インターネットに接続しているかどうかに関係なく、アプリケーションを使用できます。リポジトリをフォークして、プロジェクトに機能を追加してください。頑張ってください!