六,header組件開發(fā)
1. vue-resource
vue-resource 處理前后端請(qǐng)求數(shù)據(jù)交互沽翔。
安裝引入注冊(cè)同vue-router鲜漩,都是第三方插件。
main方法中使用的是全局配置文件诅诱,所以在這里可以配置所有的公共數(shù)據(jù)。在VUE中規(guī)定data是一個(gè)函數(shù)送朱。vue-resource 要將請(qǐng)求成功的數(shù)據(jù)轉(zhuǎn)換為json格式娘荡,要調(diào)用 .body 這個(gè)屬性,如果用json方法返回的是promise對(duì)象驶沼。created 是 Vue 生命周期的鉤子函數(shù)它改,會(huì)在實(shí)例化的過程中自動(dòng)調(diào)用的。
//App.vue
...
created() {
// 這里只寫成功的方法
this.$http.get('api/seller').then((response) => {
response = response.body;
if (response.errno === ERR_OK) {
this.seller = response.data;
console.log(this.seller);
}
});
},
...
2. 外部組件
編寫header代碼商乎,向header中傳ajax獲取的seller央拖。
<v-header :seller="seller"></v-header>
我們數(shù)據(jù)是異步得到的,最開始渲染dom的時(shí)候鹉戚,seller為空對(duì)象鲜戒。加上v-if是因?yàn)檫@里層次嵌套深,如果 seller 是 {}抹凳,那么 seller.supports 為 undefined遏餐,那么 undefined[0] 就報(bào)錯(cuò)了。比如你需要訪問3級(jí)數(shù)據(jù)比如 a.b.c 需要判斷 v-if a.b否則會(huì)報(bào)錯(cuò)赢底。
幾個(gè)不錯(cuò)的QA:
Q: 感覺如果從子組件中的mounted鉤子中發(fā)送ajax然后拿到數(shù)據(jù)渲染失都,也就不用寫v-if判斷了啊?
A: 有些情況,如果是想保持組件的純凈幸冻,只負(fù)責(zé)渲染粹庞,數(shù)據(jù)通過 props 的傳入,對(duì)組件的復(fù)用性也會(huì)更好洽损。有些情況庞溜,如果組件內(nèi)部有一些交互邏輯需要組件閉環(huán)的話,確實(shí)可以把 ajax 請(qǐng)求寫在組件內(nèi)部碑定。
header.vue中的props部分相當(dāng)于要給header組件傳入的參數(shù)流码,對(duì)應(yīng)于App的template中的:seller傳入。
為了消除inline-block由于空白字符產(chǎn)生的空隙延刘,給父元素設(shè)置font-size=0漫试。但是在bulletin中為了顯示省略號(hào)不能用font-size的方式,就直接刪除html中span之間的空格好了碘赖。
white-space:nowrap;overflow: hidden;text-overflow: ellipsis;
這三個(gè)屬性組合起來就是單行顯示驾荣,多余文字省略號(hào)表示了
bulletin 的垂直對(duì)齊可以用我自己的方法設(shè)置title圖片middle外构,或者用老師的方法先全部top對(duì)齊然后調(diào)整title圖片的margin-top。
引入不同dpr的圖片秘车,在mixin.styl中定義這樣的函數(shù)典勇。
background的模糊效果圖片背景通過絕對(duì)定位+filter實(shí)現(xiàn)劫哼。注意由于filter的blur效果header底部有溢出的模糊叮趴,所以要在header中添加overflow樣式。
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")
品牌圖片在頁面中被webpack打包成了base64地址权烧。
關(guān)于不用rem em而用px的解釋眯亦,http://coding.imooc.com/learn/questiondetail/3357.html
supports欄的書寫要用class map實(shí)現(xiàn)。展示的是supports[0]般码,但是type是動(dòng)態(tài)的妻率。supports的text中font-size為10px,在chrome下看不出來因?yàn)閏hrome下最小是12px板祝。但是手機(jī)端看得出來宫静。
垂直居中對(duì)齊問題用vertical-align: top 和調(diào)整line-height解決。
header.vue代碼如下
<template>
<div class="header">
<div class="content-wrapper">
<div class="avatar">
![](seller.avatar)
</div>
<div class="content">
<div class="title">
<span class="brand"></span>
<span class="name">{{seller.name}}</span>
</div>
<div class="description">
{{seller.description}}/{{seller.deliveryTime}}分鐘到達(dá)
</div>
<div v-if="seller.supports" class="supports">
<span class="icon" :class="classMap[seller.supports[0].type]"></span>
<span class="text">{{seller.supports[0].description}}</span>
</div>
</div>
<div v-if="seller.supports" class="support-count">
<span class="count">{{seller.supports.length}}個(gè)</span>
<i class="icon-keyboard_arrow_right"></i>
</div>
</div>
<div class="bulletin-wrapper">
<span class="bulletin-title"></span><span class="bulletin-text">{{seller.bulletin}}</span>
<i class="icon-keyboard_arrow_right"></i>
</div>
<div class="background">
![](seller.avatar)
</div>
</div>
</template>
<script type="text/ecmascript-6">
export default {
props: {
seller: {
type: Object
}
},
created() {
this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee'];
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import "../../common/stylus/mixin"
.header
position: relative
overflow: hidden
color: white
background-color: rgba(7, 17, 27, 0.5)
.content-wrapper
padding: 24px 12px 18px 24px
position: relative
font-size: 0
.avatar
display: inline-block
vertical-align: top
img
border-radius: 2px
.content
display: inline-block
margin-left: 16px
.title
margin: 2px 0 8px 0
.brand
display: inline-block
vertical-align: top
width: 30px
height: 18px
bg-image(brand)
background-size: 30px 18px
background-repeat: no-repeat
.name
margin-left: 16px
font-size: 16px
line-height: 18px;
font-weight: bold
.description
margin-bottom: 10px
line-height: 12px
font-size: 12px
.supports
.icon
display: inline-block
vertical-align: top
width: 12px
height: 12px
margin-right: 4px
background-size: 12px 12px
background-repeat: no-repeat
&.decrease
bg-image(decrease_1)
&.discount
bg-image(discount_1)
&.guarantee
bg-image(guarantee_1)
&.invoice
bg-image(invoice_1)
&.special
bg-image(special_1)
.text
line-height: 12px
font-size: 10px
.support-count
position: absolute
right: 12px
bottom: 14px
padding: 0 8px
height: 24px
line-height: 24px
border-radius: 14px
background: rgba(0, 0, 0, 0.2)
text-align: center
.count
vertical-align: top
font-size: 10px
.icon-keyboard_arrow_right
line-height: 24px
margin-left: 2px
font-size: 10px
.bulletin-wrapper
position: relative
height: 28px
line-height: 28px
padding: 0 22px 0 12px
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
background-color: rgba(7, 17, 27, 0.2)
.bulletin-title
display: inline-block
vertical-align: middle
width: 22px
height: 12px
bg-image(bulletin)
background-size: 22px 12px
background-repeat: no-repeat
.bulletin-text
margin: 0 4px
font-size: 10px
.icon-keyboard_arrow_right
position: absolute
right: 12px
bottom: 8px
font-size: 10px
.background
position: absolute
top: 0px
left: 0px
width: 100%
height: 100%
z-index: -1
filter: blur(10px)
</style>
3. 詳情彈層頁
這一部分也可以單獨(dú)寫一個(gè)組件券时,這里我們繼續(xù)在header中編寫代碼孤里。
3.1. 實(shí)現(xiàn)彈出層
彈出層是一個(gè)叫detail的div。用fixed布局橘洞。在對(duì)應(yīng)的地方添加@click函數(shù)捌袜,并寫入data和methods的vue方法控制點(diǎn)擊出現(xiàn)事件,不需要編寫改變dom事件炸枣,vue非常方便虏等。
<div v-if="seller.supports" class="support-count" @click="showDetail">
data() {
return {
detailShow: false
};
},
methods: {
showDetail() {
this.detailShow = true;
}
},
.detail
position: fixed
z-index: 100
top: 0
left: 0
width: 100%
height: 100%
overflow: auto
background-color: rgba(7, 17, 27, 0.8)
關(guān)于vue的new vue, export default語法的問題http://coding.imooc.com/learn/questiondetail/15435.html
Q: data 與 created 里定義變量的區(qū)別?
A: 如果你想為這個(gè)屬性添加 getter 和 setter适肠,就放在 data 里霍衫,data數(shù)據(jù)是響應(yīng)的。如果不需要就直接掛在當(dāng)前實(shí)例下侯养。
3.2. CSS Sticky Footer
CSS秘密花園有文章介紹sticky footer慕淡。www.w3cplus.com/css3/css-secrets/sticky-footers.html
這里我們用一個(gè)兼容性較好的套路去解決sticky footer。
detail-main中的padding-bottom是必須的沸毁,本例子中用margin-bottom也可以峰髓。然后關(guān)閉icon部分用margin。這里關(guān)鍵點(diǎn)三個(gè): 一息尺、wrapper占據(jù)最小100%尺寸携兵,此時(shí)footer層被擠到屏幕外。 二搂誉、(當(dāng)內(nèi)容不滿一屏?xí)r徐紧,)footer層負(fù)位移從屏幕外層移回來。 三、(當(dāng)內(nèi)容滿一屏?xí)r并级,)main層向外擠拂檩,防止footer疊加在main上。 此方法由于要計(jì)算footer負(fù)位移嘲碧,不可處理變長(zhǎng)div的footer稻励。
如果用w3c鏈接的方法,使用flex布局可省略wrapper層愈涩。 直接給detail賦予display:flex 調(diào)整flex流向即可 望抽,并賦予main部分flex:1。flex:1等價(jià)于flex-grow:1履婉。當(dāng)所有元素排布時(shí)煤篙,該元素自動(dòng)放大。由于flex布局時(shí)毁腿,float辑奈、clear屬性失效,故無需再調(diào)整float相關(guān)屬性已烤。flex布局簡(jiǎn)單鸠窗,且可處理高度變化的footer,vue組件中用flex有預(yù)處理草戈。
本文在detail-wrapper中清除了浮動(dòng)(用clearfix)塌鸯,其實(shí)在本例中也可以不清除,只是為了標(biāo)準(zhǔn)寫法兼容其他場(chǎng)景唐片。注意我們將detail-wrapper設(shè)置成了inline-block丙猬,為了避免垂直外邊距合并問題。
<div v-show="detailShow" class="detail">
<div class="detail-wrapper clearfix">
<div class="detail-main">
</div>
</div>
<div class="detail-close">
<i class="icon-close"></i>
</div>
</div>
................................................................
.detail
position: fixed
z-index: 100
top: 0
left: 0
width: 100%
height: 100%
overflow: auto
background-color: rgba(7, 17, 27, 0.8)
.detail-wrapper
width: 100%
min-height: 100%
.detail-main
margin-top: 64px
padding-bottom: 64px
.detail-close
position: relative
width: 32px
height: 32px
margin: -64px auto 0 auto
clear: both
font-size: 32px
3.3. star組件抽象
實(shí)現(xiàn)detail-main部分费韭。創(chuàng)建star組件茧球。利用v-for指令。星級(jí)評(píng)價(jià)的星星切開單獨(dú)用星持,不用雪碧圖抢埋。有些class是通用css屬性,有些是為了指定單獨(dú)屬性督暂。stylus部分我們?cè)谶@里定義不同的揪垄。
//star.vue
<template>
<div class="star" :class="starType">
<span v-for="itemClass in itemClasses" :class="itemClass" class="star-item" track-by="$index">
</span>
</div>
</template>
<script type="text/ecmascript-6">
const LENGTH = 5;
const CLS_ON = 'on';
const CLS_HALF = 'half';
const CLS_OFF = 'off';
export default {
props: {
size: {
type: Number
},
score: {
type: Number
}
},
computed: {
starType() {
return 'star-' + this.size;
},
itemClasses() {
let result = [];
let score = Math.floor(this.score * 2) / 2;
let hasDecimal = score % 1 !== 0;
let integer = Math.floor(score);
for (let i = 0; i < integer; i++) {
result.push(CLS_ON);
}
if (hasDecimal) {
result.push(CLS_HALF);
}
while (result.length < LENGTH) {
result.push(CLS_OFF);
}
return result;
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import "../../common/stylus/mixin";
.star
font-size: 0
.star-item
display: inline-block
background-repeat: no-repeat
&.star-24
.star-item
width: 20px
height: 20px
margin-right: 22px
background-size: 20px 20px
&:last-child
margin-right: 0
&.on
bg-image('star48_on')
&.half
bg-image('star48_half')
&.off
bg-image('star48_off')
&.star-36
.star-item
width: 20px
height: 20px
margin-right: 22px
background-size: 20px 20px
&:last-child
margin-right: 0
&.on
bg-image('star48_on')
&.half
bg-image('star48_half')
&.off
bg-image('star48_off')
&.star-48
.star-item
width: 20px
height: 20px
margin-right: 22px
background-size: 20px 20px
&:last-child
margin-right: 0
&.on
bg-image('star48_on')
&.half
bg-image('star48_half')
&.off
bg-image('star48_off')
</style>
//header.vue
<div class="detail-main">
<h1 class="name">{{seller.name}}</h1>
<div class="star-wrapper">
<star :size="48" :score=seller.score></star>
</div>
</div>
...
components: {
star
}
...
3.4. 小標(biāo)題自適應(yīng)flex布局
這里又有面試題了:“優(yōu)惠信息”居中,兩條線自適應(yīng)逻翁,背景還是透明的饥努,不能做成一整條長(zhǎng)線。 解決方法是flex布局八回,參考阮一峰教程酷愧。
<div class="title">
<div class="line"></div>
<div class="text">優(yōu)惠信息</div>
<div class="line"></div>
</div>
...
.title
display: flex
width: 80%
margin: 28px 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
font-weight: 700
3.5. header剩余部分實(shí)現(xiàn)
知識(shí)點(diǎn)沒有什么新的驾诈,完善了一下剩余部分。給彈出層加了一個(gè)css3動(dòng)畫溶浴。
vue1.0中這樣寫:
<div v-show="detailShow" class="detail" transition="fade">
...
...
transition: all 0.5s
&.fade-transition
opacity: 1
background-color: rgba(7, 17, 27, 0.8)
&.fade-enter, &.fade-leave
opacity: 0
background: rgba(7, 17, 27, 0)
注意彈出層下層有毛玻璃效果乍迄,這里就不能用filter屬性了,要用backdrop-filter士败。但是backdrop支持有限基本只有ios safari可以看到闯两,可以看這篇https://hran.me/achieves/css3-backdrop-filter.html。