手把手教你制作一个PWA应用教程
來源 |?https://segmentfault.com/a/1190000019414253
簡介
Web前端的同學是否想過學習應用開發,以彌補自己的移動端能力的不足?但在面對一眾的選擇時很多同學略感迷茫,是學習ios還是android開發?是學習原生開發,混合開發(例如:Ionic),還是使用react native或者flutter這樣的跨平臺框架?而應用開發的學習周期長,學習成本高也讓一部分人望而卻步。
得益于前端技術的飛速發展,瀏覽器性能的不斷提高,使用網頁技術開發出接近原生體驗的應用重新實現的現實,PWA就在這樣的背景下應運而生。可以用自己熟悉的HTML,CSS,Javascript開發出替代美原生應用的網站,而擁有接近原生應用的流暢程度,并且某些某些原生app才有的特性,
例如:a。可以在主屏上安裝應用圖標,b。離線狀態下訪問,c。獲取消息通知,等等。。PWA的出現讓大家看到了希望!
對比原生應用
那分別PWA和原生應用比例到底有何競爭力呢?我們分別看一下原生應用和PWA的特點:
原生應用:
使用原生SDK和開發工具開發
需要考慮跨平臺,不同系統經常需要獨立開發
需要發布到應用商店才能下載使用
可以安裝到手機主屏,生成應用圖標
直接運行于操作系統上,訪問系統資源方便
可以離線使用
可以獲取消息通知
PWA應用:
使用HTML,CSS,JS開發
無需考慮跨平臺,只需要考慮瀏覽器兼容性
通過網址訪問,無需發布到應用商店
可以安裝到手機主屏,生成應用圖標
運行于瀏覽器中,可訪問系統資源
可以離線使用
可以獲取消息通知
可以發現PWA本質上是原生應用的主要能力,但是開發流程卻比原生應用更加簡潔:
a、html / css / js的群眾基礎更好,開發效率更高;
b、省去了為不同系統開發獨立版本的大量成本;
c、省去了上架到應用市場的繁瑣流程;
d、無需進一步應用商店下載,用戶使用起來也更加方便。但是稍微的是,PWA還是相對比較新的技術,實現規范還有很多調整的空間,部分瀏覽器對PWA的支持也還不完善,但是PWA是一個趨勢,所以現在學習正合適!
本文將通過一個簡單的列子(一個簡單的郵編查詢應用)向大家展示PWA的開發流程,項目參考:Traversy Media-使用Vue和Ionic4構建PWA。完成后的效果是這樣的。
創建項目
項目使用Vue + Ionic的組合進行開發。此處主要關注PWA的構造,因此vue,ionic等技術不做過多描述。使用VSCode的同學,建議安裝Vetur插件提高開發效率。
1.首先大致安裝@vue/cli:
npm install -g @vue/cli2.初始化vue項目:
3.因為ionic的vue-router路由依賴于,所以接下來安裝vue-router:
4.安裝?@ionic/vue
npm install @ionic/vue5.在src/main.js中添加對ionic的
6.在src/router.js中使用IonicVueRouter替換預設的vue路由器:
import Vue from 'vue'import { IonicVueRouter } from '@ionic/vue';import Home from './views/Home.vue' Vue.use(IonicVueRouter) export default new IonicVueRouter({ mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home } ]})7.將src/App.vue內容修改為:
<template> <div id="app"> <ion-app> <ion-vue-router/> </ion-app> </div></template>8.將src/views/Home.vue內容修改為:
<template> <div class="ion-page"> <ion-header> <ion-toolbar> <ion-title> ZipInfo </ion-title> </ion-toolbar> </ion-header> <ion-content class="ion-padding">My App</ion-content> </div></template> <script>export default { name: 'home', components: {}}</script>最后,我們運行yarn serve看下效果:
App功能實現
App主要有三部分組成:1。搜索組件,用于輸入郵編并查詢,2。展示組件,用于展示查詢到的郵編信息,3。清除按鈕,用于清除查詢到的郵編信息
1、搜索組件
我們在src/components下面的新建ZipSearch.vue文件作為郵編搜索組件,主要邏輯為當用戶輸入一串字符,點擊搜索按鈕,如果輸入合法則觸發get-zip事件,如果不合法則提示。
ZipSearch.vue
<template> <ion-grid> <form @submit="onSubmit"> <ion-col> <ion-item> <ion-label>ZipCode:</ion-label> <ion-input :value="zip" @input="zip = $event.target.value" name="zip" placeholder="Enter US ZipCode" /> </ion-item> </ion-col> <ion-col> <ion-button type="submit" color="primary" expand="block">Find</ion-button> </ion-col> </form> </ion-grid></template> <script>export default { name: "ZipSearch", data() { return { zip: "" }; }, methods: { onSubmit(e) { e.preventDefault(); const zipRegex = /(^\d{5}$)|(^\d{5}-\d{4}$)/; const isValid = zipRegex.test(this.zip); if (!isValid) { this.showAlert(); } else { this.$emit("get-zip", this.zip); } this.zip = ""; }, showAlert() { return this.$ionic.alertController .create({ header: "Enter zipcode", message: "Please enter a valid US ZipCode", buttons: ["OK"] }) .then(a => a.present()); } }};</script>在src/views/Home.vue中約會ZipSearch組件,當Home接收到get-zip事件時調用https://www.zippopotam.us的接口,獲取郵編對應的信息:
... <ion-content class="ion-padding"> <ZipSearch v-on:get-zip="getZipInfo"/> </ion-content>... <script>import ZipSearch from "../components/ZipSearch"; export default { name: "home", components: { ZipSearch }, data() { return { info: null }; }, methods: { async getZipInfo(zip) { const res = await fetch(`https://api.zippopotam.us/us/${zip}`); if (res.status == 404) { this.showAlert(); } this.info = await res.json(); }, showAlert() { return this.$ionic.alertController .create({ header: "Not Valid", message: "Please enter a valid US ZipCode", buttons: ["OK"] }) .then(a => a.present()); } }};</script>我們先看一下搜索組件的效果:
輸入郵編格式錯誤:
2、信息展示和清除組件
獲取到郵編信息后我們需要一個展示郵編信息的組件和一個src/components清除信息的按鈕,在下面新建ZipInfo.vue和ClearInfo.vue。
ZipInfo.vue
<template> <ion-card v-if="info"> <ion-card-header> <ion-card-subtitle>{{info['post code']}}</ion-card-subtitle> <ion-card-title>{{info['places'][0]['place name']}}</ion-card-title> </ion-card-header> <ion-card-content> <ion-list> <ion-item> <ion-label> <strong>State:</strong> {{info['places'][0]['state']}} ({{info['places'][0]['state abbreviation']}}) </ion-label> </ion-item> <ion-item> <ion-label> <strong>Latitude:</strong> {{info['places'][0]['latitude']}} </ion-label> </ion-item> <ion-item> <ion-label> <strong>Longitude:</strong> {{info['places'][0]['longitude']}} </ion-label> </ion-item> </ion-list> </ion-card-content> </ion-card></template> <script>export default { name: "ZipInfo", props: ["info"]};</script>ClearInfo.vue
<template> <ion-button color="light" expand="block" v-if="info" @click="$emit('clear-info')">Clear</ion-button></template> <script>export default { name: "ClearInfo", props: ["info"]};</script>接著在Home中約會ZipInfo和ClearInfo組件:
src / views / Home.vue
... <ion-content class="ion-padding"> <ZipSearch v-on:get-zip="getZipInfo"/> <ZipInfo v-bind:info="info"/> <ClearInfo v-bind:info="info" v-on:clear-info="clearInfo"/> </ion-content>... import ZipInfo from "../components/ZipInfo";import ClearInfo from "../components/ClearInfo"; export default { name: "home", components: { ZipSearch, ZipInfo }, methods:{ ... clearInfo(){ this.info = null; } }}到此,app的主體就完成了,效果如下:
實現PWA
我們使用現成的@vue/pwa插件來給我們的app增加PWA的能力。
安裝@vue/pwa:
vue add @vue/pwa安裝完成后項目中增加了public/manifest.json和registerServiceWorker.js兩個文件。其中public/manifest.json文件內容如下:
{ "name": "vue-ionic-pwa", "short_name": "vue-ionic-pwa", "icons": [ { "src": "./img/icons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "./img/icons/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "start_url": "./index.html", "display": "standalone", "background_color": "#000000", "theme_color": "#4DBA87"}manifest.json中主要包含app的基本信息,例如名稱(名稱),圖標(圖標),顯示方式(display)等等,是web app能被以類似原生的方式安裝,展示的必要配置。更多的配置項可參考MDN Web App清單。
在Chrome瀏覽器控制臺中也可看到app的manifest配置:
registerServiceWorker.js為注冊服務人員。服務人員通俗地說就是在瀏覽器后臺獨立于網頁運行的一段腳本,服務人員可以完成一些特殊的功能,例如:消息推送,后臺同步,攔截和處理網絡請求,管理網絡緩存等。Serviceworker之于pwa的意義在于能夠為用戶提供離線體驗,即掉線狀態下用戶依舊能夠訪問網站并獲取已被緩存的數據。使用service worker需要HTTPS,并考慮瀏覽器兼容性。
registerServiceWorker.js
import { register } from 'register-service-worker' if (process.env.NODE_ENV === 'production') { register(`${process.env.BASE_URL}service-worker.js`, { ready () { console.log( 'App is being served from cache by a service worker.\n' + 'For more details, visit https://goo.gl/AFskqB' ) }, registered () { console.log('Service worker has been registered.') }, cached () { console.log('Content has been cached for offline use.') }, updatefound () { console.log('New content is downloading.') }, updated () { console.log('New content is available; please refresh.') }, offline () { console.log('No internet connection found. App is running in offline mode.') }, error (error) { console.error('Error during service worker registration:', error) } })}在Chrome瀏覽器控制臺中也可看到服務工作者的狀態:
當然,只注冊了service worker還不夠,我們還希望控制service worker的行為,通過在vue.config.js中增加相關的配置我們可以設置service worker文件的名稱,緩存邏輯等等。
vue.config.js
module.exports = { pwa: { workboxPluginMode: 'GenerateSW', workboxOptions: { navigateFallback: '/index.html', runtimeCaching: [ { urlPattern: new RegExp('^https://api.zippopotam.us/us/'), handler: 'networkFirst', options: { networkTimeoutSeconds: 20, cacheName: 'api-cache', cacheableResponse: { statuses: [0, 200] } } } ] } }}更多配置請參考:@ VUE / CLI-插件,PWA和針線-的WebPack-插件。由于@vue/cli-plugin-pwa,生成的服務人員只在生產環境生效,所以建議將項目建設之后部署到生產環境測試本文示例使用GitHub的頁面進行部署和展示。
到此,將普通的網絡應用轉成PWA的工作基本完成,我們部署到線上看下效果:
文件已被緩存用于離線訪問:
查詢一個郵編試試,可以發現請求被緩存了下來:
我們隨后關掉網絡,再查詢剛剛的那個郵編,發現在網絡請求失敗之后立即切換用本地緩存的數據:
好了,一個簡單的PWA就已經制作完成了。當然PWA的功能遠不止此所展示的,依次按壓,安裝到手機,后續有機會再跟大家分享,謝謝。
總結
以上是生活随笔為你收集整理的手把手教你制作一个PWA应用教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CCRC信息安全服务资质认证流程和认证周
- 下一篇: Qt之天气预报——界面优化篇(含源码+注