Vue 淺析與實踐

歡迎你們前往騰訊雲社區,獲取更多騰訊海量技術實踐乾貨哦~html

做者:曾柏羲 前端

導語

入職接到的第一個需求是實現一個關於K歌實體售賣的ERP系統,管理系統過去作過很多,此次打算換個姿式,基於時下正熱但早已不新鮮的Vue 2.0技術實現。本文首先對Vue的相關技術進行簡單介紹與分析,接着總結開發實踐(主要描述 Vuex 實踐)過程當中的流程規範,並記錄在此過程當中遇到的問題與關鍵點,最後作出一點實踐的總結與思考。vue

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:

  • state,做爲驅動應用的數據源,保存了組件的各類狀態;
  • mutations,相似於事件,是改變 state 的惟一入口,且裏面的操做必須是同步的;
  • actions,相似於 mutations,裏面能夠進行一些如 ajax 請求等異步的邏輯操做,但若是想對 state 的狀態進行修改,必須經過 mutaions 完成;
  • getters,能夠對 state 進行某些處理動做,並對處理後的結果提供訪問接口。

圖:vuex狀態管理

Vuex狀態管理流程如上圖所示,主要分爲以下四個步驟:

  • 視圖層中的 Components 經過 this.$store.xxx 或 getters 方法從 state 中獲取數據並渲染;
  • 用戶在 Components 中執行某些動做(如點擊按鈕fetch數據)時,經過調用 dispatch() 方法將執行動做的指令發送到 Actions 中對應的方法;
  • Actions 解析請求指令,完成相應的邏輯(如ajax數據請求),並在最後(ajax請求結束後)經過 commit() 方法通知 mutations 對 state 狀態進行修改;
  • Mutations 收到 commit 請求後,對 state 進行賦值操做,以完成數據的修改。

圖:vue devtools

對於開發過程當中 Vuex 狀態的追蹤,能夠經過 Vue Devtools 的 「Vuex」 一欄進行查看,如上圖所示,安裝方法能夠自行搜索。

項目實踐

圖:K歌erp

簡單介紹

全民K歌初期在試水麥克風售賣活動中取得了良好的市場反饋,爲繼續推進周邊實體的售賣,如今指望搭建一個屬於本身的售賣平臺。整個需求分爲H5和PC兩部分,其中H5爲用戶購買實體周邊的入口,PC則是對用戶的訂單數據進行管理。本項目爲需求中的PC部分,共由訂單數據概覽、待審覈、待發貨、已發貨和退換貨五頁組成。

圖:項目頁面

頁面具體介紹以下:

  • 概覽頁,包含了整個平臺上的交易數據,貨品的實時庫存以及當前用戶的待辦事項;
  • 待審覈,顯示最新的訂單數據,用戶能夠在該頁中執行訂單審覈動做;
  • 待發貨,已經經過審覈的訂單將會移動至該頁,該頁爲管理員提供了物流信息導入和已經確認發貨的動做;
  • 已發貨,顯示已經發貨的訂單列表,用戶能夠對發貨的地址進行修改,同時也能夠執行申請退款的操做;
  • 退換貨,包含了「待處理「、「」已經過「和「已拒絕」的tab頁。其中待處理tab顯示了前端發起退貨或erp上執行「申請退款「的訂單列表,而用戶在該列表中執行的動做(容許/拒絕退款)將會使數據移至「」已經過「或「已拒絕」的列表中。

此外,對於全部的列表頁,須要提供批量操做,如批量審覈、發貨退款和物流信息導入等,並提供分頁操做。同時登錄須要經過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屬性中進行。

(4) 其餘

路由處理,對於一個單頁應用,天然少不了路由處理,項目的路由使用官方的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?

此文已由做者受權騰訊雲技術社區發佈,轉載請註明原文出處

相關文章
相關標籤/搜索