新聞中心
“離線優(yōu)先”是一種應(yīng)用程序開發(fā)范式,在這種范式中,開發(fā)人員確保應(yīng)用程序的功能不受暫時失去網(wǎng)絡(luò)連接的影響。漸進式 Web 應(yīng)用程序(PWA)感覺像原生應(yīng)用程序,但運行起來像 Web 應(yīng)用程序,通常建立在這種范式之上。

成都創(chuàng)新互聯(lián)公司堅持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時代的石首網(wǎng)站設(shè)計、移動媒體設(shè)計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
本文將告訴你如何使用 Node.js 和 SQLite 數(shù)據(jù)庫構(gòu)建離線優(yōu)先應(yīng)用程序。首先,讓我們從認(rèn)識漸進式 Web 應(yīng)用程序開始。
PWA 簡介
漸進式 Web 應(yīng)用程序 (PWA) 是使用服務(wù)工作者、清單和其他 Web 平臺功能和漸進式增強功能為用戶提供與本機應(yīng)用程序相當(dāng)?shù)捏w驗的 Web 應(yīng)用程序。
PWA 在效率方面有時可以勝過原生應(yīng)用程序。它們按需運行,并且始終可用,無需消耗寶貴的智能手機內(nèi)存或數(shù)據(jù)。與同一應(yīng)用程序的本機版本相比,用戶在選擇 PWA 時消耗的數(shù)據(jù)更少。他們?nèi)匀豢梢詫?PWA 保存到他們的主屏幕,且無需完整下載即可安裝。
為了展示漸進式 Web 應(yīng)用程序的強大功能,我們將構(gòu)建一個簡單的博客應(yīng)用程序。
用戶將能夠像其他PWA一樣與之交互,例如Twitter PWA。讓我們開始吧。
初始化NodeJs應(yīng)用程序
首先,我們將使用以下命令創(chuàng)建項目文件夾:
mkdir PWA && cd PWA
然后,我們將使用以下命令初始化 Node.js 應(yīng)用程序:
npm init -y
上面的命令為應(yīng)用程序創(chuàng)建一個package.json文件。
接下來,在我們的項目文件夾中創(chuàng)建以下文件夾結(jié)構(gòu):
設(shè)置 Express 服務(wù)器
通過我們的應(yīng)用程序設(shè)置,讓我們安裝 Express 以使用以下命令創(chuàng)建我們的 Node.js 服務(wù)器:
npm install express
然后,我們將在 public 文件夾中創(chuàng)建幾個文件夾和文件:
- css/style.css 文件
- js/app.js 文件
接下來,在項目根目錄中創(chuàng)建一個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"));
在代碼片段中,我們導(dǎo)入express來創(chuàng)建我們的服務(wù)器和路徑模塊。我們將應(yīng)用程序配置為使用express.static方法渲染靜態(tài)文件,該方法采用靜態(tài)文件夾(公共)的路徑,我們創(chuàng)建了應(yīng)用程序的根路由并渲染了index.html文件。然后我們將應(yīng)用程序配置為偵聽端口8000。
連接到 SQLite 數(shù)據(jù)庫
為我們的應(yīng)用程序設(shè)置服務(wù)器后,讓我們創(chuàng)建并連接我們的應(yīng)用程序以保存我們的博客詳細(xì)信息。首先,運行以下命令來安裝 sqlite3 依賴項。
npm install sqlite3
然后,在入口點index.js文件中,添加以下代碼片段以創(chuàng)建應(yīng)用程序并將其連接到 SQLite 數(shù)據(jù)庫。
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.");
}
});
接下來,我們將創(chuàng)建一個博客列表,將其存儲在我們的數(shù)據(jù)庫中,稍后使用以下代碼片段呈現(xiàn)到客戶端:
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.",
},
];
我們應(yīng)用程序中的每個塊帖子都有一個id、title、 avatar和intro字段。
現(xiàn)在創(chuàng)建一個數(shù)據(jù)庫表名為blogs并使用下面的代碼片段保存我們剛剛在上面創(chuàng)建的博客詳細(xì)信息:
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 創(chuàng)建了一個表blogs 。db.run方法接受一個 SQL 查詢作為參數(shù),然后我們遍歷我們的博客數(shù)組并將它們插入到我們剛剛使用 js map 函數(shù)創(chuàng)建的 blogs 表中。
查看數(shù)據(jù)庫記錄
現(xiàn)在讓我們查看我們剛剛使用Arctype創(chuàng)建的記錄。要使用 Arctype 查看 SQLite 數(shù)據(jù)庫中的記錄,請執(zhí)行以下步驟:
- 安裝 Arctype
- 運行應(yīng)用程序node index.js以創(chuàng)建數(shù)據(jù)庫
- 啟動 Arctype 并單擊 SQLite 選項卡
- 單擊 Select SQLite file按鈕,找到運行服務(wù)器時生成的db.sqlite文件。
- 您應(yīng)該看到 blogs 表和我們創(chuàng)建的記錄,如下面的屏幕截圖所示:
渲染頁面
此時,我們已將應(yīng)用程序連接到 SQLite 數(shù)據(jù)庫,并在數(shù)據(jù)庫中插入了一些記錄?,F(xiàn)在,打開index.html文件并在下面添加以下代碼片段:
Blogger
我們在上面的文件中創(chuàng)建了一個簡單的標(biāo)記,其中包含指向我們清單的鏈接,我們將在下一部分、styles和app.js文件中創(chuàng)建它。
然后,我們將在index.js 文件中創(chuàng)建一個blogs路由,以將博客返回到客戶端。
...
app.get("/blogs", (req, res) => {
res.status(200).json({
blogs,
});
});
...
在我們的public/js/app.js文件中,我們將向博客端點發(fā)送一個獲取請求,以從我們的后端獲取博客。然后我們遍歷博客,定位容器類并顯示它們。
let result = "";
fetch("http://localhost:8000/blogs")
.then((res) => res.json())
.then(({ rows } = data) => {
rows.forEach(({ title, avatar, intro } = rows) => {
result += `
`;
});
document.querySelector(".container").innerHTML = result;
})
.catch((e) => {
console.log(e);
});
我們還將使用以下代碼片段在public/css/style.css 中為我們的應(yīng)用程序添加一些樣式:
* {
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;
}
現(xiàn)在打開package.json文件并添加啟動腳本。
"start": "node index.js"
至此,我們已經(jīng)設(shè)置了我們的應(yīng)用程序。但是當(dāng)服務(wù)器沒有運行或者沒有網(wǎng)絡(luò)連接用于生產(chǎn)時,我們無法運行我們的應(yīng)用程序。讓我們在下一節(jié)中進行設(shè)置。
優(yōu)化應(yīng)用
我們需要使我們的應(yīng)用程序與所有屏幕尺寸兼容。我們還將通過在index.html 文件的 head 部分添加下面的標(biāo)記來添加主題顏色。
創(chuàng)建清單
我們需要描述我們的應(yīng)用程序以及它在安裝在用戶設(shè)備上時的行為方式。我們可以通過創(chuàng)建清單來做到這一點。
在項目根目錄下創(chuàng)建manifest文件,添加如下配置:
{
"name": "Blogger"
"short_name": "Blogger"
"start_url": "/",
"display": "standalone",
"background_color": "#0e9c95",
"theme_color": "#16a0d6e7",
"orientation": "portrait",
"icons": []
}
在我們的清單中,我們定義了以下配置:
- name:這定義了應(yīng)用程序的顯示名稱。
- short_name:這定義了安裝時將在應(yīng)用程序圖標(biāo)下顯示的名稱。
- start_url:這告訴瀏覽器應(yīng)用程序的根 URL。
- display:這告訴瀏覽器如何顯示應(yīng)用程序。
- background_color: 這定義了安裝時應(yīng)用程序的背景顏色。
- theme_color: 這定義了狀態(tài)欄的顏色。
- 方向: 這定義了在應(yīng)用顯示期間使用的方向。
- 圖標(biāo): 這定義了不同大小的圖標(biāo)或圖像用作我們的應(yīng)用程序主頁圖標(biāo)。
手動創(chuàng)建我們的主屏幕圖標(biāo)可能是一項非常復(fù)雜的任務(wù),但不用擔(dān)心。我們將利用名為pwa-asset-generator的第三方模塊,使用以下命令從公共目錄中的主應(yīng)用程序圖標(biāo)生成不同大小的圖標(biāo):
#change directory to the public folder
cd public
#generate icons
npx pwa-asset-generator logo.png icons
上面的命令將在 public 文件夾中創(chuàng)建一個圖標(biāo) 文件夾,其中包含我們應(yīng)用程序的許多圖標(biāo),以及終端上的一些JSON,我們將粘貼到清單中的圖標(biāo)數(shù)組中。
我們清單中的圖標(biāo)數(shù)組應(yīng)如下所示:
"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"
}
]
此外,該命令生成了指向生成的圖標(biāo)的標(biāo)記鏈接。
將標(biāo)記復(fù)制并粘貼到public/index.html 文件中標(biāo)記的 head 部分。
設(shè)置服務(wù)工作者
創(chuàng)建清單后,讓我們設(shè)置服務(wù)工作者。Service Worker 是一段 JavaScript 代碼,您的瀏覽器在后臺運行在一個單獨的線程中,以處理您為將來的請求保存的資產(chǎn)和數(shù)據(jù)的緩存,從而為您的應(yīng)用程序啟用離線支持。
所以在public 文件夾中創(chuàng)建一個blogger.serviceWorker.js 文件。對于 service worker,有很多事件(push、activate、install、fetch、message、sync),但對于本教程的演示,我們將介紹install、activate 和fetch 事件。在此之前,我們需要創(chuàng)建一個數(shù)組來存儲我們在應(yīng)用程序中使用的所有資產(chǎn)。
const assets = [
"/",
"css/style.css",
"js/app.js",
"/images/blog1.jpg",
"/images/blog2.jpg",
"/images/blog3.jpg,"
];
然后,我們將監(jiān)聽install 事件來注冊并將我們的靜態(tài)文件保存到瀏覽器的緩存中。此過程需要一些時間才能完成。要跳過等待,我們將使用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 更新時清除緩存以刪除舊資產(chǎn)。為此,我們將收聽下面的激活 代碼片段:
...
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())
);
});
在上面的代碼片段中,我們在 service worker 上使用了waitUntil方法。此方法等待操作完成,然后在刪除之前檢查我們嘗試清除的資產(chǎn)是否是我們當(dāng)前應(yīng)用程序的資產(chǎn)。
接下來,我們需要存儲在緩存中的文件才能使用它們。
self.addEventListener("fetch", function (evt) {
evt.respondWith(
fetch(evt.request).catch(() => {
return caches.open(BLOGGER_ASSETS).then((cache) => {
return cache.match(evt.request);
});
})
);
})
當(dāng)在頁面上發(fā)出請求時,PWA 將檢查我們的緩存并從緩存中讀取數(shù)據(jù),而不是去網(wǎng)絡(luò)。然后,使用respondWith方法,我們覆蓋瀏覽器的默認(rèn)值并讓我們的事件返回一個 Promise。緩存完成后,我們可以返回evt.request對應(yīng)的緩存。當(dāng)緩存準(zhǔn)備好后,我們可以返回匹配 evt.request 的緩存。
我們已經(jīng)成功設(shè)置了我們的 service worker?,F(xiàn)在讓我們讓它在我們的應(yīng)用程序中可用。
注冊 Service Worker
現(xiàn)在讓我們在public/js/app.js 文件中注冊我們的 service worker,代碼片段如下:
...
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));
});
}
在這里,我們檢查我們的應(yīng)用程序的瀏覽器是否支持Service Worker(當(dāng)然不是所有的瀏覽器都支持Service Worker),然后注冊我們的Service Worker 文件。
現(xiàn)在使用以下命令運行應(yīng)用程序:
npm start
在瀏覽器中轉(zhuǎn)到 localhost:8000 以訪問該應(yīng)用程序。
谷歌燈塔檢查
現(xiàn)在讓我們檢查一下我們是否使用Google Lighthouse檢查正確設(shè)置了 PWA 。右鍵單擊瀏覽器并選擇“檢查”。在檢查選項卡上,選擇燈塔并單擊生成報告。如果您的應(yīng)用程序一切順利,您應(yīng)該會看到如下屏幕截圖中的輸出:
我們已經(jīng)成功地創(chuàng)建了我們的第一個應(yīng)用程序。你還可以停止服務(wù)器以在離線模式下測試應(yīng)用程序。
結(jié)論
漸進式 Web 應(yīng)用 (PWA) 使用現(xiàn)代 API 通過單個代碼庫提供增強的功能、可靠性和可安裝性。它們允許您的最終用戶使用您的應(yīng)用程序,無論他們是否有互聯(lián)網(wǎng)連接。您應(yīng)該隨時 fork存儲庫并向項目添加其他功能。
原文標(biāo)題:??Building Offline-First Apps With Node.js and SQLite??
原文作者:Clara Ekekenta
當(dāng)前文章:使用Node.js和SQLite構(gòu)建離線優(yōu)先應(yīng)用程序
本文網(wǎng)址:http://www.dlmjj.cn/article/cooggic.html


咨詢
建站咨詢
