前端思維轉變--從事件驅動到數據驅動
接觸過jQuery的小夥伴們大概在切換到mvvm初總不習慣,需要進行開發思維的轉換,從事件驅動的角度出發,到從數據驅動的角度出發,也是不小的挑戰。
事件驅動
GUI與事件
GUI(圖形用戶界面)與事件驅動的淵源可謂不淺。
GUI應用程序的特點是注重與用戶的交互,因此程序的執行取決於與用戶的實時交互情況,大部分的程序執行需要等到用戶的交互動作發生之後。
由於用戶的輸入頻率並不高,若不停輪詢獲取用戶輸入,就有點像ajax輪詢和websocket推送的關係:
- 資源利用率低。
- 不能真正做到及時同步。
由於GUI程序的執行流程由用戶控制,並且不可預期,為了適應這種特點,我們需要採用事件驅動的編程方法。普通程序的執行可概括為「啟動——做事——終止」,而事件驅動的程序的執行可概括為「啟動——事件循環(即等待事件發生並處理之)」。
事件驅動編程
事件
事件是可以被控制項識別的操作,如按下確定按鈕,選擇某個單選按鈕或者複選框。每一種控制項有自己可以識別的事件,如窗體的載入、單擊、雙擊等事件,編輯框(文本框)的文本改變事件,等等。
事件(event)是針對應用程序所發生的事情,並且應用程序需要對這種事情做出響應。
事件處理
程序對事件的響應其實就是調用預先編製好的代碼來對事件進行處理,這種代碼稱為事件處理程序(event handler)。
事件驅動編程(event-driven programming)就是針對這種「程序的執行由事件決定」的應用的一種編程范型。
Event loop
主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,所以整個的這種運行機制又稱為Event Loop(事件循環)。
關於Javascript的單線程與Event Loop,想要了解可以參考《JavaScript 運行機制詳解:再談Event Loop》。今天的主角是數據驅動,事件相關的不進行詳細說明了。
事件驅動思維
在GUI和Javascript的設計場景下,我們寫代碼的時候也會代入這樣的思維:
用戶輸入 => 事件響應 => 代碼運行 => 刷新頁面狀態
於是乎,剛開始寫應用的思路如下:
- 開發靜態頁面。
- 添加事件監聽,包括用戶輸入、http請求、定時器觸發等事件。
- 針對不同事件,編寫不同的處理邏輯,包括獲取事件狀態/輸入、計算並更新狀態等。
- 根據計算後的數據狀態,重新渲染頁面。
通俗地說,事件驅動思維是從事件響應出發,來完成應用的設計和編程。
數據驅動
數據驅動,將我們從複雜的邏輯設計帶進數據處理的世界。
何為數據
數據是什麼,官方回答:數據是科學實驗、檢驗、統計等所獲得的和用於科學研究、技術設計、查證、決策等的數值。
但其實不管是資料中、生活和工作中,所有的事物我們都可以抽象為數據。像遊戲裡面的角色、物品、經驗值、天氣、時間等等,都是數據。遊戲其實也算是對真實世界抽象的一種,而抽象之後,最終都可呈現為數據。
我認為,數據是一個抽象的過程。
回到日常寫碼中,前端寫頁面,抽象成數據常用的無非是:
- 列表 => array
- 狀態 => number/boolen
- 一個卡片 => object
- 等等
事件驅動到數據驅動
數據驅動 vs 事件驅動
要對事件驅動和數據驅動進行直觀的比較,大概是以下這樣:
事件驅動
- 構建頁面:設計DOM => 生成DOM => 綁定事件
- 監聽事件:操作UI => 觸發事件 => 響應處理 => 更新UI
數據驅動
- 構建頁面:設計數據結構 => 事件綁定邏輯 => 生成DOM
- 監聽事件:操作UI => 觸發事件 => 響應處理 => 更新數據 => 更新UI
其實最大的轉變是,以前會把組件視為DOM,把事件/邏輯處理視為Javascript,把樣式視為CSS。而當轉換思維方式之後,組件、事件、邏輯處理、樣式都是一份數據,我們只需要把數據的狀態和轉換設計好,剩下的實現則由具現方式(模版引擎、事件機制等)來實現。
數據驅動思維
轉換到數據驅動思維後,我們在編程實現的過程中,更多的是思考數據的維護和處理,而無需過於考慮UI的變化和事件的監聽。
拿一個企業網站來說,裡面的很多數據和鏈接,從前我們常用方式是直接寫成DOM,然後就產生了很長的一段DOM代碼。
如果說我們將其切換到數據,以對象和數組的方式存儲,這時候我們只需要寫一段具現方式,將這組數據轉成DOM。這種方式有以下好處:
- 數據變更方便
- DOM結構變輕
- DOM結構/樣式調整方便
- 抽象設計
- 代碼量減少,易於維護
數據驅動與mvvm
數據驅動的設計思維或許與mvvm沒有必然的聯繫,但是mvvm框架提供一些具現方式將數據驅動變得更加輕鬆。
mvvm集成具現化方法
拿vue框架來說,有以下一些很方便的具現方法:
- 模板渲染:數據 => AST => 生成DOM
- 數據綁定:交互輸入/http請求響應/定時器觸發 => 事件監聽 => 數據變更 => diff => DOM更新
- 路由引擎:url => 數據(host/path/params等) => 解析對應頁面
當我們使用了這些mvvm框架時,它們解決了如何讓數據轉變成需要的東西,將抽象具象化的問題。在這樣的情況下,我們只需要完成兩步:
- 將產品/業務/設計抽象化,將UI、交互抽象為數據。
- 將一組組的數據用邏輯處理連接起來。
mvvm推動數據驅動思維
這裡借用vue,來舉兩個例子吧。
一、獲取input輸入並更新
實現一個input的監聽輸入,並更新輸出到模板,我們能有以下代碼的變化:<!--1. 事件驅動--><input type="text" id="input" /><p id="p"></p><script>$(#input).on(click, e => { const val = e.target.value; $(#p).text(val);})</script><!--2. 數據驅動 + vue--><input type="text" v-model="inputValue" /><p>{{ inputValue }}</p>
當我們在vue中,模板引擎幫我們處理了模板渲染、數據綁定的過程,我們只需要知道這裡面只有一個有效數據,即input的值。
二、部分更新列表
我們再來看個例子,我們有一組數據,需要渲染成一個列表:const list = [ {id: 1, name: name1, href: http://href1}, {id: 2, name: name2, href: http://href2}, {id: 3, name: name3, href: http://href3}, {id: 4, name: name4, href: http://href4}]
- 當我們需要渲染成列表時:
<!--1). 事件驅動--><ul id="ul"></ul><script>const dom = $(#ul);list.forEach(item => { dom.append(`<li data-id="${item.id}"><span>${item.name}</span>: <a href="${item.href}">${item.href}</a></li>`)});</script><!--2). 數據驅動 + vue--><ul> <li v-for="item in list" :key="item.id"><span>{{item.name}}</span><a :href="item.href">{{item.href}}</a></li></ul>
- 當我們需要更新一個列表中某個id的其中一個數據時(這裡需要更改id為3的name值):
// 1). 事件驅動const dom = $(#ul);const id = 3;dom.find(`li[data-id="${id}"] span`).text(newName3);// 2). 數據驅動 + vueconst id = 3;list.find(item => item.id == 3).name == newName3;
當然這裡我們已知list裡面有id為3的值,若是未知或不確定的數據,則需要做好異常處理,如:
const id = 3;const item3 = list.find(item => item.id == 3);if(item3) item3.name == newName3;
在使用數據驅動的時候,模板渲染的事情會交給框架去完成,我們需要做的就是數據處理而已。
結束語
思維的切換和視角的轉變,是一件很有意思的事情。從更多的角度去觀察,去思考,去總結,才能更好地理解被觀察體。
推薦閱讀:
※[閱 #40] 幾點關於更好書寫 CSS 選擇器的建議
※React入門級小白指北及常見問題解答
※從process.versions了解Node.js的構成
※小程序之一鍵回到頂部和獲取滾動條當前位置
※Three.js二三事
TAG:前端開發 |