mg4377娱乐娱城官网_mg4377娱乐手机版_www.mg4377.com

当前位置: mg4377娱乐娱城官网 > www.mg4377.com > 正文

数量绑定,前端MVVM框架深入分析之双向绑定

时间:2019-06-01 09:46来源:www.mg4377.com
MVVM 框架 前者MVVM框架剖析之双向绑定,前端mvvm框架 MVVM 框架 近眼前端一个斐然的付出趋势正是架设从守旧的 MVC 格局向 MVVM格局迁移。在价值观的 MVC下,当前前端和后端发生多少交互

MVVM 框架

前者MVVM框架剖析之双向绑定,前端mvvm框架

MVVM 框架

近眼前端一个斐然的付出趋势正是架设从守旧的 MVC 格局向 MVVM 格局迁移。在价值观的 MVC 下,当前前端和后端发生多少交互后会刷新整个页面,从而导致相比较差的用户体验。因而大家由此Ajax 的点子和网关 REST API 作通信,异步的基础代谢页面包车型的士某部区块,来优化和提高体验。

MVVM 框架基本概念

图片 1

在 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 框架的粗略达成

图片 2

模仿 Vue 的双向绑定流,实现了二个简易的MVVM 框架,从上海教室中能够看到虚线方形中正是前边提到的 ViewModel 中间介层,它充当着观看者的剧中人物。别的能够窥见双向绑定流中的 View 到 Model 其实是经过 input 的事件监听函数实现的,倘若换来 React(单向绑定流) 的话,它在这一步交给状态管理工具(譬喻 Redux)来兑现。别的双向绑定流中的 Model 到 View 其实种种 MVVM 框架达成的都以工力悉敌的,都用到的主干措施是 Object.defineProperty(),通过这几个主意能够进行数量威吓,当数码发生变化时得以捕捉到相应更改,从而举办接二连三的拍卖。

图片 3

Mvvm(入口文件) 的完毕

数量绑定,前端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:1oldValue:0,至此就完事了 observer 的逻辑。

Dep(订阅者数组) 和 watcher(订阅者) 的关系

考察到变化后,大家总要通告给一定的人工产后出血,让他俩做出相应的拍卖呢。为了更有益地明白,大家得以把订阅当成是订阅了1个微信公众号,当微信公众号的原委有更新时,那么它会把内容推送(update) 到订阅了它的人。

图片 4

那么订阅了同个微信公众号的人有众几个,那么首先想到的就是要 new Array() 去存放这么些人(html 节点)吧。于是就有了如下代码:

// observer.js
function 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.js
function 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 方法中。同时也一览无遗地在 沃特cher.prototype 中窥见了 update 方法,其功能即触发 compile 中绑定的回调来更新分界面。至此解释了 Observer 中 Dep.target 和 sub.update 的由来。

来回顾下 沃特cher 的功效,其担纲了 observer 和 compile 的大桥。

一 在自身实例化的长河中,往订阅器(dep) 中增多自个儿

二 当 model 发生变动,dep.notify() 公告时,其能调用本身的 update 函数,并触发 compile 绑定的回调函数达成视图更新

最后再来看下生成 沃特cher 实例的 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 指令3连串型实行了相应的管理。

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 功效模糊的心上人能够往上回看下 沃特cher 的听从。其余在 compileModel 函数中看看了本文最开始波及的双向绑定流中的 View 到 Model 是依赖 input 监听事件变化达成的。

花色地址

正文记录了些阅读 mvvm 框架源码关于双向绑定的感受,并入手执行了3个简版的 mvvm 框架,不足之处在所无免,接待指正。

体系示范

类型地址

如上就是本文的全部内容,希望对大家的就学抱有协理,也指望大家多多援助帮客之家。

MVVM 框架 近年来前端1个刚毅的开支趋势正是架设从思想的 MVC 模式向 MVVM 形式迁移。在价值观的...

图片 5

DataBinding - Share from here

近日前端1个显著的支付趋势就是架设从古板的 MVC 形式向 MVVM 形式迁移。在古板的 MVC 下,当前前端和后端产生多少交互后会刷新整个页面,从而致使比较差的用户体验。因此我们经过 Ajax 的艺术和网关 REST API 作通讯,异步的基础代谢页面的某些区块,来优化和晋级换代体验。

品种地址

MVVM 框架基本概念

MVVM 框架

眼前前端五个大名鼎鼎的开支趋势正是架设从守旧的 MVC 情势向 MVVM 情势迁移。在理念的 MVC 下,当前前端和后端发生多少交互后会刷新整个页面,从而变成相比较差的用户体验。由此大家透过 Ajax 的点子和网关 REST API 作通信,异步的刷新页面包车型大巴某部区块,来优化和提拔体验。

图片 6

MVVM 框架基本概念

在 MVVM 框架中,View(视图) 和 Modal(数据) 是不能直接通信的,在它们之间存在着 ViewModal 那么些个中介充当着观察者的角色。当用户操作 View(视图),ViewModal 感知到变化,然后布告 Modal 产生相应更换;反之当 Modal(数据) 发生转移,ViewModal 也能感知到变化,使 View 作出相应更新。那一个一来一次的进度就是我们所熟悉的双向绑定。

在 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 框架的选取场景

MVVM 框架的粗略达成

图片 7

宪章 Vue 的双向绑定流,达成了3个简练的 MVVM 框架,从上海体育场面中能够看来虚线方形中正是事先提到的 ViewModal 中间介层,它充当着旁观者的剧中人物。此外能够发掘双向绑定流中的 View 到 Modal 其实是经过 input 的风浪监听函数达成的,要是换到React(单向绑定流) 的话,它在这一步交给状态管理工科具(比方Redux)来完成。此外双向绑定流中的 Modal 到 View 其实种种 MVVM 框架实现的都以千篇壹律的,都用到的中央措施是 Object.defineProperty(),通过这一个点子能够举行数量威迫,当数码产生变化时得以捕捉到相应改变,从而举办后续的拍卖。

图片 8

MVVM 框架的便宜由此可见:当前端对数据开始展览操作的时候,能够透过 Ajax 请求对数码长久化,只需退换 dom 里须要转移的那有个别数据内容,而毋庸刷新整个页面。极其是在移动端,刷新页面包车型客车代价太昂贵。即便有一点能源会被缓存,可是页面的dom、css、js 都会被浏览珍视新深入分析贰次,由此活动端页面日常会被做成 SPA 单页应用。由此在那基础上落地了诸多 MVVM 框架,比方React.js、Vue.js、Angular.js 等等。

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)
}

MVVM 框架的简要完成

observer(观察者) 的实现

observer 的天职是监听 Modal(JS 对象) 的扭转,最基本的部分便是用到了 Object.defineProperty() 的 get 和 set 方法,当要收获 Modal(JS 对象) 的值时,会自行调用 get 方法;当退换了 Modal(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 的逻辑。

图片 9

Dep(订阅者数组) 和 watcher(订阅者) 的涉嫌

着重到变化后,大家总要文告给一定的人群,让她们做出相应的管理啊。为了更方便地驾驭,大家能够把订阅当成是订阅了二个微信公众号,当微信公众号的从头到尾的经过有更新时,那么它会把内容推送(update) 到订阅了它的人。

图片 10

那么订阅了同个微信公众号的人有许八个,那么首先想到的正是要 new Array() 去存放那个人(html 节点)吧。于是就有了之类代码:

// observer.js
function 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.targetsub.update,由此任其自然地将目光移向 watcher,

// watcher.js
function 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
  }
}

从代码中能够见见当构造 沃特cher 实例时,会调用 get() 方法,接注重视关怀 const value = this.vm.data[this.exp] 那句,前面说了当要得到 Modal(JS 对象) 的值时,会活动调用 Object.defineProperty 的 get 方法,也正是当施行完那句的时候,Dep.target 的值传进了 observer.js 中的 Object.defineProperty 的 get 方法中。同有的时候间也许有目共睹地在 沃特cher.prototype 中窥见了 update 方法,其效果即触发 compile 中绑定的回调来更新分界面。至此解释了 Observer 中 Dep.target 和 sub.update 的由来。

来回顾下 沃特cher 的效益,其担纲了 observer 和 compile 的大桥。

1 在自身实例化的进程中,往订阅器(dep) 中增添自身

贰 当 modal 爆发改动,dep.notify() 公告时,其能调用自己的 update 函数,并触发 compile 绑定的回调函数完毕视图更新

最后再来看下生成 沃特cher 实例的 compile.js 文件。

一步一趋 Vue 的双向绑定流,达成了二个简练的MVVM 框架,从上海教室中能够看来虚线方形中正是前面提到的 ViewModel 中间介层,它充当着观望者的角色。其它能够窥见双向绑定流中的 View 到 Model 其实是透过 input 的风云监听函数达成的,假若换到React(单向绑定流) 的话,它在这一步交给状态管理工科具(比方Redux)来兑现。别的双向绑定流中的 Model 到 View 其实各种 MVVM 框架达成的都以半斤八两的,都用到的基本措施是 Object.defineProperty(),通过这么些主意能够举办多少恐吓,当数码发生变化时方可捕捉到相应改动,从而进行一连的拍卖。

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 指令3连串型实行了对应的处理。

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 到 modal 的绑定
    })
  },
}

在上述代码的 compileTest 函数中看出了期盼已久的 沃特cher 实例化,对 沃特cher 功用模糊的相恋的人可现在上回看下 沃特cher 的功效。此外在 compileModel 函数中看看了本文最初阶波及的双向绑定流中的 View 到 Modal 是依赖 input 监听事件变化完毕的。

图片 11

品种地址

正文记录了些阅读 mvvm 框架源码关于双向绑定的心得,并入手实践了三个简版的 mvvm 框架,不足之处在所难免,接待指正。

种类示范

品类地址

Mvvm(入口文件) 的贯彻

编辑:www.mg4377.com 本文来源:数量绑定,前端MVVM框架深入分析之双向绑定

关键词: 日记本 fe cloud 前端笔记