# 關於前端網頁框架 Vue
# Vue 學習筆記 (一) : 環境配置
本測試參考了下列書籍 :
- 一次搞懂熱門前端框架 (旗標)
- The Majesty of Vue (Packt)
- Vue.js 2 Cookbook (Packt)
- Vue.js 2.x by Example (Packt)
- Model (資料管理層)
- View (畫面顯示層)
- ViewModel (資料與顯示互動中介層)
Vue 透過 ViewModel 中介層的資料與畫面更新達成雙向資料綁定功能, 無論是資料 (Model) 或頁面 (View) 發生變動, 另一端都能立即刷新. Vue 的特點就是採用漸進式架構, 核心功能專注於處理模型層與視圖層之中介, 簡單易上手, 不像其他框架之高度複雜性.
使用 Vue 基本上有三個步驟 :
1. 用 script 元素指定 vue.js 來源 (自備或使用 CDN)
2. 在網頁中指定一個具有 id 屬性的元素 (p 或 div) 掛載 (mount) App
3. 用 script 元素撰寫 App 或指定 App 來源
Vue 的中介互動功能是透過 Vue 物件達成的, 因此在 App 中須建立一個 Vue 物件來傳遞資料到網頁中, 或者是接收從網頁傳來的資料, 稱為雙向資料綁定, Vue 物件提供了特定屬性與方法來達成雙向資料綁定 (Two-way binding) 功能.
一. 單向資料綁定 (從 Vue 物件傳送資料到網頁) :
首先必須在網頁中建立一個模版來掛載 App 並綁定資料來源, 這樣就能從 Vue 物件傳遞資料到網頁中了. 此網頁模版是由具 id 屬性的網頁元素如 p 或 div 構成, 綁定之資料則以兩個大括弧之 Mustache 標籤表示 {{property}}, 這個 property 就是 App 中的變數, 也就是所綁定的資料來源 :
<div id='msg'>
{{ messge }}
</div>
這個 id 為 msg 的元素是一個容器, 用來掛載 (mount) 應用程式 App, 也是 App 在網頁中的工作區. 對 App 而言, 它只能操作這個容器中的的元素, 不認得網頁中其他任何元素. 換句話說只有所掛載的容器才是 Vue 應用程式的作用區域 (Scope). 注意, Vue 的 App 只能掛載到 html 內部的網頁元素上 (通常掛載 div 元素), 如果掛載到 html 或 body 標籤會產生執行錯誤.
接著是撰寫 Javascript App, 事實上就是建立一個 Vue 物件來操作網頁中此 App 所掛載的容器. 建立 Vue 物件時至少要傳入包含 el 與 data 屬性的物件當參數, 其中 el 用來指定網頁中所掛載容器之 id, 而 data 則定義所綁定的資料變數, 例如 :
var app=new Vue({
el: '#msg',
data: {message: 'Hello World!'}
});
此處 el 屬性值 '#msg' 表示此 App 要掛載到網頁中 id 為 msg 的元素當作容器, 而屬性 data 表示此網頁容器所綁定的變數, 亦即資料來源, 此處定義了一個變數 message 並賦予初始值 'Hello World!', 當 Vue 物件建立時, 這個初始值會立即被傳遞到網頁容器中以 Mustache 標籤所綁定的位置.
屬性 el 指定元素的用法與 jQuery 一樣, 用 "#" 表示存取 id 屬性, 除此之外也可以指定 DOM 物件給 el 屬性 :
var element=document.getElementById('msg');
var app=new Vue({
el: element,
data: {message: 'Hello World!'}
});
建立 Vue 物件後此物件即掛載到指定之網頁容器上, Vue 物件會以自己建立的 Virtual DOM 來取代此容器內原來的網頁元素, 這就是為何 Vue 物件可以將變數之值動態即時注入到所綁定之位置的原因. 完整範例如下 :
測試 1 : Hello World (本機來源) [原始碼]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="js/vue.js"></script>
</head>
<body>
<div id='msg'>{{message}}</div>
<script>
new Vue({el: '#msg', data: {message: 'Hello World!'}});
</script>
</body>
</html>
瀏覽網頁結果為顯示 "Hello World!".
也可以使用 CDN 來源, 並將應用程式 new Vue() 寫成外部檔案 helloworld.js, 放在 /js 底下, 其內容為 :
new Vue({el: '#msg', data: {message: 'Hello World!'}});
例如 :
測試 2 : Hello World (外部來源) [原始碼]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id='msg'>{{message}}</div>
<script src="js/helloworld.js"></script>
</body>
</html>
結果與測試 1 完全一樣.
注意, 載入 helloworld.js 的 script 標籤不可放在 head 標籤內, 因為當 head 部分被載入會立即執行 helloworld.js, 但此時 div 元素尚未建立, 因此資料無法綁定, 會顯示 {{ message }} 而不是 'Hello World!', 例如 :
測試 3 : Hello World (外部來源) [原始碼]
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="js/helloworld.js"></script>
</head>
<body>
<div id='msg'>{{message}}</div>
</body>
</html>
上例中將應用程式 helloworld.js 放在 head 標籤中是錯誤的, 應該放在所綁定的元素後面才會正確運作, 放在 body 的結尾處最保險.
如果要放在所綁定的元素前面, 可以將 App 寫在自訂函數中, 再用 body 元素的 onload 屬性指定當 body 內元素全部載入後執行此函數即可, 例如 :
測試 4 : Hello World (使用 onload) [原始碼]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body onload='sayhello()'>
<script>
function sayhello() {
new Vue({el: '#msg', data: {message: 'Hello World!'}});
}
</script>
<div id='msg'>{{message}}</div>
</body>
</html>
本例 App 雖位於綁定元素 id='msg' 的 div 之前, 但是函數 sayhello() 會在 body 內元素都載入後才執行, 因此網頁會顯示 'Hello World!' 之正確結果.
上面提到與 Vue 綁定 id 的 div 元素是 Vue 物件操作的容器, Vue 物件傳遞過來的資料可用 Mustache 標籤放在此容器內任何元素中, 例如 :
測試 5 : 傳遞資料到 div 容器內之 h1 元素 [原始碼]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id='msg'>
<h1>{{message}}</h1>
</div>
<script>
new Vue({el: '#msg', data: {message: 'Hello World!'}});
</script>
</body>
</html>
可見 Hello World! 受到 h1 標籤控制變粗體了! 這說明 Vue 物件所傳遞的資料可放在容器元素 div 內的任何地方, 不一定要放在 div 本身.
二. 雙向資料綁定 :
上面範例都是從 Vue 物件傳送資料到網頁, Vue 提供了 v-model 屬性讓網頁元素可以反方向從網頁傳遞資料給 Vue 物件以達成雙向資料綁定功能, 亦即透過設定 v-model 我們告訴 Vue 這個 input 輸入欄位要跟 Vue 物件裡面的哪一個 data 變數綁定在一起. 輸入元素以 input 元素為例 :
<input type='text' v-model='message'>
此 input 元素透過 v-model 屬性與 Vue 物件的 data 屬性中的 message 子屬性綁定在一起, 而 message 若與網頁中 Mustache 標籤 message 綁定的話, 則在 input 中輸入的資料會立刻顯示在網頁上, 例如 :
測試 6 : 雙向資料綁定 (無按鈕) [原始碼]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id='msg'>
<p>{{message}}</p>
<input type='text' v-model="message">
</div>
<script>
new Vue({
el: '#msg',
data: {message: 'Hello World!'}
});
</script>
</body>
</html>
網頁初始執行時, p 標籤的初始內容為 Hello World!, 在底下的 input 文字欄位輸入任意文字時, 上面 p 標籤的內容也會立即同步顯示相同內容, 這就是所謂的雙向資料綁定! 因為 input 標籤以 v-model 屬性與 Vue 物件之 data 子物件的 message 屬性綁定, 因此在 input 文字欄位所輸入的任何字串都會同步修改 data 子物件的 message 屬性值; 同時, 此屬性又被 Mustache 樣版引擎投放到 p 標籤的內容中, 因此 p 標籤內容也會同步改變.
注意, p 標籤與 input 標籤都必須放在 id='msg' 的 div 容器內, 這樣雙向資料綁定才會生效, 因為此容器是 Vue 物件的工作空間.
不過若在 input 文字欄位中輸入 HTML 碼的話, p 標籤將如實顯示 HTML 碼, 因為 Mustache 樣版引擎遇到 < 與 > 符號時會自動脫逸 (escapse), 因此不會顯示 HTML 效果.
解決辦法有兩個, 在 Vue 2 以前的版本使用三重大括弧 {{{ }}} 來阻止脫逸; 但在 Vue 2 之後改用 v-html 屬性來呈現 HTML 內容 :
<p v-html='message'></p>
亦即只要將原本的 Mustache 標籤 {{ }} 改用 v-html 取代即可, 綁定方式完全不變, 此方法可顯示 HTML 與純文字內容, 例如 :
測試 7 : 雙向資料綁定 (無按鈕 HTML 版) [原始碼]
<html>
<head>
<meta charset="utf-8">
<script src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id='msg'>
<p v-html='message'></p>
<input type='text' v-model="message">
</div>
<script>
new Vue({
el: '#msg',
data: {message: 'Hello World!'}
});
</script>
</body>
</html>
上面的兩個範例是在 input 欄位輸入資料時立即反映在 p 標籤內, 如果加一個確定按鈕, 需按下按鈕才會更新資料該怎麼做? Vue 為按鈕元素提供了 v-on 事件處理屬性來與 Vue 物件的 methods 屬性綁定, 只要將事件處理函數指定給 methods 屬性就可以了 :
<input type='button' v-on:click='doSomething' value='確定'>
此按鈕中以 v-on 屬性指定了按鈕的 click 事件處理函數為 doSomething(), 此函數需在 Vue 物件中定義並以物件形式指派給 methods 屬性才能達成事件綁定功能 :
methods: {doSomethong : function() {//操作網頁顯示的程式碼}}
例如 :
測試 8 : 雙向資料綁定 (有按鈕 HTML 版) [原始碼]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id='msg'>
<p v-html='message'></p>
<input type='text' v-model='input_msg' size='30'>
<input type='button' v-on:click='doSomething' value='確定'>
</div>
<script>
new Vue({
el: '#msg',
data: {
message: 'Hello World!',
input_msg: ''
},
methods: {
doSomething: function() {
this.message='您輸入了 : ' + this.input_msg;
}
}
});
</script>
</body>
</html>
輸入 <b style='color:blue'>Tony</b> 結果如下 :
此處雙向資料綁定定義於 Vue 物件的 data 子物件的兩個屬性中, 其中 message 用來傳遞資料至網頁, 而 input_msg 則用來接收網頁傳遞過來的資料. 事件處理則是透過按鈕元素的 v-on 屬性定義 click 事件處理函數為 doSomething(), 當使用者按下 '確定' 按鈕時會呼叫 Vue 物件 methods 屬性鎖定義的 doSomethong() 函數處理, 在此函數中, Vue 物件會讀取 input_msg 從網頁接收的資料 this.input_msg 串上前置字串後設定 this.message 屬性, Mustache 樣版引擎會立刻將其輸出到網頁中的 p 標籤中.
其實雙向資料綁定用 jQuery 來做也是輕而易舉的, 我參考了 "The Majesty of Vue" 這本書的 2-3 節範例, 將上面測試 7 的雙向資料綁定功能改以 jQuery 來實做如下所示 :
測試 9 : 雙向資料綁定 (jQuery 版無按鈕) [原始碼]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<div id='msg'>
<p id='message'></p>
<input type='text' id='input_msg' size='30'>
</div>
<script>
$('#message').html('Hello World!');
$('#input_msg').on('keyup', function() {
var input_msg=$('#input_msg').val();
$('#message').html('您輸入了 : ' + input_msg);
});
</script>
</body>
</html>
這裡使用 jQuery 物件的 on() 方法給輸入欄位 input_msg 掛載 keyup 事件處理函數, 每輸入一個字元就會呼叫此匿名函數, 利用 jQuery 物件的 val() 取得 input_msg 的內容, 前面串上前置字串後呼叫 html() 設定 p 元件的 innerHTML 屬性以顯示 HTML 內容, 功能與上面測試 7 完全一樣.
上面測試 8 Vue 有按鈕版若使用 jQuery 來做如下所示 :
測試 10 : 雙向資料綁定 (jQuery 版有按鈕) [原始碼]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<div id='msg'>
<p id='message'></p>
<input type='text' id='input_msg' size='30'>
<input type='button' id='button1' value='確定'>
</div>
<script>
$('#message').html('Hello World!');
$('#button1').click(function() {
var input_msg=$('#input_msg').val();
$('#message').html('您輸入了 : ' + input_msg);
});
</script>
</body>
</html>
按鈕在 jQuery 要呼叫 click() 來處理按鈕事件, 其餘跟測試 9 是一樣的. 比較兩者作法, jQuery 程式色彩較強, 且須熟悉 DOM 架構, 因為 jQuery 是直接操作 DOM 的. 而 Vue 好像只是在設定屬性而已, 而且 Vue 不直接操作 DOM, 而是隱性地與 Virtual DOM 互動, 據說執行起來比較快.
參考 :
# Vue.js 30天隨身包 系列
# Day08 - [Directives] 資料綁定(Data Binding)
2019-12-20 補充 :
Vue/React/Angular 都具有雙向資料綁定功能, 但採用的方法不同, 下面這篇文章有詳細演示 :
# 前端數據綁定之謎
看過之後覺得還是 Vue 最簡潔.
沒有留言 :
張貼留言