iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 4
0
自我挑戰組

每天來點 Vue.js 吧系列 第 4

一切的基礎:Vue instance

tags: Vuejs

Vue reactivity system ✐

Vue 引入 data-driven view 的概念,一旦改動 data,與之相對應的 view 也會隨之變動。而實現此概念的響應式系統 reactivity system ,透過 binding(綁定)DOM 及 data,使得 data 改變時 view 能同步更新,過程中不需要進行任何 手動操作 DOM 的行為,讓開發者將編寫代碼的重心放在 data 的操作,減少手動操作 DOM 的繁複以及降低命令式操作帶來錯誤的風險。

如同前兩篇所言,Vue core library 核心幫助我們同步 DOM 及物件,一旦 data 發生更新,view 隨之更動,that's it.

Just to sync some Dom with some JavaScript objects. That was the original.

用簡單的範例比較手動操作 DOM 與使用 Vue 的不同 ✐

手動操作 DOM 版本 ☼ 範例程式

看過了概念,讓我們用簡單的渲染列表作為範例,來比較手動操作 DOM 與使用 Vue 的差異:

假設我們今天想要做渲染出一個紅色水果列表 dataview 中,並且在按下新增 火龍果 後,新增 火龍果dataview 列表也隨之更新,我們要做以下的行為:

  • 透過 Document method 取得 ul HTMLElement
  • data 轉換成合適格式 <li>...</li>
  • 將剛才轉換成字串的 data 透過 ul.innerHTML 加入到 view
  • 取得 button,註冊事件監聽器,callback 增加 火龍果data
  • buttoncallback 中也要記得重新渲染列表,不然 data 更新,但是 view 不更新 ...
HTML
<section>
  <button>新增火龍果</button>
  <h1>紅色的水果</h1>
  <ul></ul>
</section>
JavaScript
const list = ["蘋果", "櫻桃", "番茄", "草莓"];
const button = document.querySelector("button");

button.addEventListener("click", addPitaya);
renderView();

// 渲染 list 到 view
function renderView() {
  const ul = document.querySelector("ul");
  const result = list.map((item) => `<li>${item}</li>`).join("");
  ul.innerHTML = result;
}

// 新增火龍果,重新渲染 view
function addPitaya() {
  list.push("一個新的紅色水果");
  // 除非再渲染一次 view,否則新增 data 後 view 不會更新
  renderView();
}

我們可以看到,在上列範例中,不僅要考慮 data,同時要考慮如何操作 DOM,在 data 更動後,也要手動的更新 DOM,否則 view 不會更動,由此可以看出我們的思緒分散在 data 操作(新增火龍果)以及 view 更新(操作 DOM)上,今天的範例仍算簡單,不過若是更複雜的情境、更複雜的 view ,操作 DOM 必定會更加的繁雜,不好維護。

那麼,看過了手動操作 DOM 後,我們來寫寫相同範例的 Vue 版本,體驗同步 DOM 與 data 為我們簡化了什麼吧!

Vue 版本 ☼ 範例程式

要使用 Vue,我們必須透過建構式 Vue 創建一個 Vue instance,並且在傳入的物件選項中選中一個 DOM 元素,Vue instance 會將該元素作為入口點渲染 template,我們將不會直接操作 DOM。

HTML
<section>
  <button>新增火龍果</button>
  <h1>紅色的水果</h1>
  <ul></ul>
</section>
JavaScript
const vm = new Vue({
  el: "section",
  data: {
    list: ["蘋果", "櫻桃", "番茄", "草莓"]
  },
  methods: {
    addPitaya() {
      const isListHasPitaya = this.list.find((item) => item === "火龍果");
      if (isListHasPitaya) return;
      this.list.push("火龍果");
    }
  },
  template: `
  <section>
  <button @click="addPitaya">新增火龍果</button>
  <h1>紅色的水果</h1>
  <ul>
    <li v-for="item of list">{{ item }}</li>
  </ul>
</section>
`
});

在本章節中,先不用在意 v-for 以及其他部分,我們單純先比較新增 火龍果 在兩種方法的差異。

在 Vue 版本中,按鈕所對應的 addPitaya 函式只單純新增 火龍果,不再需要重新渲染 view ,一旦 datalist 更新, view 也會隨之響應變動。

兩相比較,我們不難發現 Vue 版本中,我們只單純的操作了 data ,而非 DOM,這便是開頭所說 Vue 同步 DOM 以及 data ,能將開發者注意力自繁雜的 DOM 中脫離,專注在 data 操作,為 Vue reative system 所帶來便利。

要深入理解可以自 Object.defineProperty 以及 Vue 源碼開始理解 Vue 2 如何做到這一切的。

了解 Vue 所帶來同步 data 和 DOM 的便利後,可以接著使用 Vue,在引入前一章節的 <script src="..."> 後,繼續開始下一步吧!

Vue instance ✐

使用 Vue,需透過 Vue 建構式生成的 Vue instance 開始,而 Vue 建構式可以傳入一個 option 物件,我們可以透過該 option 物件設定想達成的細節。

來看一個剛才範例的簡化版本:

  • 設定 Vue instance Option
    • 在選項物件中設定 el: '你想選擇的元素',在此為 section,Vue instance 將會掛載 取代 該元素,記住由於該元素會被取代,所以請別掛載在 htmlbody 上。
    • 設定 data 作為 Vue instance 的 data 物件,Vue instance 建立觀察後才設置的 data 將無法另 view 響應。
    • 設定 template,樣板將會替換掛載的 section。若是沒有設置,會由 mounted 的元素作為 template
  • 接著建立 Vue instance,記得放入剛剛設置的 Option 選項,Vue instance 掛載成功。
// 生成 Vue instance
const vm = new Vue({
  el: "section", // 提供頁面上已存在的 DOM 作為 instance 掛載的目標,掛載元素會被替換
  data: { // 資料,在 Vue2 中須先將 data 定義好,instance 創建後加載的 data 將無法響應
    list: ["蘋果", "櫻桃", "番茄", "草莓"]
  },
  template: ` // 會取代 el 的模板
  <section>
  <button @click="addPitaya">新增火龍果</button>
  <h1>? 紅色的水果</h1>
  <ul>
    <li v-for="item of list">{{ item }}</li>
  </ul>
</section>
`
});

我們可以看到 Vue instance 成功掛載, view 也能隨 data 響應。

補充:

在 Vue 1.0 版本中 MVVM 的示意圖,可以看到 vue 作為橋樑使 viewmodel(資料)同步

Vue instance Options ✐

以下列出 Option 選項,節錄自 Vuejs.org 2.x

資料

選項 類型 內容
data Object 或 Function Vue 實例的數據物件。Vue 將會迭代將 data 的 property 轉換為 getter/setter,從而讓 data 的 property 能夠響應數據變化。
props Array<string> 或 Object props可以是陣列或物件,用於接收來自父組件的資料。
propsData { [key: string]: any } 建立實例時傳遞props。主要作用是方便測試。
computed { [key: string]: Function 或 { get: Function, set: Function } } 計算屬性將被混入到 Vue 實例中。所有 getter 和 setter 的 this 上下文自動地綁定為 Vue 實例。
methods { [key: string]: Function } methods 將被混入到 Vue 實例中。可以直接通過VM 實例訪問這些方法,或者在指令表達式中使用。方法中的 this 自動綁定為 Vue 實例。
watch { [key: string]: string、Function、Object、Array } 一個對象,鍵是需要觀察的表達式,值是對應回調函數。

DOM

選項 類型 內容
el string、Element 提供一個在頁面上已存在的 DOM 元素作為 Vue 實例的掛載目標。可以是 CSS 選擇器,也可以是一個 HTMLElement 實例。
template string 樣板將會替換掛載的元素。掛載元素的內容都將被忽略,除非樣板的內容有分發插槽。
render (createElement: () => VNode) => VNode 字串樣板的代替方案,允許你發揮 JavaScript 最大的編程能力。該渲染函數接收一個 createElement 方法作為第一個參數用來創建VNode
renderError (createElement: () => VNode, error: Error) => VNode 只在開發者環境下工作。當 render 函數遭遇錯誤時,提供另外一種渲染輸出。其錯誤將會作為第二個參數傳遞到 renderError。這個功能配合 hot-reload 非常實用。

結語

以上為此次內容,內容集中在講解 data-driven view 以及使用手動操作 DOM 的差異,另外講解了使用 Vue instance 的基礎方法,感謝看到這裡的你,我們明天見。


若是文中有任何錯誤歡迎各位大大不吝指正,筆者不慎感激 ✦ ✦ ✦

▶︎ 筆者 github:https://github.com/YUN-RU-TSENG
▶︎ 老王賣瓜之筆者另一篇鐵人:每天來點 CSS Specification

▶︎ 倘若不斷向深處扎根,似乎就能茁壯成長 - RM


參考資料:

  1. Vuejs.org 2.x
  2. Vuejs.org 1.0
  3. THE VUE INSTANCE - DATA DRIVEN APPLICATIONS
  4. 前端框架簡介

上一篇
工欲善其事,必先利其器 ⚙︎ Vue 開發環境建置
下一篇
Vue instance 生命週期 ✦
系列文
每天來點 Vue.js 吧30

尚未有邦友留言

立即登入留言