此篇是關於 Vue 的總結。Js 的總結請見前一篇:Vue + ElementUI 後臺管理系統專案心得(一)
話說過完年回來,彷彿一夜之間身邊的人都在談論 Vue,相關的元件也如雨後春筍一般湧現出來,比如大名鼎鼎的 Element UI
、iView
還有不久前剛釋出的 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
例項,其中的每個屬性都繫結了 getter
、setter
方法,具備響應式特性。這就是為什麼需要顯式的定義 data
屬性。
預設情況下 data
內的屬性會被 Vue 例項代理(Vue 例項的同名屬性指向 data
物件的屬性),如果某些屬性不想被 Vue 代理可以在屬性名稱前面加上 '$'
或 '_'
,這樣就只能通過 Vue.$data._someProp
來訪問。
在例項化一個 Vue 物件時會經歷那些階段呢?通俗的說會有兩個階段:一是生成 data
,二是掛載 template
,也就是 MVVM 中的 VM(view-model)。
生成 data
被稱作 create
,掛載 template
被稱作 mount
,兩個階段的前後分別對應著兩組生命週期節點,也就是 beforeCreate
、created
以及 beforeMound
、mounded
。
有生則有滅,銷燬響應式屬性,解除繫結事件的過程被稱作 destroy
,前後也分別對應著 beforeDestroy
和 destroyed
節點。
響應式屬性的變化會觸發檢視的更新,這個更新的過程被稱作 update
,前後自然也對應著 beforeUpdate
、updated
兩個節點。資料與檢視繫結是 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-if
、v-else
、v-else-if
和v-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>複製程式碼
(未完待續...)