// 数组响应式
// 1.替换数组原型中7个方法
const orginalProto = Array.prototype;
// 备份一份,修改备份
const arrayProto = Object.create(orginalProto);
['push', 'pop', 'shift', 'unshift'].forEach(method => {
arrayProto[method] = function () {
// 原始操作
orginalProto[method].apply(this, arguments)
// 覆盖操作:通知更新
}
});
// 1.创建Vue类执行初始化操作
class Vue {
constructor(options) {
this.$options = options
this.$data = options.data
// 2.对data执行响应化处理
observe(this.$data);
// 3.将data挂载到Vue实例上
proxy(this)
// 4.对模板进行编译
new Compile(options.el, this);
}
}
// 5.创建Watcher类,管理数据的更新函数
class Watcher {
constructor(vm, key, updateFn) {
this.$vm = vm;
this.$key = key;
this.$updateFn = updateFn;
Dep.target = this;
this.$vm[key];
Dep.target = null;
}
update() {
this.$updateFn.call(this.$vm, this.$vm[this.$key])
}
}
// 6.创建Dep类,管理watcher更新
class Dep {
constructor() {
this.deps = []
}
addDep(dep) {
this.deps.push(dep)
}
notify() {
this.deps.forEach(vm => vm.update())
}
}
class Compile {
constructor(el, vm) {
console.log(vm, 4444);
this.$vm = vm;
this.$methods = vm.$options.methods
this.$el = document.querySelector(el)
this.compile(this.$el);
}
// 遍历整个el树
compile(el) {
el.childNodes.forEach(node => {
if (this.isElement(node)) {// 判断是否为元素标签
// 获取自定义属性
this.compileElement(node);
if (node.childNodes && node.childNodes.length > 0) {
this.compile(node)
}
} else if (this.isInset(node)) {// 判断是是否为插值元素
this.compileInset(node)
}
})
}
compileElement(node) {
Array.from(node.attributes).forEach(attr => {
let { name, value } = attr;
//判断是否为自定义v-属性
if (this.isDir(name)) {
const dir = name.substring(2);
this[dir] && this[dir](node, value)
}
if (this.isEvent(name)) {
const event = name.substring(1);
node.addEventListener(event, this.$methods[value].bind(this.$vm))
}
})
}
isDir(attr) {
return attr.startsWith('v-')
}
isEvent(attr) {
return attr.startsWith('@')
}
compileInset(node) {
this.update(node, RegExp.$1, 'text')
}
model(node, exp) {
this.update(node, exp, 'model')
node.addEventListener('input', (e) => {
this.$vm[exp] = e.target.value
})
}
text(node, exp) {
this.update(node, exp, 'text')
}
html(node, exp) {
this.update(node, exp, 'html')
}
// 统一更新函数,指定节点操作
update(node, exp, dir) {
const fn = this[dir + 'Update'];
fn && fn(node, this.$vm[exp]);
new Watcher(this.$vm, exp, function (val) {
fn && fn(node, val);
})
}
textUpdate(node, val) {
node.textContent = val
}
modelUpdate(node, val) {
node.value = val
}
htmlUpdate(node, val) {
node.innerHTML = val
}
isElement(node) {
return node.nodeType == 1
}
isInset(node) {
return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent)
}
}
function proxy(vm) {
Object.keys(vm.$data).forEach(key => {
Object.defineProperty(vm, key, {
get() {
return vm.$data[key]
},
set(v) {
vm.$data[key] = v
}
})
})
}
function observe(obj) {
// 判断传入的是否为对象
if (typeof obj !== 'object' || obj === null) {
return obj
}
// 如果是对象执行依次遍历进行劫持监听
new Observer(obj)
}
class Observer {
constructor(obj) {
if (Array.isArray(obj)) {
// 如果值是数组做相应处理
this.array(obj);
} else {
// 如果是对象循环遍历每个属性进行劫持监听
this.walk(obj);
}
}
array(obj) {
// 劫持数组
// 覆盖原型,替换7个变更操作
obj.__proto__ = arrayProto;
// 对数组内元素执行相应话
for (let i = 0; i < obj.length; i++) {
observe(obj[i])
}
}
walk(obj) {
Object.keys(obj).forEach(key => {
defindReactive(obj, key, obj[key])
})
}
}
function defindReactive(obj, key, val) {
// 如果一个属性的值也是对象,继续嵌套进行劫持舰艇
observe(val);
// 8.创建依赖收集 每个key一个dep实例
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
console.log('get', key);
Dep.target && dep.addDep(Dep.target)
return val;
},
set(v) {
console.log('set', key);
if (v !== val) {
val = v;
dep.notify();
}
}
})
}
网友评论