懂一點前端—Vue快速入門

01. 什麼是 Vue

Vue (讀音 /vjuː/,類似於 view) 是一套用於構建用戶介面的漸進式框架,是當下很火的一個 JavaScript MVVM 庫,是以 數據驅動和組件化 的思想構建的。

MVVM 模式簡述

下圖不僅概括了 MVVM 模式 (Model-View-ViewModel),還描述了在 Vue.js 中 ViewModel 是如何和 View 以及 Model 進行交互的。

ViewModel 是 Vue.js 的核心,它是一個 Vue 實例。Vue 實例是作用於某一個 HTML 元素上的,這個元素可以是 HTML 的 body 元素,也可以是指定了 id 的某個元素。

當創建了 ViewModel 後,雙向綁定是如何達成的呢?

首先,我們將上圖中的 DOM ListenersData Bindings 看作兩個工具,它們是實現雙向綁定的關鍵。

  • 從 View 側看,ViewModel 中的 DOM Listeners 工具會幫我們監測頁面上DOM元素的變化,如果有變化,則更改Model中的數據;
  • 從 Model 側看,當我們更新 Model 中的數據時,Data Bindings 工具會幫我們更新頁面中的 DOM 元素。

庫和框架的區別

在這裡我們需要稍微注意一下前端 庫(Library)框架(Framework) 的區別,它們的本質都是某人編寫的,用於解決常見問題的 可復用程式碼 的集合。

比如,你有一個處理字元串的程式,你為了保持程式碼的 DRY (Don't Repeat Yourself),你編寫了如下可復用的功能程式碼:

function getWords(str) {     const words = str.split(' ');     return words;  }  function createSentence(words) {     const sentence = words.join(' ');     return sentence;  }

恭喜你,你創建了一個 JavaScript 庫!

如果我們用 「構建房子」 來類比 「構建應用」 的話,那麼 使用庫 就像是 去宜家購物 一樣,我已經有了一個家,現在我需要挑選自己喜歡的一些傢具,以達到我自己滿意的狀態,這一切 都在我的控制範圍之內;而 使用框架 就會像是已經有了一個 清裝房,在已經規劃好的藍圖和選擇之中,我們的一些想法會顯得十分地有限。

Vue.js 本身只是一個 JavaScript 庫,包括 React 也一樣,只不過平時我們所說的 Vue 框架,是指包含 Router/ Vuex 等一系列組件之後融合的 一整套解決方案

更加詳細的解釋如下:

  • 「庫」 是一個封裝好的特定的集合,提供給開發者使用,而且是特定於某一方面的集合(方法和函數),庫沒有控制權,控制權完全在於使用者本身;
  • 「框架」 顧名思義是一套架構,會基於自身的特點向用戶提供一套比較完整的解決方案,如果使用者選定了一套框架,那麼就需要根據框架本身做出一定的適應。

02. 為什麼使用 Vue?

說實話,我個人非常喜歡 Vue。在我大學剛嘗試學習 HTML + CSS + JavaScript 和 Bootstrap 融合之後,我就接觸了 Vue,它對我來說這樣的「前端小白」來說,幾乎沒有什麼開發的門檻,很平滑地就得以過渡到 Vue 的使用中去。

典型的 .vue 文件可以簡單成如下的樣子:(vue-tutorial/typical-case.html)

<template>      <!-- HTML 程式碼 -->  </template>    <script>      // JavaScript 程式碼  </script>    <style>      /* css 程式碼 */  </style>

另外我也非常喜歡尤大大本人,大家可以去看一看 Honeypot 記錄的關於 Vue 的 紀錄片 (趁著寫文的間隙我又看了一遍),當然如果英文有些吃力也可以圍觀一下在 B 站上的 帶中文字幕的版本

B 站翻譯版本截圖

Vue 從一開始的定位就是儘可能的降低前端開發的門檻,讓更多的人能夠更快地上手開發。———— 尤雨溪

理由一:易上手、學習曲線平滑

就像上面的典型 .vue 文件的展示一樣,在 Vue 中,一切都很自然,例如我們使用 Vue 來構建我們的 Hello World 程式:(vue-tutorial/hello-vue.html)

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Hello Vue!</title>  </head>  <body>  <div id="app">      {{ message }}  </div>    <!-- 開發環境版本,包含了有幫助的命令行警告 -->  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  <script>      // 創建一個 Vue 實例或者說是 VieModel 實例      var app = new Vue({          el: '#app',          data: {              message: 'Hello Vue!'          }      })  </script>  </body>  </html>

可以看到幾乎沒有多餘的部分,只是在創建 Vue 實例時,把 idapp 的對象 (此處為一個 div) 綁定到了 Vue 實例中而已。

理由二:文檔友好

由於 Vue 是國人編寫的,所以在官網中有完整的中文文檔可供開發者參考,並且藉由尤大大出色的文筆,非常地清晰易懂,相信看過的朋友會和我有一樣的感受:

官方網站的文檔

官方文檔的地址可戳 這裡

理由三:MVVM 天然的雙向綁定

Vue.js 是一個提供了 MVVM 風格的雙向數據綁定的 JavaScript 庫,這就讓我們能夠專註於 View 層的開發,這種輕量級的框架讓前端開發更加高效、便捷。

例如,我們使用 v-model 來簡單改造一下我們的 hello-vue.html 文件讓它編程一個簡單的雙向綁定示例:(vue-tutorial/v-model-demo.html)

<div id="app">    <p>{{ message }}</p>    <input type="text" v-model="message" />  </div>

message 綁定到文本框,當更改文本框的值的時候, <p>{{ message }}</p> 中的內容也會被更新:

反過來如果我們更改 message 的值的話,文本框的值也會被更新,我們可以在控制台中嘗試一下:

千萬不要把框架能力看得比你解決問題的能力還重要

這裡是借鑒了 知乎中的一個討論,不論是使用 React 還是 Vue,我們最終還是要以 解決實際的問題 為出發點。引用一下尤大大在 知乎-Vue 和 React 的優點分別是什麼? 上的回答部分截取:

說了這麼多,無非是希望大家能停下來想想所謂的 」A 技術比 B 技術牛逼「 背後到底是在爭些什麼,我們使用這些技術的初衷又是什麼。很多時候你說這方面,他說那方面,雞同鴨講,即使說到一起去,也往往缺乏對等的資訊量或者基礎共識,只是各自表達主觀看法,最後變成兩個陣營各自抱團取暖… 說到底,就算你證明了 A 比 B 牛逼,也不意味著你或者你的項目就牛逼了… 比起爭這個,不如多想想怎麼讓自己變得更牛逼吧。—————— 尤雨溪

03. Vue 常用指令

上面我們已經實際體驗了一個 Vue 的指令 v-model 了,在 Vue 中,指令都帶有 v- 前綴,以表示它們是 Vue 提供的特殊的 attribute,它們會在渲染 DOM 時進行特殊的響應式行為。

Vue 內置了一些常用的指令,接下來我們將依次來介紹:

  • v-ifv-else 條件渲染指令;
  • v-show 條件展示指令;
  • v-for 列表渲染指令
  • v-bind 條件綁定指令;
  • v-on 事件處理指令;

大部分照搬的官方教程,寫得非常具有參考性,感興趣的朋友可以直接略過下面部分去參考 官方文檔

v-if 和 v-else 條件渲染指令

v-if 指令

v-if 指令用於條件性地渲染一塊內容。這塊內容只會在指令的表達式返回 truthy 值的時候被渲染。例如:(vue-tutorial/v-if-demo)

<p v-if="seen">現在你看到我了!</p>
var app = new Vue({      el: '#app',      data: {          seen: true      }  })

頁面會正確的顯示「現在你看到我了!」這幾個字。

v-else 指令

你也可以使用 v-else 來添加一個 "else 塊" 來表達條件不滿足時應該渲染的模組:

<p v-if="seen">現在你看到我了!</p>  <p v-else>Oh no!</p>
var app = new Vue({      el: '#app',      data: {          seen: false      }  })

此時條件 seen 不滿足,頁面就會顯示「Oh no!」的字樣。

v-else-if 指令

這是 2.1.0 版本新增的指令,充當 v-if 的 "else-if 塊",可以用來連續判斷條件:

<div v-if="type === 'A'">    A  </div>  <div v-else-if="type === 'B'">    B  </div>  <div v-else-if="type === 'C'">    C  </div>  <div v-else>    Not A/B/C  </div>

類似於 v-elsev-else-if 也必須緊跟在帶 v-if 或者 v-else-if 的元素之後。

v-show 條件展示指令

另一個用於根據條件展示元素的選項是 v-show 指令。用法大致一樣:

<h1 v-show="ok">Hello!</h1>

不同的是帶有 v-show 的元素始終會被渲染並保留在 DOM 中。v-show 只是簡單地切換元素的 CSS 屬性 display (條件不滿足則把元素 display 屬性設置為 none),而 v-if 則在條件不滿足時直接不渲染出對象。

v-if 與 v-show

v-if 是「真正」的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷毀和重建。

v-if 也是 惰性 的:如果在初始渲染時條件為假,則什麼也不做——直到條件第一次變為真時,才會開始渲染條件塊。

相比之下,v-show 就簡單得多——不管初始條件是什麼,元素總是會被渲染,並且只是簡單地基於 CSS 進行切換。

一般來說,v-if 有更高的切換開銷,而 v-show 有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用 v-show 較好;如果在運行時條件很少改變,則使用 v-if 較好。

v-for 列表渲染指令

我們可以用 v-for 指令基於一個數組來渲染一個列表。v-for 指令需要使用 item in items 形式的特殊語法,其中 items 是源數據數組,而 item 則是被迭代的數組元素的別名。

<ul id="example-1">    <li v-for="item in items">      {{ item.message }}    </li>  </ul>
var example1 = new Vue({    el: '#example-1',    data: {      items: [        { message: 'Foo' },        { message: 'Bar' }      ]    }  })

結果:

  • Foo
  • Bar

注意:永遠不要把 v-if 和 v-for 同時用在同一個元素上。 因為當 Vue 處理指令時,v-forv-if 擁有更高的優先順序,所以會導致錯誤,詳細的技術細節可以 戳這裡

v-bind 條件綁定指令

我們可以傳給 v-bind:class 一個對象,以動態地切換 class:(也可以用縮寫 : 來替代 v-bind 指令)

<div v-bind:class="{ active: isActive }"></div>

上面的語法表示 active 這個 class 存在與否將取決於數據屬性 isActivetruthiness

你可以在對象中傳入更多屬性來動態切換多個 class。此外,v-bind:class 指令也可以與普通的 class 屬性共存。當有如下模板:

<div    class="static"    v-bind:class="{ active: isActive, 'text-danger': hasError }"  ></div>

和如下 data:

data: {    isActive: true,    hasError: false  }

結果渲染為:

<div class="static active"></div>

isActive 或者 hasError 變化時,class 列表將相應地更新。例如,如果 hasError 的值為 true,class 列表將變為 "static active text-danger"

v-on 事件處理指令

可以用 v-on 指令監聽 DOM 事件,並在觸發時運行一些 JavaScript 程式碼。(也可以用縮寫 @ 來替代 v-on 指令)

示例:

<div id="example-1">    <button v-on:click="counter += 1">Add 1</button>    <p>The button above has been clicked {{ counter }} times.</p>  </div>
var example1 = new Vue({    el: '#example-1',    data: {      counter: 0    }  })

結果:

image

v-on 的事件修飾符

在事件處理程式中調用 event.preventDefault()event.stopPropagation() 是非常常見的需求。儘管我們可以在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。

為了解決這個問題,Vue.js 為 v-on 提供了事件修飾符。之前提過,修飾符是由點開頭的指令後綴來表示的。

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
<!-- 阻止單擊事件繼續傳播 -->  <a v-on:click.stop="doThis"></a>    <!-- 提交事件不再重載頁面 -->  <form v-on:submit.prevent="onSubmit"></form>    <!-- 修飾符可以串聯 -->  <a v-on:click.stop.prevent="doThat"></a>    <!-- 只有修飾符 -->  <form v-on:submit.prevent></form>    <!-- 添加事件監聽器時使用事件捕獲模式 -->  <!-- 即內部元素觸發的事件先在此處理,然後才交由內部元素進行處理 -->  <div v-on:click.capture="doThis">...</div>    <!-- 只當在 event.target 是當前元素自身時觸發處理函數 -->  <!-- 即事件不是從內部元素觸發的 -->  <div v-on:click.self="doThat">...</div>

另外事件處理還可以支援 按鍵碼 (某一個鍵按下)系統修飾符 (鍵盤滑鼠按下),可以參看 官方教程

04. Todo-List 示例

上面我們了解了一些基本的指令了,接下來我們實際動動手,來搭建一個簡單的 TodoList demo 小程式。

第一步:明確需求

TodoList 想必大家都很熟悉,使用來記錄我們接下來要做的一些事情的程式,最基本的功能有增加和刪除:

很簡單,可以看出我們只需要一個輸入框 (用來記錄將要保存的數據),一個按鈕 (用來添加數據),和一個集合 (用來保存數據) 就差不多可以了,上手!

第二步:創建好需要的 data

先來創建好我們需要的數據 data:

data: {      todos: [{              id: nextTodoId++,              text: '寫程式碼'          },          {              id: nextTodoId++,              text: '還是寫程式碼'          }      ],      newTodoText: ""  }

這裡多定義了 id 屬性是為了方便我們的刪除操作。

第三步:創建好對應的 HTML

沒有任何布局,就直接定義好我們所需要的組件就好了:

<input type="text" v-model="newTodoText" />  <button @click="addItem">添加</button>  <ul>      <li v-for="item in todos">          <span>{{ item.text }}</span>          <span><button @click="removeItem(item.id)">del</button></span>      </li>  </ul>

沒有任何的特別,只是裡面包含了兩個我們 未定義 的方法:addItemremoveItem 而已。

第三步:定義並實現方法

Vue 中的方法需要定義在 Vue 實例的 methods 關鍵字下面:

methods: {      addItem(key) {          this.todos.push({              id: nextTodoId,              text: this.newTodoText          })          this.newTodoText = ""      },      removeItem(id) {          this.todos = this.todos.filter(todo => {              return todo.id !== id          })      }  }

這裡數組的更新需要用到 push,另外刪除時我們使用了一個 lambda 表達式來完成,刪除時傳入了一個要刪除元素的 id,然後從數組中挑選出所有 不等於 這個 id 的元素重新賦值給原數組,這樣就相當於是刪除了元素了。

本文涉及的所有程式碼都上傳到了【More Than Java】項目中。(地址下方)

更好的參考

上面的程式碼僅僅是簡單實現,更好的參考可以查看 Vue 官方實現的一個更加具有參考性的例子:https://codesandbox.io/s/o29j95wx9

參考資料

  1. Vue【官方文檔】 – https://cn.vuejs.org/v2/guide/
  2. 【譯】框架與庫的差異 – https://juejin.im/post/5c5fe3e751882561dd7b4e9b
  3. Vue.js——60分鐘快速入門 – https://www.cnblogs.com/keepfool/p/5619070.html