文章出處

注:文章內容都是摘錄性文字,自己閱讀的一些筆記,方便日后查看。

MVC

MVC(Model-View-Controller),M 是指業務模型,V 是指用戶界面,C 則是控制器,使用 MVC 的目的是將 M 和 V 的實現代碼分離,從而使同一個程序可以使用不同的表現形式。

交互方式(所有通信都是單向的):

  • View 傳送指令到 Controller
  • Controller 完成業務邏輯后,要求 Model 改變狀態
  • Model 將新的數據發送到 View,用戶得到反饋

更詳細的說明:

  • 模型(Model) 用于封裝與應用程序的業務邏輯相關的數據以及對數據的處理方法。“ Model ”有對數據直接訪問的權力,例如對數據庫的訪問。“Model”不依賴“View”和“Controller”,也就是說, Model 不關心它會被如何顯示或是如何被操作。但是 Model 中數據的變化一般會通過一種刷新機制被公布。為了實現這種機制,那些用于監視此 Model 的 View 必須事先在此 Model 上注冊,從而,View 可以了解在數據 Model 上發生的改變。(比較:觀察者模式(軟件設計模式))
  • 視圖(View)能夠實現數據有目的的顯示(理論上,這不是必需的)。在 View 中一般沒有程序上的邏輯。為了實現 View 上的刷新功能,View 需要訪問它監視的數據模型(Model),因此應該事先在被它監視的數據那里注冊。
  • 控制器(Controller)起到不同層面間的組織作用,用于控制應用程序的流程。它處理事件并作出響應。“事件”包括用戶的行為和數據 Model 上的改變。

MVC 模式在概念上強調 Model, View, Controller 的分離,各個模塊也遵循著由 Controller 來處理消息,Model 掌管數據源,View 負責數據顯示的職責分離原則,因此在實現上,MVC 模式的 Framework 通常會將 MVC 三個部分分離實現:

  • Model 負責數據訪問,較現代的 Framework 都會建議使用獨立的數據對象 (DTO, POCO, POJO 等) 來替代弱類型的集合對象。數據訪問的代碼會使用 Data Access 的代碼或是 ORM-based Framework,也可以進一步使用 Repository Pattern 與 Unit of Works Pattern 來切割數據源的相依性。
  • Controller 負責處理消息,較高級的 Framework 會有一個默認的實現來作為 Controller 的基礎,例如 Spring 的 DispatcherServlet 或是 ASP.NET MVC 的 Controller 等,在職責分離原則的基礎上,每個 Controller 負責的部分不同,因此會將各個 Controller 切割成不同的文件以利維護。
  • View 負責顯示數據,這個部分多為前端應用,而 Controller 會有一個機制將處理的結果 (可能是 Model, 集合或是狀態等) 交給 View,然后由 View 來決定怎么顯示。例如 Spring Framework 使用 JSP 或相應技術,ASP.NET MVC 則使用 Razor 處理數據的顯示。

一個通過 JavaScript 所實現的一個基礎 MVC 模型,請注意的是:MVC 不是一種技術,僅是一種理念。

/** 模擬 Model, View, Controller */
var M = {}, V = {}, C = {};

/** Model 負責存放資料 */
M.data = "hello world";

/** View 負責將資料輸出到螢幕上 */
V.render = function (M) { alert(M.data); }

/** Controller 作為一個 M 和 V 的橋樑 */
C.handleOnload = function () { V.render(M); }

/** 在網頁讀取的時候呼叫 Controller */
window.onload = C.handleOnLoad;

ASP.NET MVC 中的 MVC 概念,和上面的概念稍微有些不同(有點像下面的 MVP),圖示:

被動 MVC(ASP.NET MVC)與主動 MVC(傳統 MVC)的區別在于:

  1. 模型對視圖和控制器一無所知,它僅僅是被它們使用
  2. 控制器使用視圖,并通知它更新數據顯示
  3. 視圖僅僅是在控制器通知它去模型取數據的時候它才這么做(視圖并不會訂閱或監視模型的更新)
  4. 控制器負責處理模型數據的變化
  5. 控制器可以包含對視圖的渲染邏輯

資料來源:

MVP

Model-View-Presenter (MVP) 是使用者界面設計模式的一種,被廣范用于便捷自動化單元測試和在呈現邏輯中改良分離關注點(separation of concerns)。

  • Model 定義使用者界面所需要被顯示的資料模型,一個模型包含著相關的業務邏輯。
  • View 視圖為呈現使用者界面的終端,用以表現來自 Model 的資料,和使用者命令路由再經過 Presenter 對事件處理后的資料。
  • Presenter 包含著元件的事件處理,負責檢索 Model 取得資料,和將取得的資料經過格式轉換與 View 進行溝通。

  1. 各部分之間的通信,都是雙向的。
  2. View 與 Model 不發生聯系,都通過 Presenter 傳遞。
  3. View 非常薄,不部署任何業務邏輯,稱為"被動視圖"(Passive View),即沒有任何主動性,而 Presenter非常厚,所有邏輯都部署在那里。

對 MVC 的改進的思想卻是一樣的:切斷的 View 和 Model 的聯系,讓 View 只和 Presenter(原 Controller)交互,減少在需求變化中需要維護的對象的數量。

MVP 定義了 Presenter 和 View 之間的接口,讓一些可以根據已有的接口協議去各自分別獨立開發,以此去解決界面需求變化頻繁的問題。

與“被動—MVC 模式(ASP.NET MVC)”很接近,區別在于“視圖并不使用模型”。在 MVP 模式中視圖和模型是完全分離的,他們通過Presenter進行交互。

Presenter 與控制器非常相似,但是它們也有一些的區別:

  1. Presenter處理視圖發送過來的用戶操作(在MVC中視圖自己處理了這些操作)
  2. 它用更新過的數據去更新模型(在被動MVC中控制器只是通知視圖去更新過的模型中去取新的數據,而主動MVC中模型通知視圖去更新顯示,控制器不需要做工作)
  3. 檢查模型的更新(與被動MVC一樣)
  4. (與MVC的主要區別)從模型中取數據然后將它們發送到視圖中
  5. (與MVC的主要區別)將所做的更新告知視圖
  6. (與MVC的區別)用Presenter渲染視圖

MVP 的優勢

  1. 模型與視圖完全分離,我們可以修改視圖而不影響模型
  2. 可以更高效地使用模型,因為所以的交互都發生在一個地方——Presenter 內部
  3. 我們可以將一個 Presener 用于多個視圖,而不需要改變 Presenter 的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁。
  4. 如果我們把邏輯放在 Presenter 中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)。

MVP 的問題

由于對視圖的渲染放在了 Presenter 中,所以視圖和 Persenter 的交互會過于頻繁。還有一點你需要明白,如果 Presenter 過多地渲染了視圖,往往會使得它與特定的視圖的聯系過于緊密。一旦視圖需要變更,那么 Presenter 也需要變更了。

資料來源:

MVVM

MVVM(Model-View-ViewModel)的設計模式最早于 2005 年由微軟的 WPF 和 Silverlight 架構師 John Gossman 在他的博客中提到。

MVVM 是 MVP 的演化版本,它們唯一的區別是,MVVM 采用雙向綁定(data-binding):View 的變動,自動反映在 ViewModel,反之亦然。

MVVM 在使用當中,通常還會利用雙向綁定技術,使得 Model 變化時,ViewModel 會自動更新,而 ViewModel 變化時,View 也會自動變化。所以,MVVM 模式有些時候又被稱作:model-view-binder 模式。

這個圖解準確地描述了什么是 MVVM:一個 MVC 的增強版,我們正式連接了視圖和控制器,并將表示邏輯從 Controller 移出放到一個新的對象里,即 View Model。MVVM 聽起來很復雜,但它本質上就是一個精心優化的 MVC 架構,

MVVM 模式和 MVC 模式一樣,主要目的是分離視圖(View)和模型(Model),有幾大優點

  1. 低耦合:視圖(View)可以獨立于 Model 變化和修改,一個 ViewModel 可以綁定到不同的"View"上,當 View 變化的時候 Model 可以不變,當 Model 變化的時候 View 也可以不變。
  2. 可重用性:你可以把一些視圖邏輯放在一個 ViewModel 里面,讓很多 view 重用這段視圖邏輯。
  3. 獨立開發:開發人員可以專注于業務邏輯和數據的開發(ViewModel),設計人員可以專注于頁面設計,使用 Expression Blend 可以很容易設計界面并生成 xaml 代碼。
  4. 可測試:界面素來是比較難于測試的,而現在測試可以針對 ViewModel 來寫。

MVVM 的問題:

  1. 數據綁定使得 Bug 很難被調試。你看到界面異常了,有可能是你 View 的代碼有 Bug,也可能是 Model 的代碼有問題。數據綁定使得一個位置的 Bug 被快速傳遞到別的位置,要定位原始出問題的地方就變得不那么容易了。
  2. 對于過大的項目,數據綁定需要花費更多的內存。

資料來源:

Angular.js

Angular.js 是一個 MV*(Model-View-Whatever,不管是 MVC 或者 MVVM,統歸 MDV(model Drive View))JavaScript 框架。

Angular.js 是一款開源 JavaScript 庫,由 Google 維護,用來協助單一頁面應用程序運行的。它的目標是通過 MVC 模式(MVC)功能增強基于瀏覽器的應用,使開發和測試變得更加容易。

庫讀取包含附加自定義(標簽屬性)的 HTML,遵從這些自定義屬性中的指令,并將頁面中的輸入或輸出與由 JavaScript 變量表示的模型綁定起來。這些 JavaScript 變量的值可以手工設置,或者從靜態或動態 JSON 資源中獲取。

Angular.js 六大特性:

  1. 雙向數據綁定
  2. 模板
  3. MVVM
  4. 依賴注入(Dependency Injection,DI)
  5. Directives(指令)
  6. 單元測試

Angular.js 簡單示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<body>

<div ng-app="">
    <p>名字 : <input type="text" ng-model="name"></p>
    <h1>Hello {{name}}</h1>
</div>

</body>
</html>

資料來源:

Knockout.js

Knockout.js 是一個 JavaScript 庫(MVVM),它可以讓你聲明綁定元素和其對應的數據模型,達到你的UI和模型自動雙向更新。

Knockout.js 使用 js 代碼達到雙向綁定的目的,類似 Silverlight/WPF 里的綁定一樣,我們主要就是利用相關的特性進行開發的,極大地減少了代碼開發量。

Knockout.js 是一個輕量級的 UI 類庫,通過應用 MVVM 模式使 JavaScript 前端 UI 簡單化。Knockout 是一個以數據模型(data model)為基礎的能夠幫助你創建富文本,響應顯示和編輯用戶界面的 JavaScript 類庫。任何時候如果你的 UI 需要自動更新(比如:更新依賴于用戶的行為或者外部數據源的改變),KO 能夠很簡單的幫你實現并且很容易維護。

Knockout.js 有如下4大重要概念:

  • 聲明式綁定 (Declarative Bindings):使用簡明易讀的語法很容易地將模型(model)數據關聯到 DOM 元素上。
  • UI界面自動刷新 (Automatic UI Refresh):當您的模型狀態(model state)改變時,您的 UI 界面將自動更新。
  • 依賴跟蹤 (Dependency Tracking):為轉變和聯合數據,在你的模型數據之間隱式建立關系。
  • 模板 (Templating):為您的模型數據快速編寫復雜的可嵌套的 UI。

重要特性:

  • 優雅的依賴追蹤- 不管任何時候你的數據模型更新,都會自動更新相應的內容。
  • 聲明式綁定- 淺顯易懂的方式將你的用戶界面指定部分關聯到你的數據模型上。
  • 靈活全面的模板- 使用嵌套模板可以構建復雜的動態界面。
  • 輕易可擴展- 幾行代碼就可以實現自定義行為作為新的聲明式綁定。

額外的好處:

  • 純 JavaScript 類庫 – 兼容任何服務器端和客戶端技術
  • 可添加到Web程序最上部 – 不需要大的架構改變
  • 簡潔的 – Gzip 之前大約 25kb
  • 兼容任何主流瀏覽器 (IE 6+、Firefox 2+、Chrome、Safari、其它)
  • Comprehensive suite of specifications (采用行為驅動開發) - 意味著在新的瀏覽器和平臺上可以很容易通過驗證。

簡單示例:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <script type="text/javascript" src="http://knockoutjs.com/js/jquery-1.4.2.min.js"></script>
    <script type="text/javascript" src="http://knockoutjs.com/js/jquery.tmpl.js"></script>
    <script type="text/javascript" src="http://knockoutjs.com/js/knockout-1.2.1.js"></script>
</head>
<body>
    Choose a ticket class:
    <select data-bind="options: tickets, 
                       optionsCaption: 'Choose...',
                       optionsText: 'name',
                       value: chosenTicket">
    </select>
    </p>
    <p data-bind="template: 'ticketTemplate'">
    </p>
    <script id="ticketTemplate" type="text/x-jquery-tmpl">
        {{if chosenTicket}}
            You have chosen <b>${ chosenTicket().name }</b>
            ($${ chosenTicket().price })
            <button data-bind="click: resetTicket">Clear</button>
        {{/if}}
</script>
    <script type="text/javascript">
        var viewModel = {
            tickets: [
                { name: "Economy", price: 199.95 },
                { name: "Business", price: 449.22 },
                { name: "First Class", price: 1199.99 }
            ],
            chosenTicket: ko.observable(),
            resetTicket: function () { this.chosenTicket(null) }
        };
        ko.applyBindings(viewModel);
    </script>
</body>
</html>

資料來源:

Backbone.js

Backbone.js 是一套 JavaScript 框架與 RESTful JSON 的應用程序接口。也是一套大致上符合 MVC 架構的編程范型。Backbone.js 以輕量為特色,只需依賴一套 Javascript 庫即可運行。常被用來開發單頁的互聯網應用程序,以及用來維護網絡應用程序的各種部分(例如多用戶與服務器端)的同步。

Backbone 最適合的應用場景是單頁面應用,并且頁面上有大量數據模型,模型之間需要進行復雜的信息溝通。Backbone 在這種場景下,能很好的實現模塊間松耦合和事件驅動。 其他適用產品還有微博,網易微博的前端設計也是和 Backbone 類似的一個結構。

Backbone.js 的適用場景非常廣,無論是 Web-Page 還是 Web-App 都可以應用,但最合適的還是大型的 Web-App,對于中小型項目來講 Backbone.js 的 MVC 結構還是有點臃腫了,用不好很容易 over design。Backbone.js 是非常典型的 MVC 框架,但是相對于傳統的 server 端 MVC 來講還是有一些特殊的地方的。

首先 Backbone 中的幾大核心組件 View、Model、Collection、Router 中并沒有 Controller。其實 v0.5 以前是有 Backbone.Controller 這個東西的,但由于做的根本不是 C 的事情,這個名字又太具有迷惑性了,后來改名叫做 Backbone.Router。而真正的 C 其實是 Backbone.View,但這個 View 其實是部分的 C(還有一部分在 Backbone.Router 中) + 部分的 V,由于前端的模板功能有限,很多應該在 template 中做的事情不得不被拿到 Backbone.View 中來實現。

其次,由于 MVC 的概念中認為 V 其實是永遠不知道用戶輸入(鼠標、鍵盤事件等)的,C 是輸入和 V 之間的連接,但在瀏覽器中這點其實是實現不了的,V 就是 HTML,而用戶輸入是基于 HTML 頁面的,所以你可以忽略用戶輸入,把所有事件都導入到 C 去處理,但不代表 V 不知道這件事情。所以前端的 MVC 多少是對傳統的 MVC 模型做了些改變的實現,近些日子更多的人轉向 MVVM 就是這個原因。

Backbone.js 的優點:

  1. 代碼質量比較高,通讀一遍還是能學到不少東西的。
  2. 只做框架該做的事情,不做高大全的東西。所以很容易和其他的工具或框架整合。比如有人搞了 Bakcbone.js + Knockout.js 的 Knockback.js。
  3. 分層的結構很清晰,使得前端工程在擴展性和維護性上都可以進行有效控制。

Backbone.js 缺點:

  1. Model 結構比較簡單,多對多、但對多的數據模型很難搞,用對象做屬性也不行。
  2. 內存控制,View 很容易產生 memory leak 的問題,不過這也和代碼的質量有關系,近期的更新有一些是針對這方面的。

資料來源:

React.js

React 起源于 Facebook 的內部項目,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站。做出來以后,發現這套東西很好用,就在2013年5月開源了。

創造 React 是為了解決一個問題:構建隨著時間數據不斷變化的大規模應用程序

React 兩個主要的思想:

簡單:僅僅只要表達出你的應用程序在任一個時間點應該長的樣子,然后當底層的數據變了,React 會自動處理所有用戶界面的更新。
聲明式 (Declarative):數據變化后,React 概念上與點擊“刷新”按鈕類似,但僅會更新變化的部分。

React 都是關于構建可復用的組件。事實上,通過 React 你唯一要做的事情就是構建組件。得益于其良好的封裝性,組件使代碼復用、測試和關注分離(separation of concerns)更加簡單。

資料來源:

Ember.js

Ember.js 是 JavaScript 框架包中最新的成員之一。 它演變出了最初于 2007 年創建的 SproutCore 項目,Apple 在包括 MobileMe 在內的各種 web 應用程序中大量使用了該項目。 在 emberjs.com,Ember 被形容為 "一個 JavaScript 框架,用于創建可以消除樣板并提供標準應用程序架構的大型 web 應用程序。" 它本身緊密集成了名為 Handlebars 的模板引擎,該引擎為 Ember 提供了其中一個最強大的功能: 雙向數據綁定。 Ember 還提供了其他功能,比如狀態管理(某個用戶狀態是已注銷還是已登錄)、自動更新模板(當底層數據發生變化時,您的 UI 也同樣發生變化)以及計算屬性 (firstName + lastName = fullName)。 Ember 經過一年可靠的開發后,已經成為一個強大的參與者。

Ember 只有一個依賴項—jQuery。 Ember 應用程序的樣板 HTML 設置看起來應該與下面的代碼類似。 請注意,jQuery 和 Ember 都從 CDN(內容交付網絡)進行更新。 如果用戶在早些時候訪問需要這些文件的其他網站時已經下載過這些文件,這會加快用戶的頁面加載速度。

與 Ember.js 相比,Angular.js 更像一個研究項目。比如,來看看它們的學習文檔:Ember.js 主要討論模型、視圖和控制器,而 Angular.js 指南要求你去學習一些類似于范圍、指示符和 transclusion 方面的內容等。

引用示例:

<html>
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script src="http://cloud.github.com/downloads/emberjs/ember.js/ember-0.9.6.min.js"></script>
    <script src="js/app.js"></script>
</head>
<body>
</body>
</html>

資料來源:

Avalon.js

Avalon.js 是一個功能強大,體積小巧的 MVVM 框架。它遵循“操作數據即操作 DOM”的理念,讓你在代碼里基本見不到一點 DOM 操作代碼。DOM 操作全部在綁定后,交給框架處理。相當后端有了 ORM 一樣,不用你手寫 SQL,提高生產力!

與其它 js 框架相比,同樣實現著名的 todos 功能,在所有 MV* 的實現中 avalon 是讓用戶寫代碼最少的。

與其他 MV* 相比,它不僅輕量,最低支持到 IE6,而且性能是最好的。

優勢:

  • 使用簡單,在 HTML 中添加綁定,在 JS 中用 avalon.define 定義 ViewModel,再調用 avalon.scan 方法,它就能動了!
  • 兼容到 IE6(其他 mvvm 框架, knockoutjs IE6, angularjs IE7, emberjs IE8, winJS IE9 )
  • 沒有任何依賴,只有 72K,壓縮后 22K
  • 支持管道符風格的過濾函數,方便格式化輸出
  • 局部刷新的顆粒度已細化到一個文本節點,特性節點
  • 要操作的節點,在第一次掃描就與視圖刷新函數相綁定,并緩存起來,因此沒有選擇器出場的余地。
  • 讓 DOM 操作的代碼近乎絕跡
  • 使用類似 CSS 的重疊覆蓋機制,讓各個 ViewModel 分區交替地渲染頁面
  • 節點移除時,智能卸載對應的視圖刷新函數,節約內存
  • 操作數據即操作 DOM,對 ViewModel 的操作都會同步到 View 與 Model 去。

與其他框架比較:

  • 它體積更少,在主要的幾個 MVVM 框架(擁有雙向綁定機制),knockout 是三千多行,angularjs 1.6萬, emberjs 2-3 萬行, winjs 是幾 M, kendoui 是幾 M!
  • 兼容情況,kendoui 與 knockoutjs IE6, angularjs IE7, emberjs IE8, winJS IE9
  • 讓用戶寫代碼更少
  • 上手難度,與 knockout 差不多,但借鑒了 angularjs 的,更為易用。
  • 與 knockoutjs, angular, winjs 一樣是使用動態模板,至少保持第一屏數據是真實的,對 SEO 友好。
  • 源碼也是它們中最易讀的。簡單的代碼也意味著擴展調試等容易。

資料來源:

Vue.js

官方地址:http://vuejs.org/

Vue.js 尤雨溪老師寫的一個用于創建 web 交互界面的庫,是一個精簡的MVVM。從技術角度講,Vue.js 專注于 MVVM 模型的 ViewModel 層。它通過雙向數據綁定把 View 層和 Model 層連接了起來。實際的 DOM 封裝和輸出格式都被抽象為了Directives 和 Filters。Vue.js 和其他庫相比是一個小而美的庫,作者的主要目的是通過一個盡量簡單的 API 產生可反映的數據綁定和可組合的視圖組件,感覺作者的思路非常清晰。

Vue.js(讀音 /vjuː/, 類似于 view)是一個構建數據驅動的 web 界面的庫。Vue.js 的目標是通過盡可能簡單的 API 實現響應的數據綁定和組合的視圖組件。

Vue.js 自身不是一個全能框架——它只聚焦于視圖層。因此它非常容易學習,非常容易與其它庫或已有項目整合。另一方面,在與相關工具和支持庫一起使用時,Vue.js 也能完美地驅動復雜的單頁應用。

優點:

  • 簡單:官方文檔很清晰,比 Angular 簡單易學。
  • 快速:異步批處理方式更新 DOM。
  • 組合:用解耦的、可復用的組件組合你的應用程序。
  • 緊湊:~18kb min+gzip,且無依賴。
  • 強大:表達式 & 無需聲明依賴的可推導屬性 (computed properties)。
  • 對模塊友好:可以通過 NPM、Bower 或 Duo 安裝,不強迫你所有的代碼都遵循 Angular 的各種規定,使用場景更加靈活。

缺點:

  • 新生兒:Vue.js 是一個新的項目,2014 年 3 月 20 日發布的 0.10.0ReleaseCandidate 版本,目前 github 上面最新的是 0.11.4 版本,沒有 angular 那么成熟。
  • 影響度不是很大:google 了一下,有關于 Vue.js 多樣性或者說豐富性少于一些有名的庫。
  • 不支持 IE8:哈哈不過 AngularJS 1.3 也拋棄了對IE8的支持,但是 @司徒正美 老師的 avalon 是支持 IE6+ 的,應該下了很多努力去優化。這一點對于那些需要支持 IE8 的項目就不好了,不過這也是 web 前端開發的一個趨勢,像 IE 低版本就應該退出歷史舞臺了,通過改變我們的前端思維,而不是順應那些使用老版本而不去升級的人。玉伯老師就說過一句話,我覺得說的非常好“這年頭,支持 IE6、7 早就不再是特性,而是恥辱。努力推動支付寶全面不支持 IE6、7,期待更多兄弟加盟”。

總結:(1) 簡潔 (2) 輕量 (3)快速 (4) 數據驅動 (5) 模塊友好 (6) 組件化

簡單示例:

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

// js
var vm = new Vue({
    el: '#app',
    data: {
        note: ''
    }
})

資料來源:

附 JavaScript MVC framework


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()