用PWA提升Web应用的性能和用户体验

 

 

 

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 存储新任务,并将其添加到任务列表中。

JavaScript
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 = '';
  };
}

 

7.监听表单提交事件,并调用 addNewTodo 函数。

JavaScript
document.querySelector('form').addEventListener('submit', event => {
  event.preventDefault();
  addNewTodo();
});

8.监听任务列表中任务的右键点击事件,并调用 deleteTodo 函数。

 

 

最后,我们在 sw.js 中编写 Service Worker 的逻辑,包括:

 

1.缓存应用的静态资源。

JavaScript
const 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.从缓存中读取静态资源,如果没有则从网络中获取。

JavaScript
self.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

posted @ 2023-03-03 16:17  前端老赵  阅读(169)  评论(0编辑  收藏  举报