30分鐘從零開始教會你什麼是PWA

clipboard.png

前置知識儲備

PWA ( Progressive Web Apps )是網絡上談論最多的技術變革之一,在IT界從業者中得到了史無前例的勢頭。若是你是爲web構建的,我相信PWA是添加到你的工做詞彙中的最新「流行語」。這並不奇怪,由於PWA已經實現了在手機上安裝web應用程序的高不可攀的夢想。javascript

關於PWA的建設和它的優點,已經有了不少的焦點和「極客之談」。大多數介紹PWA的嘗試,特別是對新手來講,彷佛都是行話,或者代碼太多,可能會使他們不敢邁出第一步。在這篇文章中,我想要以一個簡單的案例來教會各位如何起步。css

關於PWA的概念以及前世此生我這邊不會過多贅述,網絡上有不少更加專業的文章供你學習,這篇文章只負責教會你如何使用它。html

比起用一篇文章打消你的全部關於PWA的困惑來講我更但願你能簡單瞭解概念以後將個人案例敲打一遍後再回過頭去深刻了解PWA。java

什麼是PWA
下一代 Web 應用模型 —— Progressive Web App
PWA官網webpack

起步

// 建立一個簡單的項目

mkdir pwa-project
cd pwa-project
touch index.html
touch app.js
touch style.css

相信各位都是使用chrome最新版的高端技術人才,這裏爲了省略webpack一些繁瑣的配置咱們直接編寫es6代碼運行在chrome便可git

編寫代碼

假設咱們有一個新聞站點,須要展現標題,圖片,文章,而且根據不一樣的來源切換內容
根據這個要求咱們能夠編寫如下代碼
// 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">
    <title>News</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <header>
        <h1>News</h1>
        <select id="sourceSelector"></select>
    </header>

    <main></main>

    <script src="./app.js"></script>
</body>
</html>
// style.css

html {
    line-height: 1.15;
    /* 1 */
    -webkit-text-size-adjust: 100%;
    /* 2 */
}

body {
    margin: 0;
}

h1 {
    font-size: 2em;
    margin: 0.67em 0;
}

a {
    background-color: transparent;
    text-decoration: none;
}

button,
select {
    text-transform: none;
}

這裏爲了模擬真實用戶數據咱們能夠去👇這裏申請apikey獲取一些真實數據
各位也能夠直接copy代碼來學習,代碼邏輯很簡單這裏不作講解。es6

// app.js

const apiKey = 'fa35a325ddfa4c4798102ebb76809bbb';
const main = document.querySelector('main');
const sourceSelector = document.querySelector('#sourceSelector');
const defaultSource = 'techcrunch'

// 頁面加載後執行邏輯
window.addEventListener('load', async e => {
    updateNews();
    await updateSources();
    sourceSelector.value = defaultSource;

    sourceSelector.addEventListener('change', e => {
        updateNews(e.target.value);
    });
    
    // 判斷瀏覽器是否支持serviceWorker
    if ('serviceWorker' in navigator) {
        try {
            // 嘗試註冊serviceWorker到sw.js文件中
            navigator.serviceWorker.register('sw.js');
            console.log('SW registered');
        } catch (error) {
            console.log('SW reg failed');
        }
    }
});

// 獲取新聞來源
async function updateSources() {
    const res = await fetch(`https://newsapi.org/v2/sources?apiKey=${apiKey}`);
    const json = await res.json();

    sourceSelector.innerHTML = json.sources
        .map(src => `<option value="${src.id}">${src.name}</option>`)
        .join('\n');
}

// 根據來源獲取新聞數據
async function updateNews(source = defaultSource) {
    const res = await fetch(`https://newsapi.org/v2/top-headlines?sources=${source}&apiKey=${apiKey}`);
    const json = await res.json();

    main.innerHTML = json.articles.map(createArticle).join('\n');
}

// 建立文章
function createArticle(article) {
    return `
        <div class="article">
            <a href="${article.url}">
                <h2>${article.title}</h2>
            </a>
            <img src="${article.urlToImage}" />
            <p>${article.description}</p>
        </div>
    `;
}

啓動

如今啓動咱們的項目
執行npx http-server打開咱們的localhost:8080端口(端口號根據具體狀況而定)github

咱們點開控制檯看看是否是報錯了?web

clipboard.png

在點開這裏咱們能夠發現找不到sw.js這個文件,由於咱們根本沒有嘛!😂chrome

clipboard.png

既然沒有那咱們寫一個不就行了-_-!!

編寫sw.js

首先咱們什麼都不寫,直接建立sw.js文件到項目中就不會報錯了,可是什麼都沒寫就意味着你什麼都沒有作。
那咱們到底能夠用這個文件作什麼事呢?

緩存咱們的靜態資源文件

// sw.js

const staticAssets = [
    './',
    './style.css',
    './app.js'
];

// sw.js首次被註冊時候觸發
self.addEventListener('install', async event => {
    const cache = await caches.open('news-static');
    cache.addAll(staticAssets);
})

如今咱們再次刷新頁面(記得清理緩存哈)就能夠看到咱們的靜態資源都被緩存起來了。

clipboard.png

什麼?沒有緩存起來?
那你確定是沒有告訴瀏覽器刷新時候要更新你的sw.js文件。能夠勾選這裏,而後再次刷新。

clipboard.png

可是緩存是緩存起來了,咱們要是不拿來用那也沒啥卵用。
因此咱們要攔截請求並告訴瀏覽器咱們要使用這些緩存。

// sw.js (添加如下代碼)

// sw監聽到fetch事件時候觸發
self.addEventListener('fetch', event => {
    const req = event.request;

    event.respondWith(cacheFirst(req));
});

// 使用瀏覽器緩存
async function cacheFirst(req) {
    const cachedResponse = await caches.match(req);
    return cachedResponse || fetch(req);
}

編寫完這些以後咱們能夠先勾選offline按鈕,以後刷新頁面,發現咱們的站點依然有數據。

咱們點開network看看請求。

clipboard.png

咱們發現咱們攔截了http請求而且將瀏覽器的緩存數據返回回去了!

是否是很神奇😊!

到這一步,相信你已經見識到了PWA的一些能力了。

正確使用緩存

在上面的代碼中實際上是有一些問題的,咱們在離線狀態下使用緩存是ok的,但是若是咱們處於聯網狀態狀況下,咱們還須要返回緩存嗎?固然不須要,不只不須要,咱們還應該用服務器的最新數據更新咱們的緩存。

因此咱們要根據網絡優先的原則修改下sw邏輯。

self.addEventListener('fetch', event => {
    const req = event.request;
    const url = new URL(req.url);
    
    // 當本地開發時候能夠這麼配置
    if (url.origin === location.origin) {
        event.respondWith(cacheFirst(req));
    } else if ((req.url.indexOf('http') !== -1)) {
        // chrome的https協議限制,接口必須知足https
        event.respondWith(networkFirst(req));
    }
});

// 緩存優先
async function cacheFirst(req) {
    const cachedResponse = await caches.match(req);
    return cachedResponse || fetch(req);
}

// 網絡優先
async function networkFirst(req) {
    // 將請求到的數據緩存在id爲news-dynamic中
    const cache = await caches.open('news-dynamic');

    try {
        const res = await fetch(req); // 獲取數據
        cache.put(req, res.clone()); // 更新緩存
        return res;
    } catch (error) {
        return await cache.match(req); // 報錯則使用緩存
    }
}

至此咱們的案例基本上完成了,可是仍是能夠再次優化一下。

假設用戶尚未查看過咱們的站點頁面其餘內容,也就是說咱們的緩存不完整,這個時候能夠提供一個mock數據提示用戶等待能夠聯網的時候再來查看當前頁面。(具體文件能夠從個人github倉庫中獲取)

PWA就這麼點能力嗎?

其實PWA還提供給咱們將web站點以app圖標形式放置在桌面以及移動手機中。

這裏配置與編寫瀏覽器插件過程有點相似。

咱們須要一個manifest.json文件,並在index.html中引入。

manifest.json能夠在這個站點中生成。

// html文件須要引入一行代碼

<link rel="manifest" href="manifest.json">

配置成功後咱們能夠在控制檯中查看

clipboard.png

有了這個文件和相應圖標後咱們就能夠添加至桌面端了,是否是很酷炫~

未完待續

截止項目代碼可在個人倉庫中獲取。(可否給個小星星鼓勵下呢😂)

後續會補充在具體項目中應該怎麼集成pwa,這篇文章只分析了pwa的核心代碼~

相關文章
相關標籤/搜索