热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

运用Proxy完成简朴的MVVM模子

绑定完成的汗青绑定的基础是propertyChange事宜。怎样得知viewModel成员值的转变一直是开辟MVVM框架的主要题目。主流框架的处置惩罚有一下三大类:别的开辟一套AP

绑定完成的汗青

绑定的基础是 propertyChange 事宜。怎样得知 viewModel 成员值的转变一直是开辟 MVVM 框架的主要题目。主流框架的处置惩罚有一下三大类:

  1. 别的开辟一套 API。典范框架:Backbone.js

Backbone 有本身的 模子类 和 鸠合类。如许做虽然框架开辟简朴运转效力也高,但开辟者不能不运用这套 API 操纵 viewModel,致使上手庞杂、代码烦琐。

  1. 脏搜检机制。典范框架:angularjs

特点是直接运用 JS 原生操纵对象的语法操纵 viewModel,开辟者上手简朴、代码简朴。但脏搜检机制随之带来的就是机能题目。这点在我别的的一篇博文 《Angular 1 深度剖析:脏数据搜检与 angular 机能优化》 有细致解说这里不另加赘述。

  1. 替换属性。典范框架:vuejs
    vuejs 把开辟者定义的 viewModel 对象(即 data 函数返回的对象)中一切的(除某些前缀开首的)成员替换为属性。如许既能够运用 JS 原生操纵对象的语法,又是主动触发 propertyChange 事宜,效力也高。但这类要领也有一些限定,后文会剖析。

Object.observe

Object.observe 是谷歌关于简化双向绑定机制的尝试,在 Chrome 49 中引入。然则由于机能等题目,并没有被其他各大浏览器及 ES 规范所接收。挣扎了一段时候后谷歌 Chrome 团队宣告收回 Object.observe 的发起,并在 Chrome 50 中完全删除了 Object.observe 完成。

Proxy

Proxy(代办)是 ES2015 到场的新特征,用于对某些基础操纵定义自定义行动,相似于其他语言中的面向切面编程。它的个中一个作用就是用于(部份)替换 Object.observe 以完成双向绑定。

比方有一个对象

let viewModel = {};

能够组织对应的代办类完成对 viewModel 的属性赋值操纵的监听:

viewModel = new Proxy(viewModel, {
set(obj, prop, value) {
if (obj[prop] !== value) {
obj[prop] = value;
console.log(`${prop} 属性被改成 ${value}`);
}
return true;
}
});

这时候一切对 viewModel 的属性赋值的操纵都不会直接见效,而是将这个操纵转发给 Proxy 中注册的 set 要领,个中的参数 obj 是原始对象(注重不能直接用 a,不然还会触发代办函数,形成无穷递归),prop 是被赋值的属性名,value 是待赋的值。
假如有:

viewModel.test = 1;

这时候就会输出 test 属性被改成 1

用 Proxy 完成简朴的单向绑定。

有了 Proxy 就能够得知 viewModel 中属性的变动了,还须要更新页面上绑定此属性的元素。

简朴起见,我们用 this 示意 viewModel 本身,运用 this.XXX 就示意依靠 XXX 属性。有 DOM 以下:


起首要取得一切运用了单向绑定的元素:

const bindingElements = [...document.querySelectorAll('[my-bind]')];

猎取绑定表达式:

bindingElements.forEach(el => {
const expression = el.getAttribute('my-bind');
});

由于取得的表达式是个字符串,须要组织一个函数去实行它,获得表达式的效果:

const expression = el.getAttribute('my-bind');
const result = new Function('"use strict";\nreturn ' + expression).call(viewModel);

代码中会动态建立一个函数,内容就是将字符串剖析实行后将其效果返回(相似 eval,但更平安)。将效果放到页面上就能够了:

el.textCOntent= result;

与上文的 viewModel 结合起来:

const bindingElements = [...document.querySelectorAll('[my-bind]')];
window.viewModel = new Proxy({}, { // 设置全局变量轻易调试
set(obj, prop, value) {
if (obj[prop] !== value) {
obj[prop] = value;
bindingElements.forEach(el => {
const expression = el.getAttribute('my-bind');
const result = new Function('"use strict";\nreturn ' + expression)
.call(obj);
el.textCOntent= result;
});
}
return true;
}
});

假如现实放在浏览器中运转的话,转变 viewModel 中属性的值就会触发页面的更新。

示例中写了轮回会更新一切绑定元素,比较好的体式格局是只更新对当前变动属性有依靠的元素。这时候就要剖析绑定表达式的属性依靠。
简朴起见能够运用正则表达式剖析属性依靠:

let match;
while (match = /this(?:\.(\w+))+/g.exec(expression)) {
match[1] // 属性依靠
}

增加事宜绑定

事宜绑定即绑定原生事宜,在事宜触发时实行绑定表达式,表达式挪用 viewModel 中的某个回调函数。

click 事宜为例。依然是猎取一切绑定了 click 事宜的元素,并实行表达式(表达式的值被抛弃)。与单项绑定差别的是:实行表达式须要传入事宜的 event 参数。

[...document.querySelectorAll('[my-click]')].forEach(el => {
const expression = el.getAttribute('my-click');
const fn = new Function('$event', '"use strict";\n' + expression);
el.addEventListener('click', event => {
fn.call(viewModel, event);
});
});

Function 对象的组织函数,前 n-1 个参数是天生的函数对象的参数名,末了一个是函数体。代码中组织了包括一个 $event 参数的函数,函数体就是直接实行绑定表达式。

双向绑定

双向绑定就是单项绑定和事宜绑定的结合体。绑定元素的 input 事宜来修正 viewModel 的属性,然后再单项绑定元素的 value 属性修正元素的值。

这里是一个较为完全的示例:http://sandbox.runjs.cn/show/…。完全的代码放在我的 GitHub 堆栈

运用 Proxy 完成双向绑定的优瑕玷

相较于 vuejs 的属性替换,Proxy 完成的绑定至少有以下三个长处:

  • 无需预先定义待绑定的属性。

vuejs 要做属性(getter, setter 要领)替换,起首须要晓得有哪些属性须要替换,如许致使必需预先定义须要替换的属性,也就是 vuejs 中的 data 要领。vuejs 中 data 要领必需定义完全一切绑定属性,不然对应绑定不能一般事情。
Vue 不能检测到对象属性的增加或删除:Property or method "XXX" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
Proxy 不须要,由于它监听的是悉数对象。

  • 对数组相性优越。

虽然说数组里的要领能够替换(push、pop等),然则数组下标却不能替换为属性,致使必需搞出一个 set 要领用于对数组下标赋值。

  • 更轻易调试的 viewModel 对象。

由于 vuejs 把对象中的一切成员悉数替换成了属性,假如想直接用 Chrome 的原生调试东西检察属性值,你不能不挨个去点属性背面的 (...):由于猎取属性的值现实上是实行了属性的 get 要领,实行一个要领可能会发生副作用,Chrome 把这个决定权留给开辟者。
Proxy 对象不须要。Proxyset 要领只是一层包装,Proxy 对象本身保护原始对象的值,天然也能够直接拿出原始值给开辟者看。检察一个 Proxy 对象,只须要睁开其内置属性 [[Target]] 即可看到原始对象的一切成员的值。你以至还能够看到包装原始对象的哪些 getset 函数——假如你感兴趣的话。

虽然说运用 Proxy 完成双向绑定的长处很明显,然则瑕玷也很明显:ProxyES2015 的特征,它没法被编译为 ES5,也没法 Polyfill。IE 天然全军尽没;其他各大浏览器完成的时候也较晚:Chrome 49、Safari 10。浏览器兼容性极大的限定了 Proxy 的运用。然则我置信,跟着时候的推移,基于 Proxy 的前端 MVVM 框架也会出如今开辟者面前。


推荐阅读
  • 深入理解:AJAX学习指南
    本文详细探讨了AJAX的基本概念、工作原理及其在现代Web开发中的应用,旨在为初学者提供全面的学习资料。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • 理解浏览器历史记录(2)hashchange、pushState
    阅读目录1.hashchange2.pushState本文也是一篇基础文章。继上文之后,本打算去研究pushState,偶然在一些信息中发现了锚点变 ... [详细]
  • 本文详细介绍了如何利用 Bootstrap Table 实现数据展示与操作,包括数据加载、表格配置及前后端交互等关键步骤。 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • 本文介绍了如何通过C#语言调用动态链接库(DLL)中的函数来实现IC卡的基本操作,包括初始化设备、设置密码模式、获取设备状态等,并详细展示了将TextBox中的数据写入IC卡的具体实现方法。 ... [详细]
  • OBS Studio自动化实践:利用脚本批量生成录制场景
    本文探讨了如何利用OBS Studio进行高效录屏,并通过脚本实现场景的自动生成。适合对自动化办公感兴趣的读者。 ... [详细]
  • 解决JavaScript中法语字符排序问题
    在开发一个使用JavaScript、HTML和CSS的Web应用时,遇到从SQLite数据库中提取的法语词汇排序不正确的问题,特别是带重音符号的字母未按预期排序。 ... [详细]
  • 数据类型--char一、char1.1char占用2个字节char取值范围:【0~65535】char采用unicode编码方式char类型的字面量用单引号括起来char可以存储一 ... [详细]
  • 本文探讨了在一个使用Mongoid框架的项目中,如何处理当HABTM(has_and_belongs_to_many)关系中的逆向关联设置为nil时,子对象无法正确持久化的问题。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 本文介绍了如何正确配置Ajax POST请求,以确保前端发送的数据能够被后端正确解析。重点在于前端JSON对象的键名需要与后端实体类的字段名严格匹配。 ... [详细]
  • 深入解析WebP图片格式及其应用
    随着互联网技术的发展,无论是PC端还是移动端,图片数据流量占据了很大比重。尤其在高分辨率屏幕普及的背景下,如何在保证图片质量的同时减少文件大小,成为了亟待解决的问题。本文将详细介绍Google推出的WebP图片格式,探讨其在实际项目中的应用及优化策略。 ... [详细]
  • 八段代码完全控制Promise
    1.Promise的马上实行性varpnewPromise(function(resolve,reject){console.log(createapromise);resolve ... [详细]
  • 在将 Android Studio 从 3.0 升级到 3.1 版本后,遇到项目无法正常编译的问题,具体错误信息为:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:processDemoProductDebugResources'。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有