這篇文章中,咱們將講解 Vue 實例的 Props 和 Methods,接着咱們又講解了最多見的 Vue 模板語法,並經過實例的方式將這些模板語法都實踐了一番,最後咱們講解了 Vue 組件的組合,並完成了咱們的發表商品頁面。javascript
歡迎閱讀《從零到部署:用 Vue 和 Express 實現迷你全棧電商應用》系列:html
當咱們完成了商城應用的基本頁面框架以後,咱們就能夠開始考慮具體頁面的內容了。首先咱們要考慮的就是數據的來源,即添加商品頁面。有了添加商品的入口,咱們就能夠展現商品列表,獲取商品詳情,甚至是修改商品信息。前端
不過在此以前,咱們打算先複習一下 Vue 的一些重要知識點。若是你已經很熟悉了,能夠直接跳到下面實現 ProductForm.vue 的代碼部分。vue
props
是 Vue 進行組件之間傳參的形式。好比咱們有如兩個組件 New.vue
和 ProductForm.vue
,在 New.vue
組件中須要使用到 ProductForm.vue
組件。其中 New.vue
組件是用來建立商品的,它的代碼大體是這樣的:java
import ProductForm from '@/components/ProductForm.vue';
<ProductForm :manufacturers="manufacturers" /> 複製代碼
它須要給 ProductForm.vue
組件傳遞一個 manufacturers
屬性,以確保咱們在建立商品時,能夠選擇這個商品所屬的製造商,接着咱們就能夠在 ProductForm.vue
中的 props
中取到這個 manufacturers
屬性。ProductForm.vue
的代碼大體是這樣的:後端
<template>
<!-- 模板部分 -->
</template>
<script> export default { props: ['manufacturers'], } </script>
複製代碼
能夠看到,咱們在 ProductForm.vue
的 script
部分導出的對象裏面找到 props
屬性,而後取到 manufacturers
屬性。數組
而後是 methods
,methods
是用來定義在組件中會用到的一些方法,若是說咱們前面提到的 data
,是從數據從邏輯層(JS)向視圖層(Html)流動的話,那麼這裏的 methods
就是視圖層觸發事件,如 click、submit等,反過來修改邏輯層的數據的方法,methods
使得數據能夠雙向流動。瀏覽器
讓咱們在完善一下咱們的 ProductForm.vue
,看一下 Methods 在 Vue 中是如何運做的:app
<template>
<form @submit.prevent="saveProduct">
<!-- 其餘表單,如 input 等 -->
<div class="form-group new-button">
<button class="button">Add Product</button>
</div>
</form>
</template>
<script> export default { data: { isSaved: false }, props: ['manufacturers'], methods: { saveProduct() { this.isSaved = true; // 完成一些保存建立商品的邏輯 ... } } } </script>
複製代碼
能夠看到,咱們能夠經過在 template
(視圖層)經過點擊提交按鈕,發起表單提交事件,進而調用在 script
中定義在 methods
屬性中的 saveProduct
方法,這個方法能夠進一步修改定義在定義在 data
屬性中的數據;甚至若是父組件 New.vue
傳遞了方法(以 props
的形式)給 ProductForm.vue
組件,咱們可在 saveProduct
調用這個傳遞下來的方法,進而能夠影響到父組件 New.vue
中的數據。咱們將在後面的正式實現 ProductForm
組件時講解到它。框架
接下來咱們再來談一談 v-bind 和 v-on 。
在 Vue 中,咱們經過 v-on
的方式接管了以前在 HTML 中 onEvent
:
好比以前咱們在 HTML 中的寫法是這樣的:
<div onclick="alert('I love tuture')">
Hello Tuture
</div>
複製代碼
如今在 Vue 的模板語法中咱們須要寫出這樣:
<div v-on:click="alert('I love tuture')">
Hello Tuture
</div>
複製代碼
相似的 onEvent
都要改爲 v-on:Event
。而後這樣寫顯得比較冗餘,因此 Vue 支持簡化寫法,用 @
替換 v-on:
部分,咱們就能夠寫出這樣:
<div @click="alert('I love tuture')">
Hello Tuture
</div>
複製代碼
調用事件以後咱們通常有一些這樣的操做,好比禁用瀏覽器默認行爲,而後本身去處理事件,獲取後端數據,之前咱們會這樣寫:
<div onclick="saveProduct()">
Hello Tuture
<script> var saveProduct = function (e) { e.preventDefault(); // do something you like } </script>
複製代碼
可是這樣寫又顯得特別繁瑣了,Vue 也以爲這樣能夠簡化,因而咱們直接將這些禁止默認行爲的調用做爲綁定事件的屬性來進行處理,因而乎在 Vue 中咱們能夠寫出這樣:
<template>
<div @click.prevent="saveProduct">
Hello Tuture
</div>
</template>
<script> export default { methods: { saveProduct() { // do something you like } } } </script>
複製代碼
不知道看了上面的長文,你有沒有一點暈,無論你暈不暈,我是得喝口水緩一下。 - v -
咱們已經看到在 Vue 模板中咱們可使用以下的功能:
{{}}
插值語法將 data
渲染到 HTML 元素內容中v-on
或者簡化寫法 @
,等用來取代 HTML 的事件綁定有了上面的功能,咱們可讓 HTML 動起來了,可是還缺點什麼,好比咱們的 HTML 屬性,如 id
、class
等,是否是也能動態的獲取變化值,你還別說,還真的能夠,Vue 模板語法爲咱們提供了 v-bind
用於動態綁定屬性值,咱們來看個例子:
<template>
<option v-bind:id="_id" v-bind:value="value" />
</template>
<script> export default { data: { _id: '1', value: "Xiaomi" }, } </script>
複製代碼
能夠看到,咱們在 script
中導出的對象屬性 data
中,定義了 _id
和 value
值,而後咱們經過在 <template>
模板中使用 v-bind
語法動態的給 option
標籤的 id
和 value
屬性賦值,最後的結果看起來是這樣的:
<option id="1" value="Xiaomi" />
複製代碼
固然,當須要綁定的屬性多了,每次都寫 v-bind
顯得至關繁瑣,因此 Vue 爲咱們提供了 v-bind
的簡潔語法 :
,即咱們以前的綁定語法從 v-bind:id="_id"
變成了 :id="_id"
。
上面的代碼用簡潔語法改寫以下:
<template>
<option :id="_id" :value="value" />
</template>
<script> export default { data: { _id: '1', value: "Xiaomi" }, } </script>
複製代碼
前面咱們提到經過 {{}}
插值語法渲染來自 data
的數據實現了邏輯層向視圖層的數據流動,經過 methods
在視圖層操做邏輯層的數據,實現了視圖層的數據向邏輯層的數據流動,從而達到了雙向綁定,當咱們的應用愈來愈複雜,咱們會發現這樣的數據雙向流動會愈來愈頻繁,並且粒度也會大小不一,有不少單純修改某個值的方法調用就會顯得特別繁雜,所以 Vue 經過提供 v-model
進行了視圖層和邏輯層的雙向綁定,讓咱們來看個例子:
<template>
<!-- 其餘代碼 ... -->
<input type="text" placeholder="Name" v-model="name" />
<!-- 其餘代碼 ... -->
</template>
<script> export default { data: { name: 'ProductForm' }, } </script>
複製代碼
這裏咱們經過申明 v-mode
將此 input 的值和咱們在 Vue 實例中的 model
的 name
屬性進行了雙向綁定,即當 data 中的 name
發生變化,input 的值也會跟着變化,當 input 的值發生變化,咱們 data 中的 name
的值也會被修改,這一切都是自動發生的,不須要咱們額外的添加 methods
裏面的方法調用來手動修改。
好了,Vue 替咱們接管了 HTML 元素屬性值、事件處理、元素內容,這些都還只屬於原來 HTML 的部分,它更強大的一點就是將 JS 的功能引入了模板語法中,使得咱們能夠實現相似循環,條件選擇操做等功能。
接下來咱們先來看一下 Vue 爲咱們提供的 「循環」 模板語法, 它使得咱們能夠快速渲染大量具備類似結構的數據,好比渲染一個數組的數據,生成一個 HTML 元素列表,這在咱們平時看到的新聞 App 裏面很常見,咱們瀏覽新聞時,發現其實每條新聞的結構都很類似,而且有不少條新聞(可能多大幾百上千條),若是每一條咱們都手動寫 HTML 代碼的話,無疑顯得至關繁瑣,而且數據一多,咱們手動就顯得無能爲力了,而 Vue 爲咱們提供的 「循環」 模板語法,使得咱們能夠經過很是簡單的寫法就能夠渲染大量數據,咱們來看個例子:
<!-- manufacturers = [ { _id: 1, name: 'Apple' }, { _id: 2, name: 'Xiaomi' } ] model = { _id: 1, name: 'Apple' } -->
<template v-for="manufacturer in manufacturers">
<option :value="manufacturer._id" :selected="manufacturer._id == model._id">{{manufacturer.name}}</option>
</template>
複製代碼
最後渲染的結果爲:
<option value="1" selected="true">Apple</option>
<option value="2" selected="false">Xiaomi</option>
複製代碼
注意到,若是咱們在寫 「循環」 語法時,使用了一個額外的標籤 template
來包裹咱們須要渲染的 HTML 元素,這也是 Vue 推薦的寫法;咱們在 template
標籤的屬性上添加 v-for
而後給它賦值 "manufacturer in manufacturers"
,經過這樣的形式進行列表數據的遍歷,每次從 manufacturers
中取一個元素,並賦值給 manufacturer
,而後咱們就能夠在 option
標籤中使用 manufacturer
和咱們定義的 model
進行比較。
由於咱們的 model._id
爲 1
,它和 manufacturers
數組的第一項元素的 _id
一致,因此咱們返回的兩個 option
標籤,第一個 selected
屬性爲 true
,第二個爲 false
。
上面的講述了循環是如何在 Vue 中使用的,下面咱們來看一看條件語法是如何在 Vue 中使用的:
<span v-if="isEditing">Update Product</span>
<span v-else>Add Product</span>
<script> export default { data: { isEditing: false }, } </script>
複製代碼
咱們能夠看到,經過在標籤上加 v-if
並後面緊跟加 v-else
的標籤咱們能夠判斷最終渲染的標籤,好比咱們這裏 isEditing
爲 false
,那麼咱們最終渲染的結果爲:
<span>Add Product</span>
複製代碼
固然你能夠添加諸如 v-else-if
的標籤來作多重判斷。
提示
這裏的帶
v-if
、v-else-if
或v-else
的標籤須要依次緊跟着前面的標籤,不能在這些帶條件屬性的標籤中插入其餘不帶條件的標籤,好比下面這段代碼就是錯誤的:<span v-if="isEditing">Update Product</span> <span>我是錯誤插入的標籤</span> <span v-else>Add Product</span> <script> export default { data: { isEditing: false }, } </script> 複製代碼
講解完 Vue 的基礎知識以後,咱們立刻將全部的知識運用起來,來編寫咱們的 ProductForm.vue
組件,它用來添加或者更新商品的信息。
咱們在 src/components
中建立 ProductForm.vue
表單組件,代碼以下:
<template>
<form @submit.prevent="saveProduct">
<div class="col-lg-5 col-md-5 col-sm-12 col-xs-12">
<div class="form-group">
<label>Name</label>
<input type="text" placeholder="Name" v-model="model.name" name="name" class="form-control" />
</div>
<div class="form-group">
<label>Price</label>
<input type="number" class="form-control" placeholder="Price" v-model="model.price" name="price" />
</div>
<div class="form-group">
<label>Manufacturer</label>
<select type="text" class="form-control" v-model="model.manufacturer" name="manufacturer">
<template v-for="manufacturer in manufacturers">
<option :value="manufacturer._id" :selected="manufacturer._id == (model.manufacturer && model.manufacturer._id)">{{manufacturer.name}}</option>
</template>
</select>
</div>
</div>
<div class="col-lg-4 col-md-4 col-sm-12 col-xs-12">
<div class="form-group">
<label>Image</label>
<input type="text" lass="form-control" placeholder="Image" v-model="model.image" name="image" class="form-control" />
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" placeholder="Description" rows="5" v-model="model.description" name="description" ></textarea>
</div>
<div class="form-group new-button">
<button class="button">
<i class="fa fa-pencil"></i>
<!-- Conditional rendering for input text -->
<span v-if="isEditing">Update Product</span>
<span v-else>Add Product</span>
</button>
</div>
</div>
</form>
</template>
<script> export default { props: ['model', 'manufacturers', 'isEditing'], methods: { saveProduct() { this.$emit('save-product', this.model) } } } </script>
複製代碼
這段代碼看起來很長,你可能被嚇到了,讓咱們一段一段來拆解它。
這裏咱們的 props
接收來自父組件的三個參數:model
、manufacturers
、isEditing
。
而後咱們定義了一個 saveProduct
方法,就是當用戶填寫完商品信息的表單以後,點擊提交按鈕會觸發的方法,在 saveProduct
內部,咱們調用了父組件的 save-product
方法,並把修改後的 this.model
變量內容傳給父組件。因此這裏咱們還能夠看到,methods
不只可使得數據能夠雙向流動,並且還能夠在子組件反向操做父組件的內容,使得數據還能夠上下流動。
接下來咱們再來談一談 template
裏面發生的事情。
能夠看到 template
裏面就是一個表單,這個表單定義了一個 submit
事件,而且使用了禁用默認事件的簡潔寫法 @submit.prevent
。 這個事件觸發會調用咱們上面提到的 saveProduct
方法。
接着咱們定義了好幾個 class
爲 form-group
的元素塊,每一個塊表明咱們建立商品所須要填寫的相關信息,咱們注意到,前兩個 form-group
使用 v-model
雙向綁定語法分別綁定了 model
的 name
和 price
屬性。
第三個 form-group
咱們首先在 select
標籤中使用 v-model
雙向綁定了 model.manufacturer
,表示咱們在視圖裏面進行選擇時,會修改對應的 model.manufacturer
屬性。
接着咱們對 manufacturers
進行循環遍歷,構造多個 option
標籤選項,而後使用了屬性綁定語法的簡潔寫法綁定了 option
的 value
和 selected
屬性,value
屬性賦值爲 manufacturer._id
,selected
屬性會進行判斷,model.manufacturer && model.manufacturer._id
表示首先檢驗 model
的 manufacturer
屬性是否存在,正常狀況下它應該是一個對象,若是 model.manufacturer
屬性存在,那麼獲取 model.manufacturer._id
,而後用獲取到的這個 model.manufacturer._id
和 manufacturer._id
進行比較,若是一致,那麼 selected
屬性爲 true
,不一致就爲 false
。
而後咱們來看一下第二段 form-group
,也就是第 4-6 個 form-group
。
能夠看到前兩個 form-group
使用 v-model
雙向綁定了 model.image
和 model.description
,表示當用戶上傳了商品圖片和描述以後,對應的 model.image
就會變成用戶上傳的商品圖片,model.description
就會變成用戶撰寫的商品描述。
最後一個 form-group
咱們使用了條件選擇語法,判斷 isEditing
,來渲染不一樣的按鈕文案。
編寫完上面的表單以後,咱們在 New.vue
中引入咱們建立的表單組件:
<template>
<product-form @save-product="addProduct" :model="model" :manufacturers="manufacturers" >
</product-form>
</template>
<script> import ProductForm from '@/components/products/ProductForm.vue'; export default { data() { return { model: {}, manufacturers: [ { _id: 'sam', name: 'Samsung', }, { _id: 'apple', name: 'Apple', }, ], }; }, methods: { addProduct(model) { console.log('model', model); }, }, components: { 'product-form': ProductForm } } </script>
複製代碼
當一個組件要在模板語法中使用另一個組件時,須要申明此組件,即在組件的 components
屬性中申明要使用的組件,好比咱們上面使用名爲 'product-form'
的名稱來申明使用 ProductForm
組件,這樣在 template
中咱們就能夠以 <product-form />
形式使用導入的表單組件。
同時咱們在組件的 data
中定義了 model
和 manufacturers
以及在 methods
中定義了 addProduct
方法,並將它們以屬性綁定 :model="model"
、:manufacturers="manufacturers"
和事件綁定 @save-product="saveProduct"
的方式傳遞給表單組件。
當保存上面編寫的代碼以後,咱們打開瀏覽器,點擊導航連接 Admin
,而後點擊子導航連接 New Products
,切換到咱們的 New.vue
添加商品頁面,咱們能夠看到以下的效果:
到如今爲止,咱們已經瞭解了 Vue 的基礎部分,包括:
掌握了這些知識後,咱們已經能夠實現不少前端的功能,完成一些簡單的 Vue 應用了。可是若是要完成數據邏輯複雜的大型應用,目前學到的知識就力不從心了。可是不要緊,咱們將在後面學習 Vuex 這一前端狀態管理工具,有了 Vuex 的加持,咱們就能用 Vuex 寫出任意複雜的應用了。
想要學習更多精彩的實戰技術教程?來圖雀社區逛逛吧。