vue
class vue {
constructor(options) {
// 保存选项
this.$options = options;
// 保存data
this.$data = options.data;
// 对传入data对象执行响应化处理
this.observe(this.$data);
new Compile(options.el, this);
if(options.created) {
options.created.call(this)
}
}
observe(value) {
// 参数必须是对象
if (!value || typeof value !== 'object') {
return;
}
Object.keys(value).forEach(key => {
// 执行响应化
this.defineReactive(value, key, value[key])
// 执行代理
this.proxyData(key);
})
}
defineReactive(obj, key, val) {
// 递归判断
this.observe(val);
// 创建Dep,它和key 1:1关系
const dep = new Dep();
// 定义属性
// 参数3是属性描述符,定义配置型、遍历性、可读、可写
Object.defineProperty(obj, key, {
get() {
// 依赖收集
Dep.target && dep.addDep(Dep.target)
return val;
},
set(newVal) {
if (newVal === val) {
return
}
val = newVal;
// 通知更新
dep.notify();
}
})
}
proxyData(key) {
// 想Vue实例上面定义属性key
Object.defineProperty(this, key, {
get() {
return this.$data[key]
},
set(newVal) {
this.$data[key] = newVal;
}
})
}
}
Dep: 管理若干Watcher实例,通知它们更新
class Dep {
constructor() {
this.deps = [];
}
addDep(dep) {
this.deps.push(dep);
}
notify() {
// set函数调用
this.deps.forEach(dep => dep.update())
}
}
Watcher: 执行具体更新操作
class Watcher {
constructor(vm, key, updater) {
this.vm = vm;
this.key = key;
this.updater = updater;
Dep.target = this; // 依赖收集时要用到
this.vm[this.key];
Dep.target = null;
}
update() {
// console.log('属性'+this.key+'更新了');
this.updater.call(this.vm, this.vm[this.key])
}
}
compiler:编译器
class Compile {
constructor(el, vm) {
this.$vm = vm;
this.$el = document.querySelector(el);
if (this.$el) {
// 执行编译
this.compile(this.$el);
}
}
compile(el) {
// 遍历el
const childNodes = el.childNodes;
// 每次拿出一个dom节点
Array.from(childNodes).forEach(node => {
// 判断节点类型
if (this.isElement(node)) {
// 3. 元素节点:访问节点特性,截获k-和@开头内容并解析
// console.log('编译元素'+node.nodeName);
this.compileElement(node);
} else if (this.isInter(node)) {
// 2. 文本节点:获取{{}}格式的内容并解析
// console.log('编译插值文本'+node.textContent);
this.compileText(node);
}
// 递归
if (node.childNodes && node.childNodes.length > 0) {
this.compile(node);
}
});
}
isElement(node) {
return node.nodeType === 1;
}
// 判断是否是插值表达式
isInter(node) {
return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
}
// 编译插值文本
compileText(node) {
// 获取表达式
// {{a+b()}}
const exp = RegExp.$1;
this.update(node, exp, "text");
}
// 通用方法 update(node, 'xxx', 'text')
update(node, exp, dir) {
// 构造更新函数并执行:相当于首次赋值
let updaterFn = this[dir + "Updater"];
updaterFn && updaterFn(node, this.$vm[exp]);
// 创建watcher,执行后续更新操作
// 额外传递一个更新函数:能够更新指定dom元素
new Watcher(this.$vm, exp, function(value) {
updaterFn && updaterFn(node, value);
});
}
textUpdater(node, value) {
node.textContent = value;
}
compileElement(node) {
// 获取属性
const nodeAttrs = node.attributes;
Array.from(nodeAttrs).forEach(attr => {
// k-text="test"
const attrName = attr.name; // k-text
const exp = attr.value; // test
if (attrName.indexOf("k-") === 0) {
// 指令 k-text k-model
const dir = attrName.substring(2); // text
this[dir] && this[dir](node, exp);
}
});
}
text(node, exp) {
this.update(node, exp, "text");
}
}