PWA 是一种 Web 应用的新范式,它将 Web 应用和原生应用的体验结合在一起。PWA 可以让 Web 应用离线缓存、全屏模式、桌面快捷方式等,提供与原生应用类似的用户体验。此外,PWA 还能够通过 Service Worker 技术实现增量更新,提高 Web 应用的性能。
PWA 的优势不仅在于它可以提供类似于原生应用的体验,更重要的是它可以通过一系列技术手段提高 Web 应用的性能。PWA 可以在应用启动时预加载资源,这可以大大缩短应用的启动时间。PWA 还支持离线缓存,这可以让用户在没有网络连接的情况下继续使用应用,提高用户的满意度。
PWA 还可以使用本地通知、推送通知等功能,让用户更方便地接收到应用的消息和提醒,提高应用的互动性和用户留存率。此外,PWA 还支持桌面快捷方式,用户可以在桌面上直接启动应用,减少寻找应用的时间,提高用户的使用频率。
在使用PWA时,开发者需要注意一些细节,以确保应用的性能和用户体验。首先,需要考虑应用的加载速度,保证应用能够在最短时间内启动。其次,需要优化应用的缓存策略,确保用户能够在没有网络连接的情况下继续使用应用。最后,需要注意用户体验,尽可能提供与原生应用相似的体验,以吸引更多的用户。
以下是一个简单的 PWA 应用的 demo,它是一个待办事项应用,用户可以添加和管理自己的待办事项列表,并在需要时将它们标记为已完成。
首先,在 index.html 文件中,我们需要添加一个 manifest.json 文件的链接,以及一个 Service Worker 的注册脚本:
Markup
<!DOCTYPE html>
<html>
<head>
<title>PWA Todo App</title>
<link rel="manifest" href="/manifest.json">
</head>
<body>
<h1>Todo List</h1>
<ul id="todo-list"></ul>
<form>
<input type="text" id="new-todo" placeholder="Add a new todo...">
<button type="submit">Add</button>
</form>
<script src="/sw.js"></script>
<script src="/app.js"></script>
</body>
</html>
接下来,在 manifest.json 文件中,我们可以定义应用的名称、图标、主题色等信息:
Bash
{
"name": "PWA Todo App",
"short_name": "Todo App",
"icons": [
{
"src": "/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#fff",
"theme_color": "#3367D6"
}
然后,在 app.js 文件中,我们可以实现待办事项的添加、删除和标记为已完成等功能,同时使用 IndexedDB 存储数据:
JavaScript
const todoList = document.getElementById('todo-list');
const newTodoInput = document.getElementById('new-todo');
let db;
window.addEventListener('load', async () => {
if ('serviceWorker' in navigator) {
try {
await navigator.serviceWorker.register('/sw.js');
} catch (e) {
console.log('SW registration failed');
}
}
const request = indexedDB.open('todo-db', 1);
request.onupgradeneeded = event => {
db = event.target.result;
db.createObjectStore('todos', { keyPath: 'id', autoIncrement: true });
};
request.onsuccess = event => {
db = event.target.result;
loadTodos();
};
async function loadTodos() {
const transaction = db.transaction(['todos'], 'readonly');
const store = transaction.objectStore('todos');
const todos = await store.getAll();
for (const todo of todos) {
addTodoToList(todo);
}
}
function addTodoToList(todo) {
const li = document.createElement('li');
li.dataset.id = todo.id;
li.innerText = todo.title;
if (todo.completed) {
li.classList.add('completed');
}
li.addEventListener('click', event => {
const completed = !li.classList.contains('completed');
updateTodoStatus(todo.id, completed);
li.classList.toggle('completed');
});
todoList.appendChild(li);
}
function addNewTodo() {
const title = newTodoInput.value;
if (title.trim() === '') {
return;
}
const transaction = db.transaction(['todos'], 'readwrite');
const store = transaction.objectStore('todos');
const request = store.add({ title, completed: false });
request.onsuccess = event => {
const todo = { id: event.target.result, title, completed: false };
addTodoToList(todo);
newTodoInput.value = '';
};
}
function deleteTodo(id) {
const transaction = db.transaction(['todos'], 'readwrite');
const store = transaction.objectStore('todos');
store.delete(id);
}
function updateTodoStatus(id, completed) {
const transaction = db.transaction(['todos'], 'readwrite');
const store = transaction.objectStore('todos');
store.get(id).onsuccess = event => {
const todo = event.target.result;
todo.completed = completed;
store.put(todo);
};
}
document.querySelector('form').addEventListener('submit', event => {
event.preventDefault();
addNewTodo();
});
todoList.addEventListener('contextmenu', event => {
event.preventDefault();
const li = event.target.closest('li');
if (li) {
deleteTodo(parseInt(li.dataset.id));
li.remove();
}
});
最后,在 sw.js 文件中,我们可以缓存应用所需的资源,以便在离线时仍可访问:
JavaScript
const CACHE_NAME = 'todo-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/app.js',
'/manifest.json',
'/icons/icon-72x72.png',
'/icons/icon-96x96.png',
'/icons/icon-128x128.png',
'/icons/icon-144x144.png',
'/icons/icon-152x152.png',
'/icons/icon-192x192.png',
'/icons/icon-384x384.png',
'/icons/icon-512x512.png'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
以上就是一个简单的 PWA 应用的 demo,它能够在离线时正常工作,并且可以像本地应用一样被用户添加到主屏幕上。
我们再来详细解释一下上述代码的逻辑:
首先,我们在 index.html 中定义了应用的基本结构和样式,包括一个输入框用于添加新任务,以及一个任务列表。
然后,我们在 app.js 中编写了 PWA 应用的核心逻辑,它包括:
1.判断浏览器是否支持 Service Worker,并注册 sw.js。
JavaScript
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js');
});
}
2.使用 IndexedDB 存储任务列表。
JavaScript
const request = indexedDB.open('todo-db', 1);
request.onupgradeneeded = event => {
const db = event.target.result;
const store = db.createObjectStore('todos', { keyPath: 'id', autoIncrement: true });
store.createIndex('completed', 'completed');
};
request.onsuccess = event => {
db = event.target.result;
loadTodos();
};
async function loadTodos() {
const transaction = db.transaction(['todos'], 'readonly');
const store = transaction.objectStore('todos');
const todos = await store.getAll();
for (const todo of todos) {
addTodoToList(todo);
}
}
3.将任务添加到任务列表中。
JavaScript
function addTodoToList(todo) {
const li = document.createElement('li');
li.dataset.id = todo.id;
li.innerText = todo.title;
if (todo.completed) {
li.classList.add('completed');
}
li.addEventListener('click', event => {
const completed = !li.classList.contains('completed');
updateTodoStatus(todo.id, completed);
li.classList.toggle('completed');
});
todoList.appendChild(li);
}
4.从任务列表中删除任务。
JavaScript
function deleteTodo(id) {
const transaction = db.transaction(['todos'], 'readwrite');
const store = transaction.objectStore('todos');
store.delete(id);
}
5.更新任务的完成状态。
JavaScript
function updateTodoStatus(id, completed) {
const transaction = db.transaction(['todos'], 'readwrite');
const store = transaction.objectStore('todos');
store.get(id).onsuccess = event => {
const todo = event.target.result;
todo.completed = completed;
store.put(todo);
};
}
6.使用 IndexedDB 存储新任务,并将其添加到任务列表中。
JavaScriptfunction addNewTodo() {
const title = newTodoInput.value;
if (title.trim() === '') {
return;
}
const transaction = db.transaction(['todos'], 'readwrite');
const store = transaction.objectStore('todos');
const request = store.add({ title, completed: false });
request.onsuccess = event => {
const todo = { id: event.target.result, title, completed: false };
addTodoToList(todo);
newTodoInput.value = '';
};
}
7.监听表单提交事件,并调用 addNewTodo 函数。
JavaScriptdocument.querySelector('form').addEventListener('submit', event => {
event.preventDefault();
addNewTodo();
});
8.监听任务列表中任务的右键点击事件,并调用 deleteTodo 函数。
最后,我们在 sw.js 中编写 Service Worker 的逻辑,包括:
1.缓存应用的静态资源。
JavaScriptconst cacheName = 'todo-app-cache';
self.addEventListener('install', event => {
event.waitUntil(
caches.open(cacheName).then(cache => {
return cache.addAll([
'/',
'/index.html',
'/app.js',
'/style.css',
'/manifest.json'
]);
})
);
});
2.从缓存中读取静态资源,如果没有则从网络中获取。
JavaScriptself.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
3.删除旧的缓存,保留最新的缓存。
JavaScript
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.filter(name => name !== cacheName).map(name => caches.delete(name))
);
})
);
});
到此为止,我们的 PWA 应用就完成了。用户可以使用它添加、完成和删除任务,应用还具有离线缓存、添加到主屏幕等 PWA 特性,大大提升了应用的性能和用户体验。
如果你想体验一下这个应用,可以将上述代码保存到本地,然后在浏览器中打开 index.html 文件即可。
综上所述,PWA 是一种非常有前途的技术,可以提高 Web 应用的性能和用户体验。使用 PWA 可以让 Web 应用更像原生应用,更加快速、可靠和易用。在未来,PWA 将会成为 Web 应用的重要发展方向,带来更多的创新和变革。
原文链接:http://lao-zhao.com/post/14.html
标签:Web,transaction,const,completed,PWA,体验,todo,event,png From: https://www.cnblogs.com/lao-zhao/p/17176094.html