顯示具有 Vue 標籤的文章。 顯示所有文章
顯示具有 Vue 標籤的文章。 顯示所有文章

2022年7月16日 星期六

三本好書 (Python 可視化, 機器學習, Vue)

昨天去河堤還書時路過明儀就進去看看有甚麼新書, 結果找到了下面三本值得一看的好書 :



Source : 博客來


此書原版係日文, 翻閱發現內容很實用, 以介紹 Seaborn, Plotly, 與 Folium 為主, 特別是套繪地圖部分對 Folium 有詳細說明. 




Source : 博客來


這本譯自 Oreilly 的好書我從原文版出來後就覬覦很久了, 但因為使用 PyTorch 有門檻上不去, 一直沒買. 最近開始學習 PyTorch 後覺得距離沒這麼遙遠, 可以一看. 




Source : 博客來


這本書是 Vue.js 達人 Kuro 多年教學結晶, 我翻閱前面覺得很容易理解, 果然是千錘百鍊之作. 目前三大前端框架 (Vue, React, Angular) 大概只有 Vue 我可以看得下去, 之前我在 Vue 2.0 時曾經淺學一陣子 (現在已經到第 3 版了), 那時就覺得 Vue 的學習曲線不陡, 算是可以無痛上手的框架, 沒有繼續學的原因是時間有限, 而且待在 jQuery 的舒適圈就夠用了啊! 幹嘛學這麼多武功? 不過如果有時間的話會繼續學習 Vue, 畢竟還是要跟得上時代. 

好消息是 : 這三本書母校圖書館都有, 真是太好了! 


2022-08-16 補充 :

昨天趁午休去母校把這三本書借出來囉! 樂獨中. 兩周前就借過一次, 但因為自己搞錯時間, 超過一天才想起忘記去取書, 接著就忙了兩周的 GitHub 架站, 累累. 

2019年12月17日 星期二

Vue 學習筆記索引

其實 Vue 我只是淺嚐即止就沒時間繼續學習, 但我很喜歡 Vue 的簡約風格, 如同當初學 jQuery 那樣很容易燃起學習的火花. 今日看到程式前沿的這篇 "Vue中容易被忽視的知識點" 讓我想起了 Vue, 就做了索引方便複習, 也刺激自己繼續加油 !

# 關於前端網頁框架 Vue
# Vue 學習筆記 (一) : 環境配置
# Vue 學習筆記 (二) : MVVM 架構與雙向資料綁定
# Vue 學習筆記 (三) : computed (計算函數) 與 filters (過濾器) 測試
# Vue 學習筆記 (四) : Directives (樣版指令) 測試

~進行中~

2019年3月7日 星期四

久違的 EasyUI

今天在測試 Django 的模版時想起之前在玩 GAE 時曾使用 EasyUI 寫了一個可在 GAE 上快速架站的 EasyUICMS GAE 版, 嘿, 這個 EasyUI 框架還有持續在開發吧? 連上官網讓我非常驚訝, 目前更新到 v1.7.5 版, 而且除了原來的 jQuery 版之外, 隨著前端三大天王 React, Angular, 以及 Vue 的崛起, EasyUI 也有了這三大天王的版本, 真是讓我感到驚艷, 參考 :

# https://www.jeasyui.com
# https://www.jeasyui.com/download/index.php

我是在 2014 年左右開始接觸 EasyUI 這個 jQuery 與 HTML5 為核心的前端 UI 框架, 以此為基礎建造了一個迷你 PHP 架站機, 可快速在一般 PHP 虛擬主機上佈署網頁應用程式, 當時主要是為了開發股票資訊彙整自動化系統. 但 2017 年之後我投入物聯網研究, 研究重心轉移到 Arduino, 樹莓派, 以及 ESP8266 等板子上, 也就沒時間繼續開發 Fintech 方面的專案了, 所租的PHP 虛擬主機轉眼間即將在年底到期, 租了三年幾乎是空轉了兩年, 實在有點可惜.

雖然前陣子摸了一下 Vue, 但只是學了些皮毛, 其實還不太會使. 看來還是繼續用自己最熟悉的 jQuery 版吧! 我打算複習 EasyUI 用法, 將其整合到 Django 網頁應用程式中, 在樹莓派上建構資訊系統. 今年決定暫時放下物聯網研究專攻 Fintech, 不達目標絕不切換 (嚴防見異思遷, 心猿意馬喔). 參考 :

# 如何在 GAE 佈署 jQuery EasyUI 專案 (一)


2019-11-07 補充 :

這幾天測試 Django 的框架模板, 發現 EasyUI 最新版的 Tabs 有 bug, 標頭與內容區沒有對正, 內容寬度較大一些會凸出來, 而之前用的 1.4.1 版不會, 因此若專案有用到 Tabs 時要用本地供檔, 不要用 CDN.

2019年2月12日 星期二

Vue 學習筆記 (三) : computed (計算函數) 與 filters (過濾器) 測試

在前一篇測試中, 要從 Vue 物件透過 Mustache 樣版引擎將物件中 data 屬性所儲存的變數即時傳遞到網頁中, 只要使用 Mustache 標籤 {{ propertyName }} 將變數綁定至標籤內即可. 事實上, Mustache 標籤 {{ }} 內還可以放置運算式或加上所謂的 filters (過濾器) 在變數渲染前做一些前置處理.

本系列之前的測試文章參考 :

# 關於前端網頁框架 Vue
# Vue 學習筆記 (一) : 環境配置
# Vue 學習筆記 (二) : MVVM 架構與雙向資料綁定

本測試參考了下列書籍中的範例加以改寫 :
  1. 一次搞懂熱門前端框架 (旗標)
  2. The Majesty of Vue (Packt)
  3. Vue.js 2 Cookbook (Packt)
  4. Vue.js 2.x by Example (Packt)
關於 Mustache 樣版 (Template) 語法參考 :

# https://vuejs.org/v2/guide/syntax.html


一. Mustache 樣版運算式 :

在 Mustache 標籤內可以使用 Javascript 運算式 (Expression) 對 Vue 物件傳遞過來的變數進行運算, 運算可在 Mustache 模版標籤內或在 App 的 data 物件內進行, 但只限於簡單的單行運算式, 且只能使用有限的全域物件例如 Date 或 Math 物件, 不可使用自訂義之全域變數, 也不可使用 Javascript 敘述 (Statements), 邏輯判斷或迴圈等指令, 參考 :

# https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions

例如 :

測試 1 : Mustache 運算式  [原始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://unpkg.com/vue"></script>
  </head>
  <body>
    <div id='msg'>
      <p>{{ 2+3 }}</p>
      <p>{{ 'Hello' + 'World!' }}</p>
      <p> There are {{ 5 }} people here.</p>
      <p> We need {{ deskCount }} more desks.</p>
      <p> 陣列 : {{ characters }}</p>
      <p> 物件 : {{ car }}</p>
      <p> 主演 : {{ characters.join('&') }}</p>
      <p> 男主角 : {{ characters[1] }}</p>
      <p> 廠牌 : {{ car.type }}</p>
    </div>
    <script>
      const app=new Vue({
        el: '#msg',
        data: {
          deskCount: 10 + 20,
          characters: ['李世榮','呂珍九'],
          car: {type:"Fiat", model:"500", color:"white"}
          }
        });
    </script>
  </body>
</html>

此 App 綁定了 deskCount, characters, 以及 car (物件) 三個資料變數 (可變), 其餘都是在 Mustache 直接進行 Javascript 的固定運算 (不可變). 在 Mustache 樣版中使用了陣列方法 join(), 陣列索引 [1], 以及物件屬性 .type 等運算與計算式來控制最後網頁輸出的結果.  執行結果如下 :




綁定的資料變數與網頁呈現為即時雙向互動的, 亦即不論是使用者或 App 更改了資料, 另一方也會立即改變. 例如只要在 App 中透過修改 app.car.type 的屬性值, 則網頁視圖中的廠牌也會立刻被修改.

在 Chrome 瀏覽器中按 Ctrl + Shift + I 進入開發人員模式, 然後切到 Console (控制台) 頁籤, 在 > 提示號後面輸入 app.car.type='Toyota' 後按 Enter 即可看到網頁中的廠牌會從原本的 Fiat 立刻被改為 Toyota :




上面的範例是在 Mustache 樣版標籤內進行簡單的 Javascript 運算, 但只限於可直接輸出的運算式. 事實上 Vue 提供了 computed 計算屬性, 可在 App 內定義一個可於 Mustache 標籤內呼叫的無參數函數, 用法如下 :

computed: {
    doSomething() {
        //result=calculation or assignment
        return result;
        }
    }   

或者寫成下面的形式也可以 :

computed: {
    doSomething: function() {
        //result=calculation or assignment
        return result;
        }
    }

此 computed 屬性中定義了一個無參數函數 doSomething(), 經過運算後以 return 傳回結果, 這時在 Mustache 標籤內使用函數名稱便能呼叫此函數 (不可加括弧), 輸出值即為函數之傳回值 :

<p> {{ doSomething }} </p>

例如下面範例會輸出 1+2+3+...+10 之和, 根據等差級數和公式 n(A1+An)/2=10*(1+10)/2=55 :

測試 2 : 用 computed 屬性定義計算函數 (1)  [原始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://vuejs.org/js/vue.js"></script>
  </head>
  <body>
    <div id='msg'>
      <p> {{ sum }}</p>
    </div>
    <script>
      new Vue({
        el: '#msg',
        computed: {
          sum() {
            var total=0
            for (var i=1; i<=10; i++) {
              total += i;
              }
            return total;
            }
          }
        });
    </script>
  </body>
</html>

由於 computed 定義的計算函數無法傳遞參數, 因此上面的程式只能執行固定的計算 (等差為 1 的級數末項=10). 如果要用參數動態更改計算結果可以利用 data 屬性綁定網頁上的輸入欄位來達成, 例如 :

測試 3 : 用 computed 屬性定義計算函數 (2)  [原始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://vuejs.org/js/vue.js"></script>
  </head>
  <body>
    <div id='msg'>
      <p> {{ '1+2+3+...+' + number + '=' + sum }}</p>
      <input type='text' v-model="number">
    </div>
    <script>
      new Vue({
        el: '#msg',
        data: {
          number: '10'
          },
        computed: {
          sum() {           //寫成 sum: function() { 也可以
            var total=0
            for (var i=1; i<=this.number; i++) {
              total += i;
              }
            return total;
            }
          }
        });
    </script>
  </body>
</html>




此例網頁中新增了一個 input 文字欄位來設定級數的末項, 然後將此欄位與 Vue 物件的 data 屬性之 number 變數綁定, 如此一來使用者在網頁上輸入的數字立即會更新 number 變數. 而在 computed 屬性定義之計算函數 sum() 中則用 this.number 取得 Vue 物件裡的 number 變數並設為迴圈之終止條件, 只要輸入的數值被改變, 則 sum() 函數計算之總和也會立即更新並輸出到 p 元素中, 因此 computed 中的回傳變數 sum 也是與 p 元素中的 sum 綁定的.

不過上面的範例中, computed 屬性定義的計算函數只能從 Vue 物件的 data 屬性中取得資料 (data 的 getter), 計算的結果只能回傳給 Mustache 樣版引擎輸出, 可否不回傳直接去設定 data 屬性中的變數呢 (data 的 setter)? 可以的, 但是 computed 屬性須以下列格式設定 :

computed: {
    property:  {
        get: function() {
            //result=calculations by getting variables from data (this.variable)
            return result;
            },
        set: function(val) {
             //setting variables in data with val, e.g. : this.variable=calculations of val
             }
        }
    }

此處 property 屬性定義了兩個計算函數 get 與 set, 其中 get 功能與上面兩個範例一樣, 就是從 data 屬性中取得變數值, 運算後傳回給屬性 property; 而 set 函數用來設定 data 屬性中變數 (不傳回值). set 函數的傳入參數可以用任何識別字, val 只是任選的, 事實上它就是與網頁輸入元素綁定的 property.

例如下面這個華氏溫度與攝氏溫度換算的範例 :

測試 4 : 用 computed 屬性定義計算函數 (3)  [原始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://vuejs.org/js/vue.js"></script>
  </head>
  <body>
    <div id='msg'>
      攝氏(C)<input type='text' v-model="celsius"> = 
      華氏(F)<input type='text' v-model="fahrenheit">
    </div>
    <script>
      new Vue({
        el: '#msg',
        data: {
          celsius: 0
          },
        computed: {
          fahrenheit: {
            get: function() {
              return Math.round(100*(this.celsius*(9/5)+32))/100;  //傳回 fahrenheit
              },
            set: function(val) {
              this.celsius=Math.round(100*((val-32)*5/9))/100;  //設定 data 內變數
              }
            }
          }
        });
    </script>
  </body>
</html>




此例網頁中有兩個文字輸入欄位, 分別綁定 Vue 物件中 data 屬性裡的 celsius 變數與 computed 屬性裡的 fahrenheit 變數. 不論在哪一個欄位輸入數值, 另一個欄位馬上就會更新.

若輸入攝氏度數, 則與它綁定的 data 屬性之 celsius 變數立刻被改變, 那麼 computed 計算函數中的 get 函數會被呼叫, 利用 this.celsius 取得所綁定之輸入值代入公式計算出華氏度數後傳回給 fahrenheit, 而此變數與華氏 input 欄位綁定, 因此會立即更新輸出值.

若輸入華氏度數, 則與它綁定的 computed 屬性之 fahrenheit 變數立刻被改變, 函數 set 會被呼叫並傳入華氏度數給 val 參數, 代入公式計算出攝氏度數後利用 this.celsius 設定 data 內之變數 celsius, 由於此變數與攝氏輸入欄位綁定, 因此輸出會立即更新.

注意, 由於 Javascript 的 Math.rround() 函數無法指定四捨五入到小數點後指定位數, 此處先將原值乘以 100 後取四捨五入再除以 100, 這樣便能取四捨五入到小數點後兩位了 (若至小數點後三位, 就先乘 1000 再除 1000). 參考 :

# JavaScript 四捨五入、無條件捨去、無條件進位


二. 過濾器 :

除了可用 computed 屬性在 Vue 物件內自行定義 Javascript 函數於 Mustache 樣版標籤內呼叫外, Vue 也內建了一些稱為過濾器 (filter) 的常用函數可直接於樣版標籤內串接 (呼叫) 使用. 簡言之, 過濾器其實是一組特定用途的函數, 用來把欲輸出之變數經過處理為特定格式後再透過 Mustache 樣版引擎輸出.

在 "一次搞懂熱門前端框架" 這本書的 4-2 節列出如下的過濾器 :

 Vue 的過濾器 說明
 json 將物件轉換成 JSON 格式
 capitalize 將第一個英文單字首字母大寫, 其餘小寫
 uppercase 將所有字母轉為大寫
 lowercase 將所有字母轉為小寫
 pluralize 將英文單字轉為複數
 currency 將數值每三位數家逗號, 並加上指定之貨幣單位
 debounce 延遲指定時間 (單位 ms) 後顯示, 例如 debounce 50 為延遲 50 ms 後輸出
 orderBy 將物件陣列以指定之屬性值排列, 例如 orderBy age 依據屬性 age 排序輸出 
 limitBy 限制陣列輸出範圍, 例如 limitBy 3, 2 表示從索引 2 開始輸出 3 個元素
 filterBy 過濾陣列輸出範圍, 例如 filterBy 'th' 只輸出 'th' 開頭的元素

過濾器可以依序用管線符號 "|" 串接, 格式如下 :

{{ propertyName | filter1 | filter2 | filter3 | ... }}

不過從 Vue 2 版後這些過濾器已經從 Vue 核心移除, 在 Vue 1 的最後版本 1.0.8 版還可使用, 例如 :

測試 5 : 過濾器測試 : (1)  [原始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.8/vue.js"></script>
  </head>
  <body>
    <div id='msg'>
      <p>{{ message | lowercase }}</p>
      <p>{{ message | uppercase }}</p>
      <p>{{ 'hello world!' | capitalize }}</p>
      <p>{{ message | pluralize }}</p>
      <p>{{ 'Debounce 1 seccond' | debounce 1000 }}</p>
      <p>{{ 1000000 | currency }}</p>
    </div>
    <script>
      new Vue({
        el: '#msg',
        data: {
          message: 'Hello World!'
          }
        });
    </script>
  </body>
</html>

執行結果如下 :

hello world!                  (lowercase)
HELLO WORLD!        (uppercase)
Hello world!                 (capitalize)
undefineds                    (pluralize)
function () { context = this args = arguments timestamp = Date.now() if (!timeout) { timeout = setTimeout(later, wait) } return result }     (debounce)
$1,000,000.00               (currency)

其中 pluralize 未定義, 而 debounce 竟然顯示實作之程式碼, 真是太奇怪了. 如果用 Vue 2 執行, 這些過濾器均無作用, 例如 :

測試 5-1 : 過濾器測試 : (2)  [原始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.4/vue.js"></script>
  </head>
  <body>
    <div id='msg'>
      <p>{{ message | lowercase }}</p>
      <p>{{ message | uppercase }}</p>
      <p>{{ 'hello world!' | capitalize }}</p>
      <p>{{ message | pluralize }}</p>
      <p>{{ 'Debounce 1 seccond' | debounce 1000 }}</p>
    </div>
    <script>
      new Vue({
        el: '#msg',
        data: {
          message: 'Hello World!'
          }
        });
    </script>
  </body>
</html>

用 Vue 2.6.4 版執行結果如下 (全無作用) :

Hello World!
Hello World!
hello world!
Hello World!
Debounce 1 seccond

用 Chrome 的開發人員工具檢查 Console 會發現有錯誤 :




因此下面還是用 Vue 1.0.8 版來測試其餘過濾器, 例如 :

測試 6 : 過濾器測試 : (3)  [原始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.8/vue.js"></script>
  </head>
  <body>
    <div id='msg'>
      <p>{{ arr }}</p>
      <p>{{ arr | json }}</p>
      <p>{{ obj }}</p>
      <p>{{ obj | json }}</p>
      <p>{{ arr | filterBy 'A' }}</p>
      <p>{{ arr | limitBy 1,2 }}</p>
      <p>{{ obj | orderBy name | json }}</p>
    </div>
    <script>
      new Vue({
        el: '#msg',
        data: {
          arr: ['Tony', 'Amy', 'Peter', 'Kelly'],
          obj: [{name:'Kelly', age:20},{name:'Amy', age:16}]
          }
        });
    </script>
  </body>
</html>

此範例中定義了兩個變數 : 陣列 arr 與物件 obj. 執行結果如下 :

Tony,Amy,Peter,Kelly
[ "Tony", "Amy", "Peter", "Kelly" ]
[object Object],[object Object]
[ { "name": "Kelly", "age": 20 }, { "name": "Amy", "age": 16 } ]
Amy
Tony,Amy,Peter,Kelly
[ { "name": "Kelly", "age": 20 }, { "name": "Amy", "age": 16 } ]

可見直接輸出陣列會顯示所有元素內容, 直接輸出物件則只顯示 object Object 而已, 若使用過濾器 json, 則會轉成 JSON 格式輸出. filterBy 有作用, 但 limitBy 與 orderBy 卻沒有作用.

過濾器也可以自訂, Vue 提供了 filters 屬性來自訂過濾器, 如同 computed 屬性, 事實上也是定義一個計算函數, 格式如下 :

filters: {
    filterName: function(val) {
        //result=calculation in terms of val
        return result;
        }
    }

其中傳入參數 val 就是傳給過濾器的資料, 經過自訂函數處理後用 return 回傳給 Mustache 樣版引擎輸出, 例如 :

測試 7 : 自訂過濾器  [原始碼]

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://vuejs.org/js/vue.js"></script>
  </head>
  <body>
    <div id='msg'>
      <p>{{ message | capitalize }}</p>
    </div>
    <script>
      new Vue({
        el: '#msg',
        data: {
          message: 'hello world!'
          },
        filters: {
          capitalize: function (val) {
            if (!val) {return ''}
            else {
              value=val.toString()
              return val.charAt(0).toUpperCase() + val.slice(1)
              }
            }
          }
        });
    </script>
  </body>
</html>

過濾器 filters 與計算函數 computed 功能差不多, 但過濾器適合用在較簡單的運算例如格式轉換; 而計算函數 computed 則適合用在複雜的運算上.

參考 :

# vue2-filters
# https://vuejs.org/v2/guide/filters.html
# Filters in Depth
# 用範例理解 Vue.js #7:Filters vs Computed
# Vue.js: Filter 過濾器


2019-12-17 補充 :

離開 Vue 一段時間了, 今天看到下面這篇不錯的文章, 記一下有空再研究 :

# Vue中容易被忽視的知識點 (程式前沿)

2019年2月8日 星期五

Vue 學習筆記 (二) : MVVM 架構與雙向資料綁定

配置好 Vue 的執行環境後, 接著進行 Vue 的簡單雙向資料綁定測試. 本系列之前的測試文章參考 :

# 關於前端網頁框架 Vue
# Vue 學習筆記 (一) : 環境配置

本測試參考了下列書籍 :
  1. 一次搞懂熱門前端框架 (旗標)
  2. The Majesty of Vue (Packt)
  3. Vue.js 2 Cookbook (Packt)
  4. Vue.js 2.x by Example (Packt)
Vue 在網頁技術的 MVC 架構中扮演 V (View) 視圖與 M (Model) 模型之間的中介互動角色, 稱為 ViewModel 層, 是資料層與頁面顯示層之間的橋樑, 這在前端稱為 MVVM 架構 :
  • Model (資料管理層) 
  • View (畫面顯示層)
  • ViewModel (資料與顯示互動中介層)
MVVM 架構示意圖如下 :




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  (外部來源)  [原始碼]

<!DOCTYPE html>
<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 版)  [原始碼]

<!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="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 最簡潔.

2019年2月4日 星期一

Vue 學習筆記 (一) : 環境配置

前幾天稍微涉獵了前端網頁框架的比較之後, 決定挑選 Vue.js 作為將來專案開發的前端架構, 主要是參考了下面幾本書進行功能測試. :
  1. 一次搞懂熱門前端框架 (旗標, 陳禹豪譯)
  2. 前端設計範式三大天王之 Vue.js (佳魁, 梁睿坤)
Vue 是一個用來建構互動式使用者介面的 Javascript 函式庫, 此框架的作者尤雨溪 Evan You 是前 Google 工程師, 在參與了 Angular 的開發工作後, 從中汲取了所喜歡的部分創建了輕量的 Vue.js 框架, 於 2014 年正式以 MIT 開源發布之後迅速成為 GitHub 最受歡迎的開源專案之一. 與其主要競爭對手 React 與 Angular 比起來, Vue 具有如下優勢 :
  1. 乾淨可讀性高的 HTML 輸出, 無其他框架常見的冗長自訂屬性與 class. 
  2. API 介面簡單易學易用. 
Vue 的作者稱 Vue 為 Progressive Framework (漸進式框架), 因為 Vue 核心函式庫只專注於視圖層, 開發者只要用最小的努力即可建構應用程式, 隨著功能要求增多, 可添加其他函式庫如 Vuex, Vue-Router 等來擴增功能. 參考 :

# 維基 : Vue.js
# 百度 : 尤雨溪

Vue 有完整的 API 與教學文件, 參考官網與 GitHub :

# https://vuejs.org/v2/api/
# https://vuejs.org/v2/guide/
# https://github.com/vuejs/vue

要在專案中使用 Vue.js 需先做環境配置, 第一種作法是自備 vue.js, 可在 Vue 官網下載 vue.js 或 vue.min.js, 注意, 在開發階段不要使用 "Production Version" (即 vue.min.js), 建議下載上面的 "Development Version" (即 vue.js), 因為它有完整的偵錯與警告訊息, vue.min.js 適合正式上線時用.

# https://vuejs.org/v2/guide/installation.html




目前版本是 v2.5.22 版, 下載 vue.js 後放在專案目錄下的 /js 子目錄, 例如 D:/vue/test/js :


使用本地 vue.js

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="js/vue.js"></script>
  </head>
  <body>
    <script> /* …your code here… */ </script>
  </body>
</html>


第二種作法是使用 CDN 所提供的 vue.js, 有下列 4 個 CDN 來源可用 :

1. Vue 官網 : (提供最新版本)
https://vuejs.org/js/vue.js

2. CDNJS : (預設提供最新版本, 亦可指定版本)
https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.js

3. unpkg CDN : (可指定版本)
https://unpkg.com/vue 
https://unpkg.com/vue/dist/vue.js
https://unpkg.com/vue@2.5.22/dist/vue.js

4. JS Delivr : (預設提供最新版本, 亦可指定版本)
https://cdn.jsdelivr.net/npm/vue/dist/vue.js
https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js

以 unpkg 為例 :

使用 unpkg CDN

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://unpkg.com/vue@2.5.22/dist/vue.js"></script>
  </head>
  <body>
    <script> /* …your code here… */ </script>
  </body>
</html>

準備好 Vue 函式庫來源即可開始撰寫 Javascript 進行專案開發了. 這個應用程式 (App) 可以直接寫在 HTML 中, 也可以寫在獨立的 js 檔案, 例如 :

載入外部應用程式 app.js

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="js/vue.js"></script>
    <script src="js/app.js"></script>
  </head>
  <body>
    <script> /* …your code here… */ </script>
  </body>
</html>

為了便於維護應用程式與維持 HTML 碼可讀性, 應該將應用程式寫成外部程式為宜.

2019年2月2日 星期六

關於前端網頁框架 Vue

幾年前學完 jQuery 與 jQueryUI 後就沒再接觸前端網頁框架了, 那時認為 jQuery 這麼棒, 有這個應該就夠了, 哪知這幾年陸續蹦出 React, Vue, 與 Angular 等前端框架, 多到讓我不知道為啥要有這些東西, 以及要不要學.

最近從市圖借了幾本 React, Vue, 與 Angular 的書 :
  1. 前端開發的涅槃 :React全面昇華你的Frontend境界
  2. 前端設計範式三大天王之Vue.js :最完整實例作戰
  3. 一次搞懂熱門前端框架 :React Vue.js Angular Backbone.js Aurelia
大略翻閱這三本書之後, 我迅速否決了 React 與 Angular, 這兩個對我而言簡直是天書, 怎麼會搞成這麼複雜呢? React 與 Angular 分別是 Facebook 與 Google 開發的開源框架, 其受歡迎程度也代表了兩個網路巨人在前端技術上角力的勝負.

比較之下, Vue 就顯得比較直觀, 書還可以看得下去, 而且看了下面這篇文章中對 Vue 的介紹也頗有同感 :

# 2018年前端JS框架發展趨勢-快速練功指南

此文對 Vue 的評價如下 :
  1. 輕量容易學習
  2. 有令人難以置信的好用工具
  3. 有很棒的狀態管理和路由內置
  4. 部分語法跟 Angular 類似
  5. 通過 Vue 讓生活變得簡單
  6. Vue 應該迅速成為你職涯更好的選項
關於 Angular, React, 與 Vue 的比較, 參考 :

# Angular vs. React vs. Vue: A 2017 comparison

在 "前端設計範式三大天王之Vue.js :最完整實例作戰" 這本書的序言中, 作者提到 "與 Angular 相比, 它大幅降低學習成本, 同時擁有極高效的執行效能", 作者原先採用 Angular2 + Flask 作為專案架構, 但 Angular2 開發文件缺乏, 測試工具也落後, 後來切換到 Vue 後, 只付出極少代價即將 Angular2 轉換到 Vue 上, 而 vue-cli 工具也能合乎專案需要. 總之, Vue 幾乎可說是 Angular.js 的簡化, 具有易學高效能之優點, 作者認為 Vue 是一款能與 Angular/React 比肩的前端架構.

以 Vue 開發的專案範例參考 :

# https://madewithvuejs.com/

基本上, 學習曲線太陡的東西我完全不會去碰, 因為我的耐心很有限.


2020-09-14 補充 :

過了一年多之後的今天, 我因為要重寫專案而複習了 jQuery 與 jQuery UI, 順勢又再度思考了學習新前端技術問題, 雖然 jQuery 是經典, 但畢竟有點舊了, 很多外掛甚至停止開發, 不學新的東西似乎有點跟不上時代. 我在 2019 年選擇了 Vue 並起頭學了一些, 後來沒時間繼續就擱著了, 今天看到下面這篇文章, 讓我又重燃學習 Vue 的火花 :

# Angular vs React vs Vue 三個框架的比較

作者結論很有意思 : 如果還是不知道該學哪個, 那就先學 Vue 或 React, 然後再學 Angular.

關注這三大前端框架的 GitHub 的聲勢消長 :

# github.com/facebook/react
# github.com/angular/angular
# github.com/vuejs/vue

下面這段話似乎是我的寫照耶 :

"如果你是一位無經驗的 Javascript 開發人員或者如果你在過去十年中主要使用 jQuery,那麼你應該考慮使用 Vue。Vue 看起來更像是簡化的 Javascript,同時也引入了一些新的想法:組件,事件驅動模型和單向數據流,它的體積也很小。"