亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

前端MVVM框架解析之雙向綁定

2019-11-19 14:28:58
字體:
來源:轉載
供稿:網友

MVVM 框架

近年來前端一個明顯的開發趨勢就是架構從傳統的 MVC 模式向 MVVM 模式遷移。在傳統的 MVC 下,當前前端和后端發生數據交互后會刷新整個頁面,從而導致比較差的用戶體驗。因此我們通過 Ajax 的方式和網關 REST API 作通訊,異步的刷新頁面的某個區塊,來優化和提升體驗。

MVVM 框架基本概念

在 MVVM 框架中,View(視圖) 和 Model(數據) 是不可以直接通訊的,在它們之間存在著 ViewModel 這個中間介充當著觀察者的角色。當用戶操作 View(視圖),ViewModel 感知到變化,然后通知 Model 發生相應改變;反之當 Model(數據) 發生改變,ViewModel 也能感知到變化,使 View 作出相應更新。這個一來一回的過程就是我們所熟知的雙向綁定。

MVVM 框架的應用場景

MVVM 框架的好處顯而易見:當前端對數據進行操作的時候,可以通過 Ajax 請求對數據持久化,只需改變 dom 里需要改變的那部分數據內容,而不必刷新整個頁面。特別是在移動端,刷新頁面的代價太昂貴。雖然有些資源會被緩存,但是頁面的 dom、css、js 都會被瀏覽器重新解析一遍,因此移動端頁面通常會被做成 SPA 單頁應用。由此在這基礎上誕生了很多 MVVM 框架,比如 React.js、Vue.js、Angular.js 等等。

MVVM 框架的簡單實現

模擬 Vue 的雙向綁定流,實現了一個簡單的MVVM 框架,從上圖中可以看出虛線方形中就是之前提到的 ViewModel 中間介層,它充當著觀察者的角色。另外可以發現雙向綁定流中的 View 到 Model 其實是通過 input 的事件監聽函數實現的,如果換成 React(單向綁定流) 的話,它在這一步交給狀態管理工具(比如 Redux)來實現。另外雙向綁定流中的 Model 到 View 其實各個 MVVM 框架實現的都是大同小異的,都用到的核心方法是 Object.defineProperty(),通過這個方法可以進行數據劫持,當數據發生變化時可以捕捉到相應變化,從而進行后續的處理。

Mvvm(入口文件) 的實現

一般會這樣調用 Mvvm 框架

const vm = new Mvvm({      el: '#app',      data: {       title: 'mvvm title',       name: 'mvvm name'      },     })

但是這樣子的話,如果要得到 title 屬性就要形如 vm.data.title 這樣取得,為了讓 vm.title 就能獲得 title 屬性,從而在 Mvvm 的 prototype 上加上一個代理方法,代碼如下:

function Mvvm (options) { this.data = options.data const self = this Object.keys(this.data).forEach(key =>  self.proxyKeys(key) )}Mvvm.prototype = { proxyKeys: function(key) {  const self = this  Object.defineProperty(this, key, {   get: function () { // 這里的 get 和 set 實現了 vm.data.title 和 vm.title 的值同步    return self.data[key]   },   set: function (newValue) {    self.data[key] = newValue   }  }) }}

實現了代理方法后,就步入主流程的實現

function Mvvm (options) { this.data = options.data // ... observe(this.data) new Compile(options.el, this)}

observer(觀察者) 的實現

observer 的職責是監聽 Model(JS 對象) 的變化,最核心的部分就是用到了 Object.defineProperty() 的 get 和 set 方法,當要獲取 Model(JS 對象) 的值時,會自動調用 get 方法;當改動了 Model(JS 對象) 的值時,會自動調用 set 方法;從而實現了對數據的劫持,代碼如下所示。

let data = { number: 0}observe(data)data.number = 1 // 值發生變化function observe(data) { if (!data || typeof(data) !== 'object') {  return } const self = this Object.keys(data).forEach(key =>  self.defineReactive(data, key, data[key]) )}function defineReactive(data, key, value) { observe(value) // 遍歷嵌套對象 Object.defineProperty(data, key, {  get: function() {   return value  },  set: function(newValue) {   if (value !== newValue) {    console.log('值發生變化', 'newValue:' + newValue + ' ' + 'oldValue:' + value)    value = newValue   }  } })}

運行代碼,可以看到控制臺輸出 值發生變化 newValue:1 oldValue:0,至此就完成了 observer 的邏輯。

Dep(訂閱者數組) 和 watcher(訂閱者) 的關系

觀測到變化后,我們總要通知給特定的人群,讓他們做出相應的處理吧。為了更方便地理解,我們可以把訂閱當成是訂閱了一個微信公眾號,當微信公眾號的內容有更新時,那么它會把內容推送(update) 到訂閱了它的人。

那么訂閱了同個微信公眾號的人有成千上萬個,那么首先想到的就是要 new Array() 去存放這些人(html 節點)吧。于是就有了如下代碼:

// observer.jsfunction Dep() { this.subs = [] // 存放訂閱者}Dep.prototype = { addSub: function(sub) { // 添加訂閱者  this.subs.push(sub) }, notify: function() { // 通知訂閱者更新  this.subs.forEach(function(sub) {   sub.update()  }) }}function observe(data) {...}function defineReactive(data, key, value) { var dep = new Dep() observe(value) // 遍歷嵌套對象 Object.defineProperty(data, key, {  get: function() {   if (Dep.target) { // 往訂閱器添加訂閱者    dep.addSub(Dep.target)   }   return value  },  set: function(newValue) {   if (value !== newValue) {    console.log('值發生變化', 'newValue:' + newValue + ' ' + 'oldValue:' + value)    value = newValue    dep.notify()   }  } })}

初看代碼也比較順暢了,但可能會卡在 Dep.target 和 sub.update,由此自然而然地將目光移向 watcher,

// watcher.jsfunction Watcher(vm, exp, cb) { this.vm = vm this.exp = exp this.cb = cb this.value = this.get()}Watcher.prototype = { update: function() {  this.run() }, run: function() {  // ...  if (value !== oldVal) {   this.cb.call(this.vm, value) // 觸發 compile 中的回調  } }, get: function() {  Dep.target = this // 緩存自己  const value = this.vm.data[this.exp] // 強制執行監聽器里的 get 函數  Dep.target = null // 釋放自己  return value }}

從代碼中可以看到當構造 Watcher 實例時,會調用 get() 方法,接著重點關注 const value = this.vm.data[this.exp] 這句,前面說了當要獲取 Model(JS 對象) 的值時,會自動調用 Object.defineProperty 的 get 方法,也就是當執行完這句的時候,Dep.target 的值傳進了 observer.js 中的 Object.defineProperty 的 get 方法中。同時也一目了然地在 Watcher.prototype 中發現了 update 方法,其作用即觸發 compile 中綁定的回調來更新界面。至此解釋了 Observer 中 Dep.target 和 sub.update 的由來。

來歸納下 Watcher 的作用,其充當了 observer 和 compile 的橋梁。

1 在自身實例化的過程中,往訂閱器(dep) 中添加自己

2 當 model 發生變動,dep.notify() 通知時,其能調用自身的 update 函數,并觸發 compile 綁定的回調函數實現視圖更新

最后再來看下生成 Watcher 實例的 compile.js 文件。

compile(編譯) 的實現

首先遍歷解析的過程有多次操作 dom 節點,為提高性能和效率,會先將跟節點 el 轉換成 fragment(文檔碎片) 進行解析編譯,解析完成,再將 fragment 添加回原來的真實 dom 節點中。代碼如下:

function Compile(el, vm) { this.vm = vm this.el = document.querySelector(el) this.fragment = null this.init()}Compile.prototype = { init: function() {  if (this.el) {   this.fragment = this.nodeToFragment(this.el) // 將節點轉為 fragment 文檔碎片   this.compileElement(this.fragment) // 對 fragment 進行編譯解析   this.el.appendChild(this.fragment)  } }, nodeToFragment: function(el) {  const fragment = document.createDocumentFragment()  let child = el.firstChild // △ 第一個 firstChild 是 text  while(child) {   fragment.appendChild(child)   child = el.firstChild  }  return fragment }, compileElement: function(el) {...},}

這個簡單的 mvvm 框架在對 fragment 編譯解析的過程中對 {{}} 文本元素、v-on:click 事件指令、v-model 指令三種類型進行了相應的處理。

Compile.prototype = { init: function() {  if (this.el) {   this.fragment = this.nodeToFragment(this.el) // 將節點轉為 fragment 文檔碎片   this.compileElement(this.fragment) // 對 fragment 進行編譯解析   this.el.appendChild(this.fragment)  } }, nodeToFragment: function(el) {...}, compileElement: function(el) {...}, compileText: function (node, exp) { // 對文本類型進行處理,將 {{abc}} 替換掉  const self = this  const initText = this.vm[exp]  this.updateText(node, initText) // 初始化  new Watcher(this.vm, exp, function(value) { // 實例化訂閱者   self.updateText(node, value)  }) }, compileEvent: function (node, vm, exp, dir) { // 對事件指令進行處理  const eventType = dir.split(':')[1]  const cb = vm.methods && vm.methods[exp]  if (eventType && cb) {   node.addEventListener(eventType, cb.bind(vm), false)  } }, compileModel: function (node, vm, exp) { // 對 v-model 進行處理  let val = vm[exp]  const self = this  this.modelUpdater(node, val)  node.addEventListener('input', function (e) {   const newValue = e.target.value   self.vm[exp] = newValue // 實現 view 到 model 的綁定  }) },}

在上述代碼的 compileTest 函數中看到了期盼已久的 Watcher 實例化,對 Watcher 作用模糊的朋友可以往上回顧下 Watcher 的作用。另外在 compileModel 函數中看到了本文最開始提到的雙向綁定流中的 View 到 Model 是借助 input 監聽事件變化實現的。

項目地址

本文記錄了些閱讀 mvvm 框架源碼關于雙向綁定的心得,并動手實踐了一個簡版的 mvvm 框架,不足之處在所難免,歡迎指正。

項目演示

項目地址

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲无限乱码一二三四麻| 亚洲欧美在线磁力| 精品亚洲一区二区三区在线播放| 亚洲免费影视第一页| 91性高湖久久久久久久久_久久99| 日韩成人高清在线| 98精品国产自产在线观看| 亚洲www视频| 国产精品爽爽ⅴa在线观看| 黑人欧美xxxx| 国产欧美日韩中文字幕在线| 欧美最猛黑人xxxx黑人猛叫黄| 在线观看欧美日韩| www亚洲欧美| 欧美日韩成人在线观看| 国产国产精品人在线视| 亚洲影院色在线观看免费| 国产91精品网站| 国产精品日韩av| 一个人www欧美| 欧美激情视频网址| 91亚洲国产成人久久精品网站| 国产精品亚洲第一区| 国产综合香蕉五月婷在线| 2020久久国产精品| 欧美丰满少妇xxxxx| 欧美日韩免费一区| 日韩电影中文字幕一区| 亚洲国产97在线精品一区| 亚洲国产日韩欧美在线动漫| 久久夜色精品亚洲噜噜国产mv| 中文字幕日韩电影| 精品久久久久久久久久久久久| 在线观看欧美日韩国产| 亚洲va欧美va在线观看| 欧美大全免费观看电视剧大泉洋| 动漫精品一区二区| 成人久久一区二区三区| 91精品视频在线| 一区二区三区视频观看| 国产一区二区三区18| 久久国产精品首页| 国产婷婷成人久久av免费高清| 欧美老少配视频| 日韩美女免费观看| 国产不卡一区二区在线播放| 日韩在线观看高清| 亚洲伊人一本大道中文字幕| 亚洲国产精品久久久久| 欧美日韩国产精品一区| 中文字幕在线视频日韩| 韩国国内大量揄拍精品视频| 精品一区二区电影| 久久精品91久久久久久再现| 98精品国产自产在线观看| 亚洲www永久成人夜色| 日韩视频免费大全中文字幕| 国产女人18毛片水18精品| 亚洲国产精久久久久久| 8x拔播拔播x8国产精品| 国产精品www色诱视频| 日韩女优在线播放| 国产伦精品一区二区三区精品视频| 亚洲午夜未满十八勿入免费观看全集| 欧美影院在线播放| 成人在线视频网| 亚洲三级黄色在线观看| 狠狠躁夜夜躁人人躁婷婷91| 97成人精品区在线播放| 欧美精品九九久久| 国产午夜精品免费一区二区三区| 亚洲娇小xxxx欧美娇小| 亚洲最大成人网色| 亚洲国产精品成人一区二区| 欧美激情免费视频| 亚洲视频精品在线| 尤物九九久久国产精品的特点| 91精品视频免费看| 久久人人爽人人爽人人片av高请| 亚洲欧美日韩国产成人| 色婷婷**av毛片一区| 欧美性猛交xxxx免费看久久久| 欧美电影在线观看| 亚洲加勒比久久88色综合| 欧美疯狂xxxx大交乱88av| 亚洲最新av在线网站| 18性欧美xxxⅹ性满足| 69久久夜色精品国产69乱青草| 亚洲四色影视在线观看| 最近2019中文字幕在线高清| 91久久久精品| 国产亚洲精品久久久| 精品美女久久久久久免费| 成人在线视频福利| 欧美激情精品久久久久久黑人| 欧美一区二区三区四区在线| 日韩中文在线视频| 欧美成年人视频网站欧美| 91精品久久久久久久久久久久久| 国产一区二区三区三区在线观看| 精品国产91乱高清在线观看| 在线中文字幕日韩| 国产精品99久久久久久白浆小说| 亚洲色图国产精品| 日韩美女在线播放| 国产性猛交xxxx免费看久久| 国产欧美一区二区三区视频| 欧美性猛交丰臀xxxxx网站| 免费91麻豆精品国产自产在线观看| 欧美日韩在线免费观看| 亚洲免费电影在线观看| 日韩在线视频观看正片免费网站| 成人黄色大片在线免费观看| 亚洲国语精品自产拍在线观看| 在线电影中文日韩| 亚洲免费电影在线观看| 国产精品www网站| 夜夜嗨av色综合久久久综合网| 久久久久99精品久久久久| 国产精品视频xxxx| 中文字幕不卡在线视频极品| 在线视频精品一| 精品视频—区二区三区免费| 亚洲成人av在线| 2018中文字幕一区二区三区| 精品久久久999| 国产精品高精视频免费| 91国内免费在线视频| 欧美大全免费观看电视剧大泉洋| 欧美日韩一区二区三区在线免费观看| 久操成人在线视频| 亚洲免费av片| 亚洲欧美一区二区三区久久| 欧美一乱一性一交一视频| 中文字幕自拍vr一区二区三区| 中文字幕自拍vr一区二区三区| 视频在线观看一区二区| xxx欧美精品| 中文字幕亚洲二区| 亚洲精品一区二区三区不| 亚洲欧洲中文天堂| 中文字幕亚洲一区| 欧美激情在线有限公司| 亚洲新声在线观看| 欧美激情精品久久久久久大尺度| 92版电视剧仙鹤神针在线观看| 国产一区二区三区精品久久久| 欧美肥臀大乳一区二区免费视频| 国产性色av一区二区| 精品美女永久免费视频| 日韩av在线影院| 黄色成人av在线| 伊人久久综合97精品| 深夜福利日韩在线看| 亚洲欧美激情视频| www.日韩免费| 国产精品www色诱视频| 91精品国产自产在线观看永久| 久久av在线看| 91中文字幕一区| 亚洲自拍高清视频网站| 欧美国产日韩一区| 一本色道久久88综合亚洲精品ⅰ| 国产精品自产拍在线观看|