SOURCE

console 命令行工具 X clear

                    
>
console
class MyVue{
    constructor(options){   //options为对象里面的配置项
        //接收所有的配置项
        this.$options=options;      
        //接收配置项里面的值
        this.$data=options.data;    
        //接收挂载点
        this.$el=document.getElementById(options.el);  
        //数据劫持
        this.observer(this.$data);
        //进行编译
        new Compile(this.$el,this);
    }
    observer(data){
        //这里我们要对传进来的data值进行判断,只有是对象形式才走下面的代码。
        if(!data || typeof data!='object') return; 
        //获取到data身上的所有的key值进行遍历
        Object.keys(data).forEach((key)=>{
            //给data身上所有的属性添加getter和setter方法
            this.defineRective(data,key,data[key]);
            //将data身上的所有的属性到实例身上
            this.ProxyData(key);
        })
    }
    ProxyData(key){
        Object.defineProperty(this,key,{
            get(){
                //访问
                return this.$data[key];
            },
            set(newvalue){
                this.$data[key]=newvalue;
            }
        })
    }
    defineRective(data,key,value){
        //递归   检测data的属性的值是否还是一个对象,如果是在进行遍历
        this.observer(value);
        var dep=new Dep();
        //添加getter和setter方法
        Object.defineProperty(data,key,{
            get(){
                //收集依赖
                Dep.target && dep.addDep(Dep.target)

                //访问
                return value;
            },
            set(newvalue){
                //设置
                if(newvalue===value) return;
                value=newvalue;

                //当设置的时候我们只需要做一次更新即可
                dep.message();
            }
        })
    }
}

class Compile {
    constructor(el, vm) {
        this.$el = el;
        this.$vm = vm;
        if (this.$el) {
            //1、获取app下面的所有节点,this.$Fragment为获取到的挂载点下的所有节点
            this.$Fragment = this.getNode(this.$el);
            //2、进行编译
            this.compile(this.$Fragment)
            //3、将编译好的节点插入到挂载点中
            this.$el.appendChild(this.$Fragment)
        }
    }
    getNode(root) {
        //创建文档碎片
        var frag = document.createDocumentFragment();
        var child;
        while (child = root.firstChild) {
            //将节点保存到了JS内存当中,这样页面上就不会有这个节点了
            frag.appendChild(child)
        }
        return frag;
    }
    compile(fragment) {
        //遍历所有的子节点
        Array.from(fragment.childNodes).forEach((node) => {
            //判断当前节点是文本节点还是元素节点
            //判断是否是文本节点,并且文本节点中必须要有{{内容}}
            if (node.nodeType === 3 && /\{\{(.+)\}\}/.test(node.textContent)) {
                //操作文本节点方法
                this.compileText(node)
            } else if (node.nodeType === 1) { //判断是否是元素节点
                //获取当前节点的所有属性
                var attrs = node.attributes;

                Array.from(attrs).forEach(attr => {
                    var key = attr.name; //拿到元素身上的属性   如v-text  @
                    var value = attr.value; //拿到元素身上属性的值  如sex  handleclick

                    //判断是否是指令
                    if (key.indexOf('v-') === 0) {
                        //判断是否是v-text指令,如果是,则把属性上对应的值给节点内容
                        if (key.substring(2) === 'text') {
                            node.textContent = value
                        }
                    }
                    //判断是否是事件
                    else if (key.indexOf('@') === 0) {
                        //事件处理
                        
                        //拿到vm实例上的方法取名fn
                        var fn = this.$vm.$options.methods[value];
                        //拿到方法的类型,点击或者输入类型
                        var dir = key.substr(1); 
                        //将事件绑定到该节点上
                        node.addEventListener(dir, fn.bind(this.$vm));
                    }
                })

            }
            //如果子节点下面还有子节点,那么就进行递归,遍历所有的子节点
            if (node.childNodes && node.childNodes.length > 0) {
                this.compile(node)
            }
        })
    }
    compileText(node) {
        this.update(node, this.$vm, RegExp.$1);
    }
    update(node, vm, exp) {
        node.textContent = vm[exp];

        new Watcher(node,vm,exp,(value)=>{
            node.textContent = vm[exp];
        })
    }
}

class Dep{
    constructor(){
        //存放所有的依赖
        this.deps=[];
    }
    addDep(rely){
        this.deps.push(rely);
    }
    message(){
        //通知状态
        this.deps.forEach((item)=>{
            item.update()
        })
    }
}

class Watcher{
    constructor(node,vm,exp,cb){
        this.$vm=vm;
        this.$exp=exp;
        this.cb=cb;

        Dep.target=this;
        //这一步是在做get的触发
        this.$vm[this.$exp];
        Dep.target=null;
    }
    update(){
        this.cb.call(this.$vm,this.$vm[this.$exp])
    }
}

var vm=new MyVue({
    el:'app',
    data:{
        username:'健哥',
        age:'18',
        sex:'男'
    },
    methods: {
        handlclick(){
            this.username='张三'
        }
    },
})
<div id="app">
    <div>{{username}}</div>
    <div>{{age}}</div>
    <div v-text="sex"></div>
    <button @click="handlclick">快点我!</button>
</div>


<!-- 主要实现了三个功能 -->
<!-- 1:数据劫持
2:编译
3:收集依赖和watcher监听 -->