vue+vuex構建單頁應用

基本

  • 構建工具:webpackjavascript

  • 語言:ES6css

  • 分號:行首分號規則(行尾不加分好,[,(,/,+,-開頭時在行首加分號)html

  • 配套設施:webpack全家桶,vue全家桶前端

clipboard.png

項目結構

  • 基本目錄結構vue

clipboard.png

  • api:封裝與後端接口交互的操做java

  • common:放置一些reset.css之類的react

  • components:組件webpack

  • entry:項目入口文件index.js,index.css,index.htmlgit

  • filters:過濾器。注:雖然vue2.0已經基本廢棄(只保留了對文本的過濾)了Vue.filter的用法,此目錄下的方法仍然可用於官方推薦用來替代過濾器的計算屬性的計算中es6

  • mixins:一些通用類的混入部分。好比全選、多選可抽出通用的list-toggle

  • mock:本地開發的mock數據

  • utils:封裝的工具,如對上傳文件、日期處理等的封裝

  • views:單頁應用的視圖(視圖也是組件,也可放到components中,但我的以爲放在這裏比較一目瞭然)

  • vuex:放置store,actions,mutations,state

  • fis-conf.js:用於測試環境聯調時fis實時將前端資源推送到開發機上

若是有自定義指令,還能夠加上directives目錄(vue的幾個可擴展的地方均可以單獨作一個目錄)。對於項目目錄,也可使用官方提供的另外一個工具vue-cli來生成,它還會自動構建單元測試(unit)和端對端測試(e2e)的目錄和簡單示例。

基礎組件

vue除了雙向綁定外的一個最大特色就是提供了強大的組件樹系統,組件化也是 web發展的趨勢.
每個Vue實例就是一個組件,構造一個組件的也很簡單:

var myComponent = Vue.extend({
    template: '',
    ...
})
// 全局註冊組件,tag 爲 my-component
Vue.component('my-component', MyComponent)

更推薦的作法是寫成*.vue形式的單文件組件,搭配vue-loader使用(下圖來自官方文檔)。
Alt text

更多關於組件的內容,見官方文檔組件
另外,在使用單文件組件時,樣式會被打包到js中並在運行時會以<style>節點的形式插入到<head>裏面。此時若是想將組件的樣式打包到輸出的css文件中,只須要在webpack.config.jsmodule.exports中加上如下配置便可:

vue: {
    loaders: {
        js: 'babel-loader?presets[]=es2015&plugins[]=transform-runtime&comments=false',
        css:ExtractTextPlugin.extract(['css-loader'])
    }
}

剛開始一個項目時,若是在有本身特定的UI設計風格,可能須要單獨封裝一些textinput,checkbox,radio等基礎組件;若是沒有的話(如普通的後臺管理系統),也可使用Google Material Design,已經有對應的實現material-design-lite。而且vue社區中也已經有針對它的vue組件封裝vue-mdl

應用骨架

以「xx管理後臺」爲例,首先分爲上(導航)下(主體內容)兩部分,基本結構爲:

clipboard.png

接下來在views裏面心間user.vue,做爲用戶管理模塊入口,若是每一個模塊還須要包含二級導航(一般是在頁面左側部分),user.vue能夠像這樣:

clipboard.png

這兩個文件中用到的router-view,都是vue官方路由插件vue-router提供的。
而後是配置單頁應用的路由:

clipboard.png

在對應的視圖組件中,經過route選項的鉤子函數,來肯定時圖在出現和消失的過程當中須要執行的行爲。更多路由相關,見官方文檔

這樣,一個基本的:上->[左|右]的單頁應用骨架就有了。(其餘類型的應用也可依此類推)

應用狀態管理

應用組件化以後,就須要解決組件之間的通訊問題。針對組件之間的通訊問題,vue提供了三種方式: props屬性傳遞,直接經過引用調用組件方法,自定義事件通訊,經過v-ref(在vue2.0已簡化爲ref)來創建子組件索引從而調用子組件方法。

  • porps:基於屬性傳遞,vue提供了單次綁定、單向綁定和雙向綁定。(雖然雙向綁定在vue2.0 中已經廢棄)

  • 經過引用:子組件能夠用this.$parent訪問它的父組件。根實例的後代能夠用this.$root訪問它。父組件有一個數組this.$children,包含它全部的子元素。

  • 經過自定義事件通訊:每一個Vue實例都是一個事件觸發器:

    • 使用$on()監聽事件

    • 使用$emit()在它上面觸發事件

    • 使用$dispatch()派發事件,事件沿着父鏈冒泡(vue2.0已廢棄)

    • 使用$broadcast()廣播事件,事件向下傳導給全部的後代(vue2.0已廢棄)
      vue2.0中,能夠單獨使用一個Vue實例來來擔任eventBus的做用。

除了這幾種方式,當應用比較複雜時,官方推薦使用另外一個官方插件vuex

相似於reactreduxvuevuexstore也包含一個全局的狀態樹state;修改state的行爲mutations(對應reduxactions);執行修改的動做actions(對應reduxcreateAction)。

clipboard.png

以全局alert組件的狀態爲例:

  • 建立state

export default {
    alert: {
        show: false,
        type: 'alert',
        message: '',
        onSure: null,
        onClose: null
    }
}
  • 建立mutations

export default {
    SHOW_ALERT (state, data) {
        data.show = true
        state.alert = data
    },
    HIDE_ALERT (state) {
        state.alert.show = false
    }
}
  • 建立actions

/*主頁面涉及到的actions*/
let noop = () => {}
/*顯示浮層alert*/
export const showAlert = ({dispatch}, message = '') => {
    if(!message) {
        return false
    }
    dispatch('SHOW_ALERT', {
        type: 'alert',
        message: message,
        onClose: noop
    })
}
/*顯示浮層confirm*/
export const showConfirm = ({dispatch}, data = {}) => {
    if(!data.message) {
        return false
    }
    data.type = 'confirm'
    if(typeof data.onClose != 'function') {
        data.onClose = noop
    }
    if(typeof data.onConfirm != 'function') {
        data.onConfirm = noop
    }
    dispatch('SHOW_ALERT', data)
}
/*隱藏浮層*/
export const hideAlert = ({dispatch}) => dispatch('HIDE_ALERT')
  • 構建store

import Vue from 'vue'
import Vuex from 'vuex'
import actions from './actions'
import mutations from './mutations'
import state from "./state"
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
Vue.config.debug = debug
export default new Vuex.Store({
    state,
    mutations,
    actions,
    strict: debug
})

而後在應用的根組件中,經過如下方式獲取vuex的功能:

/*引入vuex*/
import store from "../vuex/store"
let App = Vue.extend({
    store,
    components: {
        'admin-header': adminHeader,
        'alert': alert
    }
})

而後再在自組件中的vuex模塊經過如下方式獲取狀態以及觸發狀態改變的動做:

clipboard.png

應用的數據交互

api層

記得以前看過民工叔叔(徐飛)的某篇文章裏說的,數據層可以跟UI層分離,這樣即便UI底層庫更換了,也可使用數據層。同理若是想要對api交互進行替換(如想把某些ajax庫換成瀏覽器支持的fetch api),也能夠直接在這一層進行更改。

mock數據

在開發階段,有時須要mock一些數據來測試應用。推薦一個對restful api友好的第三方工具json-server

clipboard.png

index.js

var users = require('./database/users')
module.exports = function() {
    return {
        "users": users
    }
}

database/users.js

module.exports = {
    "users": [
        {
            "user_id": "233", 
            "user_name": "哈哈哈", 
        }, 
        {
            "user_id": "233", 
            "user_name": "哈哈哈", 
        }
    ], 
    "more": true, 
    "result": "SUCCESS"
}

而後終端執行json-server mock/index.js -port 9999就開啓了一個restful的服務了。(也能夠把這句寫到npm script中)

接下來還差一步,就是須要用到webpack-dev-serverproxy配置:

clipboard.png

這樣,全部訪問/rest/*的接口都會被代理到json-server的服務上.

應用測試

一個完整的應用應該還具有單元測試、端對端測試等。目前比較成熟的測試框架社區中也有很多,但因爲還沒油深刻研究過,此處不展開。

總結

文章主要是從搭建一款單頁應用的總體架構上來進行描述,可能有一些地方不是那麼詳細,之後有時間在針對單獨某些模塊再詳細描述吧。。
順便安利一發開源的兩個vue組件,歡迎拍磚:

相關文章
相關標籤/搜索