vue全家桶上手小專案
實現功能
主要用到的技術:vue-cli + vue2 + vue-router2 + vuex2 + axios + es6 + sass + eslint
主要實現的功能:
頁面的資料通過 axios 模擬請求本地的 json 檔案獲得;
vue-router2 實現各頁面的相互跳轉;
vuex2 全域性狀態的管理,如頭部導航的標題內容,側欄的顯示狀態;
簡易購物車功能,詳情頁加入購物車的商品,隨機生成單價、商品名字;
購物車的資訊通過localstorage儲存在本地;
註冊登入的資訊也是通過localstorage儲存在本地。
專案目錄結構
proj5-shop 目錄結構,主要看src目錄和static目錄的:
│--build
|--config
|--dist
|--src
|--assets
|--logo.png
|--components
|--cart 購物車頁
|--cate 商品列表頁,商品詳情頁
|--center 個人中心,註冊登入
|--com 公共模組
|--header.vue 頭部
|--loading.vue 載入
|--sidebar.vue 導航側欄
|--swiper.vue 輪播
|--jam.js 公共功能函式
|--localDB.js localStorage本地儲存
|--page 首頁
|--Hello.vue
|--static 本地資料模擬請求(需放static目錄下)
|--data
|--cart.json
|--cate.json
|--index.json
| .gitkeep
|--test
│ .babelrc
│ .editorconfig
│ .eslintignore
│ .eslintrc.js
│ .gitignore
│ index.html
│ package.json
│ README.md
vue-cli 初始化及配置修改
vue-cli 腳手架官方安裝:https://github.com/vuejs-templates/webpack
$ npm install -g vue-cli
$ vue init webpack proj5-shop
$ cd proj5-shop
$ npm install
$ npm run dev
vue-cli初始化完成後,繼續新增安裝以下依賴:
cnpm install axios node-sass vuex sass-loader vue-swipe --save-dev
修改 build/webpack.base.conf.js
,使其對import引入的sass支援:
{
test: /.vue$/,
loader: `vue-loader`,
options: vueLoaderConfig
}
// 將上面的修改成下面的:
{
test: /.vue$/,
loader: `vue-loader`,
options: {
loaders: {
`scss`: `vue-style-loader!css-loader!sass-loader`,
`sass`: `vue-style-loader!css-loader!sass-loader?indentedSyntax`
}
}
}
關鍵功能技術點剖析
template 與指令
商品分類頁 src/components/cate/cate.vue
的 template:
<template>
<div class="s-cate">
<div class="cate-nav">
<div class="nav-out">
<div class="nav">
<a class="nav-a" href="javascript:;"
v-for="(type, index) in types"
:class="{`nav-a-act`: index==nowIndex}"
@click="clickType(type.type_now, index)">
{{type.type_name}}
</a>
</div>
</div>
</div>
<div class="cate-cont">
<ul>
<li v-for="brand in allBrand" v-if="nowType==brand.type || nowType==`type_all`">
<router-link to="detail" class="cont-li" href="javascript:;">
<img class="pic" :src="brand.brand_pic_url"/>
<span class="name">{{brand.brand_name}}</span>
<span class="price">{{brand.brand_price}}</span>
</router-link>
</li>
</ul>
</div>
</div>
</template>
商品詳情頁 src/components/cate/detail.vue
的 template:
<template>
<div class="s-detail">
<comSwiper></comSwiper>
<div class="cont">
<p class="name">{{detailData.cart_name}}</p>
<span class="price">¥{{detailData.cart_price}}</span>
<div class="goods-counter">
<a href="javascript:;" class="btn-sub" @click="changeNum(-1, detailData)"> - </a>
<input type="text" class="goods-num" readonly="readonly" v-model="detailData.cart_num">
<a href="javascript:;" class="btn-add" @click="changeNum(1, detailData)"> + </a>
</div>
</div>
<div class="bot">
<!-- <router-link class="add-cart" v-on:click.native="addCart" to="/cart">加入購物車</router-link> -->
<a class="add-cart" href="javascript:;" @click="addCart">加入購物車</a>
</div>
</div>
</template>
axios 資料請求
首頁的資料請求:
首先在入口檔案 main.js
引入 axios,並將其掛在到 Vue 全域性方法下:
// main.js
import axios from `axios`
Vue.prototype.$http = axios
在首頁 page/index.vue
使用 axios:
<script>
import comSwiper from `../com/swiper`
import `../../css/index.scss`
export default {
// 首先宣告一些頁面資料變數
data () {
return {
dataIndex: {},
temai: {},
rexiao: {},
jingpin: {}
}
},
created () {
// created的時候請求資料
this.getDataIndex()
},
methods: {
// 請求到資料並賦值給data裡宣告的變數
getDataIndex () {
this.$http.get(`../../static/data/index.json`).then((response) => {
this.dataIndex = response.data
this.temai = this.dataIndex.data.temai
this.rexiao = this.dataIndex.data.rexiao
this.jingpin = this.dataIndex.data.jingpin
}, (response) => {
// error
})
}
}
}
</script>
最終將資料渲染在 template 上:
<div class="cont-main cont-rexiao">
<router-link to="/detail" class="cont-left" href="javascript:;"
v-for="(brand, key, index) in rexiao"
v-if="key==0"
:key="brand.id">
<span class="name">{{brand.brand_name}}</span>
<span class="desc">{{brand.brand_desc}}</span>
<img class="pic" :src="brand.brand_pic_url"/>
</router-link>
<div class="cont-right">
<router-link to="/detail" class="cont-right-one" href="javascript:;"
v-for="(brand, key, index) in rexiao"
v-if="key>=1"
:key="brand.id">
<p class="text">
<span class="name">{{brand.brand_name}}</span>
<span class="desc">{{brand.brand_desc}}</span>
</p>
<img class="pic" :src="brand.brand_pic_url"/>
</router-link>
</div>
</div>
router 的跳轉
router/router.js
路由:
import Vue from `vue`
import VueRouter from `vue-router`
Vue.use(VueRouter)
import App from `../App.vue`
import Index from `../components/page/index.vue`
import Cate from `../components/cate/cate.vue`
import Detail from `../components/cate/detail.vue`
import Center from `../components/center/center.vue`
import Cart from `../components/cart/cart.vue`
export default new VueRouter({
routes: [
{
path: `/`,
redirect: `/index`,
component: App,
children: [
{path: `index`, name: `index`, component: Index},
{path: `cate`, name: `cate`, component: Cate},
{path: `detail`, name: `detail`, component: Detail},
{path: `center`, name: `center`, component: Center},
{path: `cart`, name: `cart`, component: Cart}
]
}
],
linkActiveClass: `footer-act`
})
主要是通過 router-link
來跳轉,比如導航欄 com/sidebar.vue
的跳轉:
<ul class="ul-nav" v-show="show">
<li><router-link to="/index"><span>首頁</span><i>></i></router-link></li>
<li><router-link to="/cate"><span>分類</span><i>></i></router-link></li>
<li><router-link to="/center"><span>我的</span><i>></i></router-link></li>
<li><router-link to="/cart"><span>購物車</span><i>></i></router-link></li>
</ul>
當然,在 加入購物車
的時候,採用的程式設計式導航跳轉路由:
<!-- <router-link class="add-cart" v-on:click.native="addCart" to="/cart">加入購物車</router-link> -->
<a class="add-cart" href="javascript:;" @click="addCart">加入購物車</a>
// 程式設計式導航,點選時觸發路由跳轉
router.push({ path: `cart` })
vuex 狀態管理
vuex 狀態管理主要是頭部的顯示資訊、導航欄的顯示隱藏狀態:
先來看 store/store.js
:
import Vue from `vue`
import Vuex from `vuex`
Vue.use(Vuex)
export default new Vuex.Store({
state: {
sideBarState: false, //導航側欄的顯示狀態
headerTitle: `預設的頭部標題` //不同頁面頭部標題的變更
},
mutations: {
changeSideBarState (state, boolean) {
state.sideBarState = boolean
},
changeHeaderTitle (state, str) {
state.headerTitle = str
}
},
actions: {
// changeSideBarState (context, status) {
// context.commit(`changeSideBarState`, status)
// }
// es6解構寫法
changeSideBarState ({commit}, status) {
commit(`changeSideBarState`, status)
},
changeHeaderTitle ({commit}, str) {
commit(`changeHeaderTitle`, str)
}
},
getters: {
getSideBarState (state) {
return state.sideBarState
},
getHeaderTitle (state) {
return state.headerTitle
}
}
})
例如,在進入分類頁 cate/cate.vue
時,會在 created
的時候觸發頭部標題的變更;
當點選頭部 導航
時,又會觸發導航側欄的顯示狀態的變更:
created () {
this.$store.dispatch(`changeHeaderTitle`, `分類`)
},
methods: {
showSideBar () {
return this.$store.dispatch(`changeSideBarState`, true)
// return this.$store.commit(`changeSideBarState`, true)
},
hideSideBar () {
return this.$store.dispatch(`changeSideBarState`, false)
}
}
購物車
在進入商品詳情頁的時,會隨機生成商品的名稱和價格,使得 加入購物車
時能在購物車頁面區分開個商品(主要是還沒做後端node+mongodb的資料):
// cate/detail.vue
data () {
return {
detailData: {
id: 100048,
type: `type_man`,
isSelect: true,
cart_img: `http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg`,
cart_name: `商品名字` + this.getRandom(10, 100),
cart_num: 1,
cart_price: this.getRandom(10, 100)
}
}
}
點選 加入購物車
,實際就是將該商品的 data 資訊加入到 localStorage 的本地儲存中;這裡主要用到一個自己定義的 localDB.js
:
export default class todoDb {
constructor (name) {
this.name = name
if (JSON.stringify(this.get(this.name)) === `{}`) {
this.set([])
}
}
set (val) {
window.localStorage.setItem(this.name, JSON.stringify(val))
}
get () {
return JSON.parse(window.localStorage.getItem(this.name)) || {}
}
}
購物車 cart/cart.vue
主要思路就是:讀取本地儲存購物車中的localStorage 所有產品資訊並顯示出來;根據使用者增刪操作、來更新本地購物車儲存的產品資訊。
具體的實現直接看 購物車原始碼。
註冊登入
註冊登入的使用者中心頁面 center/center.vue
,主要是控制三種狀態的顯示與隱藏:登入、註冊、登入成功後的使用者中心,主要是data裡面的 showState ;
還有就是表單資料的一些繫結驗證,主要是data裡面的 dataLogin 。
<input name="mobile" type="tel" placeholder="請輸入手機號" maxlength="11" v-model="dataLogin.name">
<input name="password" type="tel" placeholder="請輸入密碼" maxlength="6" v-model="dataLogin.pass">
<script>
data () {
return {
tips: ``,
showState: `logined`,
dataLogin: {
name: ``,
pass: ``,
code: ``
},
// showState: `register`
// showState: `logining`
}
}
</script>
詳情看github原始碼中的 center/center.vue
。
新手的小坑總結
build 後需在伺服器開啟訪問
執行 npm run build
後生成的dist,直接在瀏覽器以本地檔案 file:// … 開啟裡面的index.html 、是訪問不了的;需要放在伺服器上才能訪問;或者自己在本地開啟一個伺服器。
引入的component,外層要有容器
如下面的 <comHeader></comHeader> ,它外層一定要有容器、把它包裹著:
<div class="shop">
<comHeader></comHeader>
</div>
v-for key
component lists rendered with v-for should have explicit keys:
<router-link to="/detail" class="cont-one" v-for="brand in temai" :key="brand.id">
// ...
</router-link>
詳見:https://cn.vuejs.org/v2/guide/list.html#key
原生html報waring
當我們引入的component命名成原生的html時,會報warning,於是把
<hearder></header> // 報 warning
<comHearder></comHeader>
import comHeader from `./components/com/header.vue`
components: {
// hearder: hearder
comHeader: comHeader
}
判斷物件為空
if (typeof myObj == "undefined") {
var myObj = {};
}
webstorm卡頓
webstorm 需要把 node_models 資料夾排除掉(exclued),不然很卡頓。
目前正學習node+mongodb,準備以此取代該專案中模擬的本地資料請求和localStorage,專案程式碼:https://github.com/gjincai/vue-node-proj