vue js demo
parent
c82470971b
commit
101e638ba0
@ -0,0 +1,385 @@
|
||||
# [Tutorial](https://medium.com/sicara/a-progressive-web-application-with-vue-js-webpack-material-design-part-1-c243e2e6e402) vue pwa
|
||||
|
||||
* Vue PWA template is built on top of Vue webpack template
|
||||
* Webpack is a modern and powerful module bundler for Javascript application
|
||||
* Project layout
|
||||
* build: webpack and vue-loader configuration files
|
||||
* config: app config (environments, parameters)
|
||||
* src: application source code
|
||||
* statis: images, css & other public assets
|
||||
* test: unit test files (Karma & Mocha)
|
||||
|
||||
|
||||
|
||||
```sh
|
||||
$ npm install -g vue-cli
|
||||
|
||||
// use pwa template to scaffold a project
|
||||
$ vue init pwa cropchat
|
||||
|
||||
$ cd cropchat
|
||||
$ npm install
|
||||
$ npm run dev
|
||||
|
||||
// launch browser at http://localhost:8080
|
||||
|
||||
```
|
||||
|
||||
* Vue pwa template provide default `static/manifest.json`. Edit it to customize the project
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "cropchat",
|
||||
"short_name": "cropchat",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/img/icons/cropchat-icon-64x64.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/cropchat-icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/cropchat-icon-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/img/icons/cropchat-icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display": "fullscreen",
|
||||
"orientation": "portrait",
|
||||
"background_color": "#2196f3",
|
||||
"theme_color": "#2196f3"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
* `index.html` already provide the manifest link
|
||||
|
||||
```html
|
||||
...
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
...
|
||||
<link rel="manifest" href="<%= htmlWebpackPlugin.files.publicPath %>static/manifest.json">
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Access from mobile
|
||||
|
||||
* use `ngrok` to forward internet address to localhost host
|
||||
|
||||
```sh
|
||||
$ npm install -g ngrok
|
||||
|
||||
$ ngrok http 8080
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
```text
|
||||
ngrok by @inconshreveable (Ctrl+C to quit)
|
||||
|
||||
Session Status online
|
||||
Version 2.1.18
|
||||
Region United States (us)
|
||||
Web Interface http://127.0.0.1:4040
|
||||
Forwarding http://5ef29506.ngrok.io -> localhost:8080
|
||||
Forwarding https://5ef29506.ngrok.io -> localhost:8080
|
||||
|
||||
Connections ttl opn rt1 rt5 p50 p90
|
||||
39 3 0.01 0.01 120.01 881.89
|
||||
```
|
||||
|
||||
|
||||
|
||||
* Access `https://5ef29506.ngrok.io` from mobile
|
||||
|
||||
## Develop Vue [App](https://medium.com/sicara/a-progressive-web-application-with-vue-js-webpack-material-design-part-1-c243e2e6e402)
|
||||
|
||||
* `src/components/HomeView.vue`
|
||||
|
||||
```vue
|
||||
|
||||
<template>
|
||||
<ul class="list">
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.list {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
|
||||
|
||||
* `src/components/DetailView.vue`
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="card-image">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
```
|
||||
|
||||
* `src/components/PostView.vue`
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="waiting">
|
||||
Not yet available
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.waiting {
|
||||
padding: 10px;
|
||||
color: #555;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
* Update `src/router/index.js`
|
||||
|
||||
```js
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import HomeView from '@/components/HomeView'
|
||||
import DetailView from '@/components/DetailView'
|
||||
import PostView from '@/components/PostView'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView
|
||||
},
|
||||
{
|
||||
path: '/detail/:id',
|
||||
name: 'detail',
|
||||
component: DetailView
|
||||
},
|
||||
{
|
||||
path: '/post',
|
||||
name: 'post',
|
||||
component: PostView
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
* Remove `Hello.vue`
|
||||
|
||||
### Layout Design
|
||||
|
||||
* install material Design
|
||||
|
||||
```sh
|
||||
$ npm install material-design-lite --save
|
||||
```
|
||||
|
||||
* Update `src/App.vue`
|
||||
|
||||
```vue
|
||||
<script>
|
||||
require('material-design-lite')
|
||||
...
|
||||
</script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/icon?family=Material+Icons');
|
||||
@import url('https://code.getmdl.io/1.2.1/material.blue-red.min.css');
|
||||
</style>
|
||||
```
|
||||
|
||||
* Add Navigation Bar. Update `src/App.vue` template
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
|
||||
<header class="mdl-layout__header">
|
||||
<div class="mdl-layout__header-row">
|
||||
<span class="mdl-layout-title">CropChat</span>
|
||||
</div>
|
||||
</header>
|
||||
<div class="mdl-layout__drawer">
|
||||
<span class="mdl-layout-title">CropChat</span>
|
||||
<nav class="mdl-navigation">
|
||||
<router-link class="mdl-navigation__link" to="/" @click.native="hideMenu">Home</router-link>
|
||||
<router-link class="mdl-navigation__link" to="/post" @click.native="hideMenu">Post a picture</router-link>
|
||||
</nav>
|
||||
</div>
|
||||
<main class="mdl-layout__content">
|
||||
<div class="page-content">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
* Hide burger menu upon click menu link
|
||||
|
||||
```vue
|
||||
<script>
|
||||
...
|
||||
export default {
|
||||
name: 'app',
|
||||
methods: {
|
||||
hideMenu: function () {
|
||||
document.getElementsByClassName('mdl-layout__drawer')[0].classList.remove('is-visible')
|
||||
document.getElementsByClassName('mdl-layout__obfuscator')[0].classList.remove('is-visible')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
* Populate views
|
||||
* Create `src/data.js`
|
||||
|
||||
```js
|
||||
export default {
|
||||
pictures: [
|
||||
{
|
||||
'id': 0,
|
||||
'url': 'https://25.media.tumblr.com/tumblr_m40h4ksiUa1qbyxr0o1_400.gif',
|
||||
'comment': 'A cat game',
|
||||
'info': 'Posted by Kevin on Friday'
|
||||
},
|
||||
{
|
||||
'id': 1,
|
||||
'url': 'https://25.media.tumblr.com/tumblr_lhd7n9Qec01qgnva2o1_500.jpg',
|
||||
'comment': 'Tatoo & cat',
|
||||
'info': 'Posted by Charles on Tuesday'
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'url': 'https://24.media.tumblr.com/tumblr_m4j2atctRm1qejbiro1_1280.jpg',
|
||||
'comment': 'Santa cat',
|
||||
'info': 'Posted by Richard on Monday'
|
||||
},
|
||||
{
|
||||
'id': 3,
|
||||
'url': 'https://25.media.tumblr.com/tumblr_m3rmbwhVB51qhwmnpo1_1280.jpg',
|
||||
'comment': 'Mexico cat',
|
||||
'info': 'Posted by Richard on Monday'
|
||||
},
|
||||
{
|
||||
'id': 4,
|
||||
'url': 'https://24.media.tumblr.com/tumblr_mceknxs4Lo1qd477zo1_500.jpg',
|
||||
'comment': 'Curious cat',
|
||||
'info': 'Posted by Richard on Monday'
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
* Update `src/components/HomeView.vue` to display mocked data
|
||||
|
||||
```vue
|
||||
<script>
|
||||
import data from '../data'
|
||||
export default {
|
||||
methods: {
|
||||
displayDetails (id) {
|
||||
this.$router.push({name: 'detail', params: { id: id }})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
'pictures': data.pictures
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
* Update temple ans style
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<div class="mdl-grid">
|
||||
<div class="mdl-cell mdl-cell--3-col mdl-cell mdl-cell--1-col-tablet mdl-cell--hide-phone"></div>
|
||||
<div class="mdl-cell mdl-cell--6-col mdl-cell--4-col-phone">
|
||||
<div v-for="picture in this.pictures" class="image-card" @click="displayDetails(picture.id)">
|
||||
<div class="image-card__picture">
|
||||
<img :src="picture.url" />
|
||||
</div>
|
||||
<div class="image-card__comment mdl-card__actions">
|
||||
<span>{{ picture.comment }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<router-link class="add-picture-button mdl-button mdl-js-button mdl-button--fab mdl-button--colored" to="/post">
|
||||
<i class="material-icons">add</i>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
...
|
||||
<style scoped>
|
||||
.add-picture-button {
|
||||
position: fixed;
|
||||
right: 24px;
|
||||
bottom: 24px;
|
||||
z-index: 998;
|
||||
}
|
||||
.image-card {
|
||||
position: relative;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.image-card__picture > img {
|
||||
width:100%;
|
||||
}
|
||||
.image-card__comment {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 52px;
|
||||
padding: 16px;
|
||||
text-align: right;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.image-card__comment > span {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
Loading…
Reference in New Issue