Vue電商項目實戰(三)

一、顯示頭部

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: '加購物車成功'});
},

運行效果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章