回顧
基於vue-cli3 + axios 構建多頁面應用H5移動端電商網站(上)
最近收到部分前端小夥伴的留言,期盼分享接上一篇未完成的移動電商項目實戰案例。雖然不負衆望,新鮮出爐。但由於本人精力有限,寫作水平一般,出土質量不高,望海涵。如果有web前端html+css+js+vue基礎的小夥伴,可以直接跳過上一篇,直接閱讀此文。接下來要講的,也是最主要的頁面功能開發階段。整個項目完成階段分爲:需求分析、原型討論、UI設計,
前後端開發
,測試,上線。廢話不多說,馬上進入主題。
移動端開發必備
- 使用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。
- 禁用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);
}
- 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">
- webkit表單輸入框placeholder的顏色值改變
input::-webkit-input-placeholder{
color: red;
}
- 消除原生外觀,在iOS手機端加上這個屬性才能給按鈕和輸入框自定義樣式
input, textarea {
-webkit-appearance: none;
}
- 在iOS下禁止長按頁面時彈出菜單
a, img {
-webkit-touch-callout: none;
}
-
calc基本語法,支持加,減,乘,除; 在做手機端的時候非常有用的一個知識點。優點如下:
(1) 支持使用 “+”、"-"、"*"、"/" 四則運算
(2) 可以混合使用百分比(%)、px、em、rem等作爲單位可進行計算瀏覽器兼容性:IE9+、FF4.0+、chrome19+、safari6+
用法如下:
.box { width: calc(100% - 20px - 2em); } <div class="box">測試文本</div>
-
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三個文件如下圖:
首頁效果圖
瞭解了每個頁面組成部分,接下來可以進行頁面佈局,開發靜態界面,最後寫業務邏輯與後端聯調接口。之前有提到前端組件化思維,根據實際項目需求使用,經過分析,一般頭部或底部導航、商品分類、商品列表、返回頂部按鈕、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應用項目
- 區塊鏈相關項目開發
- 等等。。。
歡迎關注個人公衆號:懶人碼農