基於VUE2.0的高仿餓了麼分析與總結

        首先,慕課網提供的視頻是基於vue1.0的規範寫的,所以有一些地方在vue2.0下不能正常運行。雖然課程補充文件也提供了vue2.0的代碼,但多多少少也有一些問題。參考他人寫的代碼以及官方文檔,修改了部份內容,整個項目可以流暢運行,不會報錯。javascript

        項目請移步個人github地址:https://github.com/gqbwoai2016/-VUE2.0-APP-。裏面內容還不是不少,之後會繼續豐富的。css

-------------------------------------------------------------------------------------------------------------------------------------------------html

廢話就這麼多,開始實質的內容吧:前端

        涉及的工做有:由商家模塊入手,進行需求分析,利用腳手架工具搭建,mock數據,架構設計,代碼實現,自測以及編譯打包。本項目注重代碼規範:架構設計,組建抽象,模塊拆分,代碼風格統一,JS變量命名規範及CSS代碼規範。vue

        所涉及的技術有:vue-cli腳手架工具;vue-resource與後端數據交互(相似AJAX);vue-router插件管理路由;第三方庫better-csroll;最大程度組件化(優勢:可維護性和複用性);localstorage;flex彈性佈局;java

        實現的功能node

•    Goods、Ratings、Seller組件視圖都可上下滾動
•    商品頁 點擊左側menu,右側list對應跳轉到相應位置
•    點擊list查看商品詳情頁,父子組件的通訊
•    評論內容夠能夠篩選查看
•    購物車組件,包括添加刪除商品及動效,購物控件與購物車組件之間非父子組件通訊,點擊購物車圖標,展現選擇的商品列表
•    商家實景圖片能夠左右滑動 
•    loaclStorage緩存商家信息(id、name)

        0、用vue-cli腳手架工具搭建項目結構,有如下結構webpack

項目名稱
 ->build          ------------------------------------------  webpack配置相關文件
 ->config         ------------------------------------------  webpack配置相關文件      
 ->node_modules   ------------------------------------------  依賴代碼庫
 ->src            ------------------------------------------  存放源碼
   ->common       ---- 通用的css和fonts
   ->components   ---- Vue 組件
   ->router       ---- vue-router相關配置(linkActiveClass,routes註冊組件路由)
   ->build        ---- webpack 的打包編譯配置文件
   ->config       ---- 一些配置項,好比咱們服務器訪問的端口配置等
   ->dist         ---- 項目通過 build 以後纔會生成
   ->prod.server.js----模擬的服務器配置,用來運行dist裏面的文件,在config/index.js中,build對象中
                        添加一條端口設置port:9000,
   ->App.vue      ---- 根組件,全部的子組件都將在這裏被引用
   ->index.html   ---- 整個項目的入口文件,將會引用咱們的根組件 App.vue
   ->main.js      ---- 入口文件的 js 邏輯,在 webpack 打包以後將被注入到 index.html 中
 ->static         ------------------------------------------  存放第三方靜態資源
 ->.babelrc.JSON  ------------------------------------------  babe的配製,用於從es6編譯至es5
 ->.editorconfig  ------------------------------------------  編譯器配置
 ->.eslintignore  ------------------------------------------  忽略語法檢查的目錄文件
 ->.eslintrc.js   ------------------------------------------  eslint的配置
 ->.gitignore     ------------------------------------------  git忽略的目錄或文件
 ->index.html     ------------------------------------------  入口文件,項目編譯過程當中會自動插入其中
 ->package.json   ------------------------------------------  配置文件,描述一個項目
 ->README.md      ------------------------------------------  項目描述文件

        全部項目文件放在src下,main.js爲入口文件,APP.vue爲整個頁面的vue,components存放組建文件,裏面有許多子目錄,子目錄中存放.vue文件及圖片等資源(知足組件化開發的就近維護)。common包含公共模塊資源(js,scss,fonts等)。        ios

  • Hot-reload Vue的熱更新,修改代碼以後無需手動刷新網頁,對前端開發來講很是方便
  • PostCss,不再用去管兼容性的問題了,只針對chrome這樣的現代瀏覽器寫css代碼,會自動編譯生成兼容多款瀏覽器的css代碼
  • ESlint,統一代碼風格,規避低級錯誤(在eslintrc.js文件裏面:rules是自定義的檢查規則,能夠覆蓋默認的檢查規則,例如要加分號,函數要加空格,這個跟代碼風格有關)。在.eslintrc.js裏的rules中寫自定義規則,如'space-before-function-paren': 0,函數定義是括號前空格取消。
  • Bable,ES2015出來已經有一段時間了,可是很多瀏覽器尚未兼容ES6,有了bable,放心使用ES6語法,它會自動轉義成ES5語法
  • SCSS,一款 CSS預處理器,編譯後成正常的CSS文件。爲CSS增長一些編程的特性

       一、 數據mockcss3

            mock就是作假數據,這樣能夠便於先後端分離開發,前端不須要等後端作好數據來開發或者測試驗證

            假數據data.json.實現:sell(項目總文件)->插入data.json,而後在build->webpack.dev.conf.js中寫數據接口,而後寫數據的路由。

        坑1,最新版的vue中dev-server.js被替換成了webpack.dev.conf.js,修改時注意

        二、reset

            sell->static->css->reset.css,寫好後給index.html用link標籤引用這個文件

        三、meta

            對其設置viewport以規範顯示大小,縮放等移動端視口初始化。

        四、header組件開發

            a)使用vue-router實現點擊切換;在這裏,當前模塊樣式在vue-router中有個默認屬性linkActiveClass,在main.js對其配置後,在APP.vue中經過<vue-link to:"/goods">建立點擊部分以及<router-view>建立展現部分;

            vue-router詳解:

                路由,其實就是指向的意思。咱們頁面中全部內容都是組件化的,咱們只要把路徑和組件對應起來就能夠了,而後在頁面中把組件渲染出來。    

                頁面實現:定義了兩個標籤<router-link> 和<router-view>來對應點擊和顯示部分。<router-link> 就是定義頁面中點擊的部分,<router-view> 定義顯示部分,就是點擊後,區配的內容顯示在什麼地方。因此 <router-link> 還有一個很是重要的屬性 to,定義點擊以後,要到哪裏去, 如:<router-link  to="/home">Home</router-link>   

                js中配置路由:首先定義route,由path,component組成,前者指路徑,後者指組件,以下所示,最後建立router管理路由。配置完成後,把router 實例注入到 vue 根實例中,就可使用路由了

const routes = [
  { path: '/home', component: Home },
  { path: '/about', component: About }
]
//管理路由
const router = new VueRouter({
      routes // routes: routes 的簡寫
})
//配置完成,注入到實例中
const app = new Vue({
  router
}).$mount('#app')//$mount爲手動掛載
//上面這個寫法也能夠改成
new Vue({
    el:"#box",
    router
});

                注意:當首次進入頁面的時候,頁面中並無顯示任何內容。這是由於首次進入頁面時,它的路徑是 '/',咱們並無給這個路徑作相應的配置。在這裏使用重定向解決問題。

const routes = [
    {
        path:"/home",
        component: home
    },
    {
        path: "/about",
        component: about
    },
    // 重定向
    {
        path: '/', 
        redirect: '/home' 
    }
]

            b)1像素實現:在APP端因爲dpr不一樣,展現會有問題,所以添加minxin.scss設置通用樣式後再經過base.scss使用@media 媒體查詢來分別設置不一樣dpr的scale控制樣式;

             window.devicePixelRatio是設備上物理像素和設備獨立像素(device-independent pixels (dips))的比例。公式表示就是:window.devicePixelRatio = 物理像素 / dips           

            非視網膜屏幕的iphone上等於1,在視網膜屏幕的iphone上,屏幕物理像素640像素,獨立像素仍是320像素,所以等於2。

            2x和3x圖是爲了適應不一樣dpr比例的,不一樣比例的顯示是不同的.2x就是普通的dpr爲1的屏幕使用的;  對於高清屏幕就是用3x,dpr爲2或者以上;  2x和3x就是尺寸的大小,2x的圖片比3x的小

           c)滿減圖標在export default中create()內使用

classMap=['discount'...]

建立,在標籤中經過

:class="classMap[seller.supports[0].type]"

引用。

            d)背景模糊:整個header組件中有模糊背景圖,可經過建立一個div而後絕對定位至相關位置,再用z-index=-1設置後,使用

filter:blur(15px)增長模糊效果。

           e)css sticky footers模塊:簡而言之就是底部有一個固定圖標,只有移動至底部區域才展現,不然不顯示的一種佈局。

                    父級 position:fixed,內容設 爲padding-bottom:64px,頁腳相對定位,margin-top:-64px,clear:both
爲了保證兼容性,父級要清除浮動           

        坑2:新舊版vue-router不一樣,舊版支持router.map(),新版則使用const router=new VueRouter()直接在裏面寫便可   

            g)flex佈局:

                    意爲"彈性佈局",用來爲盒狀模型提供最大的靈活性。設爲Flex佈局之後,子元素的floatclearvertical-align屬性將失效。如下六個屬性設置在容器上:

                    flex-direction  容器內項目的排列方向(默認橫向排列)。可選值爲row(默認)沿水平主軸由左向右排列、row-reverse沿水平主軸由右向左排列、column沿垂直主軸右上到下和column-reverse

                    flex-wrap  換行方式。nowrap(默認)不換行、wrap換行(第一行在上方)和wrap-reverse

                    flex-flow  以上兩個屬性的簡寫方式 .box {  flex-flow: <flex-direction> || <flex-wrap>;}將上述兩種方法的值用||鏈接便可

                    justify-content  主軸上的對齊方式。主軸到底是哪一個軸要看屬性flex-direction的設置了flex-start:在主軸上由左或者上開始排列;flex-end:在主軸上由右或者下開始排列;center:在主軸上居中排列;space-between:在主軸上左右兩端或者上下兩端開始排列;space-around:每一個項目兩側的間隔相等。

                    align-items  交叉軸上如何對齊(縱軸對齊方式)flex-start頂對齊| flex-end底對齊 | center居中 | baseline文字基準線 | stretch撐開整個盒子

                    align-content  定義了多根軸線的對齊方式。若是項目只有一根軸線,該屬性不起做用。

            設置在子元素的屬性也有六個:

                    order:項目排列順序,數值越小排名靠前,默認爲0;

                    flex-grow:項目放大比例,默認0;

                    flex-shrink:縮小比例,默認1;

                    flex-basis:計算主軸是否有多餘空間,默認auto,項目原本大小;

                    flex:是上面三個簡寫,默認0 1 auto;

                    align-self:容許單個項目有與其餘項目不同的對齊方式。

            f)自適應佈局

                    左側寬度固定,右側寬度自適應

// 左側固定width:80px,右側自適應
parent:
    display:flex;
child-left:
    flex:0 0 80px
child-right:
    flex:1

        五、goods模塊

            a)採用better-scroll實現滾動,在package.json代表版本,使用npm安裝。BScroll接受兩個參數,第一個是DOM對象,第二個是方式,在method中寫方法,須要設置 click:true,不然移動端滑動無效。

            注意:vue提供一種方法獲取DOM,在須要獲取地方添加ref="**",而後在js中經過this.$ref.**來引用;

                      DOM真正映射實在nextTick後,所以一些計算屬性應該寫在裏面,不然在頁面中無響應;

        六、seller組件

                a)商家實景圖片橫向滾動:

                    解決方案:每一個 li 要 display:inline-block,由於width不會自動撐開父級ul,因此須要將計算後的寬度賦值給ul的width,(每一張圖片的width+margin)*圖片數量-一個margin,由於最後一張圖片沒有margin,同時new BScroll裏面要設置scrollX: true, eventPassthrough: 'vertical', // 滾動方向橫向

                b)打開seller頁面,沒法滾動

                    問題分析:出現這種現象是由於better-scroll插件是嚴格基於DOM的,數據是採用異步傳輸的,頁面剛打開,DOM並無被渲染,因此,要確保DOM渲染了,才能使用 better-scroll;解決方案:用到mounted鉤子函數,同時必須搭配this.$nextTick()

                c)刷新後,沒法滾動

                    問題分析:出現這種狀況是由於mounted函數在整個生命週期中只會只行一次;解決方案:使用watch方法監控數據變化,並執行滾動函數 this._initScroll();this._initPicScroll();

                d)緩存數據

                    使用window.localstorage保存舍設置緩存信息,封裝在store.js中

                e)axios

                    在vue1.x的時候,vue的官方推薦HTTP請求工具是vue-resource,可是在vue2.0的時候將推薦工具改爲了axios。若是想像之前使用 vue-resource 那樣 this.$http.get 調用,要這樣定義:Vue.prototype.$http=axios;

                    經過 this.$http.get 來定義經過vue實例來發送get請求,而後經過then後面的回調函數將請求成功的數據接收,經過狀態碼來判斷是否成功以及複製給vue的數據對象。因爲這裏是用的mock數據(模擬後臺數據),因此用的模擬狀態碼。

const ERR_OK = 0;//表示沒有錯誤信息,即獲取數據成功
this.$http.get('/api/seller').then((response) => {
  response = response.data;
  if (response.errno === ERR_OK) {
    this.seller = Object.assign({}, this.seller, response.data);
  }
});

                vue-resource詳解:

                    vue-resource是Vue.js的一款插件,它能夠經過XMLHttpRequest或JSONP發起請求並處理響應。也就是說,$.ajax能作的事情,vue-resource插件同樣也能作到,並且vue-resource的API更爲簡潔。

                優勢:體積小;支持主流瀏覽器;支持PromiseAPI和URI Templates;支持攔截器。

                使用:

                    首先引入vue-resourec;而後發送請求便可

import VueResource from 'vue-resource';
Vue.use(VueResource);

this.$http.get('/someUrl',jsonData).then(function(response){  //jsonData是傳給後端的數據
    // 響應成功回調
}, function(response){
    // 響應錯誤回調
});

                   下面給個實例,從a.txt取數據

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8">
    <script src="http://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/vue.resource/1.0.3/vue-resource.min.js"></script>
    <script type="text/javascript">
        window.onload = function(){
            var vm = new Vue({
                el:'#box',
                data:{
                    msg:'Hello World!',
                },
                methods:{
                    get:function(){
                        //發送get請求
                        this.$http.get('a.txt').then(function(res){
                            alert(res.body);    
                        },function(){
                            console.log('請求失敗處理');
                        });
                    }
                }
            });
        }
    </script>
</head>
<body> 
    <div id="box">
        <input type="button" @click="get()" value="按鈕">
    </div>
</body>
</html>

                    最新版Vue推薦使用axio,兩個功能相似,首先也要安裝而後引入,最後再使用:(注:他的註冊不能使用Vue.use()方法)

import axios from "axios";
//註冊
Vue.prototype.$axios = axios;
//使用axios
created:function(){
  this.$axios.get("/seller",{"id":123}).then(res=>{
   console.log(res.data);
 });
}

                f)組件間通信               

                父傳子: props 

// 父組件
<v-header :seller="seller"></v-header>

// 子組件 header.vue
props: {
  seller: {
    type: Object
  }
}

                子傳父: $emit       若是是子組件想傳遞數據給父組件,須要派發自定義事件,使用 $emit 派發,父組件使用v-on接收監控(v-on能夠簡寫成@)

// 子組件 RatingSelect.vue,派發自定義事件isContent,將this.onlyContent數據傳給父級

this.$emit('isContent', this.onlyContent);
this.$emit('selRatings', this.selectType);

// 父組件 foodInfo.vue 在子組件的模板標籤裏,使用v-on監控isContent傳過來的數據

<v-ratingselect @selRatings="filterRatings" @isContent="iscontent"></v-ratingselect>

                非父子組件之間通訊: 1. 大型項目能夠用 Vue官方推薦的vuex;event bus: 利用一箇中間組件來做爲信息傳遞中介;

                g)better-scroll

                better-scroll是一個移動端滾動的解決方案,基於iscroll的重寫(多年沒人維護了,有不少bug;有些狀況是基於js實現的幀動畫,體驗差,bs基於css3等)

                使用:首先應有一個父容器div,固定高度且overflow:hidden,給它設置bscroll,子元素高度由內容撐開。

<template> 
  <div class="wrapper" ref="wrapper">//主div
   <ul class="content"> //子元素
    <li>...</li> 
    <li>...</li>
     ... 
   </ul>
  </div> 
</template> 
<script> 
 import BScroll from 'better-scroll'
  export default {
   mounted() {
    this.$nextTick(() => {
     this.scroll = new Bscroll(this.$refs.wrapper, {})
    })
   } 
  } 
</script>

***重要:在mounted鉤子函數裏,this.$nextTick的回調函數中初始化better-scroll。是由於wrapper的DOM在這個時候已經渲染了,能夠正確計算其高度。

                異步數據處理

<template>
  <div class="wrapper" ref="wrapper">
   <ul class="content">
    <li v-for="item in data">{{item}}</li>
   </ul>
  </div>
 </template>
 <script>
 import BScroll from 'better-scroll'
 export default {
  data() {
  return { data: [] } },
   created() {
    requestData().then((res) => { //requestData()是僞代碼,實際用axios活vue-resource
     this.data = res.data
     this.$nextTick(() => {
      this.scroll = new Bscroll(this.$refs.wrapper, {})
     })
    })
   }
  }
 </script>

                初始化bs須要在數據獲取以後。之因此放在created而不是mounted,是由於requestData是一個異步過程,拿到相應數據時,DOM已經渲染好,可是,數據改變後到DOM從新渲染仍然是異步過程,因此仍是要異步初始化bs(nextTick)。

                注意:在PC上,點擊事件會執行兩次。因爲better-scroll派發的事件有event_constructed:true屬性。能夠進行處理。

if (!event._constructed) {
          return;
 }

        七、sass

            Sass的學名叫「CSS預處理器」,就是在CSS的基礎上,引入了變量、嵌套、mixin(混合)、運算以及函數等功能,增長了代碼的靈活性,可讓咱們以更少的代碼實現一樣的效果,並且代碼的整潔度、可讀性更強。

            .scss是Sass3引入的新語法,基本寫法與CSS大體相同

            基本語法:

                a)變量。css屬性的值(1px,bold)均可替換爲變量。

$box-color: red;        //定義變量
ul{
    color: $box-color;      //引用
}
li{
    background-color: $box-color;       //引用
}

                b)嵌套

/***********************************************選擇器嵌套*******************************************/
div {
    h1 {
        color: #333;
    }
    p {
        margin-bottom: 1.4px;
        a {
            color: #999;
        }
        :hover{
            color: #888;
        }
    }
}

 /* 編譯後 */
div h1 { color: #333; }
div p { margin-bottom: 1.4px; }
div p a { color: #999; }
div p:hover{ color: #888; }

/***********************************************屬性嵌套*******************************************/
div {
    border: {
      style: solid;
      width: 1px;
      color: #ccc;
    }
}

//編譯後
div {
  border-style: solid;
  border-width: 1px;
  border-color: #ccc;
}

                c)繼承:使用選擇器的繼承,要使用關鍵詞@extend,後面緊跟須要繼承的選擇器。

.class1 {
    border: 1px solid #333;
}

.class2 {
    @extend .class1;
    background-color: #999;
}

//編譯後
.class1, .class2 {
  border: 1px solid #333;
}

.class2 {
  background-color: #999;
}

                d)Mixin混合器:使用@mixin聲明,經過@include minxin名稱調用

@mixin mixName {        
    float: left;
    margin-left: 10px;
}

div {
    @include mixName;
}

//編譯後:
div {
  float: left;
  margin-left: 10px;
}
/*帶參數的聲明及調用*/
@mixin left($value: 10px) {
    float: left;
    margin-left: $value;
}
div {
    @include left(66px);
}

//編譯後:
div {
  float: left;
  margin-left: 66px;
}

            e)顏色函數     

$box-color: red;
li{
 background-color: darken($box-color,30%);
}
//編譯後
li{
background-color: #660000;
}
//更多
lighten(#cc3,10%)//#d6d65c
grayscale(#cc3)//#808080
complement(#cc3)//#33c

            f)導入:@import "地址";   如@import "../../common/scss/mixin.scss";

        八、優化:

            每次切換模塊後,都會從新渲染DOM,解決方法爲給APP.vue下的<vue-router 加入keep-alive></vue-router>,組建的切換狀態就會保留。

        九、打包:

        使用npm run build打包文件,其執行了node build/build.js腳本,最終產生一個dist目錄,裏面包含css,js,index.html文件,打包後文件經過HTTP server啓動(sell總目錄下建立一個produ.server.js寫完代碼後使用node prode.server.js啓動)    

       技巧:(1).若是要使用new建立對象,因爲本項目使用了ESlint檢查代碼規範,所以會報錯,在這裏使用/*eslint-disable no-new*/來跳過校驗;

                 (2). 在build->webpack.base.conf.js中,module.exports下的resolve的alias下能夠定義路徑,就不用每次引用時加./表示當前路徑  等相似問題了;

                 (3).圖標與文字不齊,可設置vertical-align:top來對齊,還不行能夠對圖片設置paddingtop。

                 (4).手機測試網頁技巧:將localhost換成本身的ip,而後複製地址欄地址,進入草料二維碼,而後生成二維碼,而後用手機掃一掃就能夠查看了,前提是,你手機和電腦必須在同一個局域網

相關文章
相關標籤/搜索