Vue + ElementUI 後臺管理系統專案心得(二)

osimly發表於2017-03-26

此篇是關於 Vue 的總結。Js 的總結請見前一篇:Vue + ElementUI 後臺管理系統專案心得(一)

話說過完年回來,彷彿一夜之間身邊的人都在談論 Vue,相關的元件也如雨後春筍一般湧現出來,比如大名鼎鼎的 Element UIiView 還有不久前剛釋出的 ATUI,這些元件庫雖然實現方式和介面格式有所不同,但大都介面美觀,體驗流暢,可以說和 Vue 框架提供的良好效能是密不可分的。

Vue 是一個上手簡單,卻富有內涵的框架,既可以像使用 jQuery 一樣簡單地通過 <script> 標籤引入,也支援通過打包工具構建。

// 一個簡單的title.vue模板檔案,同時具備了Web三要素:結構層、表現層和行為層。
<template>
  <div class="box">
    <span class="title-text">title</span>
  </div>
</template>

<script>
  export default {
    name: 'TitleBox',
  };
</script>

<style>
.title-text{
    font-size:18px
}
</style>複製程式碼

副檔名為 vue 的檔案會被打包工具編譯成標準 js 檔案以供瀏覽器執行,在編譯時 <template> 模板的內容會被轉換為 render 函式,換句話說 <template> 的內容只是一個配置引數,Vue 通過這種類似於 HTML 的結構來生成渲染方法,最後通過執行渲染方法來初始化一個真正的頁面。當然對於複雜的需求我們也可以不用模板,直接編寫 render 函式。

類似的還有 data 屬性,我們通常使用這樣的格式來定義 data

data() {
    return {
        commonData,
        layout,
        isShow: true,
        isLoading: false
    };
}複製程式碼

事實上這只是一個配置選項,編譯時 Vue 會根據我們傳入的選項來生成真正的 data 例項,其中的每個屬性都繫結了 gettersetter 方法,具備響應式特性。這就是為什麼需要顯式的定義 data 屬性。

預設情況下 data 內的屬性會被 Vue 例項代理(Vue 例項的同名屬性指向 data 物件的屬性),如果某些屬性不想被 Vue 代理可以在屬性名稱前面加上 '$''_' ,這樣就只能通過 Vue.$data._someProp 來訪問。

在例項化一個 Vue 物件時會經歷那些階段呢?通俗的說會有兩個階段:一是生成 data,二是掛載 template,也就是 MVVM 中的 VM(view-model)。

生成 data 被稱作 create,掛載 template 被稱作 mount,兩個階段的前後分別對應著兩組生命週期節點,也就是 beforeCreatecreated 以及 beforeMoundmounded

有生則有滅,銷燬響應式屬性,解除繫結事件的過程被稱作 destroy,前後也分別對應著 beforeDestroydestroyed 節點。

響應式屬性的變化會觸發檢視的更新,這個更新的過程被稱作 update,前後自然也對應著 beforeUpdateupdated 兩個節點。資料與檢視繫結是 VM 框架提供的核心功能,也正是所謂的資料驅動,資料驅動和事件驅動的差異有點類似於查詢和中斷的區別。

元件是一個封閉的環境,除非通過生命週期節點來探測,不然操作內部資料的進度是無法獲知的,換句話說我們從元件外部訪問 data 資料時,無法確定它是更新前還是更新後的,尤其在通過非同步方式請求後端資料時,這個問題就更加明顯。因此官方不建議通過 $parent$children$refs 等方式強行獲取資料,而是建議在資料狀態可控時通過事件觸發來回傳資料($emit、$on)。

常用的模板語法

  • 大鬍子語法(雙大括號)可以方便的在 HTML 元素內部(innerHTML)插入文字。
<span> {{ status ? '有效' : '無效' }} </span>複製程式碼
  • 設定模板標籤屬性可以直接用 <span prop="text"></span> 的格式書寫,如果需要為屬性繫結變數或表示式則需使用 v-bind 指令:<span :prop="value"></span>,沒錯,冒號 ':' 正是 v-bind 指令的縮寫,等同於 <span v-bind:prop="value"></span>,有時我們需要繫結數值(Number),而非內容為數字的字串,v-bind 指令恰好可以區分兩者。
// 後端傳送的資料
requiredData: {cityId: 3}

// 模板繫結的資料
<ex-select v-model="cityId">
    <ex-option :value="1">北京</option>
    <ex-option :value="2">上海</option>
    <ex-option :value="3">廣州</option>
    // 如果不加 ':' 會因為 3 !== '3'而找不到匹配項 
</ex-select>複製程式碼
  • 另一個支援縮寫的指令是 v-on ,可以用 '@' 表示,比如 <button @click="handleClick"></button>,指令較多時,縮寫可以使程式碼更清晰。
<ex-input
    v-model="userName"
    :placeholder="姓名"
    :disabled="isDisabled"
    :readonly="isReadonly"
    @focus="handleFocus"
    @input="handleInput"
    @change="handleChange">
</ex-input>複製程式碼
  • 控制流程的指令包括:v-ifv-elsev-else-ifv-for
// 判斷單行或多行文字框
<input type="text" v-if="inputType==='singleLine'"></input>
<textarea v-else="inputType==='multiLine'"></textarea>

// v-for 除了可以生成列表,還可以用於通過 JSON 動態生成模板
let formConfig = [{
    type: 'singleLine',
    name: 'userName'
},{
    type: 'singleLine',
    name: 'birthday'
},{
    type: 'multiLine',
    name: 'comment'
}]

<label v-for="item in formConfig">
    {{ item.name }}
    <ex-input inputType="item.type"></ex-input>
</label>複製程式碼
  • 計算屬性可以用於篩選或格式化資料,比如當後端返回時間戳時,我們可通過計算屬性獲取指定格式的日期時間。
data() {
    return {
        updateTime: 1490526754
    }
}

computed: {
    timeString: {
        get () {
            // Js預設以毫秒為單位,需要進行轉換
            let timeStamp = new Date(this.updateTime * 1000)
            return timeStamp.toLocaleString()
        },
        set (val) {
            let timeStamp = Math.round(Date.getTime() / 1000)
            this.updateTime = val || timeStamp
        }
    }
}複製程式碼
  • 如何響應式更新 data 中的陣列?
    有時我們需要在資料表格元件中顯示一組資料,後端返回給我們一個 JSON 陣列:
<ex-table
    data="dataSource"
></ex-table>
...
data () {
    return {
        dataSource: [{
            id: 1, name: lily, age: 21
        },{
            id: 2, name: terry, age: 23
        },{
            id: 3, name: dill, age: 26
        }]
    }
}複製程式碼

我們修改了其中的某一項,並通過下面的方式更新到陣列中:

let modifiedRow = {id: 2, name: frank, age: 20}
// 檢視未發生更新
this.dataSource[1] = modifiedRow複製程式碼

我們會發現,表格中顯示的資料並沒有改變,這是因為直接對陣列中的元素賦值不會觸發Vue的響應式更新,為了解決 Js 語言特性的限制,Vue 提供了一種變通的方法,可以使用 push()pop()shift() 等函式來修改陣列。

// 檢視響應式更新
dataSource.splice(1, 1, modifiedRow)複製程式碼

除此之外我們也可以通過 Vue.set() 函式修改陣列,比如:

Vue.set(dataSource, 1, modifiedRow)複製程式碼
  • 通過 v-model 指令為表單元素繫結資料

表單元素不但可以顯示資料也可以修改資料,我們當然可以通過 :value="data" 來顯示資料,並且通過 @input="data=$event.target.value" 來修改資料,不過 Vue 為我們提供了一個更簡潔的語法糖,直接使用 v-model="data" 即可同時實現兩者的功能。

其實,對於任何自定義元件只要我們實現了 value 屬性和 input 事件都可以使用 v-model 指令進行資料繫結。如果實現了 change 事件也可以配合 v-model.lazy 來呼叫。

// ex-input.vue
<input 
    type="text" 
    v-if="inputType==='singleLine'"
    :value="editValue"
    @input="handelInput"
></input>
<textarea 
    v-else="inputType==='multiLine'"
    :value="editValue"
    @input="handelInput"
></textarea>
...
data() {
    return {
        // 獲取表單初始值
        editValue: this.value
    }
},
prop: {
    // 定義value屬性
    value:[String, Number]
},
watch: {
    // 觀察並同步value屬性
    value(val) {
        this.editValue = val
    }
},
methods: {
    handelInput (event) {
        this.editValue = event.target.value
        // 實現input事件
        this.$emit('input', this.editValue)
    }
}
...
// 兩者是等價的,顯然 v-model 更簡潔一些
<ex-input v-model="data"></ex-input>
<ex-input :value="data" @input="data=arguments[0]"></ex-input>複製程式碼

(未完待續...)

相關文章