從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(三)

這篇文章中,咱們將講解 Vue 實例的 Props 和 Methods,接着咱們又講解了最多見的 Vue 模板語法,並經過實例的方式將這些模板語法都實踐了一番,最後咱們講解了 Vue 組件的組合,並完成了咱們的發表商品頁面。javascript

歡迎閱讀《從零到部署:用 Vue 和 Express 實現迷你全棧電商應用》系列:html

用模板語法和雙向綁定實現數據的添加

當咱們完成了商城應用的基本頁面框架以後,咱們就能夠開始考慮具體頁面的內容了。首先咱們要考慮的就是數據的來源,即添加商品頁面。有了添加商品的入口,咱們就能夠展現商品列表,獲取商品詳情,甚至是修改商品信息。前端

不過在此以前,咱們打算先複習一下 Vue 的一些重要知識點。若是你已經很熟悉了,能夠直接跳到下面實現 ProductForm.vue 的代碼部分。vue

Vue 實例:Props 和 Methods

Props

props 是 Vue 進行組件之間傳參的形式。好比咱們有如兩個組件 New.vueProductForm.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.vuescript 部分導出的對象裏面找到 props 屬性,而後取到 manufacturers 屬性。數組

Methods

而後是 methodsmethods 是用來定義在組件中會用到的一些方法,若是說咱們前面提到的 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-on

接下來咱們再來談一談 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 -

模板語法:v-bind

咱們已經看到在 Vue 模板中咱們可使用以下的功能:

  • {{}} 插值語法將 data 渲染到 HTML 元素內容中
  • v-on 或者簡化寫法 @ ,等用來取代 HTML 的事件綁定

有了上面的功能,咱們可讓 HTML 動起來了,可是還缺點什麼,好比咱們的 HTML 屬性,如 idclass 等,是否是也能動態的獲取變化值,你還別說,還真的能夠,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 中,定義了 _idvalue 值,而後咱們經過在 <template> 模板中使用 v-bind 語法動態的給 option 標籤的 idvalue 屬性賦值,最後的結果看起來是這樣的:

<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>
複製代碼

模板語法:v-model 雙向綁定

前面咱們提到經過 {{}} 插值語法渲染來自 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 實例中的 modelname 屬性進行了雙向綁定,即當 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._id1,它和 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 的標籤咱們能夠判斷最終渲染的標籤,好比咱們這裏 isEditingfalse,那麼咱們最終渲染的結果爲:

<span>Add Product</span>
複製代碼

固然你能夠添加諸如 v-else-if 的標籤來作多重判斷。

提示

這裏的帶 v-ifv-else-ifv-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>
複製代碼

這段代碼看起來很長,你可能被嚇到了,讓咱們一段一段來拆解它。

script 部分

這裏咱們的 props 接收來自父組件的三個參數:modelmanufacturersisEditing

而後咱們定義了一個 saveProduct 方法,就是當用戶填寫完商品信息的表單以後,點擊提交按鈕會觸發的方法,在 saveProduct 內部,咱們調用了父組件的 save-product 方法,並把修改後的 this.model 變量內容傳給父組件。因此這裏咱們還能夠看到,methods 不只可使得數據能夠雙向流動,並且還能夠在子組件反向操做父組件的內容,使得數據還能夠上下流動。

template 部分

接下來咱們再來談一談 template 裏面發生的事情。

能夠看到 template 裏面就是一個表單,這個表單定義了一個 submit 事件,而且使用了禁用默認事件的簡潔寫法 @submit.prevent。 這個事件觸發會調用咱們上面提到的 saveProduct 方法。

接着咱們定義了好幾個 classform-group 的元素塊,每一個塊表明咱們建立商品所須要填寫的相關信息,咱們注意到,前兩個 form-group 使用 v-model 雙向綁定語法分別綁定了 modelnameprice 屬性。

第三個 form-group 咱們首先在 select 標籤中使用 v-model 雙向綁定了 model.manufacturer,表示咱們在視圖裏面進行選擇時,會修改對應的 model.manufacturer 屬性。

接着咱們對 manufacturers 進行循環遍歷,構造多個 option 標籤選項,而後使用了屬性綁定語法的簡潔寫法綁定了 optionvalueselected 屬性,value 屬性賦值爲 manufacturer._idselected 屬性會進行判斷,model.manufacturer && model.manufacturer._id 表示首先檢驗 modelmanufacturer 屬性是否存在,正常狀況下它應該是一個對象,若是 model.manufacturer 屬性存在,那麼獲取 model.manufacturer._id,而後用獲取到的這個 model.manufacturer._idmanufacturer._id 進行比較,若是一致,那麼 selected 屬性爲 true,不一致就爲 false

而後咱們來看一下第二段 form-group,也就是第 4-6 個 form-group

能夠看到前兩個 form-group 使用 v-model 雙向綁定了 model.imagemodel.description ,表示當用戶上傳了商品圖片和描述以後,對應的 model.image 就會變成用戶上傳的商品圖片,model.description 就會變成用戶撰寫的商品描述。

最後一個 form-group 咱們使用了條件選擇語法,判斷 isEditing,來渲染不一樣的按鈕文案。

Vue 組件組合

編寫完上面的表單以後,咱們在 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 中定義了 modelmanufacturers 以及在 methods 中定義了 addProduct 方法,並將它們以屬性綁定 :model="model":manufacturers="manufacturers" 和事件綁定 @save-product="saveProduct" 的方式傳遞給表單組件。

當保存上面編寫的代碼以後,咱們打開瀏覽器,點擊導航連接 Admin,而後點擊子導航連接 New Products,切換到咱們的 New.vue 添加商品頁面,咱們能夠看到以下的效果:

小結

到如今爲止,咱們已經瞭解了 Vue 的基礎部分,包括:

  • 用路由進行多頁面的跳轉和導航
  • 用嵌套路由和動態路由合理組織頁面
  • Vue 組件和 Vue 實例
  • Vue 模板語法

掌握了這些知識後,咱們已經能夠實現不少前端的功能,完成一些簡單的 Vue 應用了。可是若是要完成數據邏輯複雜的大型應用,目前學到的知識就力不從心了。可是不要緊,咱們將在後面學習 Vuex 這一前端狀態管理工具,有了 Vuex 的加持,咱們就能用 Vuex 寫出任意複雜的應用了。

想要學習更多精彩的實戰技術教程?來圖雀社區逛逛吧。

相關文章
相關標籤/搜索