起步css
header 組件需完成:html
1.頭部商家信息的展現前端
2.完成公告部分vue
3.彈出層的實現webpack
在這個過程當中,很重要的一步是經過異步請求後端數據接口,接收並渲染相關商家數據。ios
如何實現:使用第三方插件 vue-resource,經過在父組件(App.vue)發送 Ajax 請求得到後端數據,而後經過 header 的 Prop 屬性將數據傳遞給 header 組件。css3
知識準備git
vue-resource 簡介github
1)vue-resource 定位
vue-resource 是 Vue.js 的一款插件,它能夠經過 XMLHttpRequest 或 JSONP 發起請求並處理響應。也就是說,它能夠用於處理先後端數據請求、數據交互,達到與 $.ajax 同等功能。web
2)vue-resource 特色:
2-1)支持 Promise API 和 URI Templates
Promise是ES6的特性,Promise的中文含義爲「先知」,Promise對象用於異步計算。URI Templates表示URI模板,有些相似於ASP.NET MVC的路由模板。
2-2)支持攔截器(https://github.com/pagekit/vue-resource/blob/develop/docs/http.md#interceptors)
攔截器是全局的,攔截器能夠在請求發送前和發送請求後作一些處理。攔截器在一些場景下會很是有用,好比請求發送前在headers中設置access_token,或者在請求失敗時,提供共通的處理方式。
2-3)支持主流的瀏覽器:和Vue.js同樣,vue-resource除了不支持IE 9如下的瀏覽器,其餘主流的瀏覽器都支持。
2-4)支持 vue 1.0版本 及 vue 2.0版本
2-5)體積小:vue-resource很是小巧,在壓縮之後只有大約14KB,服務端啓用gzip壓縮後只有5.3KB大小,這遠比jQuery的體積要小得多。
PS:
1.支持 promise api 的形式,就是經過同步鏈式的寫法來支持異步的操做。
2.關於 vue-resource 更多知識點:http://www.cnblogs.com/keepfool/p/5657065.html
1.什麼是組件?
組件 (Component) 是 Vue.js 最強大的功能之一。組件能夠擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。在有些狀況下,組件也能夠表現爲用 is 特性進行了擴展的原生 HTML 元素。
全部的 Vue 組件同時也都是 Vue 的實例,因此可接受相同的選項對象 (除了一些根級特有的選項) 並提供相同的生命週期鉤子。
2.使用組件
2)組件組合
組件設計初衷就是要配合使用的,最多見的就是造成父子組件的關係:組件 A 在它的模板中使用了組件 B。它們之間必然須要相互通訊:父組件可能要給子組件下發數據,子組件則可能要將它內部發生的事情告知父組件。然而,經過一個良好定義的接口來儘量將父子組件解耦也是很重要的。這保證了每一個組件的代碼能夠在相對隔離的環境中書寫和理解,從而提升了其可維護性和複用性。
在 Vue 中,父子組件的關係能夠總結爲 prop 向下傳遞,事件向上傳遞。父組件經過 prop 給子組件下發數據,子組件經過事件給父組件發送消息。看看它們是怎麼工做的。
3.Prop
組件實例的做用域是孤立的。這意味着不能 (也不該該) 在子組件的模板內直接引用父組件的數據。父組件的數據須要經過 prop 才能下發到子組件中。
子組件要顯式地用 props 選項聲明它預期的數據:
Vue.component('child', {
// 聲明 props
props: ['message'], // 就像 data 同樣,prop 也能夠在模板中使用 // 一樣也能夠在 vm 實例中經過 this.message 來使用 template: '<span>{{ message }}</span>' })
而後咱們能夠這樣向它傳入一個普通字符串:
<child message="hello!"></child>
結果:
hello!
2)動態 Prop
與綁定到任何普通的 HTML 特性相相似,咱們能夠用 v-bind 來動態地將 prop 綁定到父組件的數據。每當父組件的數據變化時,該變化也會傳導給子組件:
<div> <input v-model="parentMsg"> <br> <child v-bind:my-message="parentMsg"></child> </div> <!-- v-bind 的縮寫語法 --> <!-- <child :my-message="parentMsg"></child> -->
若是你想把一個對象的全部屬性做爲 prop 進行傳遞,可使用不帶任何參數的 v-bind (即用 v-bind 而不是 v-bind:prop-name)。例如,已知一個 todo 對象:
todo: {
text: 'Learn Vue', isComplete: false }
而後:
<todo-item v-bind="todo"></todo-item>
將等價於:
<todo-item v-bind:text="todo.text" v-bind:is-complete="todo.isComplete" ></todo-item>
每一個 Vue 實例在被建立時都要通過一系列的初始化過程——例如,須要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程當中也會運行一些叫作生命週期鉤子的函數,這給了用戶在不一樣階段添加本身的代碼的機會。
好比 created 鉤子能夠用來在一個實例被建立以後執行代碼:
new Vue({
data: {
a: 1 }, created: function () { // `this` 指向 vm 實例 console.log('a is: ' + this.a) } }) // => "a is: 1"
也有一些其它的鉤子,在實例生命週期的不一樣階段被調用,如 mounted、updated 和 destroyed。生命週期鉤子的 this 上下文指向調用它的 Vue 實例。
開發
header組件內容
以下圖,包括外層組件以及彈出層。在對這兩塊進行詳細設置以前,須要先經過異步請求後端數據接口,接收並渲染相關商家數據。
因此本節有如下三部分知識點:
1.獲取數據
2.外層組件
3.彈出層
PS:本節全部代碼在文章底部。
1、獲取數據
獲取後臺商家數據,得到 seller對象數據
1.安裝第三方插件 vue-resource,在 main.js 中引用而且註冊該插件。
安裝方法:npm install vue-resource --save
// 引用
import Vueresource from 'vue-resource';
// 全局註冊
Vue.use(Vueresource);
2.在 App.vue 中定義一個 seller 對象:使用 data() 方法,在函數內返回一個空對象 seller。
Q:爲何是用 data() 方法而不是 data() 對象?
A:由於在 vue.js 中,組件是能夠被複用的,也就是說,若是使用對象而不是函數的話,某一組件的修改會影響另一個組件,因此這裏定義的是方法而非函數。更多知識可看官網:data
必須是函數 。
3.在 APP.vue 中發送 ajax 請求獲取後臺商家數據(seller 對象),而後塞給上一步驟的空對象就能夠了。
1)何時去發送這樣一個 ajax 請求?vue實例化的時候都有一個生命週期,其中有個鉤子叫 created() ,vue實例被生成後就調用這個函數。通常能夠在created函數中調用ajax獲取頁面初始化所需的數據。一個vue實例被生成後還要綁定到某個html元素上,以後還要進行編譯,而後再插入到document中。每個階段都會有一個鉤子函數,方便開發者在不一樣階段處理不一樣邏輯。
2)在 created 裏面經過 get() 方法去獲取數據,獲取地址是「 /api/seller 」,請求發送之後,調用 then() 方法。then() 方法中的第一個參數是成功的回調,第二個參數是失敗的回調。由於這裏是模擬後臺數據,因此必定是成功的,因此只寫成功的回調方法。response 是一個方法,返回的內容不是json對象,而是屬性。看 文檔 能夠知道,body參數返回的數據類型是 object 類型,正是咱們所須要的。
3)接着判斷 errno 是否爲 0,爲 0 表示正常,而後返回數據。關於 errno 的設置在 webpack.dev.conf.js 中能夠找到。這裏定義一個常量 ERR_OK 爲 0,是爲了閱讀代碼時更加清晰,知道 0 表明什麼,無需通篇查找 errno = 0 的含義,代碼更爲友好。
1 const ERR_OK = 0; 2 // 註冊 3 export default { 4 created() { 5 this.$http.get('/api/seller').then(response => { 6 // get body data 7 response = response.body; 8 console.log(response); 9 if (response.errno === ERR_OK) { 10 this.seller = response.data; 11 console.log(response.data); 12 } 13 }); 14 } 15 ... 16 };
PS:在 network 能夠看到有 seller 的數據,在 console 能夠看到返回的數據是 object 類型,同時也能夠看到這些數據加上了不少 get 和 set 方法。這是 vue 自動給這些字段添加的方法,用於監聽這些字段的行爲。若是對象某些值被修改時,這些變化就能被監聽到,而且及時映射到 DOM 上。關於 get 和 set 方法,在導語1——vue簡單介紹的數據響應原理知識點處有說起到。
接下來進行外層組件和彈出層的設計。這兩部分都須要動態獲取數據,須要將 seller 對象傳遞給 header 組件,讓 header 組件將其渲染出來。能夠經過 v-bind: 將其進行數據綁定,v-bind: 縮寫爲 : 。
先看一下代碼框架:
content-wrapper:外層組件中的上層部分
bulletin-wrapper:外層組件中的公告欄部分
background:header 組件的背景層
detail:彈出層
2、外層組件
下圖能夠看出,外層組件大致分爲上下兩個部分:上層又可分爲左右兩部分,左邊是圖片,右邊是商家相關信息;下層是公告欄相關信息。除此以外,還有一個背景層。
1.上層部分
1 <div class="content-wrapper"> 2 <div class="avatar"> 3 <img :src="seller.avatar" alt="" width="64" height="64"> 4 </div> 5 <div class="content"> 6 <div class="title"> 7 <span class="brand"></span> 8 <span class="name">{{seller.name}}</span> 9 </div> 10 <div class="description"> 11 {{seller.description}}/{{seller.deliveryTime}}分鐘送達 12 </div> 13 <div class="support" v-if="seller.supports"> 14 <span class="icon" :class="classMap[seller.supports[0].type]"></span> 15 <span class="text">{{seller.supports[0].description}}</span> 16 </div> 17 </div> 18 <div v-if="seller.supports" class="support-count" v-on:click="showDetail"> 19 <span class="count">{{seller.supports.length}}個</span> 20 <i class="icon-keyboard_arrow_right"></i> 21 </div> 22 </div>
1)Q:引用頭像圖片地址,是 :src 而不是 src的緣由
A:selller.avatar 一開始是不存在的,由於 seller 是空對象,其數據是經過 ajax 請求異步獲取數據而後填充得來的。因此若是直接用 src ,編譯就會直接解析 src,此時的 seller 仍是是空的,天然就找不到 selller.avatar,就會報錯。
2)Q:爲何要用 v-if 判斷存不存在 supports ?
A:由於這是由數據決定的,有可能該數組不存在。
3)Q:不加 v-if 會怎麼樣?
A:在這裏的話,不加 v-if 也能正確顯示信息。可是控制檯會報錯。這是由於在 App.vue 的 html 代碼中,咱們將 seller 對象傳遞給了 header 組件,而在下邊 JavaScript 代碼中, seller 對象一開始被設置爲空對象,其數據是經過 ajax 請求異步獲取數據而後填充得來的。因此此時 header 組件接收的只是一個空的 seller 對象,原本就是 undefined 。編譯器此時解析的話,天然找不到 seller 對象的相關數據,天然就報錯了。
4)support 設置
如圖所示,這一塊的設置須要根據後端返回的數據,進行判斷後動態的顯示。由於有可能今天是"在線支付滿28減5"排第一位,明天是"單人精彩套餐"排第一位,程序須要根據數據準確顯示出第一位的內容。
問題:data.json 的 supports 數組只有type 和 description 屬性,怎麼才能正確取得圖片呢?
設計思路:每個 type 對應到前端中不一樣的 class,而後給不一樣的 class 添加不一樣的圖片就能夠了。因此能夠給 .support 動態綁定一個class,即根據後端返回的 type(第一位),肯定到底綁定哪個 class(例如減對應的 class 爲 decrease),而後將該 class 傳遞給動態綁定的class(:class),這樣就能正確顯示。
如何實現:
第一步:在組件 created() 中,定義一個 classMap,將 type 與 class 進行綁定(在 data.json 文件中,supports 數組中 type 順序:減,折,套餐,發票,保障,則對應class名稱:decrease,discount,special,invoice,guarantee);
第二步:給不一樣的 class 設置不一樣的圖片;
第三步:將數組 classMap 綁定到 class 上,即 :class="classMap[seller.supports[0].type]。
以下圖,html 中的 classMap 能夠訪問到this.classMap,進而訪問到數組裏的 class,每個 class 又有真正對應的值。這樣就能正確顯示出圖片。
檢驗成果:能夠在data.json中將type=2的那一塊數據調到頂端,從新編譯刷新能夠發現網頁的數據發生了變化。
5)圖標字體的使用:<i class="icon-keyboard_arrow_right"></i>
在 icon.styl 文件中能夠找到類名。
6)浮層效果的入口:絕對定位,且定位在右下角,這個定位是相對於content-wrapper
7)CSS設置
7-1)在 base.styl 文件中,定義通用樣式。
7-2)左右兩塊區域是並排,能夠直接設置爲 inline-block。
7-3)左右兩塊區域中間有空格,這是由於空白字符(換行符、空格符等)的影響,爲消除該影響,能夠設置 font-size 爲 0。
7-4)根據不一樣 dpr 進行不一樣圖片的顯示,在 mixin.styl 中設置該函數。
1 bg-image($url) 2 // 正常狀況下 3 background-image url($url + "@2x.png") 4 // 最小 dpr=3 的狀況下 5 @madia (-webkit-min-device-pixel-ratio 3) (-min-device-pixel-ratio 3) 6 background-image url($url + "@3x.png")
7-5)不能像加載 js 庫同樣,進行省略別名的使用。由於那是針對 import js 庫,css 這邊是不能夠的,因此這裏只能寫相對路徑。
@import '../../common/stylus/mixin'
7-6)Chrome 瀏覽器默認最小字體是 12px,因此設置爲 10px 沒辦法生效,能夠在手機端看效果。
8)圖片地址
在控制檯能夠看到,圖片在編譯過程被webpack自動轉換成 base64 編碼格式,而後導入代碼了,因此這裏是 base64 的地址。
2.公告欄部分
公告欄拆分:左邊圖片,中間文字,右邊圖標字體(箭頭),點擊箭頭能夠展開浮層。
1 <div class="bulletin-wrapper" v-on:click="showDetail"> 2 <span class="bulletin-title"></span><span class="bulletin-text">{{seller.bulletin}}</span> 3 <i class="icon-keyboard_arrow_right"></i> 4 </div>
1)清除空白字符的影響
方法1:設置 font-size 爲 0 。
方法2:將兩個 span 寫在一塊兒,不要換行。
這裏選第二個方法,由於後邊要進行省略號的設置,若是選第一個方法,會看不見省略號。
2)省略號設置
1 /*要爲文字限定顯示範圍,位置應該限定到箭頭前面*/ 2 padding 0 22px 0 12px 3 /*文字超過寬度省略的設置*/ 4 white-space nowrap 5 overflow hidden 6 text-overflow ellipsis
要進行省略號設置,必須先限定文字顯示範圍,因此這裏設置 padding 時,是設置到省略號前面,表明從省略號到最後那段距離都不能顯示文字。
後面三行代碼是爲了設置省略號:white-space nowrap(不自動換行)、overflow hidden(超過範圍隱藏)、text-overflow ellipsis(省略號設置)。
3)圖標字體(箭頭),即浮層效果的入口:絕對定位,這個定位是相對於父元素 bulletin-wrapper 。
4)點擊效果的實現
給元素添加點擊事件 v-on:click="showDetail" ,在 method() 方法裏定義點擊事件。
detailShow 是彈出層設置的用於控制彈出層顯示隱藏的一個變量,detailShow 值爲 true 時顯示,爲 false 時隱藏。更多知識在彈出層進行講解。
5)CSS設置
左邊圖片和中間文字不是居中對齊,這是由於兩部分的高度不一樣,因此設置 vertical-align top,讓兩部分頂部對齊。此時發現左邊圖片在上方,能夠設置文字的 margin-top:(28-12)/2=8px (父元素高28px,文字12px),便可居中對齊。
3.背景層
能夠看到,背景是虛化的圖片。
1)給背景層設置絕對定位,這個定位是相對於父元素 header 。
2)設置 z-index 爲 -1,讓該背景層處於最下方,不要遮蓋掉其餘設置。
3)寬高撐滿整個背景層。
4)使用 filter 濾鏡產生模糊效果
設置完在瀏覽器查看時,會發現背景滲透到公告下,這是由於設置了blur。能夠在父元素 header 上添加 overflow:hidden ,便可解決該問題。
3、彈出層
浮層高度大於手機時,應該有下拉功能。無論浮層中內容多少,關閉按鈕都必須固定位於下方,這裏將用到 css sticky fotter 佈局來實現該功能。該佈局通常會有 detail-wrapper 和 detail-close(關閉按鈕) 兩個層,而真正內容寫在 detail-main 中:
1 <div class="detail-wrapper clearfix"> 2 <div class="detail-main"></div> 3 </div> 4 <div class="detail-close"></div>
下面將首先對彈出層的代碼框架佈局進行詳細講解,而後再把彈出層分紅彈出層內容還有關閉按鈕兩部分進行講解。
1.代碼框架佈局
如下是彈出層的代碼框架:
1)顯示隱藏設置(v-show)
彈出層是應當實現顯示和隱藏功能,這裏可使用 vue 的條件渲染 v-show 來實現。設置一個 detailShow 變量,經過改變這個變量的值(true/false),來控制彈出層顯示或隱藏。
如何實現:給vue實例中添加一個選項data(),這個 data() 是一個function(這是以前說過的組件複用問題),該方法會返回一個 object,object 裏面的變量就是它所須要去跟蹤依賴的一些變量。也就是說,vue在實例化時,會對 data 對象中的變量去遍歷,而後添加 getter 和 setter 方法,這樣,當變量變化時,DOM 會根據變量的變化而變化,因此這裏用v-show控制detailShow。
使用 v-show 的好處:不須要額外寫 DOM 代碼,都是直接操做變量便可。
PS:代碼中有兩處須要涉及到 detailShow 的設置,爲這兩個層添加 click 事件。
原理:在方法裏面改變 detailShow 的值,由於 detailShow 是依賴跟蹤的,當點擊以後調用該方法,this.detailShow 會依賴跟蹤到 data 裏面的 detailShow,並改變其值,由於是依賴跟蹤,vue 能夠檢測到其變化,並將該變化映射到 DOM 上,這樣就能夠經過改變數據的值來實現彈層的懸浮或者隱藏。
2)過渡漸變(transition)
彈出層彈出的時候,背景顏色應該由淺到深,彈出層隱藏的時候,背景顏色應該由深到淺。這種漸變的設置能夠用 vue 的內置組件 transition 實現。如下是基本的 HTML 結構:
transition 有一個 name 屬性,添加該屬性後,會自動生成 CSS 過渡類名。例如:name: 'fade' 將自動拓展爲.fade-enter,.fade-enter-active等。在這裏設置背景顏色後,再設置過渡的類名 .fade-enter,.fade-enter-active,.fade-leave-to,.fade-leave-active 的樣式便可。
可在瀏覽器中查看效果:
3)CSS設置
3-1)彈出層相對於窗口應該是 fixed 結構。
3-2)彈出層應該位於全部層的最上方,因此父元素設置 z-index 爲 100 。
3-3)當內容超過屏幕高度,彈出層應該產生滾動條,因此設置 overflow 屬性。
3-4)背景文字虛化設置 backdrop-filter: blur(10px),這個設置支持 ios 設備,能夠在 ios 手機上查看效果。能夠查看 backdrop-filter 的使用範圍。
2.彈出層內容
1)CSS設置
1-1)要給 .detail-wrapper 層加上清除浮動 clearfix ,樣式設置寫在 base.styl 。
1-2).detail-wrapper 的最小高度應該跟視口高度同樣,因此設置爲 100%。
1-3).detail-main 中設置 padding-bottom 64px。若是沒有 padding-bottom,.detail-main 的內容會蓋住關閉按鈕。加了後會撐開高度,這部分高度會給按鈕留住位置。
2)星級組件設置(star.vue)
星級評價這一塊內容在以後多個頁面會使用到,因此將其抽象成 star 組件。
在這裏設置爲總共五顆星,評分範圍爲 0-5,評分規則是向下取0.5倍數的值,好比4到4.4會變成4(顯示四顆全星,一顆無星),4.5到4.9會變成4.5(顯示四顆全星和一顆半星)。此處有一個思路,就是根據不一樣分數範圍製做不一樣的圖片,而後引用圖片,以下圖。
可是爲了增長代碼的可用性,最好不要這樣作,咱們能夠經過分數來計算到底有多少顆全星和無星,以及是否有半星。
先建立相應的文件,在 star.vue 中編寫代碼。
實現代碼以下:
2-1)HTML 代碼
1 <template> 2 <div class="star" :class="starType"> 3 <span v-for="itemClass in itemClasses" :class="itemClass" class="star-item"></span> 4 </div> 5 </template>
先給星級組件添加父元素,類名是 star,將星級組件包裹起來,並 v-bind 方式定義一個 starType 。再添加評星,用 v-for 指令遍歷 itemClasses 數組,這個數組是最後全部星星存放的數組,遍歷該數組,獲得不一樣的 itemClass,再經過 CSS 設置不一樣 itemClass ,最終就能夠獲得不一樣的星級評定。
2-2)JavaScript 代碼
1 <script type="text/ecmascript-6"> 2 const LENGTH = 5; 3 const CLS_ON = 'on'; 4 const CLS_HALF = 'half'; 5 const CLS_OFF = 'off'; 6 7 export default { 8 props: { 9 size: { 10 type: Number 11 }, 12 score: { // 評價的分數 13 type: Number 14 } 15 }, 16 computed: { 17 starType() { 18 return 'star-' + this.size; 19 }, 20 itemClasses() { 21 let result = []; // 最終全部星星存放的數組 22 let score = Math.floor(this.score * 2) / 2; 23 let hasDecimal = score % 1 !== 0; // 小數 24 let integer = Math.floor(score); // 整數 25 for (let i = 0; i < integer; i++) { // 全星 26 result.push(CLS_ON); 27 } 28 if (hasDecimal) { // 是否有半星(半星最多有一個) 29 result.push(CLS_HALF); 30 } 31 while (result.length < LENGTH) { // 無星 32 result.push(CLS_OFF); 33 } 34 return result; 35 } 36 } 37 }; 38 </script>
如下是部分 CSS 代碼:
1 .star 2 ... 3 &.star-48 4 .star-item 5 ... 6 &.on 7 bg-image('star48_on') 8 &.half 9 bg-image('star48_half') 10 &.off 11 bg-image('star48_off') 12 &.star-36 13 ... 14 &.star-24 15 ...
2-2-1)先經過 props 去接收外部傳來的兩個參數:size 和 score,這兩個參數都是 Number 類型。size 的值是 24,36,48 中的一個,是圖片的尺寸類型,score是評分。
2-2-2)starType 是經過 size 參數計算來的,能夠經過 conputed 計算屬性計算出來,由於參數 size 自己會有 getter 和 setter 方法依賴跟蹤。經過計算屬性拼接出starType 的值,從而產生不一樣的 starType,即不一樣的 class,接下來對 class(star-48,star-36,star-24)進行樣式設計便可。其中,on、half、off 分別表明全星、半星、無星。
2-2-3)itemClasses 是數組,要根據 score 進行計算,數組內容其實就是on、half,off 的組合,經過這些類組成星級評定。好比三顆半星,那麼 itemClasses 應該是有三個 on,一個 half,一個 off。依舊在 conputed 計算屬性裏寫代碼,由於 itemClasses 是數組,因此先定義一個 result 數組,而後進行 score 的計算轉化。在轉換後的 score 的基礎上,對其求餘,有餘數表明有一顆半星(half),對 score 進行取整,該整數就是全星(on)的個數。注意:半星最多隻有一個。最後就是將 on 和 half 寫進 result 數組,判斷 result 長度是否爲 5,不爲 5 的話,要補上 off。
PS:能夠看到一開始設置了四個常量 LENGTH、CLS_ON、CLS_HALF、CLS_OFF,這是爲了增長代碼的可用性。
2-2-4)在 header.vue 中經過 components 註冊 star 組件並引用。
<star :size="48" :score="seller.score"></star>
components: {
star
}
3)小標題的設置
小標題的設置,從樣式上看,應該是左右兩邊的線有一個自適應的能力,可根據屏幕大小延伸,標題居中,標題與線之間還有一部分空白。此處用到了一個經典佈局,即 flex 佈局 。下面是 HTML代碼,從代碼中能夠看到,這裏用了三個 div 元素而不是 span 元素,這是由於用 span 的話,在一些 Android 瀏覽器上會產生一些間距問題,所以這裏用 div 。
1 <div class="title"> 2 <div class="line"></div> 3 <div class="text">優惠信息</div> 4 <div class="line"></div> 5 </div>
如下是部分 CSS 代碼,從代碼中能夠看到咱們只寫了一句 display flex,並無寫其它兼容性代碼。這是由於編譯時,vue-loader 中有 postcss 工具,能夠自動添加有兼容性代碼樣式。postcss 是根據 can i use 官網寫的代碼。
1 .title 2 display flex 3 width 80% 4 margin 28px auto 24px auto 5 .line 6 flex 1 7 position relative 8 top -6px 9 border-bottom 1px solid rgba(255, 255, 255, 0.2) 10 .text 11 padding 0 12px 12 font-weight 700 13 font-size 14px
PS:關於 flex 佈局(只挑本節涉及到的知識點進行講解)
1. flex-grow屬性:定義項目的放大比例,默認爲0,即若是存在剩餘空間,也不放大。
.item { flex-grow: <number>; /* default 0 */ }
若是全部項目的flex-grow屬性都爲1,則它們將等分剩餘空間(若是有的話)。若是一個項目的flex-grow屬性爲2,其餘項目都爲1,則前者佔據的剩餘空間將比其餘項多一倍。
2. flex-shrink屬性:定義了項目的縮小比例,默認爲1,即若是空間不足,該項目將縮小。
.item { flex-shrink: <number>; /* default 1 */ }
若是全部項目的flex-shrink屬性都爲1,當空間不足時,都將等比例縮小。若是一個項目的flex-shrink屬性爲0,其餘項目都爲1,則空間不足時,前者不縮小。負值對該屬性無效。
3. flex-basis屬性:定義了在分配多餘空間以前,項目佔據的主軸空間(main size)。瀏覽器根據這個屬性,計算主軸是否有多餘空間。它的默認值爲auto,即項目的原本大小。
.item { flex-basis: <length> | auto; /* default auto */ }
它能夠設爲跟width或height屬性同樣的值(好比350px),則項目將佔據固定空間。
4.flex屬性:是 flex-grow, flex-shrink 和 flex-basis 的簡寫,默認值爲 0 1 auto。後兩個屬性可選。
.item { flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] }
該屬性有兩個快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建議優先使用這個屬性,而不是單獨寫三個分離的屬性,由於瀏覽器會推算相關值。
4)優惠信息的設置
使用無序列表元素 ul 和 li,用 v-for 遍歷 data.json 文件中的 supports 數組,而後展現出來。由於 v-for 支持一個可選的第二個參數爲當前項的索引,因此咱們寫大量額外的代碼,只須要在 v-for 遍歷時,加上 index,下邊就能夠能夠很容易的取到正確的圖片和文字了(classMap是五個圖片類名的數組)。更多關於 v-for的知識,能夠查看:列表渲染 以及 v-for-遍歷數組時的參數順序-變動 。
1 <ul v-if="seller.supports" class="supports"> 2 <li class="support-item" v-for="(item, index) in seller.supports"> 3 <span class="icon" :class="classMap[seller.supports[index].type]"></span> 4 <span class="text">{{seller.supports[index].description}}</span> 5 </li> 6 </ul>
3.關閉按鈕
1)CSS設置
margin -64px auto 0 auto:由於 .detail-wrapper 與 .detail-close 同級,而當 .detail-wrapper 滿屏時,.detail-close 是不會搶佔底部的空間,因此須要設置一個向上的margin,這樣才能一直出如今屏幕的最底部。
2)添加關閉按鈕功能
在 HTML 代碼中加上 click 事件,注意 v-on:click 能夠縮寫爲 @click 。
1 <div class="detail-close" v-on:click="hideDetail"> 2 <i class="icon-close"></i> 3 </div>
在 JavaScript 代碼中將 detailShow 的值設置爲 false(彈出層隱藏)。
1 methods: { 2 hideDetail() { 3 this.detailShow = false; 4 } 5 },
至此,header組件已所有完成。
附代碼:
1 <template> 2 <div id="app"> 3 <v-header :seller="seller"></v-header> 4 <div class="tab border-1px"> 5 <div class="tab-item"> 6 <!-- 使用 router-link 組件來導航. --> 7 <!-- 經過傳入 `to` 屬性指定連接. --> 8 <!-- <router-link> 默認會被渲染成一個 `<a>` 標籤 --> 9 <router-link to="/goods">商品</router-link> 10 </div> 11 <div class="tab-item"> 12 <router-link to="/ratings">評論</router-link> 13 </div> 14 <div class="tab-item"> 15 <router-link to="/seller">商家</router-link> 16 </div> 17 </div> 18 <!--內容區--> 19 <div class="content"> 20 <!-- 路由出口 --> 21 <!-- 路由匹配到的組件將渲染在這裏 --> 22 <router-view></router-view> 23 </div> 24 </div> 25 26 27 </template> 28 29 <script type="text/ecmascript-6"> 30 // 引用 31 import header from 'components/header/header.vue'; 32 33 const ERR_OK = 0; 34 // 註冊 35 export default { 36 data() { 37 return { 38 seller: {} 39 }; 40 }, 41 created() { 42 this.$http.get('/api/seller').then(response => { 43 // get body data 44 response = response.body; 45 console.log(response); 46 if (response.errno === ERR_OK) { 47 this.seller = response.data; 48 console.log(response.data); 49 } 50 }); 51 }, 52 components: { 53 'v-header': header 54 } 55 }; 56 </script> 57 58 <style lang="stylus" rel="stylesheet/stylus"> 59 @import "common/stylus/mixin.styl" 60 #app 61 .tab 62 display flex 63 width 100% 64 height 40px 65 line-height 40px 66 border-1px(rgba(7, 17, 27, 0.1)) 67 .tab-item 68 flex 1 69 text-align center 70 /* &:表示父元素,即.tab-item */ 71 & > a 72 /* 區塊點擊纔能有反應 */ 73 display block 74 font-size 14px 75 color rgb(77, 85, 93) 76 &.active 77 color rgb(240, 20, 20) 78 </style>
1 <template> 2 <div class="header"> 3 <div class="content-wrapper"> 4 <div class="avatar"> 5 <img :src="seller.avatar" alt="" width="64" height="64"> 6 </div> 7 <div class="content"> 8 <div class="title"> 9 <span class="brand"></span> 10 <span class="name">{{seller.name}}</span> 11 </div> 12 <div class="description"> 13 {{seller.description}}/{{seller.deliveryTime}}分鐘送達 14 </div> 15 <div class="support" v-if="seller.supports"> 16 <span class="icon" :class="classMap[seller.supports[0].type]"></span> 17 <span class="text">{{seller.supports[0].description}}</span> 18 </div> 19 </div> 20 <div v-if="seller.supports" class="support-count" v-on:click="showDetail"> 21 <span class="count">{{seller.supports.length}}個</span> 22 <i class="icon-keyboard_arrow_right"></i> 23 </div> 24 </div> 25 <div class="bulletin-wrapper" v-on:click="showDetail"> 26 <span class="bulletin-title"></span><span class="bulletin-text">{{seller.bulletin}}</span> 27 <i class="icon-keyboard_arrow_right"></i> 28 </div> 29 <div class="background"> 30 <img :src="seller.avatar" width="100%" height="100%"> 31 </div> 32 <transition name="fade"> 33 <div v-show="detailShow" class="detail"> 34 <div class="detail-wrapper clearfix"> 35 <div class="detail-main"> 36 <h1 class="name">{{seller.name}}</h1> 37 <div class="star-wrapper"> 38 <star :size="48" :score="seller.score"></star> 39 </div> 40 <div class="title"> 41 <div class="line"></div> 42 <div class="text">優惠信息</div> 43 <div class="line"></div> 44 </div> 45 <ul v-if="seller.supports" class="supports"> 46 <li class="support-item" v-for="(item, index) in seller.supports"> 47 <span class="icon" :class="classMap[seller.supports[index].type]"></span> 48 <span class="text">{{seller.supports[index].description}}</span> 49 </li> 50 </ul> 51 <div class="title"> 52 <div class="line"></div> 53 <div class="text">商家公告</div> 54 <div class="line"></div> 55 </div> 56 <div class="bulletin"> 57 <p class="content">{{seller.bulletin}}</p> 58 </div> 59 </div> 60 </div> 61 <div class="detail-close" v-on:click="hideDetail"> 62 <i class="icon-close"></i> 63 </div> 64 </div> 65 </transition> 66 </div> 67 68 </template> 69 70 <script type="text/ecmascript-6"> 71 import star from '../star/star.vue'; 72 73 export default { 74 props: { 75 seller: { 76 type: Object 77 } 78 }, 79 // vue的data必須用function,由於可能有多個組件實例,組件又能夠複用,爲了相互之間不要互相影響,必須設置data爲function 80 // 以前有過關於getter和setter知識點的講解 81 // vue在new一個是實例是,解析到data函數,會給其中的參數加上getter和setter方法去監聽它的變化 82 data() { 83 return { 84 detailShow: false 85 }; 86 }, 87 methods: { 88 showDetail() { 89 this.detailShow = true; 90 }, 91 hideDetail() { 92 this.detailShow = false; 93 } 94 }, 95 created() { 96 this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee']; 97 }, 98 components: { 99 star 100 } 101 }; 102 </script> 103 104 <style lang="stylus" rel="stylesheet/stylus"> 105 @import '../../common/stylus/mixin' 106 107 .header 108 position relative 109 /*overflow hidden*/ 110 color #fff 111 background rgba(7, 17, 27, 0.5) 112 .content-wrapper 113 position relative 114 padding 24px 12px 18px 24px 115 /* 消除空白字符的影響 */ 116 font-size 0 /*background-color rgba(7, 17, 27, 0.5) 117 blur 5px 118 */ 119 .avatar 120 /* 左右兩個區域並排顯示 */ 121 display inline-block 122 vertical-align top 123 img 124 border-radius 2px 125 .content 126 display inline-block 127 margin-left 16px 128 .title 129 margin 2px 0 8px 0 130 .brand 131 /* 由於是 span ,必須設置爲塊級元素才能指定寬高 */ 132 display inline-block 133 /* 由於兩個inline-block是不同搞得,須要設置對齊方式 */ 134 vertical-align top 135 width 30px 136 height 18px 137 bg-image('brand') 138 background-size 30px 18px 139 background-repeat no-repeat 140 .name 141 margin-left 6px 142 font-size 16px 143 line-height 18px 144 font-weight bold 145 .description 146 margin-bottom 10px 147 line-height 12px 148 font-size 12px 149 .support 150 margin 10px 0 2px 0 151 .icon 152 display inline-block 153 vertical-align top 154 width 12px 155 height 12px 156 margin-right 4px 157 background-size 12px 12px 158 background-repeat no-repeat 159 &.decrease 160 bg-image('decrease_1') 161 &.discount 162 bg-image('discount_1') 163 &.guarantee 164 bg-image('guarantee_1') 165 &.invoice 166 bg-image('invoice_1') 167 &.special 168 bg-image('special_1') 169 .text 170 line-height 12px 171 font-size 10px 172 .support-count 173 position absolute 174 right 12px 175 bottom 14px 176 padding 0 8px 177 height 24px 178 line-height 24px 179 border-radius 14px 180 background rgba(0, 0, 0, 0.2) 181 text-align center 182 .count 183 /*字體和圖標字體是不同的大小,因此設置vertical-align top進行頂部對齊*/ 184 vertical-align top 185 font-size 10px 186 .icon-keyboard_arrow_right 187 margin-left 2px 188 /*由於在base.styl中設置了line-height:1,因此這裏要設置line-height,不然沒法垂直居中*/ 189 line-height 24px 190 font-size 10px 191 .bulletin-wrapper 192 position relative 193 height 28px 194 line-height 28px 195 /*要爲文字限定顯示範圍,位置應該限定到箭頭前面*/ 196 padding 0 22px 0 12px 197 /*文字超過寬度省略的設置*/ 198 white-space nowrap 199 overflow hidden 200 text-overflow ellipsis 201 background rgba(7, 17, 27, 0.2) 202 .bulletin-title 203 display inline-block 204 vertical-align top 205 margin-top 8px 206 width 22px 207 height 12px 208 bg-image('bulletin') 209 background-size 22px 12px 210 background-repeat no-repeat 211 .bulletin-text 212 vertical-align top 213 margin 0 4px 214 text-align center 215 font-size 10px 216 .icon-keyboard_arrow_right 217 position absolute 218 /*由於在base.styl中設置了line-height:1,因此這裏要設置line-height,不然沒法垂直居中*/ 219 font-size 10px 220 right 12px 221 bottom 8px 222 .background 223 position absolute 224 top 0 225 left 0 226 width 100% 227 height 100% 228 z-index -1 229 /*使用filter濾鏡產生模糊效果*/ 230 filter blur(10px) 231 .detail 232 position fixed 233 z-index 100 234 top: 0 235 left 0 236 width 100% 237 height 100% 238 /*當內容超過屏幕高度,能夠產生滾動條*/ 239 overflow auto 240 background-color: rgba(7, 17, 27, 0.8) 241 backdrop-filter: blur(10px) 242 /*從enter到transition,最終顯示效果爲transition*/ 243 &.fade-enter-active, &.fade-leave-active 244 transition opacity 0.5s 245 &.fade-enter, &.fade-leave-to 246 opacity 0 247 .detail-wrapper 248 /*要定義寬度,撐開空間,下面一些居中設置才能實現*/ 249 width 100% 250 /*鋪滿整個屏幕*/ 251 min-height 100% 252 .detail-main 253 margin-top 64px 254 /*下邊距包括兩部分:一部分是邊距,一部風是關閉按鈕的高度*/ 255 padding-bottom 64px 256 .name 257 line-height 16px 258 text-align center 259 font-size 16px 260 font-weight 700 261 .star-wrapper 262 margin-top 18px 263 padding 2px 0 264 text-align center 265 .title 266 display flex 267 width 80% 268 margin 28px auto 24px auto 269 .line 270 flex 1 271 position relative 272 top -6px 273 border-bottom 1px solid rgba(255, 255, 255, 0.2) 274 .text 275 padding 0 12px 276 font-weight 700 277 font-size 14px 278 .supports 279 width 80% 280 margin 0 auto 281 .support-item 282 padding 0 12px 283 margin-bottom 12px 284 font-size 0 285 &:last-child 286 margin-bottom 0 287 .icon 288 display inline-block 289 vertical-align top 290 width 16px 291 height 16px 292 margin-right 6px 293 background-size 16px 16px 294 background-repeat no-repeat 295 &.decrease 296 bg-image('decrease_2') 297 &.discount 298 bg-image('discount_2') 299 &.guarantee 300 bg-image('guarantee_2') 301 &.invoice 302 bg-image('invoice_2') 303 &.special 304 bg-image('special_2') 305 .text 306 /*與圖片高度同樣,才能垂直居中*/ 307 line-height 16px 308 font-size 12px 309 .bulletin 310 width 80% 311 margin 0 auto 312 .content 313 padding 0 12px 314 line-height 24px 315 font-size 12px 316 .detail-close 317 position relative 318 width 32px 319 height 32px 320 margin -64px auto 0 auto 321 clear both 322 font-size 32px 323 </style>
1 <template> 2 <div class="star" :class="starType"> 3 <span v-for="itemClass in itemClasses" :class="itemClass" class="star-item"></span> 4 </div> 5 </template> 6 7 <script type="text/ecmascript-6"> 8 const LENGTH = 5; 9 const CLS_ON = 'on'; 10 const CLS_HALF = 'half'; 11 const CLS_OFF = 'off'; 12 13 export default { 14 props: { 15 size: { 16 type: Number 17 }, 18 score: { // 評價的分數 19 type: Number 20 } 21 }, 22 computed: { 23 starType() { 24 return 'star-' + this.size; 25 }, 26 itemClasses() { 27 let result = []; // 最終全部星星存放的數組 28 let score = Math.floor(this.score * 2) / 2; 29 let hasDecimal = score % 1 !== 0; // 小數 30 let integer = Math.floor(score); // 整數 31 for (let i = 0; i < integer; i++) { // 全星 32 result.push(CLS_ON); 33 } 34 if (hasDecimal) { // 是否有半星(半星最多有一個) 35 result.push(CLS_HALF); 36 } 37 while (result.length < LENGTH) { // 無星 38 result.push(CLS_OFF); 39 } 40 return result; 41 } 42 } 43 }; 44 </script> 45 46 <style lang="stylus" rel="stylesheet/stylus"> 47 @import '../../common/stylus/mixin.styl'; 48 49 .star 50 /*清除空白字符影響*/ 51 font-size 0 52 .star-item 53 /*星星是橫向排列*/ 54 display inline-block 55 background-repeat no-repeat 56 &.star-48 57 .star-item 58 width: 20px 59 height: 20px 60 margin-right: 22px 61 background-size: 20px 20px 62 &:last-child 63 margin-right: 0 64 &.on 65 bg-image('star48_on') 66 &.half 67 bg-image('star48_half') 68 &.off 69 bg-image('star48_off') 70 &.star-36 71 .star-item 72 width: 15px 73 height: 15px 74 margin-right: 6px 75 background-size: 15px 15px 76 &:last-child 77 margin-right: 0 78 &.on 79 bg-image('star36_on') 80 &.half 81 bg-image('star36_half') 82 &.off 83 bg-image('star36_off') 84 &.star-24 85 .star-item 86 width: 10px 87 height: 10px 88 margin-right: 3px 89 background-size: 10px 10px 90 &:last-child 91 margin-right: 0 92 &.on 93 bg-image('star24_on') 94 &.half 95 bg-image('star24_half') 96 &.off 97 bg-image('star24_off') 98 </style>
1 body, html 2 margin 0 3 padding 0 4 line-height 1 5 font-weight 200 6 // 字體查找順序,從前到後,若是全部設置字體都不認識,則返回系統字體 7 font-family 'PingFang SC', 'STHeitiSC-Light', 'Helvetica-Light', Arial, sans-serif 8 9 ul 10 list-style none 11 padding-left 0 12 margin 0 13 14 li 15 list-style none 16 17 h1 18 margin 0 19 20 .clearfix 21 display inline-block 22 &:after 23 display block 24 content "." 25 height 0 26 line-height 0 27 visibility hidden 28 clear both 29 30 //爲何要寫 -webkit :由於這裏沒有兼容性文件控制,因此要手寫兼容性代碼 31 //本例須要的是1像素的邊框,因此當dpr爲1.5時,取0.7倍(1.5*0.7=1.05約等於1)。dpr爲2時,取0.5倍(2*0.5=1) 32 @madia (-webkit-min-device-pixel-ratio 1.5) 33 (-min-device-pixel-ratio 1.5) 34 .border-1px 35 &::after 36 -webkit-transform scaleY(0.7) 37 transform scaleY(0.7) 38 39 @madia (-webkit-min-device-pixel-ratio 2) 40 (-min-device-pixel-ratio 2) 41 .border-1px 42 &::after 43 -webkit-transform scaleY(0.5) 44 transform scaleY(0.5)
1 border-1px($color) 2 position relative 3 // 進行僞類設置,必定要設置寬度,由於元素脫離了文檔流 4 &:after 5 display block 6 position absolute 7 left 0 8 bottom 0 9 width 100% 10 border-top 1px solid $color 11 content ' ' 12 13 border-none() 14 &:after 15 display none 16 17 bg-image($url) 18 // 正常狀況下 19 background-image url($url + "@2x.png") 20 // 最小 dpr=3 的狀況下 21 @madia (-webkit-min-device-pixel-ratio 3) (-min-device-pixel-ratio 3) 22 background-image url($url + "@3x.png")