国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 語言 > JavaScript > 正文

MVVM 雙向綁定的實現代碼

2024-05-06 15:33:15
字體:
來源:轉載
供稿:網友

這篇文章主要記錄學習 JS 雙向綁定過程中的一些概念與具體的實現

MVVM 具體概念

MVVM 中有一些概念是通用的,具體如下

Directive (指令)

自定義的執行函數,例如 Vue 中的 v-click、v-bind 等。這些函數封裝了 DOM 的一些基本可復用函數API。

Filter (過濾器)

用戶希望對傳入的初始數據進行處理,然后將處理結果交給 Directive 或者下一個 Filter。例如:v-bind="time | formatTime"。formatTime 是將 time 轉換成指定格式的 Filter 函數。

表達式

類似前端普通的頁面模板表達式,作用是控制頁面內容安裝具體的條件顯示。例如:if...else 等

ViewModel

傳入的 Model 數據在內存中存放,提供一些基本的操作 API 給開發者,使其能夠對數據進行讀取與修改

雙向綁定(數據變更檢測)

View 層的變化改變 Model:通過給元素添加 onchange 事件來觸發對 Model 數據進行修改

Model 層的變化改變 View:

    手動觸發綁定 臟數據檢測 對象劫持 Proxy

實現方式

手動觸發綁定

即 Model 對象改變之后,需要顯示的去觸發 View 的更新

首先編寫 HTML 頁面

Two way binding

編寫實現 MVVM 的 代碼

// Manual triggerlet elems = [document.getElementById('el'), document.getElementById('input')]// 數據 Modellet data = { value: 'hello'}// 定義 Directivelet directive = { text: function(text) {  this.innerHTML = text }, value: function(value) {  this.setAttribute('value', value)  this.value = value }}// 掃描所有的元素function scan() { // 掃描帶指令的節點屬性 for (let elem of elems) {  elem.directive = []  for (let attr of elem.attributes) {   if (attr.nodeName.indexOf('q-') >= 0) {    directive[attr.nodeName.slice(2)].call(elem, data[attr.nodeValue])    elem.directive.push(attr.nodeName.slice(2))   }  } }}// ViewModel 更新函數function ViewModelSet(key, value) { // 修改數據對象后 data[key] = value // 手動地去觸發 View 的修改 scan()}// View 綁定監聽elems[1].addEventListener('keyup', function(e) { ViewModelSet('value', e.target.value)}, false)// -------- 程序執行 -------scan()setTimeout(() => { ViewModelSet('value', 'hello world')}, 1000);

數據劫持

數據劫持是目前比較廣泛的方式,Vue 的雙向綁定就是通過數據劫持實現。實現方式是通過 Object.defineProperty 和 Object.defineProperies 方法對 Model 對象的 get 和 set 函數進行監聽。當有數據讀取或賦值操作時,掃描(或者通知)對應的元素執行 Directive 函數,實現 View 的刷新。

HTML 的代碼不變,js 代碼如下

// Hijackinglet elems = [document.getElementById('el'), document.getElementById('input')]let data = { value: 'hello'}// 定義 Directivelet directive = { text: function(text) {  this.innerHTML = text }, value: function(value) {  this.setAttribute('value', value)  this.value = value }}// 定義對象屬性設置劫持// obj: 指定的 Model 數據對象// propName: 指定的屬性名稱function defineGetAndSet(obj, propName) { let bValue // 使用 Object.defineProperty 做數據劫持 Object.defineProperty(obj, propName, {  get: function() {   return bValue  },  set: function(value) {   bValue = value   // 在 vue 中,這里不會去掃描所有的元素,而是通過訂閱發布模式,通知那些訂閱了該數據的 view 進行更新   scan()  },  enumerable: true,  configurable: true })}// View 綁定監聽elems[1].addEventListener('keyup', function(e) { data.value = e.target.value}, false)// 掃描所有的元素function scan() { // 掃描帶指令的節點屬性 for (let elem of elems) {  elem.directive = []  for (let attr of elem.attributes) {   if (attr.nodeName.indexOf('q-') >= 0) {    directive[attr.nodeName.slice(2)].call(elem, data[attr.nodeValue])    elem.directive.push(attr.nodeName.slice(2))   }  } }}// -------- 程序執行 -------scan()defineGetAndSet(data, 'value')setTimeout(() => { // 這里為數據設置新值之后,在 set 方法中會去更新 view data.value = 'Hello world'}, 1000);            
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

主站蜘蛛池模板: 菏泽市| 乌什县| 抚顺市| 肇庆市| 二手房| 洪江市| 巴林右旗| 农安县| 雷山县| 陵川县| 宁武县| 常山县| 铁岭市| 安图县| 军事| 吴桥县| 普陀区| 周宁县| 临沭县| 阜南县| 株洲市| 丹东市| 桦南县| 建水县| 梧州市| 南涧| 安图县| 桐庐县| 阳江市| 清流县| 甘孜县| 聂荣县| 广安市| 凭祥市| 公主岭市| 万山特区| 信丰县| 临泉县| 南昌市| 天全县| 南昌市|