<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<div class="test">
<p>{{user.name}}</p>
<p>{{user.age}}</p>
</div>
<script type="text/javascript" src="hue.js"></script>
<script type="text/javascript">
let vm = new Hue({
el: '.test',
data: {
user: {
name: 'Jack',
age: '18'
}
}
});
</script>
</body>
</html>
function Hue(options) {
this.$options = options || {};
let data = this._data = this.$options.data,
self = this;
Object.keys(data).forEach(function(key) {
self._proxyData(key);
});
observe(data);
self.$compile = new Compile(self, options.el || document.body);
}
// 为 data 做了一个代理,
// 访问 vm.xxx 会触发 vm._data[xxx] 的getter,取得 vm._data[xxx] 的值,
// 为 vm.xxx 赋值则会触发 vm._data[xxx] 的setter
Hue.prototype._proxyData = function(key) {
let self = this;
Object.defineProperty(self, key, {
configurable: false,
enumerable: true,
get: function proxyGetter() {
return self._data[key];
},
set: function proxySetter(newVal) {
self._data[key] = newVal;
}
});
};
function Compile(vm, el) {
this.$vm = vm;
this.$el = this.isElementNode(el)
? el
: document.querySelector(el);
if (this.$el) {
this.$fragment = this.node2Fragment(this.$el);
this.init();
this.$el.appendChild(this.$fragment);
}
}
Compile.prototype.node2Fragment = function(el) {
let fragment = document.createDocumentFragment(),
child;
// 也许有同学不太理解这一步,不妨动手写个小例子观察一下他的行为
while (child = el.firstChild) {
fragment.appendChild(child);
}
return fragment;
};
Compile.prototype.init = function() {
// 解析 fragment
this.compileElement(this.$fragment);
};
<p>{{user.name}}</p>
<p>{{user.age}}</p>
Compile.prototype.compileElement = function(el) {
let childNodes = Array.from(el.childNodes),
self = this;
childNodes.forEach(function(node) {
let text = node.textContent,
reg = /\{\{(.*)\}\}/;
// 若为 textNode 元素,且匹配 reg 正则
// 在上例中会匹配 '{{user.name}}' 及 '{{user.age}}'
if (self.isTextNode(node) && reg.test(text)) {
// 解析 textContent,RegExp.$1 为匹配到的内容,在上例中为 'user.name' 及 'user.age'
self.compileText(node, RegExp.$1);
}
// 递归
if (node.childNodes && node.childNodes.length) {
self.compileElement(node);
}
});
};
Compile.prototype.compileText = function(node, exp) {
// this.$vm 即为 Hue 实例,exp 为正则匹配到的内容,即 'user.name' 或 'user.age'
compileUtil.text(node, this.$vm, exp);
};
let compileUtil = {
text: function(node, vm, exp) {
this.bind(node, vm, exp, 'text');
},
bind: function(node, vm, exp, dir) {
// 获取更新视图的回调函数
let updaterFn = updater[dir + 'Updater'];
// 先调用一次 updaterFn,更新视图
updaterFn && updaterFn(node, this._getVMVal(vm, exp));
// 添加 Watcher 订阅
new Watcher(vm, exp, function(value, oldValue) {
updaterFn && updaterFn(node, value, oldValue);
});
},
// 根据 exp,获得其值,在上例中即 'vm.user.name' 或 'vm.user.age'
_getVMVal: function(vm, exp) {
let val = vm;
exp = exp.trim().split('.');
exp.forEach(function(k) {
val = val[k];
});
return val;
}
};
let updater = {
// Watcher 订阅的回调函数
// 在此即更新 node.textContent,即 update view
textUpdater: function(node, value) {
node.textContent = typeof value === 'undefined'
? ''
: value;
}
};
function Observer(data) {
this.data = data;
this.walk(data);
}
Observer.prototype.walk = function(data) {
const keys = Object.keys(data);
// 遍历 data 的所有属性
for (let i = 0; i < keys.length; i++) {
// 调用 defineReactive 添加 getter 和 setter
defineReactive(data, keys[i], data[keys[i]]);
}
};
function defineReactive(obj, key, val) {
// 对属性值递归,对应属性值为对象的情况
let childObj = observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
// 直接返回属性值
return val;
},
set: function(newVal) {
if (newVal === val) {
return;
}
// 值发生变化时修改闭包中的 val,
// 保证在触发 getter 时返回正确的值
val = newVal;
// 对新赋的值进行递归,防止赋的值为对象的情况
childObj = observe(newVal);
}
});
}
function observe(val) {
// 若 val 是对象且非数组,则 new 一个 Observer 实例,val 作为参数
// 简单点说:是对象就继续。
if (!Array.isArray(val) && typeof val === "object") {
return new Observer(val);
}
}
function defineReactive(obj, key, val) {
let childObj = observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
return val;
},
set: function(newVal) {
if (newVal === val) {
return;
}
val = newVal;
childObj = observe(newVal);
// 发生变动
dep.notify();
}
});
}
// ...
get: function() {
if (Dep.target) {
dep.depend();
}
return val;
}
// ...
// 标识符,在 Watcher 中有用到,先不用管
let uid = 0;
function Dep() {
this.id = uid++;
this.subs = [];
}
Dep.prototype.depend = function() {
// 这一步相当于做了这么一件事:this.subs.push(Dep.target)
// 即添加了 Watcher 订阅,addDep 是 Watcher 的方法
Dep.target.addDep(this);
};
// 通知更新
Dep.prototype.notify = function() {
// this.subs 的每一项都为一个 Watcher 实例
this.subs.forEach(function(sub) {
// update 为 Watcher 的一个方法,更新视图
// 没错,实际上这个方法最终会调用到 Compile 中的 updaterFn,
// 也即 new Watcher(vm, exp, callback) 中的 callback
sub.update();
});
};
// 在 Watcher 中调用
Dep.prototype.addSub = function(sub) {
this.subs.push(sub);
};
// 初始时引用为空
Dep.target = null;
// ...
get: function() {
// 是否是 Watcher 触发的
if (Dep.target) {
// 是就添加进来
dep.depend();
}
return val;
}
// ...
function Watcher(vm, exp, cb) {
this.$vm = vm;
this.cb = cb;
this.exp = exp;
this.depIds = new Set();
// 返回一个用于获取相应属性值的函数
this.getter = parseGetter(exp.trim());
// 调用 get 方法,触发 getter
this.value = this.get();
}
Watcher.prototype.get = function() {
const vm = this.$vm;
// 将 Dep.target 指向当前 Watcher 实例
Dep.target = this;
// 触发 getter
let value = this.getter.call(vm, vm);
// Dep.target 置空
Dep.target = null;
return value;
};
Watcher.prototype.addDep = function(dep) {
const id = dep.id;
if (!this.depIds.has(id)) {
// 添加订阅,相当于 dep.subs.push(this)
dep.addSub(this);
this.depIds.add(id);
}
};
function parseGetter(exp) {
if (/[^\w.$]/.test(exp)) {
return;
}
let exps = exp.split(".");
return function(obj) {
for (let i = 0; i < exps.length; i++) {
if (!obj)
return;
obj = obj[exps[i]];
}
return obj;
};
}
Watcher.prototype.update = function() {
this.run();
};
Watcher.prototype.run = function() {
let value = this.get();
let oldVal = this.value;
if (value !== oldVal) {
this.value = value;
// 调用回调函数更新视图
this.cb.call(this.$vm, value, oldVal);
}
};
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有