├── .browserslistrc ├── .firebaserc ├── .gitignore ├── README.md ├── babel.config.js ├── firebase.json ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── firebase-messaging-sw.js ├── img │ └── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── msapplication-icon-144x144.png │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg ├── index.html ├── manifest.json └── robots.txt ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue ├── main.ts ├── registerServiceWorker.ts ├── router.ts ├── shims-tsx.d.ts ├── shims-vue.d.ts ├── store.ts └── views │ ├── About.vue │ └── Home.vue ├── tsconfig.json └── tslint.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "vue-pwa-tutorial" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | 23 | .firebase -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-pwa-tutorial 2 | Document 3 | 4 | Vue CLI 3 で PWA やってみた(Service Workers / Add to Home Screen / Push Notifications) 5 | https://qiita.com/n11sh1/items/5d64c337ef927ac8d5d6 6 | 7 | ## Project setup 8 | ``` 9 | npm install 10 | ``` 11 | 12 | ### Compiles and hot-reloads for development 13 | ``` 14 | npm run serve 15 | ``` 16 | 17 | ### Compiles and minifies for production 18 | ``` 19 | npm run build 20 | ``` 21 | 22 | ### Run your tests 23 | ``` 24 | npm run test 25 | ``` 26 | 27 | ### Lints and fixes files 28 | ``` 29 | npm run lint 30 | ``` 31 | 32 | ### Customize configuration 33 | See [Configuration Reference](https://cli.vuejs.org/config/). 34 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-pwa-tutorial", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "firebase": "^5.8.0", 12 | "register-service-worker": "^1.5.2", 13 | "vue": "^2.5.21", 14 | "vue-class-component": "^6.0.0", 15 | "vue-property-decorator": "^7.0.0", 16 | "vue-router": "^3.0.1", 17 | "vuex": "^3.0.1" 18 | }, 19 | "devDependencies": { 20 | "@vue/cli-plugin-babel": "^3.3.0", 21 | "@vue/cli-plugin-pwa": "^3.3.0", 22 | "@vue/cli-plugin-typescript": "^3.3.0", 23 | "@vue/cli-service": "^3.3.0", 24 | "typescript": "^3.0.0", 25 | "vue-template-compiler": "^2.5.21" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/favicon.ico -------------------------------------------------------------------------------- /public/firebase-messaging-sw.js: -------------------------------------------------------------------------------- 1 | // [START initialize_firebase_in_sw] 2 | // Give the service worker access to Firebase Messaging. 3 | // Note that you can only use Firebase Messaging here, other Firebase libraries 4 | // are not available in the service worker. 5 | importScripts('https://www.gstatic.com/firebasejs/5.5.6/firebase-app.js'); 6 | importScripts('https://www.gstatic.com/firebasejs/5.5.6/firebase-messaging.js'); 7 | // Initialize the Firebase app in the service worker by passing in the messagingSenderId. 8 | firebase.initializeApp({ 9 | 'messagingSenderId': '630674987445' 10 | }); 11 | 12 | // Retrieve an instance of Firebase Messaging so that it can handle background messages. 13 | const messaging = firebase.messaging(); 14 | // [END initialize_firebase_in_sw] -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-pwa-tutorial 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-pwa-tutorial", 3 | "short_name": "vue-pwa-tutorial", 4 | "icons": [ 5 | { 6 | "src": "./img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "./index.html", 17 | "display": "standalone", 18 | "background_color": "#000000", 19 | "theme_color": "#4DBA87", 20 | "gcm_sender_id": "103953800507" 21 | } 22 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 32 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n11sh1/vue-pwa-tutorial/8aa438386d6923cf70534adc1cf22bd971165507/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 42 | 43 | 44 | 60 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | import router from './router'; 4 | import store from './store'; 5 | import './registerServiceWorker'; 6 | import * as firebase from "firebase"; 7 | 8 | Vue.config.productionTip = false; 9 | 10 | // Initialize Firebase 11 | var config = { 12 | apiKey: "xxxxxxxxxxx", 13 | authDomain: "xxxxxxxxxxx", 14 | databaseURL: "xxxxxxxxxxx", 15 | projectId: "xxxxxxxxxxx", 16 | storageBucket: "xxxxxxxxxxx", 17 | messagingSenderId: "xxxxxxxxxxx" 18 | }; 19 | firebase.initializeApp(config); 20 | 21 | // Retrieve Firebase Messaging object. 22 | const messaging = firebase.messaging(); 23 | 24 | // Add the public key generated from the console here. 25 | messaging.usePublicVapidKey("xxxxxxxxxxx"); 26 | 27 | messaging.requestPermission().then(() => { 28 | console.log('Notification permission granted.'); 29 | messaging.getToken().then((token) => { 30 | console.log(token); 31 | }) 32 | }).catch((err) => { 33 | console.log('Unable to get permission to notify.', err); 34 | }); 35 | 36 | new Vue({ 37 | router, 38 | store, 39 | render: (h) => h(App), 40 | }).$mount('#app'); 41 | -------------------------------------------------------------------------------- /src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-console */ 2 | 3 | import { register } from 'register-service-worker'; 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready() { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB', 11 | ); 12 | }, 13 | registered() { 14 | console.log('Service worker has been registered.'); 15 | }, 16 | cached() { 17 | console.log('Content has been cached for offline use.'); 18 | }, 19 | updatefound() { 20 | console.log('New content is downloading.'); 21 | }, 22 | updated() { 23 | console.log('New content is available; please refresh.'); 24 | }, 25 | offline() { 26 | console.log('No internet connection found. App is running in offline mode.'); 27 | }, 28 | error(error) { 29 | console.error('Error during service worker registration:', error); 30 | }, 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/router.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Router from 'vue-router'; 3 | import Home from './views/Home.vue'; 4 | 5 | Vue.use(Router); 6 | 7 | export default new Router({ 8 | mode: 'history', 9 | base: process.env.BASE_URL, 10 | routes: [ 11 | { 12 | path: '/', 13 | name: 'home', 14 | component: Home, 15 | }, 16 | { 17 | path: '/about', 18 | name: 'about', 19 | // route level code-splitting 20 | // this generates a separate chunk (about.[hash].js) for this route 21 | // which is lazy-loaded when the route is visited. 22 | component: () => import(/* webpackChunkName: "about" */ './views/About.vue'), 23 | }, 24 | ], 25 | }); 26 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue'; 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /src/store.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | Vue.use(Vuex); 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | 9 | }, 10 | mutations: { 11 | 12 | }, 13 | actions: { 14 | 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env" 16 | ], 17 | "paths": { 18 | "@/*": [ 19 | "src/*" 20 | ] 21 | }, 22 | "lib": [ 23 | "esnext", 24 | "dom", 25 | "dom.iterable", 26 | "scripthost" 27 | ] 28 | }, 29 | "include": [ 30 | "src/**/*.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "tests/**/*.ts", 34 | "tests/**/*.tsx" 35 | ], 36 | "exclude": [ 37 | "node_modules" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules/**" 9 | ] 10 | }, 11 | "rules": { 12 | "quotemark": [true, "single"], 13 | "indent": [true, "spaces", 2], 14 | "interface-name": false, 15 | "ordered-imports": false, 16 | "object-literal-sort-keys": false, 17 | "no-consecutive-blank-lines": false 18 | } 19 | } 20 | --------------------------------------------------------------------------------