PWA⼊门:⼿把⼿教你制作⼀个PWA应⽤
摘要: PWA图⽂教程
原⽂:
作者:MudOnTire
经授权转载,版权归原作者所有。
简介
Web前端的同学是否想过学习app开发,以弥补⾃⼰移动端能⼒的不⾜?但在⾯对⼀众的选择时很多同学略感迷茫,是学习ios还是android 开发?是学习原⽣开发、混合开发(⽐如:),还是使⽤或者这样的跨平台框架?⽽app开发的学习周期长、学习成本⾼也让⼀部分⼈望⽽却步。得益于前端技术的飞速发展、浏览器性能的不断提⾼,使⽤⽹页技术开发出接近原⽣体验的应⽤得以变为现实,PWA就在这样的背景下应运⽽⽣。可以⽤⾃⼰熟悉的HTML、CSS、Javascript开发出媲美原⽣app的⽹站,不仅拥有接近原⽣app的流畅程度,并且具备⼀些原⽣app才有的特性,⽐如:a. 可以在主屏上安装应⽤图标,b. 离线状态下访问,c. 获取消息通知,等等。。PWA的出现让⼤家看到了希望!
对⽐原⽣应⽤
那PWA和原⽣应⽤相⽐到底有何竞争⼒呢?我们分别看⼀下原⽣应⽤和PWA的特点:
原⽣应⽤:
使⽤原⽣SDK和开发⼯具开发
需要考虑跨平台,不同系统往往需要独⽴开发
需要发布到应⽤商店才能下载使⽤
可以安装到⼿机主屏,⽣成应⽤图标
直接运⾏于操作系统上,访问系统资源⽅便
可以离线使⽤
可以获取消息通知
PWA应⽤:
使⽤HTML,CSS,JS开发
⽆需考虑跨平台,只需要考虑浏览器兼容性
通过url访问,⽆需发布到应⽤商店
可以安装到⼿机主屏,⽣成应⽤图标
运⾏于浏览器中,可访问系统资源
可以离线使⽤
可以获取消息通知
可以发现PWA具备了原⽣应⽤的主要能⼒,但是开发流程却⽐原⽣应⽤更加简洁:a. html/css/js的众基础更好,开发效率更⾼;b. 省去了为不同系统开发独⽴版本的⼤量成本;c. 省去了上架到应⽤市场的繁琐流程;d. ⽆需前往应⽤商店下载,⽤户使⽤起来也更加⽅便。但是值得注意的是,PWA还是相对⽐较新的技术,实现规范还有很多调整的空间,部分浏览器对PWA的⽀持也还不完善,但是PWA是⼀个趋势,所以现在学习正合适!
本⽂将通过⼀个简单的列⼦(⼀个简单的邮编查询app)向⼤家展⽰PWA的开发流程,项⽬参考:。完成后的效果是。
创建项⽬
项⽬使⽤Vue + Ionic的组合进⾏开发。本⽂主要关注PWA的搭建,因此vue、ionic等技术不做过多描述。使⽤VSCode的同学,建议安装插件增加开发效率。
1. ⾸先全局安装@vue/cli:
npm install -g @vue/cli
2. 初始化vue项⽬:
vue create vue-ionic-pwa
3. 因为ionic的路由依赖于vue-router,所以接下来安装vue-router:
vue add router
4. 安装@ionic/vue
npm install @ionic/vue
5. 在src/main.js中添加对ionic的引⽤:
import Ionic from '@ionic/vue'
import '@ionic/core/css/ionic.bundle.css'
Vue.use(Ionic)
6. 在src/router.js中使⽤IonicVueRouter替换默认的vue router:
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: v.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 = st(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>
...
<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(`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>编程入门先学js
<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
我们使⽤现成的插件来给我们的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的基本信息,⽐如名称(name)、图标(icons)、显⽰⽅式(display)等等,是web app能被以类似原⽣的⽅式安装、展⽰的必要配置。更多的配置项可参考。
在Chrome浏览器控制台中也可看到app的manifest配置:
registerServiceWorker.js⽤于注册service worker。service worker通俗来讲就是在浏览器后台独⽴于⽹页运⾏的⼀段脚本,service worker可以完成⼀些特殊的功能,⽐如:消息推送、后台同步、拦截和处理⽹络请求、管理⽹络缓存等。Service worker之于pwa的意义在于能够为⽤户提供离线体验,即掉线状态下⽤户依旧能够访问⽹站并获取已被缓存的数据。使⽤service worker需要HTTPS,并且考虑。registerServiceWorker.js
import { register } from 'register-service-worker'
if (v.NODE_ENV === 'production') {
register(`${v.BASE_URL}service-worker.js`, {
ready () {
console.log(
'App is being served from cache by a service worker.\n' +
'For more details, visit 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) {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。