一、顯示頭部
1.1 新建頭部組件
在components目錄下新建Header.vue文件。其中該組件接收兩個參數:title和showback。title代表頭部組件的標題內容;showback代表是否顯示後退按鈕。
<template>
<div class="header">
<h1>{{title}}</h1>
<i v-if="showback" @click="back" class="cubeic-back"></i>
<div class="extend">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '',
},
showback: {
type: Boolean,
default: true,
},
},
methods: {
back() {
this.$router.back();
}
},
}
</script>
<style lang="stylus" scoped>
.header {
position: relative;
height: 44px;
line-height: 44px;
text-align: center;
background: #edf0f4;
.cubeic-back {
position: absolute;
top: 0;
left: 0;
padding: 0 15px;
color: #fc915b;
}
.extend {
position: absolute;
top: 0;
right: 0;
padding: 0 15px;
color: #fc915b;
}
}
</style>
1.2 全局引入組件
修改main.js文件:
import KHeader from './components/Header.vue'
// 全局引入Header.vue
Vue.component('k-header', KHeader)
1.3 使用組件
在首頁Home.vue中顯示組件。
<k-header title="樂購商城"></k-header>
運行效果:
二、管理訪問歷史
2.1 Vue插件開發
資料地址:https://cn.vuejs.org/v2/guide/plugins.html
現在我們想定義一個訪問瀏覽歷史記錄的實例方法。我們把該方法添加到Vue.prototype上實現。
第一步:新建utils目錄,該目錄存放我們項目中定義的工具;
第二步:新建history.js文件,在該文件中實現插件功能;
import Vue from 'vue'
const History = {
_history: [], // 歷史記錄
install(vue) {
// 給Vue對象添加實例方法
Object.defineProperty(Vue.prototype, '$routerHistory', {
get() {
return History;
}
});
},
push(path) { // 存儲訪問頁面
this._history.push(path);
},
pop() { // 刪除歷史頁面
this._history.pop();
},
canBack() {
return this._history.length > 1;
}
}
export default History;
Vue.js 的插件應該暴露一個 install 方法。這個方法的第一個參數是 Vue 構造器,第二個參數是一個可選的選項對象。
第三步:通過全局方法Vue.use()安裝插件;
import History from '../utils/history'
Vue.use(History)
插件安裝成功後,可以使用History訪問插件方法。
History.pop(); // 刪除訪問歷史的最後記錄
2.2 路由配置
第一步:擴展Router功能,新增後退方法。
import VueRouter from 'vue-router'
VueRouter.prototype.goBack = function() {
this.isBack = true; // 新增isBack屬性,true代表當前操作是後退
this.back(); // 調用路由的back方法執行後退
}
第二步:定義路由攔截方法。
import History from '../utils/history'
// 把History當成插件引入
Vue.use(History)
// 記錄訪問歷史
router.afterEach((to, from) => {
if (router.isBack) {
// 如果執行後退,刪除訪問歷史的最後記錄
History.pop();
router.isBack = false;
} else {
// 如果不是執行後退,則添加訪問歷史記錄
History.push(to.path);
}
});
2.3 改造後退按鈕
(1)修改Header.vue的back方法:
methods: {
back() {
// this.$router.back();
this.$router.goBack(); // 替換成該方法
}
},
(2)修改後退條件:
<i v-if="$routerHistory.canBack()" @click="back" class="cubeic-back"></i>
運行效果:
二、頁面切換特效
修改App.vue,把transition標籤的name屬性改爲動態屬性。
<transition :name="transitionName">
<router-view class="child-view"/>
</transition>
在data返回的json對象中添加transitionName屬性。
data () {
return {
...
transitionName: 'route-forward',
}
},
當路由發生變化時,同步更新transitionName屬性。
watch: {
// 監控route狀態變化。當路由發生變化,同步tabs選中狀態
$route(route) {
this.selectedLabel = route.path;
this.transitionName = this.$router.transitionName;
}
},
修改頁籤樣式:
/* 頁籤滾動條樣式 */
.cube-tab-bar-slider {
top: 0;
}
/* 動畫設置 */
.route-forward-enter { /* 入場前 */
transform: translate3d(-100%, 0, 0);
}
.route-forward-leave-to { /* 出場後 */
transform: translate3d(100%, 0, 0);
}
.route-back-enter { /* 入場前 */
transform: translate3d(100%, 0, 0);
}
.route-back-leave-to { /* 出場後 */
transform: translate3d(-100%, 0, 0);
}
.route-forward-enter,
.route-forward-leave-active,
.route-back-enter,
.route-back-leave-active { /* 播放動畫過程中 */
transition: transform 0.3s; /* 完成動畫需要的時間 */
}
修改路由配置文件,在router中定義transitionName屬性。如果router.isBack爲true,代表是回退操作,則transitionName爲route-back;如果router.isBack爲false,代表前進操作,則設置transitionName爲route-forward。
router.afterEach((to, from) => {
if (router.isBack) {
History.pop(); // 刪除訪問歷史的最後記錄
router.isBack = false;
router.transitionName = 'route-back'; // 後臺特效
} else {
History.push(to.path);
router.transitionName = 'route-forward'; // 前進特效
}
});
三、加購物車添加動畫效果
實現效果:
3.1 定義小球
在Home.vue模版中定義一個div元素,該元素代表動畫中的小球。
<!-- 加購動畫的圓球 -->
<div class="ball-wrap">
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<!-- <div class="ball" v-show="ball.show">球</div> -->
<div class="ball" v-show="ball.show">
<div class="inner">
<div class="cubeic-add"></div>
</div>
</div>
</transition>
</div>
其中v-show屬性用於控制小球的顯示。因此,需要定義ball.show屬性。
data() {
return {
...
ball: {
show: false,
el: null, // 目標dom的引用
}
}
},
ball對象有兩個屬性:show代表小球的顯示狀態,true代表顯示,false不顯示;el代表的是小球所屬DOM元素的引用。
小球樣式:
/* 圓球樣式 */
.ball-wrap {
.ball {
position: fixed;
left: 50%;
bottom: 10px;
z-index: 100000;
color: red;
transition: all 0.5s cubic-bezier(0.49, -0.29, 0.75, 0.41);
.inner {
width: 16px;
height: 16px;
transition: all 0.5s linear;
.cubeic-add {
font-size: 22px;
}
}
}
}
3.2 添加動畫事件
transition標籤有三個事件屬性,它們分別代表動畫執行的三種不同狀態。
@before-enter:動畫開始前
@enter:動畫進行中
@after-enter:動畫結束後
下面是三個事件對應的處理方法:
methods: {
...
beforeEnter(el) {
console.log('動畫開始前...');
// 設置小球初始位置
// 小球移動到點擊的位置
// 1. 獲取點擊的dom位置
const dom = this.ball.el;
const rect = dom.getBoundingClientRect();
console.log(rect.top, rect.left);
// 2. 把小球移動到點擊的位置
const x = rect.left - window.innerWidth / 2;
const y = -(window.innerHeight - rect.top - 10 - 20);
el.style.display = "block";
// ball只移動y
el.style.transform = `translate3d(0, ${y}px, 0)`;
const inner = el.querySelector(".inner");
// inner只移動x
inner.style.transform = `translate3d(${x}px,0,0)`;
},
enter(el, done) { // el代表當前動畫的元素
console.log('動畫執行中...');
// 把小球移動到初始位置 加上動畫
// 獲取offsetHeight就會重繪,前面的變量名隨意 主要爲了eslint校驗
document.body.offsetHeight;
// 動畫開始,移動到初始位置
// 小球移動到購物車位置
el.style.transform = `translate3d(0, 0, 0)`;
const inner = el.querySelector(".inner");
inner.style.transform = `translate3d(0,0,0)`;
el.addEventListener("transitionend", done);
},
afterEnter(el) {
console.log('執行動畫結束後的清理工作...');
// 隱藏小球
this.ball.show = false;
el.style.display = 'none';
},
},
3.3 爲加購物車按鈕派發事件
首先修改GoodsList.vue,在addCart方法中派發addCart事件。
// 向父組件Home派發點擊事件
this.$emit('addCart', $event.target); // $event.target代表事件發生的元素
然後首頁商品列表中監聽addCart事件。
<goods-list :goods="filterGoods" @addCart="onAddCart"></goods-list>
定義addCart事件的處理方法。
onAddCart(el) {
console.log('顯示小球');
this.ball.el = el; // 初始化ball.el屬性
this.ball.show = true; // 顯示小球
},
四、全局組件
下面以toast爲例,介紹如何定義全局組件。
// toast組件的定義格式
const toast = this.$createToast({
time: 2000,
txt: message || '登錄失敗',
type: 'error'
});
toast.show();
4.1 定義組件
首先,在components目錄下新建Notice.vue。
<template>
<div class="alert">
<div class="alert-container" v-for="item in alerts" :key="item.id">
<div class="alert-content">{{item.content}}</div>
</div>
</div>
</template>
<script>
export default {
name: 'notice',
}
</script>
定義alerts屬性,該屬性用於存儲多個通知。
data() {
return {
alerts: [],
}
},
在created方法中初始化通知id。通知id應該是自增長。
created() {
this.id = 0;
},
定義添加和刪除通知的方法。
methods: {
// 添加通知,options參數代表通知選項(duration-顯示時間,content-通知內容)
add(options) {
// 通知id自增
const id = 'id_' + this.id++;
// 創建通知對象
const _alert = {
...options,
id,
}
// 保存通知
this.alerts.push(_alert);
// 自動關閉
const duration = options.duration || 1; // 延遲關閉時間,單位爲秒
setTimeout(() => {
this.del(id);
}, duration * 1000);
},
// 刪除通知
del(id) {
for (let i = 0; i < this.alerts.length; i++) {
const alert = this.alerts[i];
if (alert.id === id) {
this.alerts.splice(i, 1);
break;
}
}
}
},
添加樣式:
<style lang="stylus" scoped>
.alert {
position fixed
width 100%
top 30
left 0
text-align center
.alert-content {
display inline-block
padding 8px
background #fff
margin-bottom 10px
}
}
</style>
4.2 使用create-api模塊創建通知組件
資料地址:https://didi.github.io/cube-ui/#/zh-CN/docs/introduction
4.2.1 導入create-api模塊
修改main.js文件:
import {createAPI} from 'cube-ui'
4.2.2 加載Notice組件
修改main.js文件:
import Notice from './components/Notice.vue'
createAPI(Vue, Notice, true);
createAPI方法第三個參數代表是否單例。true代表Notice組件是單例,即在應用中只創建一次。
4.2.3 使用Notice組件
修改GoodsList.vue,在addCart方法中顯示通知。
addCart($event, item) {
// 把添加購物車的商品放在state中
this.$store.commit('addCart', item);
// 顯示通知
const notice = this.$createNotice();
notice.add({duration: 2, content: '加購物車成功'});
},
4.3 自定義通知組件
第一步:新建一個services目錄,然後在該目錄下新建notice.js文件。
第二步:導入Notice.vue,並且定義一個創建Notice實例的靜態方法。
import Vue from 'vue';
import Notice from '@/components/Notice.vue'
// 給Notice添加一個屬性函數,用於創建Notice組件實例,並動態編譯後的Notice組件掛載到頁面上
Notice.getInstance = props => {
// 創建一個Vue實例
const instance = new Vue({
// 渲染函數,該函數用於把指定模版渲染爲DOM
render(h) {
return h(Notice, {props});
}
}).$mount();
// 把Notice組件添加到Body元素中
document.body.appendChild(instance.$el); // instance.$el用於獲取當前實例裏面的DOM
// 從vue實例中獲取notice實例
return instance.$children[0];
}
注意:Notice實例在應用中只需要創建一次即可。所以我們通過對單例來創建Notice實例。
// 單例創建Notice實例
let noticeInstance = null;
function getInstance() {
if (noticeInstance == null) {
noticeInstance = Notice.getInstance();
}
return noticeInstance;
}
第三步:對外暴露接口。
// 暴露接口
export default {
info({duration = 2, content = ''}) {
getInstance().add({
duration,
content
});
}
}
第四步:把notice配置到Vue實例中。
import notice from '@/services/notice'
Vue.prototype.$notice = notice;
第五步:測試。
// 添加購物車
addCart($event, item) {
// 把添加購物車的商品放在state中
this.$store.commit('addCart', item);
// 顯示通知
// const notice = this.$createNotice();
// notice.add({duration: 2, content: '加購物車成功'});
this.$notice.info({duration: 3, content: '加購物車成功'});
},
運行效果: