基於vue-cli3 + axios 構建多頁面應用H5移動端電商網站(下)

回顧

基於vue-cli3 + axios 構建多頁面應用H5移動端電商網站(上)

最近收到部分前端小夥伴的留言,期盼分享接上一篇未完成的移動電商項目實戰案例。雖然不負衆望,新鮮出爐。但由於本人精力有限,寫作水平一般,出土質量不高,望海涵。如果有web前端html+css+js+vue基礎的小夥伴,可以直接跳過上一篇,直接閱讀此文。接下來要講的,也是最主要的頁面功能開發階段。整個項目完成階段分爲:需求分析、原型討論、UI設計,前後端開發,測試,上線。廢話不多說,馬上進入主題。

移動端開發必備

  1. 使用rem單位進行手機適配,在路徑/src/assets/js/common.js裏面加入以下代碼,作爲公用JS方法,便於每個頁面調用。
// 頁面單位rem
rem: function () {
    var docEl = document.documentElement,
    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
    recalc = function () {
        var clientWidth = docEl.clientWidth;
        if (!clientWidth) return;
        if (clientWidth >= 750) {
            docEl.style.fontSize = '100px';
        } else {
            docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
        }
    };
    recalc();
    window.addEventListener(resizeEvt, recalc, false);
}

這是rem佈局的核心代碼,如果頁面寬度超過了750px,那麼頁面中html的font-size字體大小應爲100px。否則,頁面中html的font-size字體大小爲: 100 * (當前頁面寬度 / 750)。一般UI設計師提供的設計稿寬度是640px或750px,爲了方便計算,選擇750px,那麼轉化rem的時候,像素/100等於rem。比如:圖片寬度100px,100px/100=1rem。

  1. 禁用a,button,input,select,textarea等標籤背景變暗,直接在/src/assets/css/common.css加入如下代碼即可:
// 去掉點擊鏈接和文本框對象的半透明覆蓋(iOS)或者虛框(Android)
a, button, input, optgroup, select, textarea {
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
  1. meta設置
// 頁面窗口自動調整到設備寬度,並禁止用戶縮放頁面
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0">

屬性基本含義:

屬性 含義
width=device-width 控制viewport的大小
device-width 設備的寬度
initial 初始的縮放比例
minimun-scale 允許用戶縮放到的最小比例
maximun-scale 允許用戶縮放到的最大比例
user-scalable 用戶是否可以手動縮放

更多的meta:

//編碼格式
<meta charset="UTF-8">
// 優先使用 IE 最新版本和 Chrome
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>   
// 兼容國產瀏覽器的高速模式
<meta name="renderer" content="webkit">
// UC強制全屏
<meta name=”full-screen” content=”yes”>              
// 忽略將頁面中的數字識別爲電話號碼
<meta name="format-detection" content="telephone=no">
// 忽略 android 平臺對郵箱的識別
<meta name="format-detection" content="email=no">
// 添加到主屏幕後,會全屏顯示
<meta name="apple-touch-fullscreen" content="yes" />
// 當網站添加到主屏幕快速啓動方式,可隱藏地址欄,進針對ios的safari
<meta name="apple-mobile-web-app-capable" content="yes">
// 將網站添加到主屏幕快速啓動方式,僅針對ios的safari頂端狀態條的樣式
// 可選default、black、black-translucent
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
// 頁面描述
<meta name="description" content="不超過150個字符"/>  
// 頁面關鍵詞,多個關鍵詞用逗號分隔
<meta name="keywords" content=""/>      
// 需要在網站的根目錄下存放favicon圖標,防止404請求
<link rel="shortcut icon" href="/favicon.ico">
  1. webkit表單輸入框placeholder的顏色值改變
input::-webkit-input-placeholder{
   color: red;
}
  1. 消除原生外觀,在iOS手機端加上這個屬性才能給按鈕和輸入框自定義樣式
input, textarea {
  -webkit-appearance: none;
}
  1. 在iOS下禁止長按頁面時彈出菜單
a, img {
  -webkit-touch-callout: none;
}
  1. calc基本語法,支持加,減,乘,除; 在做手機端的時候非常有用的一個知識點。優點如下:

    (1) 支持使用 “+”、"-"、"*"、"/" 四則運算
    (2) 可以混合使用百分比(%)、px、em、rem等作爲單位可進行計算

    瀏覽器兼容性:IE9+、FF4.0+、chrome19+、safari6+

    用法如下:

    .box {
       	width: calc(100% - 20px - 2em);
    }
    <div class="box">測試文本</div>
    
  2. flex彈性佈局,容器的6個屬性,一般與rem配合使用絕佳。

    (1) flex-direction
    (2) flex-wrap
    (3) flex-flow
    (4) justify-content
    (5) align-items
    (6) align-content

以上6個屬性,具體如何使用,推薦阮一峯老師的flex佈局教程,熟讀+實操。
http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html

頁面組件開發

整個網站的主體包括首頁、商品列表頁、商品搜索頁、商品詳情頁、領取優惠券等頁面組成。

根據UI設計師提供的設計稿製作靜態界面,我們在動手前先來分析一下首頁有哪些場景設計或交互效果。上一篇已經介紹首頁長什麼樣,可以說出頭部導航,搜索框,分享彈框按鈕,輪播圖,商品類目,商品列表頁,返回頂部按鈕,預加載動畫。其他頁面大家也可以開動腦筋想一想它們的場景設計或交互效果,思考多了收獲也多。

再想一想,是不是可以進行前端組件化模塊化的思維開發呢?答案:肯定是。

也許有的小夥伴一知半解,有的卻很陌生,有的經常聽到面試官提這些概念問題。其實不難理解,簡單說前端架構設計的目的是制定標準,提高質量和效率。那合理的架構就囊括工程化、組件化、模塊化、規範化。

然而前端組件化、模塊化能幫我們解決哪些問題呢?

組件化更多關注UI部分,頁面的每個部件,比如頭部導航,搜索框,商品列表甚至返回頂部按鈕都可以成爲一個組件,每個組件有獨立的HTML、css、js代碼。可以根據需要把它放在頁面的任意部位,也可以和其他組件一起形成新的組件。一個頁面是各個組件的結合,可以根據需要進行組裝。

而模塊化側重功能的封裝,主要是針對Javascript代碼,隔離、組織複製的javascript代碼,將它封裝成一個個具有特定功能的模塊。(比如常用到ES6模塊

前面講的這些,還是沒弄明白的話,可以去網上查找相關資料。

步入正軌

由於時間關係,會挑幾個功能重點細講,先看看首頁模板組成:index.html,index.js,index.vue三個文件如下圖:
index.html
index.js
index.vue
首頁效果圖
首頁
瞭解了每個頁面組成部分,接下來可以進行頁面佈局,開發靜態界面,最後寫業務邏輯與後端聯調接口。之前有提到前端組件化思維,根據實際項目需求使用,經過分析,一般頭部或底部導航、商品分類、商品列表、返回頂部按鈕、loading預加載動畫等等都可以抽離出來做成組件,在src/components/位置存放所有組件。比如頭部導航是公用父組件,裏面包括搜索框,可抽離出來做成公用子組件。由於作者很懶,沒有實現抽離,直接寫在模板頁面。小夥伴可以自己抽時間嘗試改成組件形式。

既然要做前端組件化開發,那就拿商品分類爲例。在src/components/下創建categoryList.vue文件,在template模板標籤寫佈局,代碼如下圖:
在這裏插入圖片描述
樣式寫在style標籤裏面,如果要使樣式私有化,只在當下模塊有效,可以在style標籤上加scoped屬性。代碼如下圖:
在這裏插入圖片描述
Vue實例獲取商品分類數據,是在script標籤中export default模塊實現數據初始化,因爲是對象數組,所以在data()函數裏面自定義對象數組來初始化數據。代碼如下:

<script>
export default {
  data () {
    return {
      categoryList: [
        {
            id: "1",
            category: 2,
            name: "男裝",
            sort: "100",
            imgUrl: require('../assets/img/category/nanzhuang.png')
        },
        {
            id: "2",
            category: 1,
            name: "女裝",
            sort: "100",
            imgUrl: require('../assets/img/category/nvzhuang.png')
        },
        {
            id: "3",
            category: 6,
            name: "居家",
            sort: "100",
            imgUrl: require('../assets/img/category/jujia.png')
        },
        {
            id: "4",
            category: 4,
            name: "母嬰",
            sort: "100",
            imgUrl: require('../assets/img/category/muying.png')
        },
        {
            id: "5",
            category: 3,
            name: "內衣",
            sort: "100",
            imgUrl: require('../assets/img/category/meizhuang.png')
        },
        {
            id: "6",
            category: 7,
            name: "鞋包",
            sort: "100",
            imgUrl: require('../assets/img/category/xiebao.png')
        },
        {
            id: "7",
            category: 5,
            name: "美妝",
            sort: "100",
            imgUrl: require('../assets/img/category/meizhuang.png')
        },
        {
            id: "8",
            category: 0,
            name: "更多",
            sort: "100",
            imgUrl: require('../assets/img/category/more.png')
        }
      ]
    }
  }
 }

在template模板裏面獲取初始化對象數組數據,採用v-for循環遍歷對象數組,代碼如下:

<ul class="category fix">
  <li v-for="item in categoryList">
    <a href="javascript:;" @click="gotoSearch(item.category)" target="_blank">
      <img :src="item.imgUrl" :alt="item.name" :title="item.name">
      <span class="category-tit">{{item.name}}</span>
    </a>
  </li>
</ul>

順便提一下Vue常用的點擊事件,可以用v-on指令監聽 DOM 事件,並在觸發時運行一些 JavaScript 代碼。

v-on:click = handleClick  // handleClick方法名,可帶參數
// OR 
@click = handleClick  // 點擊事件簡寫

點擊事件中調用的自定義方法名,統一寫在methods屬性裏面,代碼如下:

methods: {
  handleClick () {
    console.log('點擊我') ;
  }
}

商品分類組件完整代碼如下:

<template>
  <div class="category-list">
      <div class="floor-area">
          <ul class="category fix">
              <li v-for="item in categoryList">
                <a href="javascript:;" @click="gotoSearch(item.category)" target="_blank">
                  <img :src="item.imgUrl" :alt="item.name" :title="item.name">
                  <span class="category-tit">{{item.name}}</span>
                </a>
              </li>
          </ul>
      </div>
  </div>
</template>

<script>

export default {
  data () {
    return {
      categoryList: [
        {
            id: "1",
            category: 2,
            name: "男裝",
            sort: "100",
            imgUrl: require('../assets/img/category/nanzhuang.png')
        },
        {
            id: "2",
            category: 1,
            name: "女裝",
            sort: "100",
            imgUrl: require('../assets/img/category/nvzhuang.png')
        },
        {
            id: "3",
            category: 6,
            name: "居家",
            sort: "100",
            imgUrl: require('../assets/img/category/jujia.png')
        },
        {
            id: "4",
            category: 4,
            name: "母嬰",
            sort: "100",
            imgUrl: require('../assets/img/category/muying.png')
        },
        {
            id: "5",
            category: 3,
            name: "內衣",
            sort: "100",
            imgUrl: require('../assets/img/category/meizhuang.png')
        },
        {
            id: "6",
            category: 7,
            name: "鞋包",
            sort: "100",
            imgUrl: require('../assets/img/category/xiebao.png')
        },
        {
            id: "7",
            category: 5,
            name: "美妝",
            sort: "100",
            imgUrl: require('../assets/img/category/meizhuang.png')
        },
        {
            id: "8",
            category: 0,
            name: "更多",
            sort: "100",
            imgUrl: require('../assets/img/category/more.png')
        }
      ]
    }
  },
  methods: {
    gotoSearch (category) {
      window.location.href = `../search?category=${category}` ;
    }
  },
  created () {},
  mounted () {}
}
</script>
  
<style scoped>
.floor-area {
    background: #fff;
    text-align: center;
    overflow: hidden;
    padding-top: .2rem;
    border-top: 1px solid #eee;
}
.floor-area li {
    width: 25%;
    display: block;
    float: left;
    margin-bottom: .2rem;
}
.floor-area li a {
  display: block;
}
.floor-area li img {
    width: 1rem;
    height: 1rem;
    display: block;
    margin: 0 auto;
}
.floor-area li .category-tit {
    font-weight: 400;
    display: block;
    text-align: center;
    padding-top: .1rem;
    font-size: .24rem;
}
</style>

商品分類組件開發完成後,如何引入自定義組件,代碼如下:

<template>
  <div id="app">
  	// 模板中使用組件
  	// 第一種寫法
  	<category-list></category-list>
  	
  	// 第二種寫法
  	<category-list>
		// slot自定義插槽內容,具體用法,參考Vue官網:https://cn.vuejs.org/v2/guide/components-slots.html
	</category-list>
  	
  	// 第三種寫法
  	<categoryList />
  </div>
</template>

<script>
// 引入組件
import categoryList from '@/components/categoryList'

export default {
	components: { categoryList }, // 註冊組件
	data() {
		return {
			// 設置初始化數據
		}
	},
	methods: {},
	created() {},
	mounted() {}
}
</script

現在講講首頁輪播圖效果有自動播放、左右滑動、點擊圖片跳內頁或外鏈等等功能。如果自己想手寫Vue輪播圖特效沒問題,多花一點點時間就可以完成,但如果趕項目,可以找一些市面上很成熟的開源免費插件,直接引入到你的項目中。我直接選用cdn方式引入swiper插件,操作如下:

// 先在首頁index.html文件中引入以下外鏈
<link rel="stylesheet" href="https://unpkg.com/swiper/css/swiper.css">
<link rel="stylesheet" href="https://unpkg.com/swiper/css/swiper.min.css">
<script src="https://unpkg.com/swiper/js/swiper.js"> </script>
<script src="https://unpkg.com/swiper/js/swiper.min.js"> </script>

在template模板中使用
在這裏插入圖片描述
先在script標籤下的methods屬性中自定義初始化Swiper實例的方法initSwiper,然後再mounted生命週期函數中調用initSwiper方法,使輪播圖生效,如下圖:
在這裏插入圖片描述
Swiper插件的具體用法,可以參考官網API和DEMO示例:https://swiperjs.com/

動態獲取數據調用API接口

上面提過開發商品分類組件數據獲取方式是靜態的,如果商品列表要動態獲取數據,該怎麼操作。由於項目採用前後端分離技術,通過後端提供的API接口調用獲取動態數據。要求前後端同步進行開發,但是在後端完成前,暫時是沒有數據返回給前端使用的,如果先寫靜態後面再改,就有重複工作的內耗存在。所以我們需要一種簡單快速的模擬數據的模塊或管理工具,這樣我們自己提供或修改接口。下面提供兩種方式,博主推薦第二種操作更簡便。

一、mock文件

  • 安裝mockjs和axios
npm install -D mockjs
npm install -S axios
  • 在src目錄下新建mock文件夾,並創建index.js文件,結構如下:
    在這裏插入圖片描述
  • index.js文件內容及返回的數據結構如下(注意:返回的數據結構可以根據不同的功能模塊寫成單獨的JS文件):
import Mock from 'mockjs'

let shopData = {
	'success|1': [true, false],
	'msg': function() { 
		if (this.success) {
			return '調用成功';
		} else {
			return '調用失敗';
		}
	},
	'pageNum': 1,
	'pageSize': 10,
	'data': function() {
		if (this.success) {
			return this.result;
		} else {
			return this.result = [];
		}
	},
	'result|1-10': [{
		'id|+1': 1,
		'GoodsId': '@guid',
		'GoodsName|1': ['浙江特產手工糯米糕桂花糕250g', '【買一送一】夏季冰絲男褲休閒長褲', '仁和紅豆薏米祛茶溼茶飲小袋裝'],
		'actDate': '@now',
		'sales|0-100': 10,
		'ShopName|1': ['漢蘭圖旗艦店', '吳玉源旗艦店', '帥趣旗艦店'],
		'ImgUrl': '@image("200x200", "#00405d", "#FFF", "Mock.js")',
		'GoodsPrice|1-200.1': 50,
		'GoodsLink': '@url'
	}],
	'totalNum': function() {
		return this.data.length; 
	},
}

//格式:Mock.mock(url, post/get, 返回的數據)
Mock.mock('/api/shoplist', shopData)

export default Mock
  • 在src/js/api.js文件中添加模擬商品列表接口的get方法,代碼如下:
// mockjs 模擬商品列表接口
export function getMockData() {
  return network({
    url: '/shoplist',
    method: 'get'
  });
}
  • 在src/js/url.js文件中添加以下代碼保證與mock數據url地址一致,如下圖:
    在這裏插入圖片描述
  • 驗證mock接口,直接在首頁index.vue文件中使用
// 在script標籤下引入定義好的mock接口方法
import { getMockData } from '@/assets/js/api'

methods: {
	// 封裝mock接口方法調用
	getMockList() {
      getMockData().then(res => {
        console.log('mockData===', res)
      }).catch(err => {
        console.log(err)
      })
    }
}

mounted () {
    this.getMockList(); // 初始化調用mock數據
}
  • 返回結果如下圖:
    在這裏插入圖片描述

二、yapi管理工具

在這裏插入圖片描述
在這裏插入圖片描述
下面簡單講一下,我爲什麼會選擇yapi請看上面的截圖,或去官網瞭解一下,是基於mockjs和json5,可視化接口操作,github上小星星不少,說明使用的人很多,唯一缺點無法使用mockjs函數功能。廢話太多,直接開幹。先去官網註冊一個賬號,再進入後臺管理界面。如下圖:
在這裏插入圖片描述

  • 創建項目

第一次進來是沒有我的項目,需要自己去創建項目,上面截圖有說明。接下來點擊添加項目,進入新建項目頁面。如下圖:
在這裏插入圖片描述
一般填個項目名稱,其他默認,提交就可以了。

  • 添加接口

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
以上配置完成後,點擊保存按鈕,提示保存成功後,再回到預覽界面。如下圖:
在這裏插入圖片描述
直接打開postman,測試API接口,如調用成功,說明模擬數據接口配置有效,如下圖:
在這裏插入圖片描述
如需瞭解更多yapi的功能,請查看官網文檔:https://hellosean1025.github.io/yapi

父子組件相互傳值

先來說兩個概念:

  • 父組件通過props屬性給子組件傳值
  • 父組件監聽子組件this.$emit('事件名', 參數)方法獲取值

現在拿項目實例講解上面兩個功能,比如首頁index.vue是父組件,二維碼公衆號彈框是子組件,如下圖:
在這裏插入圖片描述
父組件傳值給子組件,先在父組件data中定義isPopup初始值,然後在子組件上綁定isPopup,代碼如下:

<template>
  <div id="app">
    <div class="wrap">

      <qrcode-pop :isPopup="isPopup" @showPopper="closeBtn">
        <h3>微信打開長按二維碼關注公衆號</h3>
        <div class="qrcode-img">
          <img src="../../assets/img/qrcode.jpg" class="qrcode">
        </div>
        <div class="close-btn" @click="closeBtn">關閉</div>
      </qrcode-pop>

    </div>
  </div>
</template>

<script>
import qrcodePop from '@/components/qrcodePop'

export default {
  components: {
    qrcodePop
  },
  data () {
    return {
      isPopup: false
    }
  },
  methods: {
    closeBtn () {
      this.isPopup = false;
    }
  }
}
</script>

<style scoped>

</style>

子組件使用props屬性,接收父組件isPopup狀態的傳值,可以設定默認傳值類型。代碼如下:

<template>
	// 二維碼彈框提示
	<div id="qrcodePop" class="qrcodePop" v-show="isPopup">
		<div class="qrcode-box">
			<slot></slot>
		</div>
		<div id="mask" @click="closeBtn"></div>
	</div>
</template>

<script>

export default {
	data () {
		return {	
		}
	},
	props: {
		isPopup: Boolean
	},
	methods: {
		closeBtn () {
			this.$emit('showPopper');
		}
	}
}
</script>

<style scoped>
#mask {
	position: fixed;
	top: 0;
	left: 0;
	bottom: 0;
	right: 0;
	height: 100%;
	width: 100%;
	z-index: 9999;
	background: rgba(0, 0, 0, .7);
}
.qrcode-box {
	position: fixed;
	top: 50%;
	left: 50%;
	width: 6rem;
	height: 7rem;
	margin-top: -3.5rem;
	margin-left: -3rem;
	display: flex;
    justify-content: space-between;
    flex-direction: column;
	background: #fff;
	border-radius: .2rem;
	z-index: 10000;
	overflow: hidden;
    text-align: center;
}
.qrcode-box h3 {
	width: 100%;
	height: auto;
	padding: .2rem 0;
	font-size: .32rem;
	color: #fff;
	background: #fc0786; 
	text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
}
.qrcode-box img {
	width: 5.16rem;
	height: 5.16rem;
}
.qrcode-box .close-btn { 
    width: 100%;
    height: auto;
    padding: .2265rem 0;
    font-size: .32rem;
    border-top: 1px solid #ddd;
    background: #f2f2f2;
}

.layer-cont {
	padding: .2rem .3rem 0 .3rem;
	line-height: .44rem;
    text-align: center;
}
.kouling-cont {
    position: relative;
    background: #fff4f8;
    padding: .2rem;
    margin: 0 auto;
    display: flex;
	align-items: center;
	justify-content: center;
}

#textarea {
	display: block;
    font-size: .24rem;
    width: 100%;
    height: 2.2rem;
    line-height: .28rem;
    color: #fc0786;
    background: #fff;
    resize: none;
    border: none;
  outline: none;
  overflow-x: hidden;
    word-wrap: break-word;
    word-break: break-all;
} 
.better-change{
	display: flex;
	align-items: center;
	justify-content: space-around;
}
.onecopy {
	width: 50%;
	padding: .25rem .0;
    background: #f8285c;
    text-align: center;
    margin: 0 auto;
    color: #fff;
    cursor: pointer;
    border-top: 1px solid #ddd;
    font-size: .32rem;
}
.kouling-tips p {
	display: block;
    border-bottom: 1px dotted #e5e5e5;
    padding: .15rem 0;
    text-align: justify;
    font-size: .28rem;
}
.kouling-tips i {
	color: #dd514c;
}
.kouling-tips p.nber {
	border-bottom: none;
}
</style>

子組件傳值給父組件,在父組件中定義方法名closeBtn,並在子組件上添加自定義事件名@showPopper,如下圖:
在這裏插入圖片描述
在這裏插入圖片描述
在子組件中將this.$emit()方法放在closeBtn點擊函數裏面,來觸發父組件事件傳遞isPopup狀態值,如下圖:
在這裏插入圖片描述
父子組件之間的傳值已完成,最後再補充一下rem適配手機頁面生效,需在每個Vue模板頁面引入即可,代碼如下:

<script>
import Export from '@/assets/js/export'

export default {
	data() {
		return  {}
	},
	methods: {},
	created () {
    	Export.rem(); // 調用rem手機頁面適配方法
  	},
  	mounted () {}
  }
}
</script>

項目中涉及的功能點基本講完,若需看整站效果或源代碼請移步去github上查看或下載。附上地址:https://github.com/jackchen0120/woyouzhe

以上講解對大家有所幫助的話,還請三連擊(點贊-評論-加關注),如有錯誤,歡迎大家批評指正,我們一起交流學習,共同進步。

後續會發表一些實戰型項目的前端技術文章,讓更多小夥伴不花錢就能學到前端實戰經驗,如:

  • SPA單頁面開發H5移動端和後臺管理系統
  • 多頁面應用開發後臺管理系統
  • 小程序開發實踐
  • 大廠前端性能優化解決方案
  • 基於Vuejs大數據可視化大屏
  • react native開發APP應用項目
  • 區塊鏈相關項目開發
  • 等等。。。

歡迎關注個人公衆號:懶人碼農
在這裏插入圖片描述

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