Vue -- MVVM
监听数据变化、并在视图更新
- Object.defineProperty
- Proxy
数据响应式
// 数据响应式
function defineReactive(obj, key, value) {
// 递归
observe(value)
Object.defineProperty(obj, key, {
get() {
console.log(`get: ${key}`);
return value;
},
set(newValue) {
if(newValue !== value) {
// 递归 --> 设置某属性值为对象时;
observe(newValue)
console.log(`set: ${key}`)
value = newValue;
}
}
})
}
// 递归实现对象的数据响应式
function observe(obj) {
if(typeof obj !== 'object' || obj == null) {
return obj;
}
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key])
})
}
// 新增加属性,自定义set; Vue.set模拟
function set(obj, key, value) {
defineReactive(obj, key, value)
}
简单vue实现,实现l-text="counter"、{{counter}}
// 数据响应式
function defineReactive(obj, key, value) {
// 递归
observe(value)
// 创建dep实例,与key一一对应
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
console.log(`get: ${key}`);
// 依赖收集
Dep.target && dep.addDeps(Dep.target)
return value;
},
set(newValue) {
if(newValue !== value) {
// 递归 --> 设置某属性值为对象时;
console.log(`set: ${key}`)
value = newValue
observe(newValue)
// 派发更新
dep.notify()
}
}
})
}
// 递归实现对象的数据响应式
function observe(obj) {
if(typeof obj !== 'object' || obj == null) {
return obj;
}
// Object.keys(obj).forEach((key) => {
// defineReactive(obj, key, obj[key])
// })
new Observe(obj)
}
// 新增加属性,自定义set; Vue.set模拟
function set(obj, key, value) {
defineReactive(obj, key, value)
}
// 代理
function proxy(vm) {
Object.keys(vm.$data).forEach(key => {
Object.defineProperty(vm, key, {
get() {
return vm.$data[key]
},
set(newValue) {
vm.$data[key] = newValue;
}
})
})
}
// 数据响应式
class Observe {
constructor(obj) {
if(Array.isArray(obj)) {
// todo 数组响应式
} else {
this.walk(obj)
}
}
walk(obj) {
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key])
})
}
}
// mini-vue
class Kvue {
constructor(options) {
// 对data进行响应式处理
this.$options = options;
this.$data = options.data;
// 1. 对data做响应式处理
observe(options.data)
// 2. 将data数据代理到Kvue实例
proxy(this)
// 3. 解析编译 compile: 初始化和更新
new Compile(options.el, this)
}
}
// 编译
class Compile {
constructor(el, vm) {
this.$vm = vm;
this.$el = document.querySelector(el);
if(this.$el) {
this.compile(this.$el)
}
}
compile(node) {
const childNodes = node.childNodes;
Array.from(childNodes).forEach(n => {
// 判断类型
if(this.isElement(n)) {
console.log(`元素: ${n.nodeName}`);
this.compileElement(n)
if(n.childNodes.length) this.compile(n)
} else if(this.isInter(n)) {
console.log(`文本: ${n.textContent}`);
this.compileText(n)
}
})
}
// 元素节点
isElement(node) {
return node.nodeType === 1;
}
// 元素节点
isElement(node) {
return node.nodeType === 1;
}
// 插值文本节点
isInter(node) {
return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
}
// 编译文本
compileText(node) {
// 获取到插值表达式 RegExp.$1 --> 分组
// node.textContent = this.$vm[RegExp.$1.trim()]
this.update(node, RegExp.$1.trim(), 'text')
}
// 编译元素
compileElement(node) {
const attrs = node.attributes;
Array.from(attrs).forEach(attr => {
// l-text="xxx"
const attrName = attr.name;
const expiration = attr.value;
if(this.isDirective(attrName)) {
const dir = attrName.substring(2)
this[dir] && this[dir](node, expiration)
}
})
}
isDirective(attrName) {
return attrName.startsWith('l-')
}
// 指令方法
text(node, exp) {
// node.textContent = this.$vm[exp];
this.update(node, exp, 'text')
}
html(node, exp) {
// node.innerHTML = this.$vm[exp]
this.update(node, exp, 'html')
}
// 更新textUpdater
textUpdater(node, value) {
node.textContent = value;
}
htmlUpdater(node, value) {
node.innerHTML = value;
}
// watcher重构,统一更新
update(node, exp, dir) {
// 初始化
const fn = this[`${dir}Updater`]
fn && fn(node, this.$vm[exp])
// 更新,闭包实现fn保留,更新函数保留
new Watcher(this.$vm, exp, val => {
fn && fn(node, val)
})
}
}
网友评论