什么是MVVM

MVVM

m:model 数据层

v:view 视图层

vm: 数据双向绑定

在这里插入图片描述
封装mvvm的思路

在这里插入图片描述
1.实现一个Observer,对数据进行劫持,监听数据的属性变更,并在变动时进行notify

Object.defineProperty(vm,key,{
    set(){},
    get(){}
})

2.实现一个compile,对指令进行解析,初始化视图,并且订阅数据的变更,绑定好更新函数:

- 解析指令,将指令模板中的变量替换成数据,对视图进行初始化操作
- 订阅数据的变化,绑定好更新函数
- 接收到数据变化,通知视图进行view update

3.实现一个watcher,将其作为以上两者的一个中介点,一方面接收Observer通过Dep传递过来的数据变化,一方面通知compile进行view update:

- 通过Dep接收数据变动的通知,实例化的时候将自己添加到dep中
- 数据变更时,接收dep的notify,调用自身update方法,触发compile中的绑定的更新函数,进而更新视图

代码实现:

//html片段
<body>
    <div id="app">
        <input type="text" v-model='user.name'>
        nihao
        {{user.age}}
        {{info}}
        <h2>{{user.name}}</h2>
        <h3>{{user.name}}</h3> 
        <li>{{user.name}}</li> 
        <p>{{user.name}}</p>
    </div>
</body>
<script src="./mvvm.js"></script>
<script>
let vm = new Vue({
    el:"#app",
    data:{
        user:{
            name:"你好",
            age:11
        },
        info:"aaaaaaaa"
    }
})


//js片段
class Vue{
    constructor(options){
        this.$el = options.el;
        this.$data = options.data;

        if(this.$el){
            //劫持数据
            new Observer(this.$data);
            console.log(this.$data)
            //编译模板
            new Complie(this.$el,this)
        }
    }
}

//

class Dep{
    constructor(){
        this.watchers = [];
    }
    add(watcher){
        this.watchers.push(watcher)
    }

    notify(){
        this.watchers.forEach(item=>{
            item.updater()
        })
    }
}



//观察者
//vm.$watcher(vm,'user.name',(newValue)=>{})
class Watcher{
    constructor(vm,expr,callback){
        this.vm = vm;
        this.expr = expr;
        this.callback = callback;

        //获取上一次的值
        this.oldValue = this.get();

    }
    get(){
        Dep.target = this;
        let value = complieUtil.getValue(this.expr,this.vm);
        Dep.target =null;
        return value;
    }
    //跟新数据
    updater(){
        let newValue = complieUtil.getValue(this.expr,this.vm);

        if(newValue != this.oldValue){
            this.callback(newValue)
        }
    }


}


//劫持数据
class Observer{
    constructor(data){
        this.observer(data)
    }
    //劫持
    observer(data){
        if(data && typeof data == 'object'){
            for (const key in data) {
                this.observerData(data,key,data[key])
            }
        }
    }

    //添加set  get  函数
    observerData(data,key,value){
        this.observer(value)
        let dep = new Dep()
        Object.defineProperty(data,key,{
            set(newValue){
                console.log('设置值')
                if(value != newValue){
                    value = newValue
                    dep.notify();
                }
            },
            get(){
                Dep.target && dep.add(Dep.target)
                return value;
            }
        })
    }
}

//编译模板
class Complie{
    constructor(el,vm){
        this.vm = vm;
        //判断el 是元素节点
        this.el =  this.isElementNode(el) ? el :document.querySelector(el);


        //把app下所有的元素保存在内存文档片段
        let fragment = this.elementToFragment(this.el);
        // console.log(fragment)
        //编译内存片段
        this.complie(fragment);

        //把编译好的内存片段从放在浏览器中
        this.el.appendChild(fragment)
        // console.log(fragment)
    }
    //编译内存文档的方法
    complie(node){
        //获取内存片段下所有的子节点
        let childNodes = node.childNodes;

        [...childNodes].forEach(child=>{
            //判断是文本还是元素
            if(this.isElementNode(child)){
                //元素节点
                this.complieElement(child);
                this.complie(child);
            }else{
                //文本节点
                this.complieText(child);
            }
        })
    }
    //complieElement编译 -v
    complieElement(node){
        let attrs = node.attributes;
        [...attrs].forEach(attr=>{
            //判断是 v-开头的指令
            let {name,value:expr} = attr
            if(this.isDirective(name)){
                let [,directive]  = name.split('-');
                complieUtil[directive](node,expr,this.vm)
            }
        })
    }

    //complieText 编译 {{}}
    complieText(node){
        let  textContent = node.textContent;
        if(/\{\{(.+?)\}\}/.test(textContent)){
            //编译{{}} 语法
            complieUtil.contentTxet(node,textContent,this.vm)

        }
    }

    //判断是否是  -v开头的属性名
    isDirective(attr){
        console.log(attr)
        console.log(attr.startsWith('v-'))
        return attr.startsWith('v-');
    }
    //elementToFragment保存在内存文档片段函数
    elementToFragment(el){
        //创建一个内存片段
        let fragment = document.createDocumentFragment();
        let firstChild;
        while (firstChild = el.firstChild) {
            fragment.appendChild(firstChild);
        }
        return fragment;
    }

    //isElementNode
    isElementNode(el){//判断是否是元素节点
        return el.nodeType == 1;
    }
}


//工具类
let complieUtil={
    //获取值得方法
    getValue(expr,vm){
        return expr.split('.').reduce((prev,cur)=>{
            return prev[cur];
        },vm.$data)
    },
    model(node,expr,vm){
        let value = this.getValue(expr,vm)
        let fn = this.updater.complieModel;
        //生成观察者
        new Watcher(vm,expr,(newValue)=>{
            fn(node,newValue)
        })
        fn(node,value)
    },
    text(){
        
    },
    html(){
        
    },

    //contentTxet//编译{{}}语法的方法
    contentTxet(node,text,vm){
        
        let value = text.replace(/\{\{(.+?)\}\}/g,(...arg)=>{
            //生成观察者
            new Watcher(vm,arg[1],(newValue)=>{
                fn(node,newValue)
            })
            return this.getValue(arg[1],vm)
        })
        let fn = this.updater.complieText;
        fn(node,value)
        
    },

    //更新数据
    updater:{
        //v-model
        complieModel(node,value){
            node.value = value
        },
        //{{}}
        complieText(node,value){
            node.textContent = value;
        }
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值