1. 程式人生 > >Vue2.0高仿餓了麼核心模組&移動端Web App專案爬坑(一)

Vue2.0高仿餓了麼核心模組&移動端Web App專案爬坑(一)

原文https://www.cnblogs.com/ljq66/p/9980372.html

前言:學習Vue.js高仿餓了麼課程過程中,總結了這個Web App專案從準備到開發完畢自己覺得很重要的知識點。這一篇主要介紹:專案準備、頁面骨架開發、header元件開發。

專案github地址:https://github.com/66Web/ljq_eleme,歡迎Star。


 

App header

 

一、專案分析&學習目標

       當前最火的MVVM框架

  • Vue.js —— 輕量、簡潔、高效、資料驅動、元件化

      高仿上線外賣App標準來開發

  • 核心模組 —— 商家模組

      開發一個webApp的全流程

  1. 需求分析
  2. 腳手架工具
  3. 資料mock
  4. 架構設計
  5. 程式碼編寫
  6. 自測
  7. 編譯打包

      以線上生產環境的程式碼質量作標準

  • 程式碼開發及測試環節:
  1. UI標註
  2. 真實資料演示
  • 程式碼規範
  1. 架構設計
  2. 元件抽象
  3. 模組拆分
  4. 程式碼風格統一
  5. JS變數命名規範
  6. CSS程式碼規範

      功能技術分析

  • vue-resource:  和後端做資料互動
  • vue-router:  做前端路由,實現單頁應用
  • 第三方JS庫better-scroll:  列表滾動的實現
  • 最大程度元件化: 提高程式碼的複用
  • html5的localstorage:【收藏商家】功能—儲存在瀏覽器端
  • 細節:圖示字型的使用
  • 移動端1畫素邊框
  • css sticky footer佈局
  • flex 彈性佈局

      學習目標

  • 掌握Vue.js在實戰中的運用
  • 學會使用Vue.js【完整的】開發移動端App
  • 學會元件化、模組化的開發

      學習內容

  • Vue.js框架介紹
  • Vue-cli 腳手架 —— 搭建基本程式碼框架
  • vue-router 官方外掛 —— 管理路由
  • vue-resource 官方外掛 —— 和後端作Ajax通訊
  • Webpack 開源構建工具(把原始碼經過編譯生成瀏覽器可以識別和執行的程式碼)
  • es6 + eslint eslint —— es6程式碼風格檢查工具
  • 工程化 元件化 模組化
  • 移動端常用開發技巧:
  1. flex彈性佈局
  2. css stickyfooter
  3. 酷炫的互動設計
二、Vue.js介紹

      近年來前端開發趨勢

  • 舊瀏覽器逐漸淘汰,移動端需求增加
  • 前端互動越來越多,功能越來越複雜
  • 架構從傳統後臺MVC 向REST API+ 前端MV* 遷移

      (前者傳統MVC:更新資料會重新整理頁面 後者前端MV*: 向後端REST API非同步請求資料,區域性重新整理頁面)

        MV* —— MVC、MVP、MVVM

      MVVM框架

      View    ViewModel    Model

       檢視        通訊           資料

  • DOM 觀察者 Javascript物件
  1. 針對具有複雜互動邏輯的前端應用
  2. 提供基礎的架構抽象
  3. 通過Ajax資料持久化,保證前端使用者體驗
  • MVVM框架技術:vue.js、react.js、Angular.js

      對比Anglar  React

  • Vue.js更輕量,gzip後大小隻有 20K+
  • Vuejs更易上手,學習曲線平穩
  • 吸收兩家之長,借鑑了angular的指令和react的元件化

      vue.js 核心思想

  • 資料驅動
  • 元件化

      元件設計原則

  • 頁面上每個獨立的可視/可互動區域視為一個元件
  • 每個元件對應一個工程目錄,元件所需要的各種資源在這個目錄下【就近維護】
  • 頁面不過是元件的容器,元件可以巢狀自由組合形成完整的頁面
三、Vue-cli開啟Vue專案

      Vue-cli 是Vue的腳手架工具 —— 幫助寫好Vue基礎程式碼的工具

  1. 目錄結構
  2. 本地除錯
  3. 程式碼部署
  4. 熱載入
  5. 單元測試

      安裝使用

1

(sudo) npm install -g vue-cli  // sudo:mac環境下有關管理許可權的命令

1

vue init webpack my-project

      專案檔案

  • src資料夾:存放專案原始碼
  • bulld目錄+ config目錄:webpack配置相關
  • node_modules資料夾:npm install 安裝的依賴程式碼庫
  1. static—>.gitkeep: 當這個目錄為空時也可以將它提交到git倉庫中
  2. babelrc : babel的一些配置,es6語法的轉換
  3. .editorconfig: 編輯器的配置
  4. .eslintignore: 忽略語法檢查的目錄檔案,一般忽略build目錄和node_modules目錄
  5. .eslintrc.js: eslint的配置檔案
  6. gitignore: 上傳git倉庫要忽略的一些檔案的配置
  7. index.html: 入口html檔案,要使用的css和js檔案會在編譯過程中自動插入
  8. package.json:整個專案的配置檔案,一般用來描述專案 ↓

        →  scripts:  配置一些需要執行的命令
        →  dependencies:開發環境中的依賴
        →  devdependencies: 編譯過程中的依賴

      專案執行

1

npm run dev 

  • src開發目錄下:
  1. main.js —— 專案入口檔案
  2. App.vue —— 主頁面元件
  • vue語法糖:  export default { } 一個物件——可以定義一個元件
【小知識點】sublime自動格式化 —— Command+option+L 或 Control+alt+L

 

  • 在父元件中使用子元件,如Hello.vue:
  1. 引用 
    import Hello from './compoments/Hello'
  2. 註冊
    export default{
          components: {
               Hello   //es6語法  相當於 'Hello': Hello
          }
    }
  3. 使用標籤
    <hello><hello>

      開發時的Webpack配置與編譯

  • build->dev-server.js 或 Webpack.dev.conf.js
  • webpack.base.conf.js : 配置各種檔案的Loader

      →  配置預設識別的路徑

複製程式碼

resolve: {
     extensions: ['.js', '.vue', '.json'],
      alias: {
           'vue$': 'vue/dist/vue.esm.js',
           '@': resolve('src'),
       }
}

複製程式碼

四、準備工作

      圖示字體制作 

  • 線上製作網站:https://icomoon.io/app/#/select
  • 將自己的SVG圖示匯入,輸出自己的圖示字型檔案
  • Import Icons → Generate Fonts → preferences修改名稱 → Download
  • 使用:icon.css和fonts資料夾下所有檔案

      專案目錄設計

  • src->common目錄下:專案公用檔案 js、style、fonts

【css的stylus語法】

  • 刪掉分號和大括號, &表示父元素,冒號也可以省略
  • 檔案字尾為styl,style中新增lang="stylus"
  • 需要安裝:

1

npm install stylus stylus-loader --save-dev


 

 

  • resource目錄下:專案圖片檔案——可以刪掉無用的assets目錄,但需要修改引用到的地方
  • components目錄下:佈局、業務功能等分模組管理元件,如header目錄,footer目錄
  • static->css目錄下:reset.css 標籤預設樣式
  • 在  index.html 中引入:
<link rel="stylesheet" type="text/css" href="static/css/reset.css">

      前後端分離

  • Vue SPA —— 前端通過 vue-resource  Ajax從後端獲取資料
  • 前端最重要的任務:mock資料(後臺資料模擬) data.json
{
    "seller":{} //商家相關欄位
    "goods":{} //商品相關欄位
    "rattings":{} //評論相關欄位
} 

      webpack.dev.conf.js中配置

  • 使用 express框架  開啟一個node server,用 express.Router 編寫這些介面請求
  1. 首先:在 const portfinder = require(‘portfinder’) 後新增

    複製程式碼

    const express = require('express')//開啟一個node server
    const app = express() //定義一個物件,包含express返回的資料
    
    var appData = require('../data.json') //定義一個物件引入data資料
    var seller = appData.seller;
    var goods = appData.goods;
    var ratings = appData.ratings;
    
    app.use('/api', apiRoutes); //呼叫app物件

    複製程式碼

  2. 然後:找到 devserver{}, 在裡面新增

    複製程式碼

    before(app) {
         app.get('/api/seller', (req, res) => {
                res.json({
                      errno: 0, //錯誤碼:實際上是業務方根據業務自己定的
                      data: seller
                }) //介面返回json資料,上面配置的資料seller就賦值給data請求後呼叫
         }),
         app.get('/api/goods', (req, res) => {
                res.json({
                      errno: 0,
                      data: goods
                })
         }),
         app.get('/api/ratings', (req, res) => {
               res.json({
                      errno: 0,
                      data: ratings
                })
         })
    }

    複製程式碼

  • 注意:每次配置完 express 之後都需要重新啟動

      檢視json資料

  • 在Google位址列中輸入:localhost:8080/api/seller 
  • 依賴Google的jsonview外掛 —— 安裝 使資料格式化

【Google安裝第三方外掛】

  • 開啟https://github.com ;
  • 搜尋 jsonView 連結:https://github.com/search?utf8=%E2%9C%93&q=jsonview;
  • 選擇需要的外掛(我下載的是這個gildas-lormeau/JSONView-for-Chrome); 
  • 點選【Download Zip】,外掛下載完成,解壓縮到相應目錄(D:\Download\JSONView-for-Chrome-master); 
  • 安裝,開啟chrome - 擴充套件程式 (位址列輸入chrome://extensions/); 
  • 右上角,選中開發模式; 
  • 點選”載入正在開發的擴充套件程式…” 
  • 選擇外掛目錄(D:\Download\JSONView-for-Chrome-master\WebContent); 
  • 安裝完成,重新載入 (Ctrl+R)。 
  • 測試地址:http://jsonview.com/example.json

—— 轉載自【小白白打醬油部落格

 

五、頁面骨架開發

      移動端視口

  • index.html   中通過meta設定視口可被縮放,初試寬高設定
<meta name="viewport" 
content="width=device-width,initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">

      App.vue 中把頁面拆為三個區塊

<div id="app">
     <div class="header">header</div>
     <div class="tab">tab</div>
     <div class="content">content</div>
</div>

    然後,分別抽成一個元件,引用 —— 所有元件自定義標籤名不可與html本身標籤重合 'v-header': header 

      移動端經典佈局  flex 

<div class="tab">
      <div class="tab-item">商品</div>
       <div class="tab-item">評論</div>
       <div class="tab-item">商家</div>
</div>

複製程式碼

.tab
   display: flex
   width: 100%
   height: 40px
   line-height: 40px
   .tab-item
         flex:1
         text-align: center

複製程式碼

      vueRouter

  1. Vue2.0 使用 <router-link> 進行【導航】
    <router-link :to="{ path: '/goods' }">商品</router-link>
  2. 【路由外鏈 】<router-view> —— 單頁面切換的內容頁,替換content div區塊
    <router-view></router-view>
  3.  main.js 中設定單頁面應用路由的【掛載元件】—— 預設App.vue 也可以自定義元件如layout.vue

    複製程式碼

    /* eslint-disable no-new */
    new Vue({
        el: '#app',
        router,
        components: { App },
        template: '<App/>' 
    })

    複製程式碼

  4. 配置【路由map】:router->index.js

    複製程式碼

    export default new Router({
           mode: 'history',
           routes: [
                          {
                             path: '/',
                             redirect: '/goods',//預設頁面重定向
                           },
                          {
                             path: '/goods',
                             component: goods
                           },
                          {
                             path: '/ratings',
                             component: ratings
                          },
                       ]
    })

    複製程式碼

  5. 【點選】高亮顯示   a.router-link-active 

    複製程式碼

    #app .tab .tab-item>a{
         display: block;
         font-size: 14px;
         color: rgb(77, 85, 93); 
    }
    
    #app .tab .tab-item>a.router-link-active{
         color: rgb(240, 20, 20)
    }

    複製程式碼

      1畫素border實現 

  • 錯誤做法:直接給tab加1畫素邊框  X
    .tab{
         border-bottom: 1px solid rgba(7,17,27,0.1)
    }
  • 問題是:這段程式碼在PC端顯示,是1畫素,但是在手機端顯示,就不是1畫素。
  • 因為手機端有一個DPR的概念:它的物理畫素是裝置畫素的兩倍。所以iPhone6上面可能就是一個2畫素的邊框

【PC開發中用手機實時預覽的小技巧】

  • 新開一個gitbash,mac環境下輸入命令:ifconfig, windows下輸入:ipconfig
  • 獲知本機IP地址192.168.1.1,替換掉localhost(表示本機)
  • 將地址http://192.168.1.1:8080/goods#/輸入到草料二維碼網站中,生成二維碼,用手機掃描,即可檢視
  • 必須與PC使用的是同一區域網

 

  • 正確做法:給 tab 加一個偽類:after , 讓它是一條1畫素的線,然後在DBR為2或3的手機端縮放  √
    <div class="tab border-1px">
  1. 定義mixin.styl:  通過css前處理器的函式方法,實現偽類線

    複製程式碼

    border-1px($color)
          position: relative
          &:before
              display: block
              position: absolute
              left:0
              top: 0
              width: 100%
              border-bottom: 1px solid $color
              content: ''
          &:after
              display: block
              position: absolute
              left:0
              bottom: 0
              width: 100%
              border-top: 1px solid $color
              content: ''

    複製程式碼

  2. 定義base.styl: 實現不同DBR的移動端的縮放

    複製程式碼

    @media(-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5)//DPR為1.5的縮放0.7倍
    .border-1px
       &:before
         -webkit-transform: scaleY(0.7)
         transform:scaleY(0.7)
       &:after
         -webkit-transform: scaleY(0.7)
         transform:scaleY(0.7)
    
    @media(-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2)//DPR為2的縮放0.5倍
    .border-1px
       &:before
         -webkit-transform: scaleY(0.5)
         transform:scaleY(0.5)
       &:after
         -webkit-transform: scaleY(0.5)
         transform:scaleY(0.5)

    複製程式碼

  3. 定義index.styl: 引用所有styl檔案,最後在 main.js 中全域性引用
    @import"./mixin"   //import後無空格
    @import"./icon"
    @import"./base"
    main.js: import '@/common/stylus/index.styl'   //import後有空格
六、header元件開發

      vue-resource

  1. 安裝 vue-resource

    1

    npm install vue-resource --save

    注意:每次install完外掛等之後需要重新啟動專案

  2. main.js 檔案中:
    import VueResource from 'vue-resource'
    Vue.use(VueResource)

    之後就可以在專案任何地方:使用 this.$http 命令

  3. App.vue 元件中:
  • export module{} 外:
    const ERR_OK = 0;  //定義常量,增強程式可讀性
  • export module{} 內:
    data() {
        return {
            seller:{}  //維護資料 seller
        }
    }
  • 非同步請求資料,返回的是Promise物件

    複製程式碼

    created: function () {
         this.$http.get('/api/seller')    //傳送get請求, 
        .then(function(res){              //.then方法 請求完成後呼叫
                                          //第一個函式是請求成功後方法
       }, function (err) {                //第二個函式是請求失敗後方法
    
       })
    }

    複製程式碼

    使用ES6 箭頭函式:箭頭函式前後必須有空格

    複製程式碼

    created: function () {
         this.$http.get('/api/seller')
         .then((res) => { 
              res = res.body //拿到response返回的promise物件的body(Data Object)
               if (res.errno === ERR_OK) {
                        this.seller = res.data;
                        //console.log(this.seller)
               } 
          }, (err) => { 
    
         })
    }

    複製程式碼

     外部元件

  • 父元件  App.vue 中 <header> 元件標籤中用v-bind繫結seller屬性,傳給子元件seller資料
    <v-header :seller="seller"></v-header>
  • 子元件 header.vue 中通過 props屬性 獲取父元件傳來的seller資料
    props: {
       seller: {
           type: Object
       }
    }
  • 模板對應位置 顯示 對應seller.xxx 子資料
  1. <img>標籤: 使用seller資料圖片地址,v-bind繫結src屬性
     :src="seller.avatar"
  2. 文字內容: 雙向資料繫結顯示seller資料
    {{seller.name}}
  3. 如果要獲取的是 seller資料物件的  子物件陣列的  某一項:因為非同步獲取資料,子物件可能為undefined,需要先 v-if 判斷是否存在
    <div class="support" v-if="seller.supports">
          <span class="icon" :class="this.classMap[seller.supports[0].type]"></span>
          <span class="text">{{seller.supports[0].description}}</span>
    </div>
  4. 定義 classMap陣列,通過獲取seller資料中的索引值,應用對應索引的class
    created (){
         this.classMap = ['decrease','descount','guarantee','invoice','special']
    }
    <span class="icon" :class="this.classMap[seller.supports[0].type]"></span>
  5. mixin.styl 檔案中偽函式:實現圖片在不同DPR下引用不同的圖片路徑
    bg-image($url)
         background-image: url($url+"@2x.png")
         @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3)
               background-image: url($url+"@3x.png")
  • 公告內容 —— 文字【省略號效果】
    white-space: nowrap
    overflow: hidden
    text-overflow: ellipsis
  • 背景圖片【模糊濾鏡效果】

    複製程式碼

    .background
        position: absolute
        top: 0
        left: 0
        width: 100%
        height: 100%
        z-index: -1
        filter: blur(10px)   

    複製程式碼

      詳情彈層頁

  • 實現彈出層
  1. v-show指令 ——  控制彈出層的顯示/隱藏
    <div class="detail" v-show="detailShow"></div>
    data () {
         return {
              detailShow: false  //通過改變資料detailShow 的true/false,控制元素的顯示/隱藏
         }
    }
  2. @click —— 觸發點選事件,執行顯示函式
    <div class="bulletin-wrapper" @click="showDetail>
    methods: {
        showDetail () {
             this.detailShow = true;
        }
    }

【Css Sticky footers佈局】

  • Sticky footers設計:
  1. 如果頁面內容不夠長的時候,頁面塊貼上在視窗底部;
  2. 如果內容足夠長時,頁面塊會被內容向下推送(區別於fixed)
  • 相對複雜但相容性最好的一個方案:
  1. 套路佈局

    1

    2

    3

    4

    5

    6

    7

    8

    <div class="detail" v-show="detailShow">

            <div class="detail-wrapper clearfix"> //外層wrapper,min-height: 100%

                    <div class="detail-main"></div> //內容層 padding-bottom: 64px

            </div>

            <div class="detail-close">

                   <i class="icon-close"></i> //要適應內容顯示的關閉按鈕 margin-top: -64px

            </div>

    </div>

  2. 關鍵:padding-bottom撐開一個高度,為關閉按鈕留下位置

    1

    2

    3

    .detail-main

         margin-top64px

         padding-bottom64px  

  3. 樣式:

    + View Code

       Star元件抽象 

  • 目標:為了增強擴充套件性,使足夠靈活
  • 思路:
  1. v-for ——  根據分數 遍歷itemClasses  顯示星星樣式

    複製程式碼

    <div class="star" :class="starType">
             <span v-for="itemClass in itemClasses" 
                   :key="itemClass.value" 
                   :class="itemClass" 
                   class="star-item">
             </span>
    </div>

    複製程式碼

  2. props —— 從父元件接收兩個引數:size尺寸,score分數

    複製程式碼

     props:{
           size: {
               type: Number
           },
           score: {
               type: Number
           }
    }

    複製程式碼

  3. :class  —— 繫結動態class,  在不同的呼叫地方, 可以設定不同的樣式

     View Code

  4. computed —— 根據size  計算出動態的class;根據score  push對應個數的全亮星星class;判斷如果有半分或不足5分的,push進半星class和灰色星class;根據陣列中對應的class顯示對應的星星圖片

    複製程式碼

    const LENGTH = 5;
    const CLS_ON = 'on';
    const CLS_HALF = 'half';
    const CLS_OFF = 'off';
    
    computed: {
            starType() {
                return 'star-' + this.size;  //根據size  計算出動態的class
            },
            itemClasses() {
                let result = [];
                let score = Math.floor(this.score*2)/2;
                let hasDecimal = score % 1 !== 0;
                let integar = Math.floor(score);
    
                for(let i=0; i<integar; i++){
                    result.push(CLS_ON)  //根據score 在itemClasses中push進對應個數的全亮星星class
                }
                if(hasDecimal) {
                    result.push(CLS_HALF);//判斷如果有半分或不足5分的,push進半星class和灰色星class
                }
                while (result.length < LENGTH) {
                    result.push(CLS_OFF)
                }
    
                return result; //根據itemClasses中對應的class顯示對應的星星圖片
            }
        }

    複製程式碼

  • 樣式: 除了通用樣式,還有根據不同size計算出的全部class的樣式

       小標題自適應線

  • 避免:寫死百分比,這樣寬螢幕會間隔很大,窄螢幕間隔會幾乎看不到
  • flex佈局:
<div class="title">
       <div class="line"></div>
       <div class="text">優惠資訊</div>
       <div class="line"></div>
</div>

複製程式碼

.title
    display: flex
    width: 80%
    margin: 30px auto 24px auto
    .line
         flex: 1
         position: relative
         top: -6px
         border-bottom: 1px solid rgba(255, 255, 255, 0.2)
    .text
         padding: 0 12px
         font-size: 14px

複製程式碼

【Postcss工具】

  • vue-loader 編譯vue檔案時,會使用Postcss工具,自動為有相容性問題的css屬性加上瀏覽器字首
  • Postcss是根據can i use官網去寫程式碼,基本不會出現相容性問題 
  • Can I Use 是一個檢測瀏覽器對JS、HTML5、CSS、SVG或者其他Web前端相關特性支援程度的列表
  • 可以檢測的瀏覽器包括桌面和移動版的主流瀏覽器:IE, Firefox, Chrome, Safari和 Opera等

 

      過渡動畫元件 transition

<transition name="fade">
        <div class="detail">
</transition>

複製程式碼

.detail
    opacity: 1
    background: rgba(7, 17, 27, 0.8) 
    &.fade-enter-active, &.fade-leave-active
           transition: all 0.5s ease
    &.fade-enter, &.fade-leave-active
           opacity: 0
           background: rgba(7, 17, 27, 0)

複製程式碼

      iPhone手機背景模糊效果

backdrop-filter: blur(10px)  // PC端和其它手機看不出效果