// 函数作用域内的参数拿来用,get set暴露给外部,利用闭包原理;
function defineReactive(obj,key,val) {
// 嵌套对象需要递归遍历
observe(val);
const dep = new Dep();
Object.defineProperty(obj,key,{
get(){
console.log(`get ${key}`)
Dep.target && dep.addDep(Dep.target)
return val;
},
set(newValue){
console.log(`set ${key}`)
if (newValue!==val){
// 新值如果是对象,则要对它执行响应式处理
observe(newValue)
val = newValue
// 通知更新
dep.notify()
}
}
})
}
function observe(obj) {
// 判断传入的值是不是对象
if (typeof obj !== 'object' || obj === null) {
return obj
}
// 创建Observe 实例
new Observer(obj);
}
class Observer {
constructor(value){
this.obj = value;
// 判断value数值是不是数组
if (Array.isArray(obj)){
// 1.找到数组的原型
const originProto = Array.prototype;
// 辈分一份 修改备份
const arrayProto = Object.create(originProto)
['push','pop','shift','unshift','splice','reverse','sort'].forEach(method=>{
arrayProto[method] =function(){
originProto[method].apply(this, arguments)
}
})
obj.__proto__ = arrayProto;
let obj = Object.keys(obj)
for (let index = 0; index < obj.length; index++) {
defineReactive(obj,index,obj[index])
}
//todo
}else {
this.walk(value);
}
}
walk(obj) {
Object.keys(obj).forEach(key=>{
defineReactive(obj, key, obj[key])
})
}
}
class KVue{
constructor(options){
this.$options = options;
this.$data = options.data;
// 对data 做响应式处理
observe(this.$data)
// 需要把data 代理到vm上 可以直接this.xxx
proxy(this)
// 执行模板编译
new Compile(options.el,this)
}
}
function proxy(vm){
Object.keys(vm.$data).forEach(key=>{
// 这样设置可以把data 挂载到vm上并且是响应式
Object.defineProperty(vm,key,{
get(){
return vm.$data[key]
},
set(val){
vm.$data[key] = val
}
})
})
}
class Compile {
constructor(el,vm){
this.$el = document.querySelector(el);
this.$vm = vm;
// 判断元素节点是不是存在
if(this.$el) {
this.compile(this.$el)
}
}
//
compile(el) {
// 获取所有的孩子节点
const childNodes = el.childNodes;
childNodes.forEach(node => {
// 1是元素节点
if (node.nodeType ===1){
// 执行编译节点上的属性
this.compileElement(node)
// 当发现node上还有子节点 进行递归遍历
if (node.hasChildNodes){
this.compile(node)
}
}else if (node.nodeType===3 && /\{\{(.*)\}\}/.test(node.textContent)) {
// 文本节点 {{XXX}}
this.compileText(node, node.textContent)
}
})
}
compileElement(node) {
// 元素节点处理属性
let nodeAttrs = node.attributes;
Array.from(nodeAttrs).forEach(attr =>{
// v-text ='xxx'
const attrName = attr.name; //v-text
const exp = attr.value; // xxx
// 判断属性是不是k-开头
if (attrName.indexOf('k-')===0) {
// 指令 每个指令有特定的处理函数
const dir = attrName.substring(2);
this[dir] && this[dir](node, exp)
}else if(attrName.indexOf('@')===0){
// 事件
const name = attrName.substring(1);
const fn =this.$vm.$options.methods && this.$vm.$options.methods[exp]
node.addEventListener(name, fn.bind(this.$vm))
}
})
}
updata(node,exp, dir) {
// 首先初始化值
const fn = this[dir+'Updater'];
// this.$vm[exp] 表示实例上的具体数值
fn&& fn(node, this.$vm[exp])
// 页面上有一个解析vue的就需要创建一个watcher
// data 里面的key
new Watcher(this.$vm,exp ,val=>{
fn && fn(node, val);
}
)}
textUpdater(node, value){
node.textContent = value
}
htmlUpdater(node, value) {
node.innerHTML = value;
}
modelUpdater(node,value){
node.value = value;
}
text(node, exp){
this.updata(node, exp,'text')
}
html(node,exp) {
this.updata(node,exp,'html')
}
model(node,exp) {
this.updata(node,exp,'model')
node.addEventListener('input',(e)=>{
this.$vm[exp] = e.target.value;
})
}
compileText(node){
this.updata(node, RegExp.$1, 'text')
}
}
class Watcher {
constructor(vm,key,updater){
this.vm = vm;
this.key = key;
this.updateFn = updater
// 把当前实例挂载到Dep.target上
Dep.target =this;
// 触发get
vm[key]
Dep.target = null;
}
// 未来执行dom更新函数,由dep调用的
update() {
this.updateFn.call(this.vm, this.vm[this.key])
}
}
class Dep{
constructor(){
this.deps = []
}
addDep(watcher){
this.deps.push(watcher)
}
notify() {
this.deps.forEach(watcher=>watcher.update())
}
}
0907-实现vue的响应式和事件的处理
于 2020-09-07 23:43:59 首次发布