国产探花免费观看_亚洲丰满少妇自慰呻吟_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);            
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

主站蜘蛛池模板: 泌阳县| 通渭县| 翁牛特旗| 光山县| 平顺县| 山阴县| 宁乡县| 北海市| 青冈县| 连南| 河南省| 荆州市| 武陟县| 涪陵区| 兴化市| 米泉市| 尼勒克县| 区。| 荣昌县| 潜山县| 奇台县| 常州市| 石屏县| 镶黄旗| 宜兰市| 侯马市| 石嘴山市| 屏东市| 长葛市| 登封市| 西华县| 桐庐县| 靖宇县| 阳西县| 罗平县| 清水县| 诸暨市| 潜江市| 峨边| 嵊州市| 昌图县|