歡迎你們前往騰訊雲社區,獲取更多騰訊海量技術實踐乾貨哦~html
做者:曾柏羲 前端
入職接到的第一個需求是實現一個關於K歌實體售賣的ERP系統,管理系統過去作過很多,此次打算換個姿式,基於時下正熱但早已不新鮮的Vue 2.0技術實現。本文首先對Vue的相關技術進行簡單介紹與分析,接着總結開發實踐(主要描述 Vuex 實踐)過程當中的流程規範,並記錄在此過程當中遇到的問題與關鍵點,最後作出一點實踐的總結與思考。vue
圖:vue 2.0react
衆所周知,現在的前端框架/解決方案數不勝數,從最初的Backbone,到Angular和Meteor等,這當中有不少都爲前端的工程化管理和建設提供了一整套解決方案,是一種「大」框架,但這樣的框架每每具有必定的排它性,使得開發的自由和靈活度受到限制。webpack
與此不一樣的是,Vue對本身的定位是一個漸進式的JavaScript框架,它最核心的部分是隻是爲了解決視圖層方面的問題,提供聲明式渲染和組件化管理模式。同時對於路由管理、狀態管理和構建工做方面又有本身的解決方案,開發者能夠自由地選擇或者組合,從而可以更加靈活自如地進行項目開發。ios
手動改變DOM操做是件損耗性能的事情,幾乎全部MVX框架都遵循一個原則:視圖的狀態應該由數據描述,而且經過數據驅動變化。git
圖:雙向數據綁定github
Vue採用發佈者-訂閱者模式實現雙向數據綁定,首先Vue將會獲取到須要監聽的對象的全部屬性,經過 Object.defineProperty 方法完成對象屬性的劫持,將其轉化爲getter和setter,當屬性被訪問或修改時,當即將變化通知給訂閱者,並由訂閱者完成相應的邏輯操做,主要流程下圖所示。web
圖:響應式原理ajax
整個過程當中主要涉及了 Observer、Dep 和 Watcher三個類,相關源碼(已精簡)以下:
Observer:
主要處理屬性監聽邏輯,將監聽屬性轉化爲get/set屬性,當屬性被訪問時,調用dep.depend() 方法,而屬性被修改時,則調用了dep.notify()方法。
export function defineReactive (
obj: Object,
key: string,
val: any
) {
const dep = new Dep()
// 監聽屬性的get和set方法
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
dep.depend()
return val
},
set: function reactiveSetter (newVal) {
val = newVal
dep.notify()
}
})
}複製代碼
Dep:
擔任發佈者的角色,維護訂閱者列表,負責訂閱者的添加和通知工做,上面所提到的depend()和notify()方法在這裏實現。
export default class Dep {
id: number;
// 訂閱者列表
subs: Array<Watcher>;
// 添加訂閱者
addSub (sub: Watcher) {
this.subs.push(sub)
}
// 屬性被訪問時調用該方法,通知依賴的目標(即訂閱者)添加該依賴,
// 同時將其加入訂閱者列表中(調用addSub()方法)
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
// 當監聽到依賴的屬性發生改變時,通知訂閱者執行狀態更新操做
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}複製代碼
Watcher:
擔任訂閱者角色,即上述代碼中的 Dep.target,能夠訂閱多個Dep,在每次收到發佈者消息通知時觸發update()方法執行更新邏輯。
export default class Watcher {
deps: Array<Dep>;
newDeps: Array<Dep>;
depIds: ISet;
newDepIds: ISet;
// 爲該指令添加依賴(發佈者)
addDep (dep: Dep) {
const id = dep.id
this.newDepIds.add(id)
this.newDeps.push(dep)
dep.addSub(this)
}
// 更新視圖邏輯,依賴的屬性值發生改變時觸發
update () {
// 省略
}
}複製代碼
過去爲了實現父子組件或者平行組件的數據通訊,常見的作法是直接或間接地使用 props 屬性和 emit() 方法來實現,這樣的作法耦合度強,且難以應付複雜場景下的狀態管理。
Vuex 的出現能夠很好地規避此類問題,它是一種Vue應用的專用狀態管理模式,負責集中式地存儲和管理整個Vue應用程序的組件狀態,實現更好的狀態共享。Vuex將組件狀態的存儲和管理放在了 Store 裏面,併爲其提供了4種特性,分別是 state、actions、mutations 和 getters:
圖:vuex狀態管理
Vuex狀態管理流程如上圖所示,主要分爲以下四個步驟:
圖:vue devtools
對於開發過程當中 Vuex 狀態的追蹤,能夠經過 Vue Devtools 的 「Vuex」 一欄進行查看,如上圖所示,安裝方法能夠自行搜索。
圖:K歌erp
全民K歌初期在試水麥克風售賣活動中取得了良好的市場反饋,爲繼續推進周邊實體的售賣,如今指望搭建一個屬於本身的售賣平臺。整個需求分爲H5和PC兩部分,其中H5爲用戶購買實體周邊的入口,PC則是對用戶的訂單數據進行管理。本項目爲需求中的PC部分,共由訂單數據概覽、待審覈、待發貨、已發貨和退換貨五頁組成。
圖:項目頁面
頁面具體介紹以下:
此外,對於全部的列表頁,須要提供批量操做,如批量審覈、發貨退款和物流信息導入等,並提供分頁操做。同時登錄須要經過K歌掃碼完成,全部的CGI調用須要在K歌的登錄態下進行。
項目的構建使用 vue-cli 完成,具體操做流程以下:
圖: vue-cli構建項目
因爲公司網絡緣由,在執行 vue-init webpack [project-name] 時會出現沒法下載模板庫的錯誤,解決辦法能夠是經過設置如圖中所示的 npm 代理,或者是直接下載 vue 模板中的wepack庫並在本地運行完成。
圖: 開發實踐
(1) 目錄規劃
本文針對 vue-cli 構建出的項目進行結構上的調整,最終得出以下所示的目錄結構:
圖:項目目錄結構
如上圖,client目錄爲客戶端的主要開發目錄,主要包含了程序入口文件 app.js、客戶端路由文件 router、客戶端視圖曾view、組件模塊components和應用狀態管理層(即Vuex)Store。
(2) Vuex規範
前面已經提到,訂單相關的頁面共有四頁,對應着待審覈、待發貨、已發貨和退換貨四種狀態,因爲每種狀態的相關操做邏輯不一樣,在開發過程當中將Store中的order模塊劃分爲review、ship、completed和refund四個子模塊。下面代碼展現了一個簡化的review子模塊的代碼,其餘模塊的代碼與之相似,都包含了 state、mutations、getters和actions對象。
// store/modules/order/review/index.js
const state = {
re_order_data: {}
}
const mutations = {
[types.GET_RE_ORDER_LIST] (state, data) {
state.re_order_data = data
}
}
const getters = {
re_order_list: state => state.re_order_list
}
const actions = {
getReOrderList ({commit , state, rootState}, params) {
common.ajax({
url: orderList,
data:{
type: 5,
filter_by: STATUS_HAVE_PAY
}
}).always(res => {
commit('GET_RE_ORDER_LIST', res.data)
})
}
}
export default {
state,
mutations,
getters,
actions
}複製代碼
此時你也許注意到,Store被劃分紅多個模塊,而每一個模塊裏面可能又會有更細粒度的劃分。在實際的運行過程當中,須要對這些模塊進行整合,這裏須要用到Vuex提供的modules屬性,相關代碼以下:
import app from './modules/app'
import menu from './modules/menu'
import order from './modules/order'
const store = new Vuex.Store({
modules: {
app,
menu,
...order
}
})複製代碼
(3) 組件調用
組件對Vuex中state狀態的調用邏輯一般是放在 data 或 computed 屬性中,但須要注意的是,若是指望獲得的是響應式的數據,則必須將調用邏輯放在計算屬性 computed 中,這樣當每次state狀態發生變化時,computed 屬性中的數據都會被從新計算,同時從新觸發更新視圖。此外組件也能夠直接調用Vuex中的mutations和actions事件,這一般放在methods屬性中進行。
路由處理,對於一個單頁應用,天然少不了路由處理,項目的路由使用官方的vue-router處理,使用router.beforeEach()方法在每次路由跳轉前進行攔截,判斷用戶是否登陸,如沒有登陸則跳轉至登陸頁。
網絡請求,Vue 2.0開始再也不維護 vue-resourse,轉而推薦 axios 做爲標準的網絡請求庫,可是因爲 axios 不支持 jsonp 跨域方式,遂放棄,在項目中使用了團隊的 ajax 模塊。
延遲加載,項目使用了webpack做爲打包構建工具,打包結束後默認狀況下會產生兩個js文件:app.js和vendor.js,而項目在一開始就已經加載了這兩個js文件,若是要想實現路由的延遲加載,須要將路由請求的組件定位爲異步組件,並結合webpack的代碼分割特性實現,方法是經過 require.ensure 的方式引入組件。
本篇文章前半部分對 vue2.0 進行了淺析:分析關於響應式方面的源碼,瞭解具體的實現原理與模式,並對Vuex 數據管理作了知識梳理與流程說明。後半部分則是在項目中應用了 Vue 的技術棧,並對實踐過程當中的代碼細節和關鍵點作了總結。
在整個開發過程當中,可以較爲深入地體會到vue對於代碼編寫的溫馨性(來自於組件化的管理方式)以及vuex對於代碼組織方面的優雅。另外因爲時間上的關係(其實是由於懶), 尚未仔細瞭解關於Vue 渲染方面的原理,但願能夠找個時間在後續補上這部份內容。
謝謝閱讀,歡迎指正 (=^_^=)
參考文獻
深刻 Vue2.x 的虛擬 DOM diff 原理
基於 TVUE 框架在中型移動端項目的直出同構實踐
騰訊工程師們怎麼玩 Vue.js?
此文已由做者受權騰訊雲技術社區發佈,轉載請註明原文出處