您是否想过框架如何工作? 该材料的作者(我们今天翻译的译文)说,很多年前,当他学习
jQuery之后 ,偶然发现
Angular.js时 ,在他看来,他所看到的东西非常复杂且难以理解。 然后出现了Vue.js,并处理了这个框架,他被启发编写了自己的双向
数据绑定系统 。 这样的实验有助于程序员的专业发展。 本文面向那些希望在现代JS框架所基于的技术领域扩展自己的知识的人。 特别是,它将着重于如何编写自己的框架的核心,以支持HTML元素的自定义属性,反应性和双向数据绑定。

关于反应系统
如果一开始就弄清楚现代框架的反应系统是如何工作的,那将是很好的。 实际上,这里的一切都很简单。 例如,Vue.js在声明新组件时使用
“代理 ”设计模式
代理每个属性 (获取器和设置器)。
结果,该框架将能够检测从代码和用户界面执行的值更改的每个事实。
代理模式
代理模式基于对对象的重载访问机制。 这类似于人们使用银行帐户的方式。
例如,没有人可以直接使用他们的帐户,根据他们的需要更改其余额。 为了对帐户执行任何操作,您需要联系有权与他合作的人,即与银行联系。 银行充当帐户持有人和帐户本身之间的代理人。
var account = { balance: 5000 } var bank = new Proxy(account, { get: function (target, prop) { return 9000000; } }); console.log(account.balance);
在此示例中,当使用
bank
对象访问由
account
对象表示的帐户余额时,getter函数将被重载,这导致以下事实:即使发出这样的请求,即使返回了9,000,000的值也总是返回,而不是不动产的值。该属性不存在。
这是重载setter函数的示例。
var bank = new Proxy(account, { set: function (target, prop, value) { // 0 return Reflect.set(target, prop, 0); } }); account.balance = 5800; console.log(account.balance); // 5,800 bank.balance = 5400; console.log(account.balance); // 0 ( )
在这里,通过重载
set
函数,可以控制其行为。 例如,您可以更改要写入属性的值,将数据写入其他属性,甚至什么都不做。
反应系统示例
现在我们已经弄清了代理模式,我们将开始开发自己的JS框架。
为了不使其复杂化,我们将使用与Angular.js中使用的语法非常相似的语法。 结果,控制器的声明以及模板元素与控制器属性的绑定将变得简单明了。
这是模板代码。
<div ng-controller="InputController"> <input ng-bind="message"/> <input ng-bind="message"/> </div> <script type="javascript"> function InputController () { this.message = 'Hello World!'; } angular.controller('InputController', InputController); </script>
首先,您需要声明一个带有属性的控制器。 接下来,在模板中使用此控制器,最后使用
ng-bind
属性,以便为元素值建立双向数据绑定。
解析模板并实例化控制器
为了使我们具有可以附加数据的某些属性,我们需要一个可以声明这些属性的位置(控制器)。 因此,有必要描述控制器并将其包含在框架中。
在使用控制器的过程中,框架将查找具有
ng-controller
属性的元素。 如果找到的内容与声明的控制器之一匹配,则框架将创建此控制器的新实例。 该控制器实例仅负责模板的此特定片段。
var controllers = {}; var addController = function (name, constructor) { // controllers[name] = { factory: constructor, instances: [] }; // , var element = document.querySelector('[ng-controller=' + name + ']'); if (!element){ return; // , } // var ctrl = new controllers[name].factory; controllers[name].instances.push(ctrl); // ..... }; addController('InputController', InputController);
以下是“自制”变量
controllers
的声明。 请注意,
controllers
对象包含通过调用
addController
在框架中声明的所有控制器。
var controllers = { InputController: { factory: function InputController(){ this.message = "Hello World!"; }, instances: [ {message: "Hello World"} ] } };
每个控制器都有一个
factory
功能,这样做是为了使您可以在必要时创建新控制器的实例。 此外,框架在
instances
属性中存储模板中使用的相同类型的控制器的所有实例。
搜索数据绑定中涉及的项目
目前,我们有一个控制器实例和使用该控制器的模板片段。 我们的下一步将是搜索包含需要绑定到控制器属性的数据的元素。
var bindings = {}; // : element DOM, Array.prototype.slice.call(element.querySelectorAll('[ng-bind]')) .map(function (element) { var boundValue = element.getAttribute('ng-bind'); if(!bindings[boundValue]) { bindings[boundValue] = { boundValue: boundValue, elements: [] } } bindings[boundValue].elements.push(element); });
此处显示了使用
哈希表存储所有对象绑定的组织。 所考虑的变量包含用于绑定的所有属性及其当前值,以及绑定到特定属性的所有DOM元素。
这是我们的
bindings
变量版本:
var bindings = { message: { // : // controllers.InputController.instances[0].message boundValue: 'Hello World', // HTML- ( ng-bind="message") elements: [ Object { ... }, Object { ... } ] } };
控制器属性的双向绑定
在框架完成初步准备后,需要进行一件有趣的事情:双向数据绑定。 其含义如下。 首先,控制器的属性需要绑定到DOM元素,这将允许它们在代码中的属性值发生更改时进行更新,其次,DOM元素也必须绑定到控制器的属性。 因此,当用户对此类元素进行操作时,这会导致控制器属性的变化。 而且,如果将几个HTML元素附加到该属性,则还会导致其状态也被更新的事实。
使用代理检测对代码所做的更改
如上所述,Vue.js将组件包装在代理中以检测属性更改。 我们将通过仅代理设置器来完成数据绑定中涉及的控制器属性,从而完成此操作:
// : ctrl - var proxy = new Proxy(ctrl, { set: function (target, prop, value) { var bind = bindings[prop]; if(bind) { // DOM, bind.elements.forEach(function (element) { element.value = value; element.setAttribute('value', value); }); } return Reflect.set(target, prop, value); } });
结果,事实证明,当将值写入绑定属性时,代理将检测到绑定到该属性的所有元素,并将它们传递给新值。
在此示例中,我们仅支持
input
元素的绑定,因为此处仅设置了
value
属性。
对元素事件的响应
现在,我们只需要提供对用户操作的系统响应即可。 为此,请考虑以下事实:DOM元素在检测到其值的更改时会引发事件。
我们组织监听这些事件,并在与元素相关的属性中记录从事件获得的新数据。 借助代理,绑定到同一属性的所有其他元素将自动更新。
Object.keys(bindings).forEach(function (boundValue) { var bind = bindings[boundValue]; // bind.elements.forEach(function (element) { element.addEventListener('input', function (event) { proxy[bind.boundValue] = event.target.value; // , }); }) });
总结
结果,将我们在这里讨论的所有内容放到一起,您将获得实现现代JS框架基本机制的自己的系统。
这是这种系统实施的工作示例。 我们希望本文能帮助所有人更好地了解现代Web开发工具的工作方式。
亲爱的读者们! 如果您专业使用现代JS框架,请告诉我们您如何开始他们的研究以及如何帮助您理解它们。
