您当前的位置: 首页 > 学无止境 > JS经典实例 网站首页JS经典实例
JavaScript使用观察者模式模拟vue双向绑定(简单的vue核心原理)
发布时间:2020-06-25 16:37:54编辑:雪饮阅读()
本次主要介绍使用原生JavaScript以观察者模式模拟vue双向绑定、模板、指令系统,即vue的核心原理。
代码理解起来有一定难度,但只要以观察者模式、双向绑定、模板(正则及文本节点等)、指令系统(节点属性)等为理解主旨,则也可以体会出来。
另外这里有偷懒,其实只要把观察者模式单独拎出来一个js则更容易理解。
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
var tempObserable;
// 观察者
function Observer () {
this.observables = [];
}
Observer.prototype.notify = function(){
for (var i = this.observables.length - 1; i >= 0; i--) {
this.observables[i].update();
}
}
Observer.prototype.subscribe = function(){
this.observables.push(tempObserable);
}
// 可观察对象 具备事件触发的能力
function Observable (node,propName,data) {
this.$node = node;
this.$propName = propName;
this.$data = data;
}
Observable.prototype.update = function(){
if(this.$node.nodeType === 1) {
this.$node.value= this.$data[this.$propName];
} else if(this.$node.nodeType === 3) {
// 会触发get 在get内部判断
this.$node.nodeValue = this.$data[this.$propName];
}
}
// MVVM 是一个构造函数 接收一个options对象,存起来
function MVVM (options) {
this.$el = options.el;
this.$options = options;
//data方法的引用
this.$data = options.data; //fn
this.$template = options.template;
//data方法的执行结果
this.$$data = this.$data(); // 数据对象
this.init();
}
MVVM.prototype.init = function(){
// 1: 监视属性,遍历对象
// 遍历对象每个key并为每个key设置get与set值时绑定观察者与订阅者模式以接受通知事件
this.defineReactive(this.$$data,this.$$data.text); // { text:'abc'}
// 2: 解析DOM
this.compiler(this.$template,this.$$data);
}
MVVM.prototype.compiler = function (tempstr,data) {
// 将tempstr 插入到app中
var box = document.querySelector(this.$el);
box.innerHTML = tempstr;
// 分析box 获取到其中的标记
var nodes = box.children[0].childNodes;
// 正则匹配
var regexText = /.*\{\{(.*)\}\}.*/;
var regexV = /^v-(.*)$/;
for (var i = nodes.length - 1; i >= 0; i--) {
var node = nodes[i];
if(node.nodeType === 3) {
// 分类判断 nodeType === 3 文本节点 nodeValue
var result = regexText.exec(node.nodeValue);
if(result) {
// 获取结果与this.$$data匹配 this.$$data[xxx]
// 触发获取
this.textMatch(result[1].trim(),node); // text
}
} else if(node.nodeType === 1) {
// NodeTypes === 1 input标签 ele.value
// 获取元素属性名称
var nodeAttrs = node.attributes;
console.log(nodeAttrs);
for(var j = 0 ; j < nodeAttrs.length; j++) {
var attr = nodeAttrs[j]; // name,value
var result = regexV.exec(attr.name);
if(result) {
console.log(attr.name,attr.value,'被匹配了',result);
// 要根据指令名称干活
this.directive[result[1]](attr.value,node,data);
}
}
}
}
}
MVVM.prototype.directive = {
model:function(propName,node,data) { // 参数是text
var self = this;
console.log(this)
tempObserable = new Observable(node,propName,data);
// 给一个初始值
node.value = data[propName]; // 触发get
// 给元素添加事件
node.addEventListener('input',function(e) {
// 触发set
data[propName] = e.target.value;
});
}
}
MVVM.prototype.textMatch = function(propName,node){
// 看这里: 1:创建存储信息的行为 可观察对象
// 2:将其挂载全局
// 3: 触发get函数,并从全局中取出1
//new的对象用全局变量tempObserable存储
tempObserable = new Observable(node,propName,this.$$data);
console.log('找到文本节点啦',propName,node);
// 替换当前node的值,并赋值为data的数据
node.nodeValue = this.$$data[propName]; // 触发get
}
/**
* [defineReactive description]
* @param {[type]} obj [description]
* @return {[type]} [description]
*/
MVVM.prototype.defineReactive = function (obj,value) {
for(var key in obj) {
// 创建观察者
var observer = new Observer(); // text
/*
简介:ES5规范开始后续版本迭代,也在致力于做一件事,就是把js底层已有的功能,提供给开发者用Object.defineProperty就是其中一个,此方法会可直接在一个对象上定义一个新的具有详细描述的属性,或者修改一个对象的现有属性,并返回这个对象。
使用:
Object.defineProperty(对象,属性,描述符);
描述符对,对象的属性的进行详细描述:
1)数据描述符:
value:‘xxx’ 属性值。默认 ‘’(空字符串)
writeable:true是否可写。默认false
configurable:true是否可配置。默认false
enumerable:true是否可枚举。默认false
2)存取描述符:
set:function(){}属性访问器 进行写操作时调用该方法
get:function(){}属性访问器 进行读操作时调用该方法
*/
Object.defineProperty(obj,key,{
set:function(v) {
// 更新观察者中的所有可观察对象中的节点数据
value = v;
console.log('set触发了');
console.log(observer)
// 通知
observer.notify();
},
get:function() {
console.log('get触发了');
if(tempObserable) {
//只有先订阅了才能接收通知
observer.subscribe();
tempObserable = null;
}
// 为节点初始化的时候触发, 将当前的节点关联上可观察对象,并加入到观察者中
return value;
}
})
}
}
var vm = new MVVM({
el:'#app',
data() {
return {
text:'abc'
}
},
template:`<div>
<input type="text" v-model="text" />
{{ text }}
</div>`
});
/**
* new Vue({
data() {
return {
text:'abc'
}
},
template:`<div>
<input type="text" v-model="text" />
</div>`
})
*/
</script>
</body>
</html>
最后查看下效果
从内存执行命令驱动数据
视图数据变得驱动内存数据
关键字词:javascript,观察者模式,vue,原理,双向绑定