[譯]基於Vue JS, Webpack 以及Material Design的漸進式web應用 [Part 1]

漸進式web應用是大勢所趨。愈來愈多的大公司開始使用這些技術(好比推特:https://mobile.twitter.com/)。css

想象你能夠在地鐵中瀏覽一個web應用,這個應用可以向用戶推送通知而且提供實時的數據,以及提供相似於app的瀏覽,這些就是PWA的大體的能力。html

漸進式web應用(PWA)是一個web應用可以提供給用戶一種相似於app的體驗。PWA得益於現代web科技創新(Service Workers, Native APIS, JS famework)以及提高的web應用質量標準。vue

若是你想了解更多關於PWA,請訪問這個很棒的Google developer pagewebpack

看一下下面的PWA!看起來很像原生的app,是否是?git

推特漸進式web應用github

從開發者的角度來看,PWA相對於原生應用具備巨大的優勢。它基本上就是一個網站,所以:web

  • 你能夠選擇任何你喜歡的框架來進行開發;vue-router

  • 一段代碼搞定一切:它是跨平臺的以及跨設備的(代碼是經過用戶的瀏覽器執行的);vue-cli

  • 很容易得到:不須要經過應用商店來下載。數據庫

然而,在2017年早期,PWA仍然面臨一些限制條件:

  • Safari不支持一些基本的PWA特性,好比 Service workers,可是蘋果公司彷佛已經準備開始着手了;

  • 一些原生的函數依然沒有獲得支持:對於更多信息,瀏覽這個頁面What web can do

教程目標

本教程的目標是利用VueJS以及Webpack從頭建立一個基本的可是完整的漸進式web應用。咱們的應用將會知足介紹裏面的全部需求:漸進式的,響應式的,鏈接獨立的等等。我想給你一個可以在PWA內完成的目標的總覽:流暢的原生式的應用,離線行爲,原生特性結構,推送通知。

爲了讓事情保持挑戰性,咱們打算構建一個貓圖信息app:CropChat!CropChat用戶可以閱讀主流的貓的圖片,而且可以打開他們瞭解更多細節以及上傳新的貓的圖片(首先從互聯網,接着是從設備驅動或者照相機)。

這個教程將會分爲幾個部分,它們將會連續地進行發佈

  • [Part 1] Lite基於Vue JS, Webpack 以及Material Design的漸進式web應用

  • [Part 2] 基於Vue-Resource以及VueFire將App和遠程的API進行鏈接

  • [Part 3] 基於Service Worker來實現離線模式

  • [Part 4] 訪問設備照相機來拍照

  • [Part 5] 訪問設備驅動來上傳圖片

  • [Part 6] 實現推送通知

  • [Part 7] 訪問設備地址

咱們的PWA的基本組件

咱們的漸進式web應用是基於你喜歡的如今組件!

讓咱們開始part 1!

[PART 1] Lite基於VueJS, Webpack 以及 Material Design Lite建立一個單頁面應用

若是你不熟悉VueJS 2,我強烈建議你閱讀官方教程

構建VueJS APP基礎

咱們打算利用Vue-cli來建立咱們的應用:

`npm install -g vue-cli`

Vue-cli自帶一些模板。咱們將會選擇webpack模板。Webpack是一個對於Javascript應用的現代模塊打包工具,它可以處理而且構建咱們的資源。Vue-cli將使用Webpack,vue-loader(熱加載!),JS linter以及測試套件來建立一個虛擬的VueJS應用。

`vue init webpack cropchat`

你可能會問一些問題。下面是我使用過的配置:

`This will install Vue 2.x version of the template.`
`For Vue 1.x use: vue init webpack#1.0 cropchat`
? Project name cropchat
? Project description Image messenging application
? Author Charles BOCHET <charlesb@theodo.fr>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Setup unit tests with Karma + Mocha? Yes
? Setup e2e tests with Nightwatch? No
`vue-cli · Generated "cropchat".`

這個過程會建立一個包含如下子文件夾的項目文件夾:

  • build: 包含webpack以及vue-loader配置文件

  • config: 包含咱們的app配置(環境,參數等等)

  • src: 咱們應用的源代碼

  • static: 圖片,css以及其它的公共資源

  • test: Karma & Mocha建立的單元測試文件

而後運行:

cd cropchat
npm install
npm run dev

這將會在你的瀏覽器打開localhost:8080

經過一個合適的Manifest讓它能夠進行安裝:

PWA的最大優勢之一就是容易安裝而且分享。讓咱們別再等待了!

爲了這樣作,咱們須要添加一個manifest.json文件而且在index.html文件中進行聲明。

pwa-manifest-webpack-plugin可以讓咱們在應用構建的時候生成文件:

`npm i pwa-manifest-webpack-plugin --save`

咱們接着可以經過編輯build/webpack.dev.conf.js 以及build/webpack.prod.conf.js來更新構建過程。

在頂部引入pwa-manifest-webpack-plugin

var path = require('path')
var manifestPlugin = require('pwa-manifest-webpack-plugin')

而且將它添加到插件:

plugins: [
  new manifestPlugin({
    name: 'CropChat',
    description: 'CropChat - Image Messenger Application',
    display: 'fullscreen',
    icon: {
      src: path.resolve('src/assets/logo.png'),
      sizes: [200]
    }
  }),

最後,在 index.html中聲明使用manifest.json

<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="manifest" href="./manifest.json">

你可能須要重啓你的應用:來這樣作,殺掉之間的進程而且再次運行:

`npm run dev`

就是它了!讓咱們在手機設備上安裝CropChat。有多種方式能夠從不一樣的手機設備上訪問localhost:8080。我最喜歡就是使用ngrok

Ngrok是一種服務,能夠遠程登陸您的本地環境,免費!

安裝它:

`npm install -G ngrok`

接着,運行:

`ngrok http 8080`

那應該會給你如下的輸出:

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

經過你的手機瀏覽ngrok的url http://5ef29506.ngrok.io 。你能夠在你的設備桌面添加!

Cropchat的源代碼可以在GitHub here上可以訪問。Git歷史符合教程的步驟:你能夠在下面的commit 5ff77fd3cd71a988fad9c187d57e87ea80d670f0種發現這個步驟的變化

想了解更多關於ngrok,你能夠閱讀Matthieu Auger的文章:Expose your local environment to the world with ngrok

建立視圖框架和句柄路由

既然咱們已經具備合適的基礎,那麼咱們打算開始構建CropChat的特性。CropChat具備三個視圖:

  • Home View: 展現一個貓的圖片列表

  • Detail View: 展現特定貓的圖片的細節(在Home View種點擊訪問)

  • Post View: 可以讓用戶上傳一個新的圖片

建立一個具備如下框架的src/component/HomeView.vue 視圖:

<template>
  <ul class="list">
  </ul>
</template>
<script>
export default {
}
</script>
<style scoped>
  .list {
    width: 100%;
    padding: 0;
  }
</style>

對於src/component/DetailView.vue視圖也是同樣的:

<template>
  <div class="card-image">
  </div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>

對於 src/component/PostView.vue也是同樣的:

<template>
  <div class="waiting">
    Not yet available
  </div>
</template>
<script>
export default {
}
</script>
<style scoped>
  .waiting {
    padding: 10px;
    color: #555;
  }
</style>

最終,更新路由文件 src/router/index.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
    }
  ]
})

也移除沒有使用的Hello.vue視圖。你應該你可以直接看到影響你手機app的變化(熱加載很棒,是否是?)

Git commit: 22ab9a2058dae8f7689b8635ff52d89652675aa6

安裝 Material Design Lite

不知道Material Design Lite?它是一個輕量級的而且容易在你的web應用上實現Material Design 的框架。

你能夠在這看到更多的文檔:Get MDL.io

更新依賴:

`npm install material-design-lite --save`

更新src/App.vue來導入MDL樣式而且加載MDL模塊:

<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>

Git commit: b726b40488132c400dd861bd397f61b15e81631e

爲你的單頁面應用提供一個導航欄:

更新主要組件src/App.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">
        <a class="mdl-navigation__link" href="/#/" @click="hideMenu">Home</a>
        <a class="mdl-navigation__link" href="/#/post" @click="hideMenu">Post a picture</a>
      </nav>
    </div>
    <main class="mdl-layout__content">
      <div class="page-content">
        <router-view></router-view>
      </div>
    </main>
  </div>
</template>

由於Material Design Lite不是特別爲單頁面應用構建的,所以在用戶點擊菜單連接的時候咱們須要隱藏burger菜單:

<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>

Git commit: 829d0af767a9f7cba13355296d9da79384d80099

傳播視圖並將你的應用帶到生活

咱們尚未鏈接一個後臺的服務。咱們如今打算暫時使用假數據。

建立一個src/data.js文件:

export default {
  pictures: [
    {
      'id': 0,
      'url': 'http://25.media.tumblr.com/tumblr_m40h4ksiUa1qbyxr0o1_400.gif',
      'comment': 'A cat game',
      'info': 'Posted by Kevin on Friday'
    },
    {
      'id': 1,
      'url': 'http://25.media.tumblr.com/tumblr_lhd7n9Qec01qgnva2o1_500.jpg',
      'comment': 'Tatoo & cat',
      'info': 'Posted by Charles on Tuesday'
    },
    {
      'id': 2,
      'url': 'http://24.media.tumblr.com/tumblr_m4j2atctRm1qejbiro1_1280.jpg',
      'comment': 'Santa cat',
      'info': 'Posted by Richard on Monday'
    },
    {
      'id': 3,
      'url': 'http://25.media.tumblr.com/tumblr_m3rmbwhVB51qhwmnpo1_1280.jpg',
      'comment': 'Mexico cat',
      'info': 'Posted by Richard on Monday'
    },
    {
      'id': 4,
      'url': 'http://24.media.tumblr.com/tumblr_mceknxs4Lo1qd477zo1_500.jpg',
      'comment': 'Curious cat',
      'info': 'Posted by Richard on Monday'
    }
  ]
}

HomeView.vue script部分中導入數據而且將圖片連接到對應的細節視圖:

<script>
import data from '../data'
export default {
  methods: {
    displayDetails (id) {
      this.$router.push({ name: 'detail', params: { id: id }})
 }
  },
  data () {
    return {
      'pictures': data.pictures
    }
  }
}
</script>

更新HomeView.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.$data.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>
    <a class="add-picture-button mdl-button mdl-js-button mdl-button--fab mdl-button--colored" href="/#/post">
      <i class="material-icons">add</i>
    </a>
  </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>

DetailView.vue進行一樣的操做:

<template>
  <div class="mdl-grid">
    <div class="mdl-cell mdl-cell--8-col">
      <div class="picture">
        <img :src="this.$data.pictures[$route.params.id].url" />
      </div>
      <div class="info">
        <span>{{ this.$data.pictures[$route.params.id].info }}</span>
      </div>
    </div>
    <div class="mdl-cell mdl-cell--4-col mdl-cell--8-col-tablet">
      <div class="comment">
        <span>{{ this.$data.pictures[$route.params.id].comment }}</span>
      </div>
      <div class="actions">
        <a class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" href="/#/post">
          ANSWER
        </a>
      </div>
    </div>
  </div>
</template>
<script>
import data from '../data'
export default {
  data () {
    return {
      'pictures': data.pictures
    }
  }
}
</script>
<style scoped>
  .picture > img {
    color: #fff;
    width:100%;
  }
  .info {
    text-align: right;
    padding: 5px;
    color: #555;
    font-size: 10px;
  }
  .comment {
    padding: 10px;
    color: #555;
  }
  .actions {
    text-align: center;
  }
</style>

Git commit: 39360f251da153c780cd148dc3cf234348bb1e87

關於'href'連接的使用:我推薦使用vuejs的組件可是在這我想使代碼儘量的簡單。

最後的結果

咱們完成了,CropChat完成啦!

源代碼可以在GitHub repository訪問: https://github.com/charlesBochet/vueJSPwa

總結

我但願我已經令你確信你可以利用VueJS和Webpack就能夠簡單地建立web應用。總結來講:

  • Vue-cli能夠在命令行種建立一個虛擬的VueJS + Webpack應用

  • 經過添加Manifest.json文件讓你的web應用可以安裝

  • 使用Vue-Router以及Material Design來建立一個相似於app用戶體驗的應用

然而,CropChat還依然不是一個漸進式web應用:讓咱們看一下PWA的需求清單:

一半的需求尚未知足。在下一個部分將會有其它的目標。未完待續!

譯者注:

安利幾個我以前翻譯過的其它的關於PWA的文章以及我本身的PWA小應用:

相關文章
相關標籤/搜索